Kubernetes - руководство оператора

Введение

Kubernetes - лидер технологии облачных сред, поддерживающих концепцию IaC - инфраструктура, как код.

Управляющий уровень состоит из нескольких компонентов.

  1. kube-apiserver - внешний сервер для управляющего уровня, который обрабатывает API-запросы.
  2. etcd - база данных, в которой Kubernetes хранит всю информацию о существующих узлах, ресурсах кластера и т. д.
  3. kube-scheduler планирует и управляет запуском pod-оболочек.
  4. kube-controller-manager отвечает за запуск контроллеров ресурсов, таких как развертывания (deployments).
  5. cloud-controller-manager взаимодействует с облачным провайдером (в облачных кластерах), управляя такими ресурсами, как балансировщики нагрузки и дисковые тома.

Участники кластера, которые выполняют компоненты управляющего уровня, называются ведущими узлами.

Участники кластера, которые выполняют пользовательские рабочие задания, называются рабочими узлами.

Каждый рабочий узел в кластере Kubernetes отвечает за следующие компоненты.

  1. kubelet отвечает за управление средой выполнения контейнера, в которой запускаются рабочие задания, запланированные для узла, а также за мониторинг их состояния.
  2. kube-proxy занимается сетевой магией, которая распределяет запросы между pod-оболочками на разных узлах, а также между pod-оболочками и Интернетом.
  3. среда выполнения контейнеров запускает и останавливает контейнеры, а также отвечает за их взаимодействие. Обычно это CRI-O, хотя Kubernetes поддерживает и другие среды выполнения контейнеров, такие как containerd, rkt, docker. Kubernetes отказался от Docker, как среды управления контейнерами после версии 1.20 ( deprecating Docker ).

Немного терминов

Pod-оболочка — это фундаментальная единица работы в Kubernetes, которая определяет один или несколько контейнеров, взаимодействующих между собой и запланированных к одновременному выполнению.

Развертывание (deployment) - высокоуровневый ресурс Kubernetes, который декларативным образом управляет pod-оболочками, их доставкой, планированием, обновлением и перезапуском, когда это необходимо.

Cервис в Kubernetes - это эквивалент балансировщика нагрузки или прокси, который направляет трафик к подходящим pod-оболочкам через единый, публичный и долгосрочный IP-адрес или доменное имя.

Планировщик Kubernetes ищет pod-оболочки, которые еще нигде не выполняются, находит для них подходящие узлы и приказывает утилите kubelet запустить эти pod-оболочки на найденных узлах.

Манифест - это объявление желаемого состояния ресурса. Такие ресурсы, как развертывания, представлены записями во внутренней базе данных Kubernetes. Внешне эти ресурсы могут иметь форму текстовых файлов (известных как манифесты) в формате YAML.

Kubectl - основной инструмент для взаимодействия с Kubernetes, который позволяет применять манифесты, запрашивать данные о ресурсах, вносить изменения, удалять ресурсы и многое другое. Ее можно использовать либо императивно (например, для запуска публичных образов контейнеров с явным выделением необходимых ресурсов Kubernetes), либо декларативно, чтобы применять конфигурацию Kubernetes в виде манифестов формата YAML.

Helm - это диспетчер пакетов для Kubernetes. Он упрощает конфигурацию и развертывание приложений, позволяя генерировать YAML-файлы с помощью группы шаблонов и единого набора значений (таких как имя приложения или порт, который оно прослушивает), вмест того чтобы писать эти файлы вручную.

Пространство имен - предоставляет способ разделения кластера на отдельные части в тех или иных целях. Является полезным механизмом контроля за потреблением ресурсов в вашем кластере.

Dashboard

Kubernetes Dashboard - это универсальный веб-интерфейс для кластеров Kubernetes. Он позволяет пользователям управлять приложениями, работающими в кластере, и устранять их неполадки, а также управлять самим кластером.

Прежде всего вам необходимо получить токен для доступа к кластеру. Это можно сделать с помощью kubectl.

kubectl -n kubernetes-dashboard get secret $(kubectl -n kubernetes-dashboard get sa/admin-user -o jsonpath="{.secrets[0].name}") -o go-template="{{.data.token | base64decode}}" | tee token

Открыть браузер и перейти по ссылке:

https://10.0.128.195:31460/#/login
  1. В открывшемся окне нужно выбрать опцию «Token».

  2. Вставить токен из файла token и нажать «Sign In».

    img

  3. Откроется окно Kubernetes Dashboard с правами администратора.

Справочник команд

Common

Список ресурсов API:

$ kubectl api-resources
# вывод команды дает большой список (у меня 58 строк), привожу несколько для примера
NAME                              SHORTNAMES   APIVERSION                             NAMESPACED   KIND
bindings                                       v1                                     true         Binding
componentstatuses                 cs           v1                                     false        ComponentStatus
nodes                             no           v1                                     false        Node
...
apiservices                                    apiregistration.k8s.io/v1              false        APIService
controllerrevisions                            apps/v1                                true         ControllerRevision
...
roles                                          rbac.authorization.k8s.io/v1           true         Role
priorityclasses                   pc           scheduling.k8s.io/v1                   false        PriorityClass
...
storageclasses                    sc           storage.k8s.io/v1                      false        StorageClass
volumeattachments                              storage.k8s.io/v1                      false        VolumeAttachment
Список сервисов API:

$ kubectl get apiservices
NAME                                   SERVICE   AVAILABLE   AGE
v1.                                    Local     True        28h
v1.admissionregistration.k8s.io        Local     True        28h
v1.apiextensions.k8s.io                Local     True        28h
v1.apps                                Local     True        28h
v1.authentication.k8s.io               Local     True        28h
v1.authorization.k8s.io                Local     True        28h
v1.autoscaling                         Local     True        28h
v1.batch                               Local     True        28h
v1.certificates.k8s.io                 Local     True        28h
v1.coordination.k8s.io                 Local     True        28h
v1.discovery.k8s.io                    Local     True        28h
v1.events.k8s.io                       Local     True        28h
v1.networking.k8s.io                   Local     True        28h
v1.node.k8s.io                         Local     True        28h
v1.policy                              Local     True        28h
v1.rbac.authorization.k8s.io           Local     True        28h
v1.scheduling.k8s.io                   Local     True        28h
v1.storage.k8s.io                      Local     True        28h
v1beta1.admissionregistration.k8s.io   Local     True        28h
v1beta1.apiextensions.k8s.io           Local     True        28h
v1beta1.authentication.k8s.io          Local     True        28h
v1beta1.authorization.k8s.io           Local     True        28h
v1beta1.batch                          Local     True        28h
v1beta1.certificates.k8s.io            Local     True        28h
v1beta1.coordination.k8s.io            Local     True        28h
v1beta1.discovery.k8s.io               Local     True        28h
v1beta1.events.k8s.io                  Local     True        28h
v1beta1.extensions                     Local     True        28h
v1beta1.flowcontrol.apiserver.k8s.io   Local     True        28h
v1beta1.networking.k8s.io              Local     True        28h
v1beta1.node.k8s.io                    Local     True        28h
v1beta1.policy                         Local     True        28h
v1beta1.rbac.authorization.k8s.io      Local     True        28h
v1beta1.scheduling.k8s.io              Local     True        28h
v1beta1.storage.k8s.io                 Local     True        28h
v2beta1.autoscaling                    Local     True        28h
v2beta2.autoscaling                    Local     True        28h

Просмотр конфигурации кластера:

$ kubectl config view
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: DATA+OMITTED
    server: https://10.0.128.114:6443
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    user: kubernetes-admin
  name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users:
- name: kubernetes-admin
  user:
    client-certificate-data: REDACTED
    client-key-data: REDACTED

Namespaces

Просмотр пространств имен:

$ kubectl get namespaces
NAME                   STATUS   AGE
default                Active   151m
kube-node-lease        Active   151m
kube-public            Active   151m
kube-system            Active   151m
kubernetes-dashboard   Active   94m

Создание пространства имен:

$ kubectl create namespace sock-shop

Удаление пространства имен:

$ kubectl delete namespace sock-shop

Надо помнить, что при удалении namespace Kubernetes самостоятельно удалит все компоненты (applications, pods and etc.) в нужной последовательности и освободит ресурсы.

Endpoints

Посмотреть точки входа сервисов:

$ kubectl get endpoints
NAME                        ENDPOINTS                             AGE
grafana                     <none>                                3h21m
kubernetes                  10.0.128.114:6443,10.0.128.223:6443   28h
mysql                       <none>                                21h
mysql-1625151580            <none>                                19h
mysql-1625151580-headless   <none>                                19h

Services

Посмотреть список сервисов:

$ kubectl get services
NAME                        TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
grafana                     LoadBalancer   10.109.195.235   <pending>     3000:31046/TCP   3h26m
kubernetes                  ClusterIP      10.96.0.1        <none>        443/TCP          28h
mysql                       ClusterIP      None             <none>        3306/TCP         21h
mysql-1625151580            ClusterIP      10.97.109.151    <none>        3306/TCP         20h
mysql-1625151580-headless   ClusterIP      None             <none>        3306/TCP         20h

Описание сервиса:

$ kubectl describe service grafana
Name:                     grafana
Namespace:                default
Labels:                   <none>
Annotations:              <none>
Selector:                 app=grafana
Type:                     LoadBalancer
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       10.109.195.235
IPs:                      10.109.195.235
Port:                     <unset>  3000/TCP
TargetPort:               http-grafana/TCP
NodePort:                 <unset>  31046/TCP
Endpoints:                <none>
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

Редактирование сервиса:

$ kubectl describe service grafana
Name:                     grafana
Namespace:                default
Labels:                   <none>
Annotations:              <none>
Selector:                 app=grafana
Type:                     LoadBalancer
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       10.109.195.235
IPs:                      10.109.195.235
Port:                     <unset>  3000/TCP
TargetPort:               http-grafana/TCP
NodePort:                 <unset>  31046/TCP
Endpoints:                <none>
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

Nodes

Ноды - это серверы, на которых установлено ПО кластера Kubernetes.

В нашем случае, это четыре виртуальных машины, созданных в отдельном проекте rgpu/k8s, две из которых являются ведущими нодами (kuber-1, kuber-2) и еще две рабочими нодами (kuber-3, kuber-4).

k8s-servers

Get

Посмотреть список нод (узлов):

# kubectl get nodes
NAME      STATUS   ROLES                  AGE   VERSION
kuber-1   Ready    control-plane,master   27h   v1.21.2
kuber-2   Ready    control-plane,master   26h   v1.21.2
kuber-3   Ready    <none>                 26h   v1.21.2
kuber-4   Ready    <none>                 26h   v1.21.2

Delete

Удалить ноду:

# kubectl delete node <node_name>

Join

Присоединить новый узел в кластер можно с помощью, расположенного в папке kuber-1:/home/ubuntu/k8s/k8s-join

#!/bin/bash
LBA='10.0.128.114'
KT='h7eyg0.i77scifkhuyxl7l7'
KH='sha256:9ffb28f76e329995e42e06722484f6cbf54395521c40d92659783167460331e2'
CPI='--control-plane'
CRI='--cri-socket /var/run/crio/crio.sock'

function usage() {
   echo 'Usage: '$0' 1 (master) or 2 (worker)'
}

function pki_get() {
mkdir -p /etc/kubernetes/pki/etcd
scp -rp ubuntu@kuber-1:/etc/kubernetes/pki/sa.* /etc/kubernetes/pki
scp -rp ubuntu@kuber-1:/etc/kubernetes/pki/front* /etc/kubernetes/pki
scp -rp ubuntu@kuber-1:/etc/kubernetes/pki/etcd/ca.* /etc/kubernetes/pki/etcd
scp -rp ubuntu@kuber-1:/etc/kubernetes/pki/etcd/healthcheck* /etc/kubernetes/pki/etcd
}

case $1 in
        1)
        pki_get
        scp -rp ubuntu@kuber-1:/etc/kubernetes/pki/ca.* /etc/kubernetes/pki
        /usr/bin/kubeadm join ${LBA}:6443 --token $KT --discovery-token-ca-cert-hash $KH $CRI $CPI
        echo 'Master node joined to cluster'
        ;;
        2)
        pki_get
        /usr/bin/kubeadm join ${LBA}:6443 --token $KT --discovery-token-ca-cert-hash $KH $CRI
        echo 'Worker node joined to cluster'
        ;;
        *)
        usage
        ;;
esac

Эту программу надо запустить на добавляемом узле, а в качестве единственного параметра указать 1 - если тип узла ведущий, или 2 - если тип ноды рабочая. Предварительно на ВМ должно быть установлено все необходимое ПО.

Describe

Детальная информация о ноде:

# kubectl describe node kuber-1
Name:               kuber-1
Roles:              control-plane,master
Labels:             beta.kubernetes.io/arch=amd64
                    beta.kubernetes.io/os=linux
                    kubernetes.io/arch=amd64
                    kubernetes.io/hostname=kuber-1
                    kubernetes.io/os=linux
                    node-role.kubernetes.io/control-plane=
                    node-role.kubernetes.io/master=
                    node.kubernetes.io/exclude-from-external-load-balancers=
Annotations:        kubeadm.alpha.kubernetes.io/cri-socket: /var/run/crio/crio.sock
                    node.alpha.kubernetes.io/ttl: 0
                    volumes.kubernetes.io/controller-managed-attach-detach: true
CreationTimestamp:  Thu, 01 Jul 2021 06:15:38 +0000
Taints:             node-role.kubernetes.io/master:NoSchedule
Unschedulable:      false
Lease:
  HolderIdentity:  kuber-1
  AcquireTime:     <unset>
  RenewTime:       Fri, 02 Jul 2021 10:06:51 +0000
Conditions:
  Type             Status  LastHeartbeatTime                 LastTransitionTime                Reason                       Message
  ----             ------  -----------------                 ------------------                ------                       -------
  MemoryPressure   False   Fri, 02 Jul 2021 10:05:46 +0000   Thu, 01 Jul 2021 06:15:33 +0000   KubeletHasSufficientMemory   kubelet has sufficient memory available
  DiskPressure     False   Fri, 02 Jul 2021 10:05:46 +0000   Thu, 01 Jul 2021 06:15:33 +0000   KubeletHasNoDiskPressure     kubelet has no disk pressure
  PIDPressure      False   Fri, 02 Jul 2021 10:05:46 +0000   Thu, 01 Jul 2021 06:15:33 +0000   KubeletHasSufficientPID      kubelet has sufficient PID available
  Ready            True    Fri, 02 Jul 2021 10:05:46 +0000   Thu, 01 Jul 2021 06:15:55 +0000   KubeletReady                 kubelet is posting ready status. AppArmor enabled
Addresses:
  InternalIP:  10.0.128.114
  Hostname:    kuber-1
Capacity:
  cpu:                16
  ephemeral-storage:  243720360Ki
  hugepages-1Gi:      0
  hugepages-2Mi:      0
  memory:             32885824Ki
  pods:               110
Allocatable:
  cpu:                16
  ephemeral-storage:  224612683405
  hugepages-1Gi:      0
  hugepages-2Mi:      0
  memory:             32783424Ki
  pods:               110
System Info:
  Machine ID:                 4ad6c322b93b4130982caf7036043d5f
  System UUID:                4ad6c322-b93b-4130-982c-af7036043d5f
  Boot ID:                    b1a5950b-6f20-4220-a58a-eed9bfa43613
  Kernel Version:             5.4.0-74-generic
  OS Image:                   Ubuntu 20.04.2 LTS
  Operating System:           linux
  Architecture:               amd64
  Container Runtime Version:  cri-o://1.21.1
  Kubelet Version:            v1.21.2
  Kube-Proxy Version:         v1.21.2
PodCIDR:                      10.85.0.0/24
PodCIDRs:                     10.85.0.0/24
Non-terminated Pods:          (8 in total)
  Namespace                   Name                               CPU Requests  CPU Limits  Memory Requests  Memory Limits  Age
  ---------                   ----                               ------------  ----------  ---------------  -------------  ---
  kube-system                 coredns-558bd4d5db-5wh5f           100m (0%)     0 (0%)      70Mi (0%)        170Mi (0%)     27h
  kube-system                 coredns-558bd4d5db-kwpsm           100m (0%)     0 (0%)      70Mi (0%)        170Mi (0%)     27h
  kube-system                 etcd-kuber-1                       100m (0%)     0 (0%)      100Mi (0%)       0 (0%)         27h
  kube-system                 kube-apiserver-kuber-1             250m (1%)     0 (0%)      0 (0%)           0 (0%)         27h
  kube-system                 kube-controller-manager-kuber-1    200m (1%)     0 (0%)      0 (0%)           0 (0%)         27h
  kube-system                 kube-proxy-8twws                   0 (0%)        0 (0%)      0 (0%)           0 (0%)         27h
  kube-system                 kube-scheduler-kuber-1             100m (0%)     0 (0%)      0 (0%)           0 (0%)         27h
  weave                       weave-scope-agent-k26hm            100m (0%)     0 (0%)      100Mi (0%)       2000Mi (6%)    24h
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource           Requests    Limits
  --------           --------    ------
  cpu                950m (5%)   0 (0%)
  memory             340Mi (1%)  2340Mi (7%)
  ephemeral-storage  0 (0%)      0 (0%)
  hugepages-1Gi      0 (0%)      0 (0%)
  hugepages-2Mi      0 (0%)      0 (0%)
Events:              <none>

Cordon

Пометить ноду как поддерживающую планировщик

$ kubectl uncordon kuber-3
node/kuber-3 already uncordoned

Пометить ноду как не поддерживающую планировщик

$ kubectl cordon kuber-3
node/kuber-3 cordoned

Pods

Под является минимальной вычислительной единицей в терминологии Kubernetes. Под - группа из одного или нескольких контейнеров приложений, включающая общие используемые хранилище (тома), IP-адрес и информацию по их запуску.

В Kubernetes есть два основных пути использования подов:

  • Под запускающий единственный контейнер. Модель “один-контейнер-на-один-под” является более распространенной в Kubernetes и в данном кейсе можно считать, что под является обёрткой для контейнера. Нужно помнить, что Kubernetes управляет именно подами, а не контейнерами напрямую.
  • Под запускающий несколько контейнеров внутри себя. Под может содержать в себе приложение состоящее из нескольких контейнеров, взаимодействие которых тесно связано и есть необходимость разделять ресурсы. Например контейнер-1, являющийся веб-сервером, отдаёт данные с общего диска “во вне”, и есть контейнер-2, который обновляет и изменяет эти данные на диске.

Второй способ, с группировкой нескольких контейнеров внутри одного пода, является довольно специфичным случаем. Не рекомендуется его использовать без явной необходимости.

Каждый под представляет из себя одиночный инстанс (экземпляр) запущенного приложения. Если возникает необходимость в горизонтальном масштабировании, чтобы предоставить больше ресурсов за счет запуска большего количества инстансов, следует запускать множество подов, по одному на каждый инстанс приложения.

В Kubernetes это обычно называется репликацией. Реплицированные поды обычно создаются и управляются объектами Kubernetes, вроде Deployment или Job.

Редко когда возникает необходимость создавать отдельные поды непосредственно в Kubernetes. Это связано с тем, что поды разработаны как относительно эфемерные и одноразовые объекты.

Run

Запуск приложения:

$ kubectl run demo --image=nginx --port=9999 --labels app=demo
pod/demo created

Теперь создадим под и запустим в нем интерактивный шелл:

$ kubectl run -it --rm --image=centos centos-pod -- /bin/bash

Get

Просмотр приложений (подов):

$ kubectl get pod
NAME   READY   STATUS    RESTARTS   AGE
demo   1/1     Running   0          2m35s

Просмотр подов в пространстве имен с отображением изменений в реальном режиме времени:

$ kubectl get pod -n kube-system -w
NAME                              READY   STATUS    RESTARTS   AGE
coredns-558bd4d5db-9q5dl          1/1     Running   0          68m
coredns-558bd4d5db-bhjb4          1/1     Running   0          68m
etcd-kuber-0                      1/1     Running   0          69m
etcd-kuber-1                      1/1     Running   0          42m
etcd-kuber-2                      1/1     Running   0          37m
kube-apiserver-kuber-0            1/1     Running   0          69m
kube-apiserver-kuber-1            1/1     Running   0          42m
kube-apiserver-kuber-2            1/1     Running   0          37m
kube-controller-manager-kuber-0   1/1     Running   1          69m
kube-controller-manager-kuber-1   1/1     Running   0          42m
kube-controller-manager-kuber-2   1/1     Running   0          37m
kube-proxy-fbv4j                  1/1     Running   0          22m
kube-proxy-np29j                  1/1     Running   0          37m
kube-proxy-wr5fp                  1/1     Running   0          68m
kube-proxy-wv8c6                  1/1     Running   0          42m
kube-scheduler-kuber-0            1/1     Running   1          69m
kube-scheduler-kuber-1            1/1     Running   0          42m
kube-scheduler-kuber-2            1/1     Running   0          37m

Delete

Удаление pod:

$ kubectl delete pod hello
# или по селектору
kubectl delete all --selector app=demo

Describe

Подробное описание пода:

~$ kubectl describe pod etcd-kuber-1 -n  kube-system
Name:                 etcd-kuber-1
Namespace:            kube-system
Priority:             2000001000
Priority Class Name:  system-node-critical
Node:                 kuber-1/10.0.128.114
Start Time:           Thu, 01 Jul 2021 09:15:46 +0300
Labels:               component=etcd
                      tier=control-plane
Annotations:          kubeadm.kubernetes.io/etcd.advertise-client-urls: https://10.0.128.114:2379
                      kubernetes.io/config.hash: ca362061057becc1426410d2709de7b7
                      kubernetes.io/config.mirror: ca362061057becc1426410d2709de7b7
                      kubernetes.io/config.seen: 2021-07-01T06:15:41.992252994Z
                      kubernetes.io/config.source: file
Status:               Running
IP:                   10.0.128.114
IPs:
  IP:           10.0.128.114
Controlled By:  Node/kuber-1
Containers:
  etcd:
    Container ID:  cri-o://2e841ce5fbd4261549ba1796c2ccd14d2e2a02878ffe94d6f9ea5d9e4a45f19b
    Image:         k8s.gcr.io/etcd:3.4.13-0
    Image ID:      k8s.gcr.io/etcd@sha256:4ad90a11b55313b182afc186b9876c8e891531b8db4c9bf1541953021618d0e2
    Port:          <none>
    Host Port:     <none>
    Command:
      etcd
      --advertise-client-urls=https://10.0.128.114:2379
      --cert-file=/etc/kubernetes/pki/etcd/server.crt
      --client-cert-auth=true
      --data-dir=/var/lib/etcd
      --initial-advertise-peer-urls=https://10.0.128.114:2380
      --initial-cluster=kuber-1=https://10.0.128.114:2380
      --key-file=/etc/kubernetes/pki/etcd/server.key
      --listen-client-urls=https://127.0.0.1:2379,https://10.0.128.114:2379
      --listen-metrics-urls=http://127.0.0.1:2381
      --listen-peer-urls=https://10.0.128.114:2380
      --name=kuber-1
      --peer-cert-file=/etc/kubernetes/pki/etcd/peer.crt
      --peer-client-cert-auth=true
      --peer-key-file=/etc/kubernetes/pki/etcd/peer.key
      --peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt
      --snapshot-count=10000
      --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt
    State:          Running
      Started:      Thu, 01 Jul 2021 09:15:32 +0300
    Ready:          True
    Restart Count:  0
    Requests:
      cpu:        100m
      memory:     100Mi
    Liveness:     http-get http://127.0.0.1:2381/health delay=10s timeout=15s period=10s #success=1 #failure=8
    Startup:      http-get http://127.0.0.1:2381/health delay=10s timeout=15s period=10s #success=1 #failure=24
    Environment:  <none>
    Mounts:
      /etc/kubernetes/pki/etcd from etcd-certs (rw)
      /var/lib/etcd from etcd-data (rw)
Conditions:
  Type              Status
  Initialized       True
  Ready             True
  ContainersReady   True
  PodScheduled      True
Volumes:
  etcd-certs:
    Type:          HostPath (bare host directory volume)
    Path:          /etc/kubernetes/pki/etcd
    HostPathType:  DirectoryOrCreate
  etcd-data:
    Type:          HostPath (bare host directory volume)
    Path:          /var/lib/etcd
    HostPathType:  DirectoryOrCreate
QoS Class:         Burstable
Node-Selectors:    <none>
Tolerations:       :NoExecute op=Exists
Events:            <none>

Logs

Просмотр лога конкретного пода:

$ kubectl logs pod grafana-6fd7f9f87f-gxtcb
Error from server (NotFound): pods "pod" not found
ubuntu@kuber-2:~$ kubectl logs pod/grafana-6fd7f9f87f-gxtcb
ubuntu@kuber-2:~$ kubectl logs pod/nginx-deployment-674454969-85hpt
10.254.0.94 - - [01/Jul/2021:08:16:12 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0" "-"
2021/07/01 08:16:13 [error] 7#7: *1 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 10.254.0.94, server: localhost, request: "GET /favicon.ico HTTP/1.1", host: "10.0.128.195:8888", referrer: "http://10.0.128.195:8888/"
10.254.0.94 - - [01/Jul/2021:08:16:13 +0000] "GET /favicon.ico HTTP/1.1" 404 169 "http://10.0.128.195:8888/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0" "-"
10.254.0.96 - - [02/Jul/2021:11:10:24 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0" "-"
2021/07/02 11:10:24 [error] 7#7: *1 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 10.254.0.96, server: localhost, request: "GET /favicon.ico HTTP/1.1", host: "10.0.128.195:8888", referrer: "http://10.0.128.195:8888/"
10.254.0.96 - - [02/Jul/2021:11:10:24 +0000] "GET /favicon.ico HTTP/1.1" 404 169 "http://10.0.128.195:8888/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0" "-"
10.254.0.96 - - [02/Jul/2021:11:10:25 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0" "-"
10.254.0.96 - - [02/Jul/2021:11:10:25 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0" "-"

Annotate

Установить аннотацию для приложения:

$ kubectl annotate pods my-pod icon-url=http://goo.gl/XXBTWq

Label

Поставить метку:

$ kubectl label pod mysql-1625151580-client new-label=awesome
pod/mysql-1625151580-client labeled

Deployments

Get

Просмотр развертываний:

kubectl get deployments -n kube-system
NAME      READY   UP-TO-DATE   AVAILABLE   AGE
coredns   2/2     2            2           71m

Describe

Детальная информация о развертывании:

kubectl describe deployments

Apply

Запуск приложения из файла описания (YAML, JSON):

kubectl apply -f application/deployment.yaml

Еще примеры см. в пункте “Манифесты ресурсов”

Autoscale

Задать параметры масштабирования для приложения:

$ kubectl autoscale deployment nginx-deployment --min=2 --max=5
horizontalpodautoscaler.autoscaling/nginx-deployment autoscaled

LimitRange

Set

Создаем манифест с типом (kind) LimitRange:

---
apiVersion: v1
kind: LimitRange
metadata:
  name: sock-limitrange
spec:
  limits:
  - default:
      cpu: "500m"
      memory: "256Mi"
    defaultRequest:
      cpu: "200m"
      memory: "128Mi"
    type: Container

Применяем его к пространству имен:

$ kubectl apply -f resource-quota-set.yaml -n sock-shop
limitrange/sock-limitrange created

Проверим, что лимит установлен:

$ kubectl get limitrange -n sock-shop
NAME              CREATED AT
sock-limitrange   2021-07-05T10:40:58Z

Describe

Просмотрим подробнее, какие лимиты установлены:

$ kubectl describe limitrange -n sock-shop
Name:       sock-limitrange
Namespace:  sock-shop
Type        Resource  Min  Max  Default Request  Default Limit  Max Limit/Request Ratio
----        --------  ---  ---  ---------------  -------------  -----------------------
Container   cpu       -    -    200m             500m           -
Container   memory    -    -    128Mi            256Mi          -

Edit

Отредактировать лимиты оперативно (без файла манифеста):

$ kubectl edit limitrange -n sock-shop

Images

Images (list)

Список образов:

$ crictl images
IMAGE                                  TAG                 IMAGE ID            SIZE
docker.io/dsagkomega/omegabi_angular   latest              01c4c9b8e5164       477MB
k8s.gcr.io/coredns/coredns             v1.8.0              296a6d5035e2d       42.6MB
k8s.gcr.io/etcd                        3.4.13-0            0369cf4303ffd       255MB
k8s.gcr.io/kube-apiserver              v1.21.2             106ff58d43082       127MB
k8s.gcr.io/kube-controller-manager     v1.21.2             ae24db9aa2cc0       121MB
k8s.gcr.io/kube-proxy                  v1.21.2             a6ebd1c1ad981       133MB
k8s.gcr.io/kube-scheduler              v1.21.2             f917b8c8f55b7       51.9MB
k8s.gcr.io/pause                       3.4.1               0f8457a4c2eca       690kB
k8s.gcr.io/pause                       3.5                 ed210e3e4a5ba       690kB

Еще один вариант для получения списка образов

$ kubeadm config images list
k8s.gcr.io/kube-apiserver:v1.21.2
k8s.gcr.io/kube-controller-manager:v1.21.2
k8s.gcr.io/kube-scheduler:v1.21.2
k8s.gcr.io/kube-proxy:v1.21.2
k8s.gcr.io/pause:3.4.1
k8s.gcr.io/etcd:3.4.13-0
k8s.gcr.io/coredns/coredns:v1.8.0

Pull (create)

Скачивается из репозитория и устанавливается образ контейнера

crictl pull php

Rmi (delete)

crictl rmi <image_id>

Volumes

Том emptyDir идеально подходит для кэширования и обмена временными файлами, однако некоторым приложениям — например, базам данных необходимо хранить постоянную информацию. Постоянное хранение данных значительно усложняет конфигурацию Kubernetes для вашего приложения, потому что использует дополнительные облачные ресурсы и требует наличия резервного копирования. Но если вам в Kubernetes нужны постоянные тома, ресурс PersistentVolume - это то, что вы ищете. Больше о PersistentVolume можно почитать в документации Kubernetes (kubernetes.io/docs/concepts/storage/persistent-volumes).

Create

Создаем манифест для PVC pvc-add-new.yaml

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: rgpu-pv
spec:
  capacity:
    storage: 100Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  storageClassName: local-storage
  local:
    path: /data
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - rgpu-node

Создаем PersistentVolume

$ kubectl apply -f pvc-add-new.yaml

Get

Вывести постоянные тома (PersistentVolumes), отсортированные по емкости

$ kubectl get pv --sort-by=.spec.capacity.storage

Получить метку версии всех подов с меткой app=cassandra:

$ kubectl get pods --selector=app=cassandra -o jsonpath='{.items[*].metadata.labels.version}'

Describe

$ kubectl describe pv/rgpu-pv
Name:              rgpu-pv
Labels:            <none>
Annotations:       <none>
Finalizers:        [kubernetes.io/pv-protection]
StorageClass:      local-storage
Status:            Available
Claim:
Reclaim Policy:    Delete
Access Modes:      RWO
VolumeMode:        Filesystem
Capacity:          100Gi
Node Affinity:
  Required Terms:
    Term 0:        kubernetes.io/hostname in [rgpu-node]
Message:
Source:
    Type:  LocalVolume (a persistent volume backed by local storage on a node)
    Path:  /data
Events:    <none>

Манифесты ресурсов

Использование команды kubectl run для создания развертываний хоть и полезно, но имеет ограничения. Представьте, что вам хочется что-то поменять в спецификации развертывания: скажем, имя или версию образа. Вы могли бы удалить существующий объект Deployment (с помощью kubectl delete) и создать новый с подходящими полями. Но есть лучший способ.

Поскольку система Kubernetes по своей природе является декларативной и непрерывно согласовывает реальное состояние с желаемым, вам достаточно лишь поменять последнее (спецификацию развертывания), и Kubernetes все сделает за вас. Как это достигается?

Все ресурсы Kubernetes, такие как развертывания и pod-оболочки, представлены записями во внутренней базе данных. Цикл согласования следит за любыми изменениями в записях и предпринимает соответствующие действия. На самом деле команда kubectl run лишь добавляет в базу данных новую запись о развертывании, а система делает все остальное. Но для взаимодействия с Kubernetes вовсе не обязательно использовать команду kubectl run — вы можете создавать манифесты ресурсов (спецификацию их желаемого состояния) напрямую. Если хранить файл манифеста в системе контроля версий, то вместо выполнения императивных команд можно просто подменить этот файл и затем заставить Kubernetes прочитать обновленные данные.

Итак, приступим.

Сначала смотрим какая версия API применима для деплоя.

$ kubectl api-resources | grep deployment
deployments                       deploy       apps/v1                                true         Deployment

Посмотреть документацию по манифестам подов

$ kubectl explain pods
KIND:     Pod
VERSION:  v1

DESCRIPTION:
     Pod is a collection of containers that can run on a host. This resource is
     created by clients and scheduled onto hosts.

FIELDS:
   apiVersion   <string>
     APIVersion defines the versioned schema of this representation of an
     object. Servers should convert recognized schemas to the latest internal
     value, and may reject unrecognized values. More info:
     https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources

   kind <string>
     Kind is a string value representing the REST resource this object
     represents. Servers may infer this from the endpoint the client submits
     requests to. Cannot be updated. In CamelCase. More info:
     https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds

   metadata     <Object>
     Standard object's metadata. More info:
     https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata

   spec <Object>
     Specification of the desired behavior of the pod. More info:
     https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status

   status       <Object>
     Most recently observed status of the pod. This data may not be up to date.
     Populated by the system. Read-only. More info:
     https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status

Теперь создадим файл манифеста application/deployment.yaml

apiVersion: apps/v1 # до версии 1.9.0 нужно использовать apps/v1beta2
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2 # запускает 2 пода, созданных по шаблону
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

Запускаем приложение:

kubectl apply -f application/deployment.yaml
deployment.apps/nginx-deployment created

Смотрим подробности:

$ kubectl describe deployments
Name:                   nginx-deployment
Namespace:              default
CreationTimestamp:      Tue, 29 Jun 2021 14:58:22 +0300
Labels:                 <none>
Annotations:            deployment.kubernetes.io/revision: 1
Selector:               app=nginx
Replicas:               2 desired | 2 updated | 2 total | 0 available | 2 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  app=nginx
  Containers:
   nginx:
    Image:        nginx:1.14.2
    Port:         80/TCP
    Host Port:    0/TCP
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      False   MinimumReplicasUnavailable
  Progressing    False   ProgressDeadlineExceeded
OldReplicaSets:  <none>
NewReplicaSet:   nginx-deployment-66b6c48dd5 (2/2 replicas created)
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  12m   deployment-controller  Scaled up replica set nginx-deployment-66b6c48dd5 to 2

Видим, что “Host Port” не назначен.

Исправим конфигурацию пода, назначив ему порт 8888 на хосте:

$ kubectl edit deployment.v1.apps/nginx-deployment
 containers:
  - image: nginx:1.14.2
    imagePullPolicy: IfNotPresent
    name: nginx
    ports:
    - containerPort: 80
      hostPort: 8888
      protocol: TCP 

После этого, убедимся, что на рабочем узле порт 8888 открылся:

root@kuber-3:/home/ubuntu# sudo netstat -tulpn | grep 8888
tcp        0      0 0.0.0.0:8888            0.0.0.0:*               LISTEN      761/crio
tcp6       0      0 :::8888                 :::*                    LISTEN      761/crio

Теперь можно посмотреть работу приложения:

nginx-view

Пример приложения

В качестве примера, как это сделано в официальной документации, предлагаю вам запустить тестовое микросервисное приложение sock-shop (Интернет-магазин носков).

Создаем отдельное пространство имен:

$ kubectl create namespace sock-shop

Запускаем приложение:

$ kubectl apply -n sock-shop -f "https://github.com/microservices-demo/microservices-demo/blob/master/deploy/kubernetes/complete-demo.yaml?raw=true"

Следим за созданием подов:

$ kubectl get pods -n sock-shop
NAME                            READY   STATUS              RESTARTS   AGE
carts-b4d4ffb5c-ztrgk           0/1     ContainerCreating   0          88s
carts-db-6c6c68b747-rmhtj       0/1     ContainerCreating   0          88s
catalogue-759cc6b86-62dpf       0/1     ContainerCreating   0          88s
catalogue-db-96f6f6b4c-wmwtt    0/1     ContainerCreating   0          88s
front-end-5c89db9f57-qlvm9      0/1     ContainerCreating   0          88s
orders-7664c64d75-44lr6         0/1     ContainerCreating   0          87s
orders-db-659949975f-5dd5l      0/1     ContainerCreating   0          87s
payment-7bcdbf45c9-v8xqv        0/1     ContainerCreating   0          87s
queue-master-5f6d6d4796-xvt5j   0/1     ContainerCreating   0          87s
rabbitmq-5bcbb547d7-tvcc5       0/2     ContainerCreating   0          87s
session-db-7cf97f8d4f-98jch     0/1     ContainerCreating   0          87s
shipping-7f7999ffb7-4fv7d       0/1     ContainerCreating   0          86s
user-68df64db9c-7mvbr           0/1     ContainerCreating   0          86s
user-db-6df7444fc-zkmqf         0/1     ContainerCreating   0          86s

Как только все контейнеры будут запущены, необходимо выяснить, на каком порту опубликовано это приложение.

Для этого используем команду:

$ kubectl describe svc front-end -n sock-shop
Name:                     front-end
Namespace:                sock-shop
Labels:                   name=front-end
Annotations:              prometheus.io/scrape: true
Selector:                 name=front-end
Type:                     NodePort
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       10.110.238.210
IPs:                      10.110.238.210
Port:                     <unset>  80/TCP
TargetPort:               8079/TCP
NodePort:                 <unset>  30001/TCP
Endpoints:                10.85.3.160:8079
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

Здесь видно, что frontend-приложение запущено по адресу http://10.0.128.233:30001

$ kubectl get svc -n sock-shop
NAME           TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)             AGE
carts          ClusterIP   10.107.94.25     <none>        80/TCP              69m
carts-db       ClusterIP   10.108.113.150   <none>        27017/TCP           69m
catalogue      ClusterIP   10.99.209.178    <none>        80/TCP              69m
catalogue-db   ClusterIP   10.98.178.199    <none>        3306/TCP            69m
front-end      NodePort    10.110.238.210   <none>        80:30001/TCP        69m
orders         ClusterIP   10.98.139.44     <none>        80/TCP              69m
orders-db      ClusterIP   10.108.171.149   <none>        27017/TCP           69m
payment        ClusterIP   10.108.155.212   <none>        80/TCP              69m
queue-master   ClusterIP   10.96.95.92      <none>        80/TCP              69m
rabbitmq       ClusterIP   10.96.104.171    <none>        5672/TCP,9090/TCP   69m
session-db     ClusterIP   10.104.88.145    <none>        6379/TCP            69m
shipping       ClusterIP   10.98.104.222    <none>        80/TCP              69m
user           ClusterIP   10.109.61.179    <none>        80/TCP              69m
user-db        ClusterIP   10.106.92.76     <none>        27017/TCP           69m