Основи StatefulSet

Цей підручник надає вступ до управління застосунками за допомогою StatefulSets. Він демонструє, як створювати, видаляти, масштабувати та оновлювати Podʼи StatefulSets.

Перш ніж ви розпочнете

Перш ніж розпочати цей підручник, вам слід ознайомитися з наступними концепціями Kubernetes:

Вам треба мати кластер Kubernetes, а також інструмент командного рядка kubectl має бути налаштований для роботи з вашим кластером. Рекомендується виконувати ці настанови у кластері, що має щонайменше два вузли, які не виконують роль вузлів управління. Якщо у вас немає кластера, ви можете створити його, за допомогою minikube або використовувати одну з цих пісочниць:

Вам слід налаштувати kubectl для використання контексту, який використовує простір імен default. Якщо ви використовуєте наявний кластер, переконайтеся, що можна використовувати простір імен цього кластера для практики. Ідеальною буде практика в кластері, де не запущені реальні робочі навантаження.

Також корисно прочитати сторінку з концепціями про StatefulSets.

Цілі

StatefulSets призначені для використання з застосунками, які зберігаються свій стан, та розподіленими системами. Однак, адміністрування стану застосунків та розподілених систем у Kubernetes є широкою та складною темою. Щоб продемонструвати базові можливості StatefulSet і не змішувати першу тему з другою, ви розгорнете простий вебзастосунок за допомогою StatefulSet.

Після цього уроку ви будете знайомі з наступним.

  • Як створити StatefulSet
  • Як StatefulSet керує своїми Podʼами
  • Як видалити StatefulSet
  • Як масштабувати StatefulSet
  • Як оновлювати Podʼи StatefulSet

Створення StatefulSet

Почніть зі створення StatefulSet (і Service, на який він спирається) за допомогою наведеного нижче прикладу. Він схожий на приклад, наведений у концепції StatefulSets. Він створює headless Service, nginx, щоб опублікувати IP-адреси Podʼів у StatefulSet, web.

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
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: registry.k8s.io/nginx-slim:0.21
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi

Вам знадобляться принаймні два термінали. У першому терміналі використовуйте kubectl get для спостереження за створенням Podʼів StatefulSet.

# використовуйте цей термінал для виконання команд із зазначенням --watch
# завершіть цей watch, коли вам буде запропоновано розпочати новий watch
kubectl get pods --watch -l app=nginx

У другому терміналі використовуйте kubectl apply для створення headless Service та StatefulSet:

kubectl apply -f https://k8s.io/examples/application/web/web.yaml
service/nginx created
statefulset.apps/web created

Наведена вище команда створює два Podʼи, кожен з яких запускає вебсервер NGINX. Отримайте інформацію про nginx Service…

kubectl get service nginx
NAME      TYPE         CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
nginx     ClusterIP    None         <none>        80/TCP    12s

…потім отримайте інформацію про web StatefulSet, щоб переконатися, що обидва були створені успішно:

kubectl get statefulset web
NAME   READY   AGE
web    2/2     37s

Упорядковане створення Podʼів

Типово StatefulSet створює свої Podʼи в строгому порядку.

Для StatefulSet з n репліками, коли Podʼи розгортаються, вони створюються послідовно, впорядковані від {0..n-1}. Перегляньте вивід команди kubectl get у першому терміналі. Зрештою вивід буде виглядати як у наведеному нижче прикладі.

# Не починайте новий watch;
# це вже повинно працювати
kubectl get pods --watch -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     0/1       Pending   0          0s
web-0     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         19s
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         18s

Зверніть увагу, що Pod web-1 не запускається, поки Pod web-0 не буде Running (див. Фази Pod) та Ready (див. type у Стани Pod).

Пізніше в цьому підручнику ви будете практикувати паралельний запуск.

Podʼи в StatefulSet

Podʼи в StatefulSet мають унікальний порядковий індекс та стабільну мережеву ідентичність.

Перевірка порядкового індексу Podʼів

Отримайте Podʼи StatefulSet:

kubectl get pods -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          1m
web-1     1/1       Running   0          1m

Як зазначено в концепції StatefulSets, Podʼи в StatefulSet мають фіксовану, унікальну ідентичність. Ця ідентичність базується на унікальному порядковому індексі, який призначається кожному Podʼу контролером StatefulSet controller. Імена Podʼів приймають форму <ім'я statefulset>-<порядковий індекс>. Оскільки StatefulSet web має дві репліки, він створює два Podʼи, web-0 та web-1.

Використання стабільних мережевих ідентичностей

Кожен Pod має стабільне імʼя хосту на основі свого порядкового індексу. Використовуйте kubectl exec для виконання команди hostname в кожному Podʼі:

for i in 0 1; do kubectl exec "web-$i" -- sh -c 'hostname'; done
web-0
web-1

Використайте kubectl run для запуску контейнера, який надає команду nslookup з пакунка dnsutils. Використовуючи nslookup з іменами хостів Podʼів, ви можете переглянути їх внутрішні адреси DNS:

kubectl run -i --tty --image busybox:1.28 dns-test --restart=Never --rm

що запускає нову оболонку. У цій новій оболонці запустіть:

# Виконайте це в оболонці контейнера dns-test
nslookup web-0.nginx

Вивід схожий на:

Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-0.nginx
Address 1: 10.244.1.6

nslookup web-1.nginx
Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-1.nginx
Address 1: 10.244.2.6

(і тепер вийдіть з оболонки контейнера: exit)

CNAME headless сервісу вказує на SRV записи (один для кожного Podʼу, який виконується та готовий). SRV записи вказують на записи A, які містять IP-адреси Podʼів.

У одному терміналі спостерігайте за Podʼами StatefulSet:

# Розпочніть новий watch
# Завершіть цей watch, коли бачите, що видалення завершено
kubectl get pod --watch -l app=nginx

У другому терміналі використовуйте kubectl delete для видалення всіх Podʼів у StatefulSet:

kubectl delete pod -l app=nginx
Pod "web-0" видалено
Pod "web-1" видалено

Чекайте, поки StatefulSet перезапустить їх, і обидва Podʼи перейдуть до стану Running та Ready:

# Це вже має працювати
kubectl get pod --watch -l app=nginx
NAME      READY     STATUS              RESTARTS   AGE
web-0     0/1       ContainerCreating   0          0s
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          2s
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         34s

Використовуйте kubectl exec та kubectl run, щоб переглянути імена хостів Podʼів та внутрішні DNS-записи. Спочатку перегляньте імена хостів Podʼів:

for i in 0 1; do kubectl exec web-$i -- sh -c 'hostname'; done
web-0
web-1

потім, запустіть:

kubectl run -i --tty --image busybox:1.28 dns-test --restart=Never --rm

що запускає новиe оболонку. У цій новій оболонці запустіть:

# Виконайте це в оболонці контейнера dns-test
nslookup web-0.nginx

Вивід схожий на:

Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-0.nginx
Address 1: 10.244.1.7

nslookup web-1.nginx
Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-1.nginx
Address 1: 10.244.2.8

(і тепер вийдіть з оболонки контейнера: exit)

Порядкові номери, імена хостів Podʼів, SRV записи та імена записів A не змінилися, але IP-адреси, повʼязані з Podʼами, можуть змінюватися. У кластері, який використовується для цього навчального посібника, вони змінюються. Тому важливо не налаштовувати інші застосунки на підʼєднання до Podʼів у StatefulSet за IP-адресою конкретного Podʼу (можна підключатися до Podʼів, за їх іменем хосту).

Виявлення конкретних Podʼів у StatefulSet

Якщо вам потрібно знайти та підʼєднатись до активних учасників StatefulSet, вам слід запитувати CNAME headless Service (nginx.default.svc.cluster.local). SRV записи, повʼязані з CNAME, будуть містити лише Podʼ у StatefulSet, які виконуються та готові.

Якщо ваш застосунок вже реалізував логіку підʼєднання, яка перевіряє працездатність та готовність, ви можете використовувати SRV записи Podʼів (web-0.nginx.default.svc.cluster.local, web-1.nginx.default.svc.cluster.local), оскільки вони стабільні, і ваш застосунок зможе виявляти адреси Podʼів, коли вони переходять до стану Running та Ready.

Якщо ваш застосунок хоче знайти будь-який здоровий Pod у StatefulSet і, отже, не потрібно відстежувати кожний конкретний Pod, ви також можете підʼєднатись до IP-адреси type: ClusterIP сервісу, Podʼів в цьому StatefulSet. Ви можете використовувати той самий Service, який відстежує StatefulSet (вказаний у serviceName StatefulSet) або окремий Service, який вибирає відповідний набір Podʼів.

Запис у стійке сховище

Отримайте PersistentVolumeClaims для web-0 та web-1:

kubectl get pvc -l app=nginx

Вивід схожий на:

NAME        STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE
www-web-0   Bound     pvc-15c268c7-b507-11e6-932f-42010a800002   1Gi        RWO           48s
www-web-1   Bound     pvc-15c79307-b507-11e6-932f-42010a800002   1Gi        RWO           48s

Контролер StatefulSet створив два PersistentVolumeClaims, які привʼязані до двох PersistentVolumes.

Оскільки кластер, використаний у цьому посібнику, налаштований на динамічну підготовку PersistentVolumes, PersistentVolumes були створені та привʼязані автоматично.

NGINX вебсервер типово обслуговує індексний файл із /usr/share/nginx/html/index.html. Поле volumeMounts в spec StatefulSet забезпечує, що тека /usr/share/nginx/html є зберігається у PersistentVolume.

Запишіть імена хостів Podʼів у їх файли index.html та перевірте, що вебсервери NGINX обслуговують імена хостів:

for i in 0 1; do kubectl exec "web-$i" -- sh -c 'echo "$(hostname)" > /usr/share/nginx/html/index.html'; done

for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1

У одному терміналі спостерігайте за Podʼами StatefulSet:

# Завершіть цей watch, коли ви дійдете до кінця розділу.
# На початку "Scaling a StatefulSet" ви розпочнете новий watch.
kubectl get pod --watch -l app=nginx

У другому терміналі видаліть всі Podʼи StatefulSet:

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

Дослідіть вивід команди kubectl get у першому терміналі та зачекайте, поки всі Podʼи перейдуть до стану Running та Ready.

# Це вже має працювати
kubectl get pod --watch -l app=nginx
NAME      READY     STATUS              RESTARTS   AGE
web-0     0/1       ContainerCreating   0          0s
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          2s
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         34s

Перевірте, чи продовжують вебсервери обслуговувати свої імена хостів:

for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1

Навіть якщо web-0 та web-1 переплановані, вони продовжують обслуговувати свої імена хостів, оскільки PersistentVolumes, повʼязані з їхніми PersistentVolumeClaims, знову монтується до їхніх volumeMounts. Незалежно від того, на якому вузлі заплановані web-0 та web-1, їхні PersistentVolumes будуть підключені до відповідних точок монтування.

Масштабування StatefulSet

Масштабування StatefulSet передбачає збільшення або зменшення кількості реплік (горизонтальне масштабування). Це досягається шляхом оновлення поля replicas. Ви можете використовувати або kubectl scale, або kubectl patch, щоб масштабувати StatefulSet.

Збільшення масштабу

Збільшення масштабу означає додавання додаткових реплік. Забезпечивши те, що ваш застосунок може розподіляти роботу між StatefulSet, новий більший набір Podʼів може виконувати більше цієї роботи.

У одному вікні термінала спостерігайте за Podʼами у StatefulSet:

# Якщо у вас вже запущений watch, ви можете продовжити його використовувати.
# В іншому випадку запустіть один.
# Закінчіть цей watch, коли для StatefulSet буде 5 справних Podʼів
kubectl get pods --watch -l app=nginx

У іншому вікні термінала використовуйте kubectl scale, щоб змінити кількість реплік на 5:

kubectl scale sts web --replicas=5
statefulset.apps/web scaled

Дослідіть вивід команди kubectl get у першому терміналі та зачекайте, доки три додаткові Podʼи перейдуть у стан Running та Ready.

# Це вже має працювати
kubectl get pod --watch -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          2h
web-1     1/1       Running   0          2h
NAME      READY     STATUS    RESTARTS   AGE
web-2     0/1       Pending   0          0s
web-2     0/1       Pending   0         0s
web-2     0/1       ContainerCreating   0         0s
web-2     1/1       Running   0         19s
web-3     0/1       Pending   0         0s
web-3     0/1       Pending   0         0s
web-3     0/1       ContainerCreating   0         0s
web-3     1/1       Running   0         18s
web-4     0/1       Pending   0         0s
web-4     0/1       Pending   0         0s
web-4     0/1       ContainerCreating   0         0s
web-4     1/1       Running   0         19s

Контролер StatefulSet змінив кількість реплік. Як і при створенні StatefulSet, контролер StatefulSet створював кожен Pod послідовно з урахуванням його порядкового індексу, і чекав, доки попередній Pod перейде у стан Running та Ready перед запуском наступного Podʼа.

Зменшення масштабу

Зменшення масштабу означає зменшення кількості реплік. Наприклад, ви можете це зробити через те, що рівень трафіку до служби зменшився, і на поточному масштабі є ресурси, що простоюють.

У одному вікні термінала спостерігайте за Podʼами у StatefulSet:

# Закінчіть цей watch, коли лишиться лише 3 Podʼи для StatefulSet
kubectl get pod --watch -l app=nginx

У іншому вікні термінала використовуйте kubectl patch, щоб зменшити кількість реплік StatefulSet до трьох:

kubectl patch sts web -p '{"spec":{"replicas":3}}'
statefulset.apps/web patched

Зачекайте, доки web-4 і web-3 перейдуть у стан Terminating.

# Це вже має працювати
kubectl get pods --watch -l app=nginx
NAME      READY     STATUS              RESTARTS   AGE
web-0     1/1       Running             0          3h
web-1     1/1       Running             0          3h
web-2     1/1       Running             0          55s
web-3     1/1       Running             0          36s
web-4     0/1       ContainerCreating   0          18s
NAME      READY     STATUS    RESTARTS   AGE
web-4     1/1       Running   0          19s
web-4     1/1       Terminating   0         24s
web-4     1/1       Terminating   0         24s
web-3     1/1       Terminating   0         42s
web-3     1/1       Terminating   0         42s

Послідовне завершення Podʼів

Панель управління видаляє кожний Pod по одному, у зворотньому порядку щодо його порядкового індексу, і чекає, доки кожен Pod повністю не зупиниться перед видаленням наступного.

Отримайте PersistentVolumeClaims StatefulSet:

kubectl get pvc -l app=nginx
NAME        STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE
www-web-0   Bound     pvc-15c268c7-b507-11e6-932f-42010a800002   1Gi        RWO           13h
www-web-1   Bound     pvc-15c79307-b507-11e6-932f-42010a800002   1Gi        RWO           13h
www-web-2   Bound     pvc-e1125b27-b508-11e6-932f-42010a800002   1Gi        RWO           13h
www-web-3   Bound     pvc-e1176df6-b508-11e6-932f-42010a800002   1Gi        RWO           13h
www-web-4   Bound     pvc-e11bb5f8-b508-11e6-932f-42010a800002   1Gi        RWO           13h

Ще існують пʼять PersistentVolumeClaims та пʼять PersistentVolumes. Під час дослідження стабільного сховища Podʼа, ви бачили, що PersistentVolumes, змонтовані у Podʼи StatefulSet, не видаляються, коли Podʼи StatefulSet видаляються. Це також вірно, коли видалення Podʼів викликано зменшенням масштабу StatefulSet.

Оновлення StatefulSets

Контролер StatefulSet підтримує автоматизоване оновлення. Стратегія, яка використовується, визначається полем spec.updateStrategy обʼєкта API StatefulSet. Ця функція може бути використана для оновлення образів контейнерів, запитів ресурсів та/або лімітів, міток та анотацій Podʼів у StatefulSet.

Існують дві стратегії оновлення: RollingUpdate (типово) та OnDelete.

RollingUpdate

Стратегія оновлення RollingUpdate оновлює всі Podʼи в StatefulSet у зворотньому порядку щодо їх порядкового індексу, з дотриманням гарантій StatefulSet.

Ви можете розділити оновлення StatefulSet, який використовує стратегію RollingUpdate, на партиції, вказавши .spec.updateStrategy.rollingUpdate.partition. Ви будете практикувати це пізніше у цьому посібнику.

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

В одному вікні термінала відредагуйте StatefulSet web, щоб знову змінити образ контейнера:

kubectl patch statefulset web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"registry.k8s.io/nginx-slim:0.24"}]'
statefulset.apps/web patched

В іншому терміналі спостерігайте за Podʼами у StatefulSet:

# Закінчіть це спостереження, коли відбудеться розгортання
#
# Якщо ви не впевнені, залиште його ще на одну хвилину
kubectl get pod -l app=nginx --watch

Вивід буде подібний до:

NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          7m
web-1     1/1       Running   0          7m
web-2     1/1       Running   0          8m
web-2     1/1       Terminating   0         8m
web-2     1/1       Terminating   0         8m
web-2     0/1       Terminating   0         8m
web-2     0/1       Terminating   0         8m
web-2     0/1       Terminating   0         8m
web-2     0/1       Terminating   0         8m
web-2     0/1       Pending   0         0s
web-2     0/1       Pending   0         0s
web-2     0/1       ContainerCreating   0         0s
web-2     1/1       Running   0         19s
web-1     1/1       Terminating   0         8m
web-1     0/1       Terminating   0         8m
web-1     0/1       Terminating   0         8m
web-1     0/1       Terminating   0         8m
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         6s
web-0     1/1       Terminating   0         7m
web-0     1/1       Terminating   0         7m
web-0     0/1       Terminating   0         7m
web-0     0/1       Terminating   0         7m
web-0     0/1       Terminating   0         7m
web-0     0/1       Terminating   0         7m
web-0     0/1       Pending   0         0s
web-0     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         10s

Podʼи в StatefulSet оновлюються у зворотньому порядку щодо їх порядкового індексу. Контролер StatefulSet завершує кожен Pod і чекає, доки він не перейде в стан Running та Ready, перед тим як оновити наступний Pod. Зверніть увагу, що, навіть якщо контролер StatefulSet не продовжить оновлювати наступний Pod, поки його порядковий наступник не буде Running та Ready, він відновить будь-який Pod, який зазнав помилки під час оновлення до попередньої версії.

Podʼи, які вже отримали оновлення, будуть відновлені до оновленої версії, а Podʼи, які ще не отримали оновлення, будуть відновлені до попередньої версії. Таким чином, контролер намагається продовжувати забезпечувати працездатність застосунку та зберігати оновлення в однорідному стані при наявності тимчасових збоїв.

Отримайте Podʼи, щоб переглянути їх образи контейнерів:

for p in 0 1 2; do kubectl get pod "web-$p" --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'; echo; done
registry.k8s.io/nginx-slim:0.24
registry.k8s.io/nginx-slim:0.24
registry.k8s.io/nginx-slim:0.24

Всі Podʼи в StatefulSet зараз використовують попередній образ контейнера.

Підготовка оновлення

Ви можете розбити оновлення StatefulSet, який використовує стратегію RollingUpdate, на розділи, вказавши .spec.updateStrategy.rollingUpdate.partition.

Для отримання додаткового контексту ви можете прочитати Поточні оновлення частинами на сторінці концепції StatefulSet.

Ви можете підготувати оновлення StatefulSet, використовуючи поле partition всередині .spec.updateStrategy.rollingUpdate. Для цього оновлення ви залишите наявні Podʼи в StatefulSet без змін, поки змінюєте шаблон Podʼа для StatefulSet. Потім ви (або, це поза цим навчальним посібником, якась зовнішня автоматизація) можете запустити це підготовлене оновлення.

Спочатку виправте StatefulSet web, щоб додати розділ до поля updateStrategy:

# Значення "partition" визначає, до яких порядкових номерів застосовується зміна
# Переконайтеся, що використовуєте число, більше, ніж останній порядковий номер для
# StatefulSet
kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":3}}}}'
statefulset.apps/web patched

Знову виправте StatefulSet, щоб змінити образ контейнера, який використовує цей StatefulSet:

kubectl patch statefulset web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"registry.k8s.io/nginx-slim:0.21"}]'
statefulset.apps/web patched

Видаліть Pod у StatefulSet:

kubectl delete pod web-2
pod "web-2" deleted

Зачекайте, поки замінений Pod web-2 буде запущений і готовий:

# Закінчіть перегляд, коли побачите, що web-2 справний
kubectl get pod -l app=nginx --watch
NAME      READY     STATUS              RESTARTS   AGE
web-0     1/1       Running             0          4m
web-1     1/1       Running             0          4m
web-2     0/1       ContainerCreating   0          11s
web-2     1/1       Running   0         18s

Отримайте образ контейнера Podʼа:

kubectl get pod web-2 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
registry.k8s.io/nginx-slim:0.24

Зверніть увагу, що, навіть якщо стратегія оновлення — RollingUpdate, StatefulSet відновив Pod з початковим образом контейнера. Це тому, що порядковий номер Podʼа менше, ніж partition, вказаний у updateStrategy.

Канаркове оновлення

Тепер ви спробуєте канаркове оновлення цієї підготовленої зміни.

Ви можете виконати канаркове оновлення (для тестування зміненого шаблону) шляхом зменшення partition, який ви вказали вище.

Виправте StatefulSet, щоб зменшити розділ:

# Значення "partition" повинно відповідати найвищому існуючому порядковому номеру для
# StatefulSet
kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":2}}}}'
statefulset.apps/web patched

Панель управління спровокує заміну для web-2 (реалізовану через належне видалення, а потім створення нового Podʼа, як тільки видалення завершиться). Зачекайте, поки новий Pod web-2 буде запущений і готовий.

# Це вже має бути запущено
kubectl get pod -l app=nginx --watch
NAME      READY     STATUS              RESTARTS   AGE
web-0     1/1       Running             0          4m
web-1     1/1       Running             0          4m
web-2     0/1       ContainerCreating   0          11s
web-2     1/1       Running   0         18s

Отримайте інформацію про контейнер Podʼа:

kubectl get pod web-2 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
registry.k8s.io/nginx-slim:0.21

Коли ви змінили partition, контролер StatefulSet автоматично оновив Pod web-2, оскільки порядковий номер Podʼа був більшим або рівним partition.

Видаліть Pod web-1:

kubectl delete pod web-1
pod "web-1" deleted

Зачекайте, поки Pod web-1 буде запущений і готовий.

# Це вже має бути запущено
kubectl get pod -l app=nginx --watch

Вивід подібний до:

NAME      READY     STATUS        RESTARTS   AGE
web-0     1/1       Running       0          6m
web-1     0/1       Terminating   0          6m
web-2     1/1       Running       0          2m
web-1     0/1       Terminating   0         6m
web-1     0/1       Terminating   0         6m
web-1     0/1       Terminating   0         6m
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         18s

Отримайте образ контейнера Podʼа web-1:

kubectl get pod web-1 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
registry.k8s.io/nginx-slim:0.24

web-1 був відновлений до своєї початкової конфігурації, тому що порядковий номер Podʼа був меншим за partition. Коли вказано partition, всі Podʼи з порядковим номером, який більше або дорівнює partition, будуть оновлені, коли буде оновлено .spec.template StatefulSet. Якщо Pod з порядковим номером, який менший за partition, буде видалений або інакше припинений, він буде відновлено до початкової конфігурації.

Поступові оновлення

Ви можете виконати поступове оновлення (наприклад, лінійне, геометричне або експоненціальне оновлення) за допомогою розділеного оновлення з аналогічним методом до того, як ви впровадили канаркове оновлення. Щоб виконати поступове оновлення, встановіть partition на порядковий номер, на якому ви хочете, щоб контролер призупинив оновлення.

Розділ в цей момент встановлено на 2. Встановіть розділ на 0:

kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":0}}}}'
statefulset.apps/web patched

Зачекайте, поки всі Podʼи в StatefulSet стануть запущеними і готовими.

# Це вже має бути запущено
kubectl get pod -l app=nginx --watch

Вивід подібний до:

NAME      READY     STATUS              RESTARTS   AGE
web-0     1/1       Running             0          3m
web-1     0/1       ContainerCreating   0          11s
web-2     1/1       Running             0          2m
web-1     1/1       Running   0         18s
web-0     1/1       Terminating   0         3m
web-0     1/1       Terminating   0         3m
web-0     0/1       Terminating   0         3m
web-0     0/1       Terminating   0         3m
web-0     0/1       Terminating   0         3m
web-0     0/1       Terminating   0         3m
web-0     0/1       Pending   0         0s
web-0     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         3s

Отримайте деталі образу контейнера для Podʼів у StatefulSet:

for p in 0 1 2; do kubectl get pod "web-$p" --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'; echo; done
registry.k8s.io/nginx-slim:0.21
registry.k8s.io/nginx-slim:0.21
registry.k8s.io/nginx-slim:0.21

Переміщуючи partition на 0, ви дозволили StatefulSet продовжити процес оновлення.

OnDelete

Ви обираєте цю стратегію оновлення для StatefulSet, встановивши .spec.template.updateStrategy.type на OnDelete.

Виправте StatefulSet web, щоб використовувати стратегію оновлення OnDelete:

kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"OnDelete"}}}'
statefulset.apps/web patched

Коли ви обираєте цю стратегію оновлення, контролер StatefulSet не автоматично оновлює Podʼи, коли вноситься зміна до поля .spec.template StatefulSet. Вам потрібно керувати оновленням самостійно — або вручну, або за допомогою окремої автоматизації.

Видалення StatefulSet

StatefulSet підтримує як не каскадне, так і каскадне видалення. При не каскадному видаленні Podʼи StatefulSet не видаляються при видаленні самого StatefulSet. При каскадному видаленні видаляються як StatefulSet, так і його Podʼи.

Прочитайте Використання каскадного видалення у кластері, щоб дізнатися про каскадне видалення загалом.

Некаскадне видалення

В одному вікні термінала спостерігайте за Podʼами у StatefulSet.

# Завершіть цей перегляд, коли не буде Podʼів для StatefulSet
kubectl get pods --watch -l app=nginx

Використовуйте kubectl delete, щоб видалити StatefulSet. Переконайтеся, що ви передали параметр --cascade=orphan до команди. Цей параметр повідомляє Kubernetes видаляти лише StatefulSet, і не видаляти жодного з його Podʼів.

kubectl delete statefulset web --cascade=orphan
statefulset.apps "web" deleted

Отримайте Podʼи, щоб перевірити їх статус:

kubectl get pods -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          6m
web-1     1/1       Running   0          7m
web-2     1/1       Running   0          5m

Навіть якщо web був видалений, всі Podʼи все ще запущені і готові. Видаліть web-0:

kubectl delete pod web-0
pod "web-0" deleted

Отримайте Podʼи StatefulSet:

kubectl get pods -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-1     1/1       Running   0          10m
web-2     1/1       Running   0          7m

Оскільки StatefulSet web було видалено, web-0 не було перезапущено.

В одному терміналі спостерігайте за Podʼами StatefulSet.

# Залиште цей перегляд запущеним до наступного разу, коли ви почнете перегляд
kubectl get pods --watch -l app=nginx

У другому терміналі створіть знову StatefulSet. Зверніть увагу, якщо ви не видалили nginx Service (що ви не повинні були робити), ви побачите помилку, яка вказує, що Service вже існує.

kubectl apply -f https://k8s.io/examples/application/web/web.yaml
statefulset.apps/web created
service/nginx unchanged

Ігноруйте помилку. Вона лише вказує на те, що була спроба створити headless Service nginx, незважаючи на те, що цей Service вже існує.

Дослідіть вихідні дані команди kubectl get, яка працює у першому терміналі.

# Це вже має бути запущено
kubectl get pods --watch -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-1     1/1       Running   0          16m
web-2     1/1       Running   0          2m
NAME      READY     STATUS    RESTARTS   AGE
web-0     0/1       Pending   0          0s
web-0     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         18s
web-2     1/1       Terminating   0         3m
web-2     0/1       Terminating   0         3m
web-2     0/1       Terminating   0         3m
web-2     0/1       Terminating   0         3m

Коли StatefulSet web був відновлений, він спочатку перезапустив web-0. Оскільки web-1 вже був запущений і готовий, коли web-0 перейшов до стану Running і Ready, він став резервним для цього Podʼа. Оскільки ви відновили StatefulSet з replicas рівним 2, як тільки web-0 був відновлений, і як тільки web-1 вже був визначений як Running і Ready, web-2 був закінчений.

Тепер ще раз розгляньте вміст файлу index.html, який обслуговують вебсервери Podʼів:

for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1

Навіть якщо ви видалили як StatefulSet, так і Pod web-0, він все ще обслуговує імʼя хоста, яке спочатку було введено в його файл index.html. Це тому, що StatefulSet ніколи не видаляє PersistentVolumes, повʼязані з Podʼом. Коли ви відновили StatefulSet і перезапустили web-0, його оригінальний PersistentVolume знову змонтувався.

Каскадне видалення

В одному вікні термінала спостерігайте за Podʼами у StatefulSet.

# Залиште це запущеним до наступного розділу сторінки
kubectl get pods --watch -l app=nginx

В іншому терміналі знову видаліть StatefulSet. Цього разу не використовуйте параметр --cascade=orphan.

kubectl delete statefulset web
statefulset.apps "web" deleted

Дослідіть вихідні дані команди kubectl get, яка працює у першому терміналі, і зачекайте, поки всі Podʼи перейдуть у стан Terminating.

# Це вже має бути запущено
kubectl get pods --watch -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          11m
web-1     1/1       Running   0          27m
NAME      READY     STATUS        RESTARTS   AGE
web-0     1/1       Terminating   0          12m
web-1     1/1       Terminating   0         29m
web-0     0/1       Terminating   0         12m
web-0     0/1       Terminating   0         12m
web-0     0/1       Terminating   0         12m
web-1     0/1       Terminating   0         29m
web-1     0/1       Terminating   0         29m
web-1     0/1       Terminating   0         29m

Як ви бачили в розділі Зменшення мастабу, Podʼи видаляються по одному, з урахуванням зворотного порядку їх порядкових індексів. Перед видаленням Podʼа контролер StatefulSet чекає, поки Pod-наступник буде повністю видалено.

kubectl delete service nginx
service "nginx" deleted

Знову створіть StatefulSet і headless Service:

kubectl apply -f https://k8s.io/examples/application/web/web.yaml
service/nginx created
statefulset.apps/web created

Коли всі Podʼи StatefulSet перейдуть у стан Running і Ready, отримайте вміст їх файлів index.html:

for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1

Навіть якщо ви повністю видалили StatefulSet і всі його Podʼи, Podʼи перестворюються з монтуванням їх PersistentVolumes, і web-0 й web-1 продовжують обслуговувати свої імена хостів.

Нарешті, видаліть Service nginx

kubectl delete service nginx
service "nginx" deleted

…і StatefulSet web:

kubectl delete statefulset web
statefulset "web" deleted

Політика управління Podʼами

Для деяких розподілених систем порядок, гарантований StatefulSet, є непотрібним або небажаним. Ці системи потребують лише унікальності та ідентичності.

Ви можете вказати політику управління Podʼами, щоб уникнути цього строгого порядку; або OrderedReady (типово), або Parallel.

Політика управління Podʼами OrderedReady

Управління Podʼами OrderedReady є стандартним значенням для StatefulSet. Воно каже контролеру StatefulSet дотримуватися гарантій порядку, показаних вище.

Використовуйте цей параметр, коли ваш застосунок вимагає або очікує, що зміни, такі як запуск нової версії вашого застосунку, відбуваються у строгому порядку за порядковим номером (номером Pod), який надає StatefulSet. Іншими словами, якщо у вас є Podʼи app-0, app-1 та app-2, Kubernetes оновить app-0 першим і перевірить його. Якщо перевірка пройшла успішно, Kubernetes оновить app-1, а потім app-2.

Якщо ви додали ще два Podʼи, Kubernetes налаштує app-3 та почекає, доки він не стане справним, перед тим як розгорнути app-4.

Оскільки це стандартне налаштування, ви вже практикували його використання.

Політика управління Podʼами Parallel

Альтернатива, управління Podʼами Parallel, каже контролеру StatefulSet запускати або видаляти всі Podʼи паралельно, і не чекати, поки Podʼи не стануть Running і Ready або повністю видаленими перед запуском або видаленням іншого Podʼа.

Опція управління Podʼами Parallel впливає лише на поведінку при масштабуванні. Оновлення не піддаються впливу; Kubernetes все ще впроваджує зміни по черзі. Для цього навчального посібника застосунок дуже простий: вебсервер, який повідомляє вам його імʼя хосту (тому що це StatefulSet, імʼя хосту для кожного Podʼа є різним і передбачуваним).

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  podManagementPolicy: "Parallel"
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: registry.k8s.io/nginx-slim:0.24
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi

Цей маніфест ідентичний тому, який ви завантажили вище, за винятком того, що .spec.podManagementPolicy StatefulSet web встановлено на Parallel.

В одному терміналі спостерігайте за Podʼами в StatefulSet.

# Залиште це запущеним до кінця розділу
kubectl get pod -l app=nginx --watch

В іншому терміналі змініть конфігурацію StatefulSet на управління Podʼами Parallel:

kubectl apply -f https://k8s.io/examples/application/web/web-parallel.yaml
service/nginx updated
statefulset.apps/web updated

Залиште відкритим термінал, де ви запустили спостереження. У іншому вікні терміналу масштабуйте StatefulSet:

kubectl scale statefulset/web --replicas=5
statefulset.apps/web scaled

Дослідіть вихідні дані терміналу, де запущена команда kubectl get. Вони можуть виглядати приблизно так

web-3     0/1       Pending   0         0s
web-3     0/1       Pending   0         0s
web-3     0/1       Pending   0         7s
web-3     0/1       ContainerCreating   0         7s
web-2     0/1       Pending   0         0s
web-4     0/1       Pending   0         0s
web-2     1/1       Running   0         8s
web-4     0/1       ContainerCreating   0         4s
web-3     1/1       Running   0         26s
web-4     1/1       Running   0         2s

StatefulSet запустив три нові Podʼи, і він не чекав, поки перший стане Running і Ready перед запуском другого і третього Podʼів.

Цей підхід є корисним, якщо ваше робоче навантаження зберігає стан або Podʼи мають можливість ідентифікувати один одного за передбачуваною назвою, особливо якщо вам іноді потрібно швидко надати більше потужності. Якщо цей простий вебсервіс для навчального посібника раптово отримав додаткові 1,000,000 запитів на хвилину, то ви хотіли б запустити додаткові Podʼи — але ви також не хотіли б чекати, поки кожен новий Pod буде запущений. Запуск додаткових Podʼів паралельно скорочує час між запитом на додаткову потужність і її доступністю для використання.

Очищення

У вас маєть бути два термінали, готові для запуску команд kubectl для проведення очищення.

kubectl delete sts web
# sts - скорочення для statefulset

Ви можете відстежувати виконання команди kubectl get, щоб переглянути видалення цих ресурсів.

# завершіть перегляд, коли побачите необхідне
kubectl get pod -l app=nginx --watch
web-3     1/1       Terminating   0         9m
web-2     1/1       Terminating   0         9m
web-3     1/1       Terminating   0         9m
web-2     1/1       Terminating   0         9m
web-1     1/1       Terminating   0         44m
web-0     1/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-3     0/1       Terminating   0         9m
web-2     0/1       Terminating   0         9m
web-1     0/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-2     0/1       Terminating   0         9m
web-2     0/1       Terminating   0         9m
web-2     0/1       Terminating   0         9m
web-1     0/1       Terminating   0         44m
web-1     0/1       Terminating   0         44m
web-1     0/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-3     0/1       Terminating   0         9m
web-3     0/1       Terminating   0         9m
web-3     0/1       Terminating   0         9m

Під час видалення StatefulSet видаляються всі Podʼи одночасно; не чекаючи завершення роботи наступного Podʼа у черзі.

Закрийте термінал, де виконується команда kubectl get, та видаліть Service nginx:

kubectl delete svc nginx

Видаліть носій постійного зберігання для PersistentVolumes, що використовувалися в цьому посібнику.

kubectl get pvc
NAME        STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
www-web-0   Bound    pvc-2bf00408-d366-4a12-bad0-1869c65d0bee   1Gi        RWO            standard       25m
www-web-1   Bound    pvc-ba3bfe9c-413e-4b95-a2c0-3ea8a54dbab4   1Gi        RWO            standard       24m
www-web-2   Bound    pvc-cba6cfa6-3a47-486b-a138-db5930207eaf   1Gi        RWO            standard       15m
www-web-3   Bound    pvc-0c04d7f0-787a-4977-8da3-d9d3a6d8d752   1Gi        RWO            standard       15m
www-web-4   Bound    pvc-b2c73489-e70b-4a4e-9ec1-9eab439aa43e   1Gi        RWO            standard       14m
kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM               STORAGECLASS   REASON   AGE
pvc-0c04d7f0-787a-4977-8da3-d9d3a6d8d752   1Gi        RWO            Delete           Bound    default/www-web-3   standard                15m
pvc-2bf00408-d366-4a12-bad0-1869c65d0bee   1Gi        RWO            Delete           Bound    default/www-web-0   standard                25m
pvc-b2c73489-e70b-4a4e-9ec1-9eab439aa43e   1Gi        RWO            Delete           Bound    default/www-web-4   standard                14m
pvc-ba3bfe9c-413e-4b95-a2c0-3ea8a54dbab4   1Gi        RWO            Delete           Bound    default/www-web-1   standard                24m
pvc-cba6cfa6-3a47-486b-a138-db5930207eaf   1Gi        RWO            Delete           Bound    default/www-web-2   standard                15m
kubectl delete pvc www-web-0 www-web-1 www-web-2 www-web-3 www-web-4
persistentvolumeclaim "www-web-0" deleted
persistentvolumeclaim "www-web-1" deleted
persistentvolumeclaim "www-web-2" deleted
persistentvolumeclaim "www-web-3" deleted
persistentvolumeclaim "www-web-4" deleted
kubectl get pvc
No resources found in default namespace.
Змінено June 20, 2024 at 12:44 PM PST: Sync changest from andygol/k8s-website (36d05bc8a1)