DevOps в серии буткемпов K8s

Обратите внимание, полная ментальная карта DevOps в K8s доступна по адресу: «DevOps в ментальной карте K8s»

В «DevOps в K8s — StatefulSets, часть первая» я представил концепцию K8s StatefulSets и службы Headless, давайте продолжим изучение StatefulSets в этой статье.

Демонстрация StatefulSets

Начнем с демо.

Безголовый сервис

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

headless-svc.yml

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx

StatefulSets YAML

Теперь подготовьте следующий StatefulSets YAML:

statefulsets.yml

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.21.1
        ports:
        - containerPort: 80
          name: web

Обратите внимание на часть serviceName: "nginx", цель этого поля — указать контроллеру StatefulSet использовать безголовый сервис nginx для обеспечения «разрешимой идентификации» пода при выполнении цикла управления.

Итак, после создания Service и StatefulSet вы должны увидеть следующие два объекта:

$ kubectl create -f headless-svc.yml
service/nginx created

$ kubectl create -f statefulset.yml
statefulset.apps/web created

$ kubectl get pods -w -l app=nginx
NAME    READY   STATUS    RESTARTS   AGE
web-0   1/1     Running   0          31s
web-1   1/1     Running   0          27s

Нам нетрудно увидеть, что StatefulSet использует соглашение об именах <statefulset name>-<ordinal index>.

Эти индексы накапливаются, начиная с 0, и соответствуют каждому экземпляру Pod StatefulSet один к одному и никогда не повторяются. Что еще более важно, создание этих подов также осуществляется строго в порядке номеров. Например, пока веб-0 не перейдет в состояние «Выполняется», а состояние подразделения (Условия) не станет «Готово», веб-1 останется в состоянии «Ожидание».

Как только оба модуля перейдут в статус «Работает», мы можем проверить их имя хоста:

$ kubectl exec web-0 -- sh -c 'hostname'
web-0
$ kubectl exec web-1 -- sh -c 'hostname'
web-1

Имена хостов совпадают с именами подов, и им присвоены соответствующие номера. Далее попробуем еще раз получить доступ к этому безголовому сервису через DNS с помощью одноразового пода:

$ kubectl run -i --tty --image busybox:1.28.4 dns-test --restart=Never --rm /bin/sh
If you don't see a command prompt, try pressing enter.
/ # nslookup web-0.nginx
Server:    172.20.0.10
Address 1: 172.20.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-0.nginx
Address 1: 10.10.2.161 ip-10-10-2-161.ec2.internal

/ # nslookup web-1.nginx
Server:    172.20.0.10
Address 1: 172.20.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-1.nginx
Address 1: 10.10.2.135 web-1.nginx.default.svc.cluster.local

Из вывода команды nslookup видно, что при доступе к web-0.nginx последним проанализированным адресом является IP-адрес пода web-0; и при доступе к web-1.nginx он разрешается как IP-адрес web-1.

Теперь, если вы намеренно удалите эти два модуля:

$ kubectl delete pod -l app=nginx
pod "web-0" deleted
pod "web-1" deleted

$ kubectl get pod -w -l app=nginx
NAME    READY   STATUS              RESTARTS   AGE
web-0   0/1       ContainerCreating   0          0s
web-0   1/1     Running             0          2s
web-1   0/1     ContainerCreating   0          0s
web-1   1/1     Running             0          2s

Например, если web-0 — это главный узел, который нужно запустить первым, а web-1 — подчиненный узел, который нужно запустить позже, пока StatefulSet не будет удален, вы всегда попадете на главный узел при доступе к web-0.nginx. . При обращении к web-1.nginx он всегда будет падать на ведомый узел, и это отношение никогда не изменится.

И когда мы удалим эти два модуля, K8s создаст два новых модуля в порядке исходной нумерации. Более того, k8s по-прежнему присваивает им те же «сетевые идентификаторы», что и раньше: web-0.nginx и web-1.nginx.

$ kubectl run -i --tty --image busybox:1.28.4 dns-test --restart=Never --rm /bin/sh
If you don't see a command prompt, try pressing enter.
/ # nslookup web-0
Server:    172.20.0.10
Address 1: 172.20.0.10 kube-dns.kube-system.svc.cluster.local

nslookup: can't resolve 'web-0'
/ # nslookup web-0.nginx
Server:    172.20.0.10
Address 1: 172.20.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-0.nginx
Address 1: 10.10.2.200 ip-10-10-2-200.ec2.internal
/ # nslookup web-1.nginx
Server:    172.20.0.10
Address 1: 172.20.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-1.nginx
Address 1: 10.10.2.35 web-1.nginx.default.svc.cluster.local
/ #

Два имени хоста все еще разрешимы, даже IP-адрес другой. Благодаря этому строгому правилу соответствия StatefulSet гарантирует стабильность сетевой идентификации Pod.

Таким образом, K8s успешно зафиксировали статус топологии пода (какой узел запускается первым, какой узел запускается позже) в соответствии с методом пода «имя + номер». Кроме того, k8s также предоставляет фиксированную и уникальную запись доступа для каждого пода, а именно: запись DNS, соответствующую этому поду.

Эти состояния останутся неизменными в течение всего жизненного цикла StatefulSet и никогда не станут недействительными из-за удаления или повторного создания соответствующего пода.

podManagementPolicy

podManagementPolicy — это параметр конфигурации в спецификации StatefulSet, который определяет, как создаются, обновляются и завершаются модули. Есть два возможных значения для podManagementPolicy: «OrderedReady» и «Parallel».

  • OrderedReady (по умолчанию): создает и обновляет модули последовательно, соблюдая порядок, определенный StatefulSet. Он будет ждать, пока каждый модуль станет «готовым» (т. е. пройдет проверку готовности), прежде чем перейти к следующему. Во время масштабирования или обновлений модули завершаются в обратном порядке.
  • Параллельно: создает, обновляет и удаляет все модули параллельно, не дожидаясь готовности какого-либо конкретного модуля. Это может быть полезно для приложений, не требующих строгого порядка, или когда требуется более быстрое масштабирование и обновления.

Вы можете указать podManagementPolicy в манифесте StatefulSet YAML следующим образом:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: my-statefulset
spec:
  podManagementPolicy: Parallel
  ...

spec.updateStrategy.type

Это поле определяет стратегию, используемую при обновлении модулей в StatefulSet. Есть два возможных значения для .spec.updateStrategy.type: RollingUpdate и OnDelete.

  • RollingUpdate (по умолчанию). Контролируемое и последовательное обновление модулей. Поды обновляются по одному в порядке, определенном StatefulSet, начиная с самого высокого индекса и двигаясь к самому низкому. Каждый модуль завершается и заменяется новой версией в соответствии с обновленной конфигурацией. При использовании стратегии RollingUpdate вы также можете настроить поле .spec.updateStrategy.rollingUpdate.partition для управления количеством одновременно обновляемых модулей.
  • OnDelete: обновленная конфигурация применяется только тогда, когда модуль удаляется пользователем вручную. Когда модуль будет воссоздан, он будет использовать новую конфигурацию. Эта стратегия дает вам полный контроль над тем, когда и как обновлять каждый модуль, что может быть полезно для конкретных случаев использования или когда вам нужно вручную координировать процесс обновления.
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: my-statefulset
spec:
  updateStrategy:
    type: RollingUpdate
  ...

Заключение