Це багатосторінковий друкований вигляд цього розділу. Натисність щоб друкувати.
Stateful застосунки
1 - Основи StatefulSet
Цей підручник надає вступ до управління застосунками за допомогою StatefulSets. Він демонструє, як створювати, видаляти, масштабувати та оновлювати Podʼи StatefulSets.
Перш ніж ви розпочнете
Перш ніж розпочати цей підручник, вам слід ознайомитися з наступними концепціями Kubernetes:
- Podʼи
- Cluster DNS
- Headless Services
- PersistentVolumes
- PersistentVolume Provisioning
- Інструмент командного рядка kubectl
Вам треба мати кластер Kubernetes, а також інструмент командного рядка kubectl має бути налаштований для роботи з вашим кластером. Рекомендується виконувати ці настанови у кластері, що має щонайменше два вузли, які не виконують роль вузлів управління. Якщо у вас немає кластера, ви можете створити його, за допомогою minikube або використовувати одну з цих пісочниць:
Вам слід налаштувати kubectl
для використання контексту, який використовує простір імен default
. Якщо ви використовуєте наявний кластер, переконайтеся, що можна використовувати
простір імен цього кластера для практики. Ідеальною буде практика в кластері, де не запущені реальні робочі навантаження.
Також корисно прочитати сторінку з концепціями про StatefulSets.
Примітка:
Цей підручник передбачає, що ваш кластер налаштований на динамічне забезпечення PersistentVolumes. Вам також потрібно мати типовий StorageClass. Якщо ваш кластер не налаштований для динамічного забезпечення сховища, вам доведеться вручну забезпечити два томи по 1 GiB перед початком цього уроку та налаштувати ваш кластер так, щоб ці PersistentVolumes відповідали шаблонам PersistentVolumeClaim, які визначає StatefulSet.Цілі
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
для спостереження (watch) за створенням 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ʼи в 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
Примітка:
Якщо замість цього ви бачите відповіді 403 Forbidden для вищезазначеної команди curl, вам потрібно виправити дозволи для теки, змонтованого volumeMounts
(через помилку під час використання томів hostPath),
запустивши:
for i in 0 1; do kubectl exec web-$i -- chmod 755 /usr/share/nginx/html; done
перш ніж повторити команду curl
вище.
У одному терміналі спостерігайте за 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 зараз використовують попередній образ контейнера.
Примітка:
Ви також можете використовуватиkubectl rollout status sts/<name>
для перегляду стану поетапного оновлення 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-наступник буде повністю видалено.
Примітка:
Хоча каскадне видалення видаляє StatefulSet разом з його Podʼами, каскад не видаляє headless Service, повʼязаний з StatefulSet. Ви повинні видалити Servicenginx
вручну.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.
Примітка:
Вам також потрібно видалити носій постійне зберігання для PersistentVolumes, що використовувалось в цьому посібнику. Виконайте необхідні дії згідно з вашим середовищем, конфігурацією зберігання та методом надання послуг, щоб забезпечити повторне використання всього сховища.2 - Приклад: Розгортання WordPress та MySQL з постійними томами
У цьому посібнику ви дізнаєтеся, як розгорнути сайт WordPress та базу даних MySQL за допомогою Minikube. Обидва застосунки використовують PersistentVolumes та PersistentVolumeClaims для зберігання даних.
Постійні томи (PersistentVolume (PV)) — це частина системи зберігання в кластері, яку адміністратор вручну надав або яку Kubernetes автоматично надав за допомогою StorageClass. Запити на постійні томи (PersistentVolumeClaim (PVC)) — це запит на зберігання, який користувач може отримати через PV. PersistentVolumes та PersistentVolumeClaims незалежні від життєвого циклу Podʼів і зберігають дані під час перезапуску, перепланування та навіть видалення Podʼів.
Попередження:
Це розгортання не підходить для використання в операційній діяльності, оскільки використовує Podʼи з одним екземпляром WordPress та MySQL. Розгляньте використання WordPress Helm Chart, щоб розгорнути WordPress для промислової експлуатації.Примітка:
Файли, наведені в цьому посібнику, використовують API-інтерфейси Deployment GA та є специфічними для версії Kubernetes 1.9 і пізніших. Якщо ви хочете скористатися цим посібником з ранньою версією Kubernetes, оновіть, будь ласка, відповідно версію API або звертайтеся до раніших версій цього посібника.Цілі
- Створення PersistentVolumeClaims and PersistentVolumes
- Створення
kustomization.yaml
з- генератором Secret
- конфігураціями ресурсів MySQL
- конфігураціями ресурсів WordPress
- Застосування теки kustomization за допомогою
kubectl apply -k ./
- Очищення
Перш ніж ви розпочнете
Вам треба мати кластер Kubernetes, а також інструмент командного рядка kubectl має бути налаштований для роботи з вашим кластером. Рекомендується виконувати ці настанови у кластері, що має щонайменше два вузли, які не виконують роль вузлів управління. Якщо у вас немає кластера, ви можете створити його, за допомогою minikube або використовувати одну з цих пісочниць:
Для перевірки версії введітьkubectl version
.Наведений на цій сторінці приклад працює з kubectl
версії 1.27 і вище.
Завантажте наступні конфігураційні файли:
Створення запитів на постійні томи та постійних томів
Для зберігання даних MySQL та Wordpress кожному потрібен постійний том (PersistentVolume). Їх запити на постійні томи (PersistentVolumeClaim) будуть створені на етапі розгортання.
У багатьох середовищах кластера встановлено типовий StorageClass. Якщо StorageClass не вказано у запиті на постійний том (PersistentVolumeClaim), то використовується типовий StorageClass кластера.
Коли створюється запит на постійний том (PersistentVolumeClaim), постійний том (PersistentVolume) динамічно надається на основі конфігурації StorageClass.
Попередження:
У локальних кластерах типовий StorageClass використовує провізорhostPath
. Томи hostPath
підходять лише для розробки та тестування. З томами hostPath
, ваші дані зберігаються в /tmp
на вузлі, на який заплановано Pod, і не переміщуються між вузлами. Якщо Pod перестає існувати та переплановується на інший вузол в кластері або вузол перезавантажується, дані втрачаються.Примітка:
Якщо ви запускаєте кластер, який потребує використання провізораhostPath
, прапорець --enable-hostpath-provisioner
повинен бути встановлений в компоненті controller-manager
.Примітка:
Якщо у вас є кластер Kubernetes, що працює на Google Kubernetes Engine, будь ласка, дотримуйтесь рекомендацій цього посібника.Створення файлу kustomization.yaml
Додавання генератора Secret
Secret — це обʼєкт, який зберігає чутливі дані, такі як паролі або ключі. Починаючи з версії 1.14, kubectl
підтримує керування обʼєктами Kubernetes за допомогою файлу kustomization. Ви можете створити Secret за допомогою генераторів у файлі kustomization.yaml
.
Додайте генератор Secret у файл kustomization.yaml
за допомогою наступної команди. Вам потрібно буде замінити YOUR_PASSWORD
на пароль, який ви хочете використовувати.
cat <<EOF >./kustomization.yaml
secretGenerator:
- name: mysql-pass
literals:
- password=YOUR_PASSWORD
EOF
Додавання конфігурації ресурсів для MySQL та WordPress
Наступний маніфест описує Deployment одного екземпляра MySQL. Контейнер MySQL монтує PersistentVolume у /var/lib/mysql
. Змінна оточення MYSQL_ROOT_PASSWORD
встановлює пароль бази даних з Secret.
apiVersion: v1
kind: Service
metadata:
name: wordpress-mysql
labels:
app: wordpress
spec:
ports:
- port: 3306
selector:
app: wordpress
tier: mysql
clusterIP: None
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pv-claim
labels:
app: wordpress
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress-mysql
labels:
app: wordpress
spec:
selector:
matchLabels:
app: wordpress
tier: mysql
strategy:
type: Recreate
template:
metadata:
labels:
app: wordpress
tier: mysql
spec:
containers:
- image: mysql:8.0
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-pass
key: password
- name: MYSQL_DATABASE
value: wordpress
- name: MYSQL_USER
value: wordpress
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-pass
key: password
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: mysql-persistent-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-persistent-storage
persistentVolumeClaim:
claimName: mysql-pv-claim
Наступний маніфест описує Deployment одного екземпляра WordPress. Контейнер WordPress монтує PersistentVolume у /var/www/html
для файлів даних вебсайту. Змінна оточення WORDPRESS_DB_HOST
встановлює імʼя служби MySQL, визначеної вище, і WordPress буде звертатися до бази даних через службу. Змінна оточення WORDPRESS_DB_PASSWORD
встановлює пароль бази даних згенерованого kustomize Secret.
apiVersion: v1
kind: Service
metadata:
name: wordpress
labels:
app: wordpress
spec:
ports:
- port: 80
selector:
app: wordpress
tier: frontend
type: LoadBalancer
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wp-pv-claim
labels:
app: wordpress
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress
labels:
app: wordpress
spec:
selector:
matchLabels:
app: wordpress
tier: frontend
strategy:
type: Recreate
template:
metadata:
labels:
app: wordpress
tier: frontend
spec:
containers:
- image: wordpress:6.2.1-apache
name: wordpress
env:
- name: WORDPRESS_DB_HOST
value: wordpress-mysql
- name: WORDPRESS_DB_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-pass
key: password
- name: WORDPRESS_DB_USER
value: wordpress
ports:
- containerPort: 80
name: wordpress
volumeMounts:
- name: wordpress-persistent-storage
mountPath: /var/www/html
volumes:
- name: wordpress-persistent-storage
persistentVolumeClaim:
claimName: wp-pv-claim
Завантажте файл конфігурації розгортання MySQL.
curl -LO https://k8s.io/examples/application/wordpress/mysql-deployment.yaml
Завантажте файл конфігурації розгортання WordPress.
curl -LO https://k8s.io/examples/application/wordpress/wordpress-deployment.yaml
Додайте їх до файлу
kustomization.yaml
.cat <<EOF >>./kustomization.yaml resources: - mysql-deployment.yaml - wordpress-deployment.yaml EOF
Застосування та перевірка
Файл kustomization.yaml
містить всі ресурси для розгортання сайту WordPress та бази даних MySQL. Ви можете застосувати цю теку командою
kubectl apply -k ./
Тепер перевірте, що всі обʼєкти існують.
Перевірте, що Secret існує, виконавши наступну команду:
kubectl get secrets
Відповідь має бути подібною до цієї:
NAME TYPE DATA AGE mysql-pass-c57bb4t7mf Opaque 1 9s
Перевірте, що PersistentVolume був динамічно наданий.
kubectl get pvc
Примітка:
Процес надання PV може зайняти кілька хвилин.Відповідь має бути подібною до цієї:
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE mysql-pv-claim Bound pvc-8cbd7b2e-4044-11e9-b2bb-42010a800002 20Gi RWO standard 77s wp-pv-claim Bound pvc-8cd0df54-4044-11e9-b2bb-42010a800002 20Gi RWO standard 77s
Перевірте, що Pod працює, виконавши наступну команду:
kubectl get pods
Примітка:
Статус Pod може бутиRUNNING
через кілька хвилин.Відповідь має бути подібною до цієї:
NAME READY STATUS RESTARTS AGE wordpress-mysql-1894417608-x5dzt 1/1 Running 0 40s
Перевірте, що Service працює, виконавши наступну команду:
kubectl get services wordpress
Відповідь має бути подібною до цієї:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE wordpress LoadBalancer 10.0.0.89 <pending> 80:32406/TCP 4m
Примітка:
Minikube може використовувати лишеNodePort
для викладання служб. Зовнішня IP-адреса завжди знаходиться в стані очікування.Виконайте наступну команду, щоб отримати IP-адресу для Service WordPress:
minikube service wordpress --url
Відповідь має бути подібною до цієї:
http://1.2.3.4:32406
Скопіюйте IP-адресу та завантажте сторінку у своєму оглядачі, щоб переглянути ваш сайт.
Ви повинні побачити сторінку налаштування WordPress, схожу на знімок екрана нижче.
Попередження:
Не залишайте вашу установку WordPress на цій сторінці. Якщо інший користувач знайде її, вони можуть налаштувати вебсайт на вашому екземплярі та використовувати його для обслуговування шкідливого вмісту.
Або встановіть WordPress, створивши імʼя користувача та пароль, або видаліть свій екземпляр.
Очищення
Виконайте наступну команду, щоб видалити ваш Secret, Deployments, Services та PersistentVolumeClaims:
kubectl delete -k ./
Що далі
- Дізнайтеся більше про Інтроспекцію та Налагодження
- Дізнайтеся більше про Job
- Дізнайтеся більше про Переадресацію портів
- Дізнайтеся, як Отримати доступ до оболонки командного рядка в контейнері
3 - Приклад: Розгортання Cassandra з використанням StatefulSet
Цей підручник покаже вам, як запустити Apache Cassandra у Kubernetes. Cassandra, база даних, потребує постійного сховища для забезпечення стійкості даних (стан застосунку). У цьому прикладі використовується власний постачальник насіння Cassandra, що дозволяє базі даних виявляти нові екземпляри Cassandra, коли вони приєднуються до кластера Cassandra.
StatefulSet полегшує розгортання стійких застосунків у вашому кластері Kubernetes. Для отримання додаткової інформації про використані у цьому підручнику функції дивіться StatefulSet.
Примітка:
Cassandra та Kubernetes використовують термін вузол для позначення члена кластера. У цьому підручнику Podʼи, що належать StatefulSet, є вузлами Cassandra та є членами кластера Cassandra (званого кільцем (ring)). Коли ці Podʼи працюють у вашому кластері Kubernetes, панель управління Kubernetes розміщує ці Podʼи на Вузлах Kubernetes.
Коли вузол Cassandra стартує, він використовує список насіння для відкриття виявлення інших вузлів у кільці. У цьому підручнику розгортається власний постачальник насіння Cassandra, який дозволяє базі даних виявляти нові Podʼи Cassandra в міру їх появи у вашому кластері Kubernetes.
Цілі
- Створення та перевірка Cassandra headless Service.
- Використання StatefulSet для створення кільця Cassandra.
- Перевірка StatefulSet.
- Зміна StatefulSet.
- Видалення StatefulSet та його Podʼів.
Перш ніж ви розпочнете
Вам треба мати кластер Kubernetes, а також інструмент командного рядка kubectl має бути налаштований для роботи з вашим кластером. Рекомендується виконувати ці настанови у кластері, що має щонайменше два вузли, які не виконують роль вузлів управління. Якщо у вас немає кластера, ви можете створити його, за допомогою minikube або використовувати одну з цих пісочниць:
Для роботи з цим посібником ви повинні вже мати базові знання про Podʼи, Сервіси, та StatefulSets.
Додаткові інструкції щодо налаштування Minikube
Увага:
Minikube стандартно використовує 2048 МБ пам’яті та 2 ЦП. Запуск Minikube зі стандартною конфігурацією ресурсів призводить до помилок недостатності ресурсів під час виконання настанов цього посібника. Щоб уникнути цих помилок, запустіть Minikube з наступними налаштуваннями:
minikube start --memory 5120 --cpus=4
Створення headless Service для Cassandra
У Kubernetes Service описує набір Podʼів, які виконують одну й ту ж задачу.
Наступний Service використовується для DNS-пошуку між Podʼами Cassandra та клієнтами у вашому кластері:
apiVersion: v1
kind: Service
metadata:
labels:
app: cassandra
name: cassandra
spec:
clusterIP: None
ports:
- port: 9042
selector:
app: cassandra
Створіть Service для відстеження всіх членів StatefulSet Cassandra з файлу cassandra-service.yaml
:
kubectl apply -f https://k8s.io/examples/application/cassandra/cassandra-service.yaml
Перевірка (необов’язково)
Отримайте інформацію про Cassandra Service.
kubectl get svc cassandra
Відповідь буде
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
cassandra ClusterIP None <none> 9042/TCP 45s
Якщо ви не бачите Service з назвою cassandra
, це означає, що його створення не вдалося. Прочитайте Налагодження Service, щоб отримати довідку з усунення загальних проблем.
Використання StatefulSet для створення кільця Cassandra
Маніфест StatefulSet, наведений нижче, створює кільце Cassandra, що складається з трьох Podʼів.
Примітка:
У цьому прикладі використовується стандартний провізор для Minikube. Будь ласка, оновіть наступний StatefulSet для хмари, з якою ви працюєте.apiVersion: apps/v1
kind: StatefulSet
metadata:
name: cassandra
labels:
app: cassandra
spec:
serviceName: cassandra
replicas: 3
selector:
matchLabels:
app: cassandra
template:
metadata:
labels:
app: cassandra
spec:
terminationGracePeriodSeconds: 500
containers:
- name: cassandra
image: gcr.io/google-samples/cassandra:v13
imagePullPolicy: Always
ports:
- containerPort: 7000
name: intra-node
- containerPort: 7001
name: tls-intra-node
- containerPort: 7199
name: jmx
- containerPort: 9042
name: cql
resources:
limits:
cpu: "500m"
memory: 1Gi
requests:
cpu: "500m"
memory: 1Gi
securityContext:
capabilities:
add:
- IPC_LOCK
lifecycle:
preStop:
exec:
command:
- /bin/sh
- -c
- nodetool drain
env:
- name: MAX_HEAP_SIZE
value: 512M
- name: HEAP_NEWSIZE
value: 100M
- name: CASSANDRA_SEEDS
value: "cassandra-0.cassandra.default.svc.cluster.local"
- name: CASSANDRA_CLUSTER_NAME
value: "K8Demo"
- name: CASSANDRA_DC
value: "DC1-K8Demo"
- name: CASSANDRA_RACK
value: "Rack1-K8Demo"
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
readinessProbe:
exec:
command:
- /bin/bash
- -c
- /ready-probe.sh
initialDelaySeconds: 15
timeoutSeconds: 5
# Ці точки монтування томів є постійними. Вони подібни до вбудованих заявок,
# але не зовсіс, тому що імена повинні точно збігатись з одним з томів
# томів Podʼів stateful.
volumeMounts:
- name: cassandra-data
mountPath: /cassandra_data
# Це перетворюється у заявки на томи контролером
# та монтується в шлях, зазначений вище.
# не використовуйте це в операційній діяльності, допоки тип ssd є GCEPersistentDisk чи інший ssd pd
volumeClaimTemplates:
- metadata:
name: cassandra-data
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: fast
resources:
requests:
storage: 1Gi
---
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: fast
provisioner: k8s.io/minikube-hostpath
parameters:
type: pd-ssd
Створіть StatefulSet Cassandra з файлу cassandra-statefulset.yaml
:
# Використовуйте це, якщо ви можете застосувати cassandra-statefulset.yaml без змін
kubectl apply -f https://k8s.io/examples/application/cassandra/cassandra-statefulset.yaml
Якщо вам потрібно змінити cassandra-statefulset.yaml
для вашого кластера, завантажте https://k8s.io/examples/application/cassandra/cassandra-statefulset.yaml та застосуйте цей маніфест, з теки, в яку ви зберегли змінену версію:
# Використовуйте це, якщо ви змінили cassandra-statefulset.yaml локально
kubectl apply -f cassandra-statefulset.yaml
Перевірка StatefulSet Cassandra
Отримайте StatefulSet Cassandra:
kubectl get statefulset cassandra
Відповідь буде схожа на:
NAME DESIRED CURRENT AGE cassandra 3 0 13s
Ресурс
StatefulSet
розгортає Podʼи послідовно.Отримайте Podʼи, щоб побачити статус створення в зазначеному порядку:
kubectl get pods -l="app=cassandra"
Відповідь буде схожа на:
NAME READY STATUS RESTARTS AGE cassandra-0 1/1 Running 0 1m cassandra-1 0/1 ContainerCreating 0 8s
На створення всіх трьох Podʼів може піти декілька хвилин. Після їх розгортання ця ж команда повертає вихідні дані, схожі на:
NAME READY STATUS RESTARTS AGE cassandra-0 1/1 Running 0 10m cassandra-1 1/1 Running 0 9m cassandra-2 1/1 Running 0 8m
Виконайте інструмент nodetool Cassandra всередині першого Podʼа, щоб переглянути стан кільця.
kubectl exec -it cassandra-0 -- nodetool status
Відповідь буде схожою на:
Datacenter: DC1-K8Demo ====================== Status=Up/Down |/ State=Normal/Leaving/Joining/Moving -- Address Load Tokens Owns (effective) Host ID Rack UN 172.17.0.5 83.57 KiB 32 74.0% e2dd09e6-d9d3-477e-96c5-45094c08db0f Rack1-K8Demo UN 172.17.0.4 101.04 KiB 32 58.8% f89d6835-3a42-4419-92b3-0e62cae1479c Rack1-K8Demo UN 172.17.0.6 84.74 KiB 32 67.1% a6a1e8c2-3dc5-4417-b1a0-26507af2aaad Rack1-K8Demo
Зміна StatefulSet Cassandra
Використовуйте kubectl edit
, щоб змінити розмір StatefulSet Cassandra.
Виконайте наступну команду:
kubectl edit statefulset cassandra
Ця команда відкриває редактор у вашому терміналі. Рядок, який вам потрібно змінити, — це поле
replicas
. Наведений нижче приклад — це уривок з файлу StatefulSet:# Будь ласка, відредагуйте об’єкт нижче. Рядки, що починаються з '#', будуть проігноровані, # і пустий файл призведе до відмови редагування. Якщо під час збереження файлу виникне помилка, # цей файл буде знову відкритий із відповідними несправностями. # apiVersion: apps/v1 kind: StatefulSet metadata: creationTimestamp: 2016-08-13T18:40:58Z generation: 1 labels: app: cassandra name: cassandra namespace: default resourceVersion: "323" uid: 7a219483-6185-11e6-a910-42010a8a0fc0 spec: replicas: 3
Змініть кількість реплік на 4 і збережіть маніфест.
Тепер StatefulSet масштабується для роботи з 4 Podʼами.
Отримайте StatefulSet Cassandra, щоб перевірити зміни:
kubectl get statefulset cassandra
Відповідь буде схожа на:
NAME DESIRED CURRENT AGE cassandra 4 4 36m
Очищення
Видалення або зменшення масштабу StatefulSet не призводить до видалення томів, повʼязаних із StatefulSet. Це налаштування захищає вас, оскільки ваші дані цінніші, ніж автоматичне очищення всіх повʼязаних ресурсів StatefulSet.
Попередження:
Залежно від класу сховища та політики вилучення, видалення PersistentVolumeClaims може призвести до вилучення також повʼязаних томів. Ніколи не припускайте, що ви зможете отримати доступ до даних, якщо їх томи будуть видалені.Виконайте наступні команди (послідовно обʼєднані в одну команду) для видалення всього в StatefulSet Cassandra:
grace=$(kubectl get pod cassandra-0 -o=jsonpath='{.spec.terminationGracePeriodSeconds}') \ && kubectl delete statefulset -l app=cassandra \ && echo "Sleeping ${grace} seconds" 1>&2 \ && sleep $grace \ && kubectl delete persistentvolumeclaim -l app=cassandra
Виконайте наступну команду для видалення Service, який ви налаштували для Cassandra:
kubectl delete service -l app=cassandra
Змінні середовища контейнера Cassandra
Podʼи в цьому посібнику використовують образ gcr.io/google-samples/cassandra:v13
з реєстру контейнерів Google. Докер-образ вище базується на debian-base і містить OpenJDK 8.
Цей образ включає стандартну установку Cassandra з репозиторію Apache Debian. За допомогою змінних середовища ви можете змінити значення, які вставляються в cassandra.yaml
.
Змінна середовища | Стандартне значенняанням |
---|---|
CASSANDRA_CLUSTER_NAME | 'Test Cluster' |
CASSANDRA_NUM_TOKENS | 32 |
CASSANDRA_RPC_ADDRESS | 0.0.0.0 |
Що далі
- Дізнайтеся, як Масштабувати StatefulSet.
- Дізнайтеся більше про KubernetesSeedProvider
- Подивіться інші налаштування постачальника насіння
4 - Запуск ZooKeeper, розподіленого системного координатора
Цей посібник демонструє як запускати Apache Zookeeper в Kubernetes, використовуючи StatefulSets, PodDisruptionBudgets, та PodAntiAffinity.
Перш ніж ви розпочнете
Перед тим як розпочати, переконайтеся, що ви маєте уявлення про:
- Podʼи
- DNS кластера
- Headless Services
- PersistentVolumes
- PersistentVolume Provisioning
- StatefulSets
- PodDisruptionBudgets
- PodAntiAffinity
- kubectl CLI
Вам необхідно мати кластер із щонайменше чотирма вузлами, і кожен вузол повинен мати щонайменше 2 ЦП та 4 ГБ памʼяті. У цьому посібнику ви будете закривати (cordon) та очищувати для обслуговування (drain) вузли кластера. Це означає, що кластер припинить роботу та виселить всі Podʼи зі своїх вузлів, і вузли тимчасово стануть не придатними до розміщення Podʼів. Вам слід використовувати окремий кластер для цього посібника, або ви повинні забезпечити, що порушення, яке ви викличете, не нашкодить іншим мешканцям кластера.
У цьому посібнику передбачається, що ви налаштували свій кластер для динамічного надання постійних томів (PersistentVolumes). Якщо ваш кластер не налаштований на це, вам доведеться вручну створити три томи обсягом 20 ГБ перед початком виконання кроків цього посібника.
Цілі
Після проходження цього посібника ви будете знати, як:
- Розгортати ансамбль Apache Zookeeper використовуючи StatefulSet.
- Як надійно налаштовувати ансамбль.
- Як поширювати розгортання серверів Zookeeper в ансамблі.
- Як використовувати PodDisruptionBudgets для забезпечення високої доступності послуг під час запланованого обслуговування.
ZooKeeper
Apache ZooKeeper — це розподілена, відкрита координаційна служба для розподілених застосунків. ZooKeeper дозволяє читати, записувати та спостерігати за оновленнями даних. Дані організовані у вигляді ієрархії файлової системи та реплікуються на всі сервери ZooKeeper в ансамблі (набір серверів ZooKeeper). Всі операції з даними є атомарними та послідовно консистентними. ZooKeeper забезпечує це, використовуючи протокол консенсусу Zab для реплікації машини стану на всіх серверах в ансамблі.
Ансамбль використовує протокол Zab для вибору лідера, ансамбль не може записувати дані, поки цей вибір лідера не завершиться. Після його завершення ансамбль використовує Zab, щоб забезпечити реплікацію всіх записів до кворуму, перш ніж він підтверджує їх та робить їх видимими для клієнтів. Без зваженого кворуму, кворум — буде більшістю складових ансамблю, які містять поточного лідера. Наприклад, якщо в ансамблі є три сервери, компонент, який містить лідера і ще один сервер, становитимуть кворум. Якщо ансамбль не може досягти кворуму, він не може записувати дані.
Сервери ZooKeeper зберігають свою повну машину стану в памʼяті та записують кожну зміну до довгострокового WAL (Write Ahead Log) на носії інформації. Коли сервер аварійно завершує роботу, він може відновити свій попередній стан, відтворюючи WAL. Щоб запобігти безмежному зростанню WAL, сервери ZooKeeper періодично роблять знімки їхнього стану в памʼяті на носій інформації. Ці знімки можуть бути завантажені безпосередньо в памʼять, а всі записи WAL, які передували знімку, можуть бути видалені.
Створення ансамблю ZooKeeper
Маніфест нижче містить:
apiVersion: v1
kind: Service
metadata:
name: zk-hs
labels:
app: zk
spec:
ports:
- port: 2888
name: server
- port: 3888
name: leader-election
clusterIP: None
selector:
app: zk
---
apiVersion: v1
kind: Service
metadata:
name: zk-cs
labels:
app: zk
spec:
ports:
- port: 2181
name: client
selector:
app: zk
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: zk-pdb
spec:
selector:
matchLabels:
app: zk
maxUnavailable: 1
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: zk
spec:
selector:
matchLabels:
app: zk
serviceName: zk-hs
replicas: 3
updateStrategy:
type: RollingUpdate
podManagementPolicy: OrderedReady
template:
metadata:
labels:
app: zk
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: "app"
operator: In
values:
- zk
topologyKey: "kubernetes.io/hostname"
containers:
- name: kubernetes-zookeeper
imagePullPolicy: Always
image: "registry.k8s.io/kubernetes-zookeeper:1.0-3.4.10"
resources:
requests:
memory: "1Gi"
cpu: "0.5"
ports:
- containerPort: 2181
name: client
- containerPort: 2888
name: server
- containerPort: 3888
name: leader-election
command:
- sh
- -c
- "start-zookeeper \
--servers=3 \
--data_dir=/var/lib/zookeeper/data \
--data_log_dir=/var/lib/zookeeper/data/log \
--conf_dir=/opt/zookeeper/conf \
--client_port=2181 \
--election_port=3888 \
--server_port=2888 \
--tick_time=2000 \
--init_limit=10 \
--sync_limit=5 \
--heap=512M \
--max_client_cnxns=60 \
--snap_retain_count=3 \
--purge_interval=12 \
--max_session_timeout=40000 \
--min_session_timeout=4000 \
--log_level=INFO"
readinessProbe:
exec:
command:
- sh
- -c
- "zookeeper-ready 2181"
initialDelaySeconds: 10
timeoutSeconds: 5
livenessProbe:
exec:
command:
- sh
- -c
- "zookeeper-ready 2181"
initialDelaySeconds: 10
timeoutSeconds: 5
volumeMounts:
- name: datadir
mountPath: /var/lib/zookeeper
securityContext:
runAsUser: 1000
fsGroup: 1000
volumeClaimTemplates:
- metadata:
name: datadir
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 10Gi
Відкрийте термінал і скористайтеся командою kubectl apply
, щоб створити маніфест.
kubectl apply -f https://k8s.io/examples/application/zookeeper/zookeeper.yaml
Це створить zk-hs
Headless Service, zk-cs
Service, zk-pdb
PodDisruptionBudget та zk
StatefulSet.
service/zk-hs створено
service/zk-cs створено
poddisruptionbudget.policy/zk-pdb створено
statefulset.apps/zk створено
Скористайтеся kubectl get
, щоб спостерігати за створенням StatefulSet контролером Podʼів StatefulSet.
kubectl get pods -w -l app=zk
Коли Pod zk-2
в стані Running та Ready, скористайтеся CTRL-C
, щоб припинити виконання kubectl.
NAME READY STATUS RESTARTS AGE
zk-0 0/1 Pending 0 0s
zk-0 0/1 Pending 0 0s
zk-0 0/1 ContainerCreating 0 0s
zk-0 0/1 Running 0 19s
zk-0 1/1 Running 0 40s
zk-1 0/1 Pending 0 0s
zk-1 0/1 Pending 0 0s
zk-1 0/1 ContainerCreating 0 0s
zk-1 0/1 Running 0 18s
zk-1 1/1 Running 0 40s
zk-2 0/1 Pending 0 0s
zk-2 0/1 Pending 0 0s
zk-2 0/1 ContainerCreating 0 0s
zk-2 0/1 Running 0 19s
zk-2 1/1 Running 0 40s
Контролер StatefulSet створює три Podʼи, і кожен Pod містить контейнер з сервером ZooKeeper.
Забезпечення виборів лідера
Оскільки в анонімній мережі немає алгоритму завершення для вибору лідера, Zab вимагає явної конфігурації членства для виконання виборів лідера. Кожен сервер в ансамблі повинен мати унікальний ідентифікатор, всі сервери повинні знати глобальний набір ідентифікаторів, і кожен ідентифікатор повинен бути повʼязаний з мережевою адресою.
Використовуйте kubectl exec
, щоб отримати імена хостів Podʼів у StatefulSet zk
.
for i in 0 1 2; do kubectl exec zk-$i -- hostname; done
Контролер StatefulSet надає кожному Podʼу унікальне імʼя хосту на основі його порядкового індексу. Імена хостів мають форму <імʼя statefulset>-<порядковий індекс>
. Оскільки поле replicas
StatefulSet zk
встановлено на 3
, контролер набору створює три Podʼа з іменами хостів zk-0
, zk-1
і zk-2
.
zk-0
zk-1
zk-2
Сервери в ансамблі ZooKeeper використовують натуральні числа як унікальні ідентифікатори, і зберігають ідентифікатор кожного сервера у файлі, який називається myid
, в теці даних сервера.
Щоб переглянути вміст файлу myid
для кожного сервера, скористайтеся такою командою.
for i in 0 1 2; do echo "myid zk-$i";kubectl exec zk-$i -- cat /var/lib/zookeeper/data/myid; done
Оскільки ідентифікатори є натуральними числами, а порядкові індекси є не відʼємними цілими числами, можна згенерувати ідентифікатор, додавши 1 до порядкового номера.
myid zk-0
1
myid zk-1
2
myid zk-2
3
Щоб отримати повне доменне імʼя (FQDN) кожного Podʼа у StatefulSet zk
, використовуйте таку команду.
for i in 0 1 2; do kubectl exec zk-$i -- hostname -f; done
Service zk-hs
створює домен для всіх Podʼів, zk-hs.default.svc.cluster.local
.
zk-0.zk-hs.default.svc.cluster.local
zk-1.zk-hs.default.svc.cluster.local
zk-2.zk-hs.default.svc.cluster.local
Записи A в DNS Kubernetes перетворюють FQDN в IP-адреси Podʼів. Якщо Kubernetes переплановує Podʼи, він оновлює записи A із новими IP-адресами Podʼів, але імена записів A не змінюються.
ZooKeeper зберігає свою конфігурацію застосунку в файлі з іменем zoo.cfg
. Використовуйте kubectl exec
, щоб переглянути вміст файлу zoo.cfg
у Поді zk-0
.
kubectl exec zk-0 -- cat /opt/zookeeper/conf/zoo.cfg
У властивостях server.1
, server.2
та server.3
внизу файлу, 1
, 2
та 3
відповідають ідентифікаторам у файлах myid
серверів ZooKeeper. Вони встановлені на FQDN для Podʼів у StatefulSet zk
.
clientPort=2181
dataDir=/var/lib/zookeeper/data
dataLogDir=/var/lib/zookeeper/log
tickTime=2000
initLimit=10
syncLimit=2000
maxClientCnxns=60
minSessionTimeout= 4000
maxSessionTimeout= 40000
autopurge.snapRetainCount=3
autopurge.purgeInterval=0
server.1=zk-0.zk-hs.default.svc.cluster.local:2888:3888
server.2=zk-1.zk-hs.default.svc.cluster.local:2888:3888
server.3=zk-2.zk-hs.default.svc.cluster.local:2888:3888
Досягнення консенсусу
Протоколи консенсусу вимагають, щоб ідентифікатори кожного учасника були унікальними. Два учасники у протоколі Zab не повинні претендувати на той самий унікальний ідентифікатор. Це необхідно для того, щоб процеси в системі могли погодитися щодо того, які процеси затвердили які дані. Якщо запускаються два Podʼа з тим самим порядковим номером, два сервери ZooKeeper ідентифікуватимуть себе як один і той самий сервер.
kubectl get pods -w -l app=zk
NAME READY STATUS RESTARTS AGE
zk-0 0/1 Pending 0 0s
zk-0 0/1 Pending 0 0s
zk-0 0/1 ContainerCreating 0 0s
zk-0 0/1 Running 0 19s
zk-0 1/1 Running 0 40s
zk-1 0/1 Pending 0 0s
zk-1 0/1 Pending 0 0s
zk-1 0/1 ContainerCreating 0 0s
zk-1 0/1 Running 0 18s
zk-1 1/1 Running 0 40s
zk-2 0/1 Pending 0 0s
zk-2 0/1 Pending 0 0s
zk-2 0/1 ContainerCreating 0 0s
zk-2 0/1 Running 0 19s
zk-2 1/1 Running 0 40s
Записи A для кожного Pod вводяться, коли Pod стає готовим. Тому, FQDN серверів ZooKeeper посилається на єдину точку доступу, і ця точка доступу буде унікальним сервером ZooKeeper, який претендує на ідентифікацію, налаштовану в його файлі myid
.
zk-0.zk-hs.default.svc.cluster.local
zk-1.zk-hs.default.svc.cluster.local
zk-2.zk-hs.default.svc.cluster.local
Це забезпечує, що властивості servers
у файлах zoo.cfg
ZooKeepers становлять собою правильно налаштований ансамбль.
server.1=zk-0.zk-hs.default.svc.cluster.local:2888:3888
server.2=zk-1.zk-hs.default.svc.cluster.local:2888:3888
server.3=zk-2.zk-hs.default.svc.cluster.local:2888:3888
Коли сервери використовують протокол Zab, щоб спробувати затвердити значення, вони або досягатимуть консенсусу і затверджуватимуть значення (якщо вибори лідера пройшли успішно і принаймні два Podʼа працюють та готові), або вони не зможуть цього зробити (якщо будь-яка з умов не виконується). Ні один стан не призведе до того, що один сервер підтверджує запис від імені іншого.
Перевірка адекватності ансамблю
Найбільш базове тестування на адекватність — це запис даних на один сервер ZooKeeper і читання даних з іншого.
Наведена нижче команда виконує скрипт zkCli.sh
, щоб записати world
в шлях /hello
у Pod zk-0
в ансамблі.
kubectl exec zk-0 -- zkCli.sh create /hello world
WATCHER::
WatchedEvent state:SyncConnected type:None path:null
Created /hello
Щоб отримати дані з Podʼа zk-1
, використовуйте таку команду.
kubectl exec zk-1 -- zkCli.sh get /hello
Дані, які ви створили на zk-0
, доступні на всіх серверах в ансамблі.
WATCHER::
WatchedEvent state:SyncConnected type:None path:null
world
cZxid = 0x100000002
ctime = Thu Dec 08 15:13:30 UTC 2016
mZxid = 0x100000002
mtime = Thu Dec 08 15:13:30 UTC 2016
pZxid = 0x100000002
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
Забезпечення стійкості зберігання
Як зазначено в розділі Основи ZooKeeper, ZooKeeper фіксує всі записи в стійкому журналі (WAL) та періодично записує знімки стану памʼяті на носії. Використання WAL для забезпечення стійкості є поширеною технікою для застосунків, які використовують протоколи консенсусу для досягнення реплікованої машини станів.
Використовуйте команду kubectl delete
, щоб видалити обʼєкт StatefulSet
zk
.
kubectl delete statefulset zk
statefulset.apps "zk" deleted
Спостерігайте за завершенням роботи Podʼів у StatefulSet
.
kubectl get pods -w -l app=zk
Коли zk-0
повністю завершить роботу, використовуйте CTRL-C
, щоб завершити виконання kubectl.
zk-2 1/1 Terminating 0 9m
zk-0 1/1 Terminating 0 11m
zk-1 1/1 Terminating 0 10m
zk-2 0/1 Terminating 0 9m
zk-2 0/1 Terminating 0 9m
zk-2 0/1 Terminating 0 9m
zk-1 0/1 Terminating 0 10m
zk-1 0/1 Terminating 0 10m
zk-1 0/1 Terminating 0 10m
zk-0 0/1 Terminating 0 11m
zk-0 0/1 Terminating 0 11m
zk-0 0/1 Terminating 0 11m
Повторно застосуйте маніфест у файлі zookeeper.yaml
.
kubectl apply -f https://k8s.io/examples/application/zookeeper/zookeeper.yaml
Це створює обʼєкт StatefulSet
zk
, але інші обʼєкти API у маніфесті не модифікуються, оскільки вони вже існують.
Спостерігайте, як контролер StatefulSet
перестворює Podʼи StatefulSet
.
kubectl get pods -w -l app=zk
Коли Pod zk-2
відзначається як Running і Ready, використовуйте CTRL-C
, щоб завершити виконання kubectl.
NAME READY STATUS RESTARTS AGE
zk-0 0/1 Pending 0 0s
zk-0 0/1 Pending 0 0s
zk-0 0/1 ContainerCreating 0 0s
zk-0 0/1 Running 0 19s
zk-0 1/1 Running 0 40s
zk-1 0/1 Pending 0 0s
zk-1 0/1 Pending 0 0s
zk-1 0/1 ContainerCreating 0 0s
zk-1 0/1 Running 0 18s
zk-1 1/1 Running 0 40s
zk-2 0/1 Pending 0 0s
zk-2 0/1 Pending 0 0s
zk-2 0/1 ContainerCreating 0 0s
zk-2 0/1 Running 0 19s
zk-2 1/1 Running 0 40s
Використайте наведену нижче команду, щоб отримати значення, яке ви ввели під час тестування на адекватність, з Podʼа zk-2
.
kubectl exec zk-2 zkCli.sh get /hello
Навіть якщо ви зупинили та знову створили всі Podʼи в StatefulSet
zk
, ансамбль все ще обслуговує початкове значення.
WATCHER::
WatchedEvent state:SyncConnected type:None path:null
world
cZxid = 0x100000002
ctime = Thu Dec 08 15:13:30 UTC 2016
mZxid = 0x100000002
mtime = Thu Dec 08 15:13:30 UTC 2016
pZxid = 0x100000002
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
Секція volumeClaimTemplates
поля spec
обʼєкта zk
StatefulSet
вказує на PersistentVolume, який створюється для кожного Podʼа.
volumeClaimTemplates:
- metadata:
name: datadir
annotations:
volume.alpha.kubernetes.io/storage-class: anything
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 20Gi
Контролер StatefulSet
генерує PersistentVolumeClaim
для кожного Podʼа у StatefulSet
.
Використайте наступну команду, щоб отримати PersistentVolumeClaims
StatefulSet
.
kubectl get pvc -l app=zk
Коли StatefulSet
повторно створює Podʼи, він повторно монтує PersistentVolumes Podʼів.
NAME STATUS VOLUME CAPACITY ACCESSMODES AGE
datadir-zk-0 Bound pvc-bed742cd-bcb1-11e6-994f-42010a800002 20Gi RWO 1h
datadir-zk-1 Bound pvc-bedd27d2-bcb1-11e6-994f-42010a800002 20Gi RWO 1h
datadir-zk-2 Bound pvc-bee0817e-bcb1-11e6-994f-42010a800002 20Gi RWO 1h
Розділ volumeMounts
контейнера template
StatefulSet
монтує PersistentVolumes в теки даних серверів ZooKeeper.
volumeMounts:
- name: datadir
mountPath: /var/lib/zookeeper
Коли Pod у zk
StatefulSet
(пере)планується, у нього завжди монтується той самий PersistentVolume
у теку даних сервера ZooKeeper. Навіть коли Podʼи переплануються, всі записи, зроблені у логах WAL серверів ZooKeeper, та всі їх знімки залишаються стійкими.
Забезпечення однорідної конфігурації
Як вказано в розділах Забезпечення виборів лідера та Досягнення консенсусу, сервери в ансамблі ZooKeeper потребують однорідної конфігурації для вибору лідера та формування кворуму. Також потрібна однорідна конфігурація протоколу Zab для коректної роботи протоколу мережею. У нашому прикладі ми досягаємо однорідної конфігурації, вбудувавши конфігурацію безпосередньо у маніфест.
Отримайте zk
StatefulSet.
kubectl get sts zk -o yaml
…
command:
- sh
- -c
- "start-zookeeper \
--servers=3 \
--data_dir=/var/lib/zookeeper/data \
--data_log_dir=/var/lib/zookeeper/data/log \
--conf_dir=/opt/zookeeper/conf \
--client_port=2181 \
--election_port=3888 \
--server_port=2888 \
--tick_time=2000 \
--init_limit=10 \
--sync_limit=5 \
--heap=512M \
--max_client_cnxns=60 \
--snap_retain_count=3 \
--purge_interval=12 \
--max_session_timeout=40000 \
--min_session_timeout=4000 \
--log_level=INFO"
…
Команда, яка використовується для запуску серверів ZooKeeper, передає конфігурацію як параметр командного рядка. Ви також можете використовувати змінні середовища для передачі конфігурації в ансамбль.
Налаштування системи логування
Один з файлів, що генерується сценарієм zkGenConfig.sh
, керує логуванням ZooKeeper. ZooKeeper використовує Log4j, і типово він використовує інструмент постійного додавання, який покладається на час та розмір для конфігурації логування.
Використайте команду нижче, щоб отримати конфігурацію логування з одного з контейнерів у StatefulSet zk
.
kubectl exec zk-0 cat /usr/etc/zookeeper/log4j.properties
Конфігурація логування нижче призведе до того, що процес ZooKeeper буде записувати всі свої логи у вихідний файловий потік стандартного виводу.
zookeeper.root.logger=CONSOLE
zookeeper.console.threshold=INFO
log4j.rootLogger=${zookeeper.root.logger}
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Threshold=${zookeeper.console.threshold}
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n
Це найпростіший спосіб безпечного логування всередині контейнера. Тому що програми записують логи у стандартний вивід, Kubernetes буде відповідальним за ротацію логів для вас. Kubernetes також реалізує розумну політику зберігання, яка гарантує, що логи застосунків, записані у потоки стандартного виводу та помилок, не виснажують локальні носії інформації.
Використайте kubectl logs
, щоб отримати останні 20 рядків логів з одного з контейнерів.
kubectl logs zk-0 --tail 20
Ви можете переглядати логи застосунків, записані у стандартний вивід або стандартну помилку, використовуючи kubectl logs
та з Kubernetes Dashboard.
2016-12-06 19:34:16,236 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52740
2016-12-06 19:34:16,237 [myid:1] - INFO [Thread-1136:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52740 (no session established for client)
2016-12-06 19:34:26,155 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52749
2016-12-06 19:34:26,155 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52749
2016-12-06 19:34:26,156 [myid:1] - INFO [Thread-1137:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52749 (no session established for client)
2016-12-06 19:34:26,222 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52750
2016-12-06 19:34:26,222 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52750
2016-12-06 19:34:26,226 [myid:1] - INFO [Thread-1138:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52750 (no session established for client)
2016-12-06 19:34:36,151 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52760
2016-12-06 19:34:36,152 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52760
2016-12-06 19:34:36,152 [myid:1] - INFO [Thread-1139:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52760 (no session established for client)
2016-12-06 19:34:36,230 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52761
2016-12-06 19:34:36,231 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52761
2016-12-06 19:34:36,231 [myid:1] - INFO [Thread-1140:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52761 (no session established for client)
2016-12-06 19:34:46,149 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52767
2016-12-06 19:34:46,149 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52767
2016-12-06 19:34:46,149 [myid:1] - INFO [Thread-1141:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52767 (no session established for client)
2016-12-06 19:34:46,230 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52768
2016-12-06 19:34:46,230 [myid:1] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing ruok command from /127.0.0.1:52768
2016-12-06 19:34:46,230 [myid:1] - INFO [Thread-1142:NIOServerCnxn@1008] - Closed socket connection for client /127.0.0.1:52768 (no session established for client)
Kubernetes інтегрується з багатьма рішеннями для логування. Ви можете вибрати рішення для логування, яке найкраще підходить для вашого кластера та застосунків. Для логування та агрегації на рівні кластера розгляньте розгортання sidecar контейнера, для ротації та надсилання ваших логів.
Налаштування не привілейованого користувача
Найкращі практики дозволити застосунку працювати як привілейований користувач всередині контейнера — це предмет для обговорення. Якщо ваша організація вимагає, щоб застосунки працювали як непривілейований користувач, ви можете використовувати SecurityContext для контролю над користувачем, під яким запускається точка входу.
У template
Podʼів StatefulSet
zk
є SecurityContext
.
securityContext:
runAsUser: 1000
fsGroup: 1000
У контейнерах Podʼів, UID 1000 відповідає користувачеві zookeeper, а GID 1000 відповідає групі zookeeper.
Отримайте інформацію про процес ZooKeeper з Поду zk-0
.
kubectl exec zk-0 -- ps -elf
Оскільки поле runAsUser
обʼєкта securityContext
встановлене на 1000, замість того, щоб запускатися як root, процес ZooKeeper запускається як користувач zookeeper.
F S UID PID PPID C PRI NI ADDR SZ WCHAN STIME TTY TIME CMD
4 S zookeep+ 1 0 0 80 0 - 1127 - 20:46 ? 00:00:00 sh -c zkGenConfig.sh && zkServer.sh start-foreground
0 S zookeep+ 27 1 0 80 0 - 1155556 - 20:46 ? 00:00:19 /usr/lib/jvm/java-8-openjdk-amd64/bin/java -Dzookeeper.log.dir=/var/log/zookeeper -Dzookeeper.root.logger=INFO,CONSOLE -cp /usr/bin/../build/classes:/usr/bin/../build/lib/*.jar:/usr/bin/../share/zookeeper/zookeeper-3.4.9.jar:/usr/bin/../share/zookeeper/slf4j-log4j12-1.6.1.jar:/usr/bin/../share/zookeeper/slf4j-api-1.6.1.jar:/usr/bin/../share/zookeeper/netty-3.10.5.Final.jar:/usr/bin/../share/zookeeper/log4j-1.2.16.jar:/usr/bin/../share/zookeeper/jline-0.9.94.jar:/usr/bin/../src/java/lib/*.jar:/usr/bin/../etc/zookeeper: -Xmx2G -Xms2G -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=false org.apache.zookeeper.server.quorum.QuorumPeerMain /usr/bin/../etc/zookeeper/zoo.cfg
Типово, коли PersistentVolumes Поду монтується в теку даних сервера ZooKeeper, він доступний тільки для користувача root. Ця конфігурація заважає процесу ZooKeeper записувати свій лог та зберігати свої знімки.
Використовуйте команду нижче, щоб отримати права доступу до файлу теки даних ZooKeeper на Поді zk-0
.
kubectl exec -ti zk-0 -- ls -ld /var/lib/zookeeper/data
Оскільки поле fsGroup
обʼєкта securityContext
встановлене на 1000, власність PersistentVolumes Podʼів встановлюється на групу zookeeper, і процес ZooKeeper може читати та записувати свої дані.
drwxr-sr-x 3 zookeeper zookeeper 4096 Dec 5 20:45 /var/lib/zookeeper/data
Управління процесом ZooKeeper
В документації ZooKeeper зазначено, що "Вам захочеться мати наглядовий процес, який керує кожним з ваших процесів сервера ZooKeeper (JVM)." Використання watchdog (наглядового процесу) для перезапуску процесів, що зазнали збою, у розподіленій системі є загальним шаблоном. При розгортанні застосунку в Kubernetes, замість використання зовнішньої утиліти як наглядового процесу, ви повинні використовувати Kubernetes як watchdog для вашого застосунку.
Оновлення ансамблю
StatefulSet
zk
налаштований на використання стратегії оновлення RollingUpdate
.
Ви можете використовувати kubectl patch
, щоб оновити кількість cpus
, виділених серверам.
kubectl patch sts zk --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/resources/requests/cpu", "value":"0.3"}]'
statefulset.apps/zk відредаговано
Використовуйте kubectl rollout status
, щоб спостерігати за статусом оновлення.
kubectl rollout status sts/zk
waiting for statefulset rolling update to complete 0 pods at revision zk-5db4499664...
Waiting for 1 pods to be ready...
Waiting for 1 pods to be ready...
waiting for statefulset rolling update to complete 1 pods at revision zk-5db4499664...
Waiting for 1 pods to be ready...
Waiting for 1 pods to be ready...
waiting for statefulset rolling update to complete 2 pods at revision zk-5db4499664...
Waiting for 1 pods to be ready...
Waiting for 1 pods to be ready...
statefulset rolling update complete 3 pods at revision zk-5db4499664...
Це припиняє роботу Podʼів, по одному, у зворотному порядку, і перестворює їх з новою конфігурацією. Це забезпечує підтримку кворуму під час постійного оновлення.
Використовуйте команду kubectl rollout history
, щоб переглянути історію або попередні конфігурації.
kubectl rollout history sts/zk
Вивід схожий на такий:
statefulsets "zk"
REVISION
1
2
Використовуйте команду kubectl rollout undo
, щоб скасувати модифікацію.
kubectl rollout undo sts/zk
Вивід схожий на такий:
statefulset.apps/zk повернувся до попереднього стану
Обробка відмови процесу
Політики перезапуску контролюють, як Kubernetes обробляє відмови процесів для вхідної точки контейнера в Pod. Для Podʼів у StatefulSet
єдине припустиме значення RestartPolicy
— це Always, і це є стандартним значенням. Для StatefulSet застосунків ви ніколи не повинні змінювати стандартні значення.
Використовуйте наступну команду, щоб переглянути дерево процесів сервера ZooKeeper, який працює в поді zk-0
.
kubectl exec zk-0 -- ps -ef
Команда, яка використовується як точка входу контейнера, має PID 1, а процес ZooKeeper, нащадок точки входу, має PID 27.
UID PID PPID C STIME TTY TIME CMD
zookeep+ 1 0 0 15:03 ? 00:00:00 sh -c zkGenConfig.sh && zkServer.sh start-foreground
zookeep+ 27 1 0 15:03 ? 00:00:03 /usr/lib/jvm/java-8-openjdk-amd64/bin/java -Dzookeeper.log.dir=/var/log/zookeeper -Dzookeeper.root.logger=INFO,CONSOLE -cp /usr/bin/../build/classes:/usr/bin/../build/lib/*.jar:/usr/bin/../share/zookeeper/zookeeper-3.4.9.jar:/usr/bin/../share/zookeeper/slf4j-log4j12-1.6.1.jar:/usr/bin/../share/zookeeper/slf4j-api-1.6.1.jar:/usr/bin/../share/zookeeper/netty-3.10.5.Final.jar:/usr/bin/../share/zookeeper/log4j-1.2.16.jar:/usr/bin/../share/zookeeper/jline-0.9.94.jar:/usr/bin/../src/java/lib/*.jar:/usr/bin/../etc/zookeeper: -Xmx2G -Xms2G -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=false org.apache.zookeeper.server.quorum.QuorumPeerMain /usr/bin/../etc/zookeeper/zoo.cfg
У іншому терміналі спостерігайте за Podʼами у StatefulSet
zk
за допомогою наступної команди.
kubectl get pod -w -l app=zk
У іншому терміналі завершіть процес ZooKeeper у поді zk-0
за допомогою наступної команди.
kubectl exec zk-0 -- pkill java
Завершення процесу ZooKeeper призвело до завершення його батьківського процесу. Оскільки RestartPolicy
контейнера — завжди, він перезапустив батьківський процес.
NAME READY STATUS RESTARTS AGE
zk-0 1/1 Running 0 21m
zk-1 1/1 Running 0 20m
zk-2 1/1 Running 0 19m
NAME READY STATUS RESTARTS AGE
zk-0 0/1 Error 0 29m
zk-0 0/1 Running 1 29m
zk-0 1/1 Running 1 29m
Якщо ваш застосунок використовує сценарій (наприклад, zkServer.sh
) для запуску процесу, який реалізує бізнес-логіку застосунку, сценарій повинен завершуватися разом з дочірнім процесом. Це забезпечує перезапуск контейнера застосунку, коли процес, що реалізує бізнес-логіку застосунку, зазнає збою.
Тестування на доступність
Налаштування вашого застосунку на перезапуск збійних процесів це ще не все, щоб забезпечити справність розподіленої системи. Існують сценарії, коли процеси системи можуть бути живими, але недоступними або інакше несправними. Вам слід використовувати проби на доступність, щоб повідомити Kubernetes про те, що процеси вашого застосунку є несправними, і його слід перезапустити.
Шаблон Pod для StatefulSet
zk
визначає пробу на доступність.
livenessProbe:
exec:
command:
- sh
- -c
- "zookeeper-ready 2181"
initialDelaySeconds: 15
timeoutSeconds: 5
Проба викликає скрипт bash, який використовує чотирилітерне слово ruok
ZooKeeper для перевірки стану справності сервера.
OK=$(echo ruok | nc 127.0.0.1 $1)
if [ "$OK" == "imok" ]; then
exit 0
else
exit 1
fi
У одному вікні термінала використовуйте наступну команду, щоб спостерігати за подами у StatefulSet
zk
.
kubectl get pod -w -l app=zk
У іншому вікні, використовуйте наступну команду, щоб видалити скрипт zookeeper-ready
з файлової системи Podʼа zk-0
.
kubectl exec zk-0 -- rm /opt/zookeeper/bin/zookeeper-ready
Коли проба на доступність для процесу ZooKeeper невдала, Kubernetes автоматично перезапустить процес за вас, забезпечуючи тим самим перезапуск несправних процесів у наборі.
kubectl get pod -w -l app=zk
NAME READY STATUS RESTARTS AGE
zk-0 1/1 Running 0 1h
zk-1 1/1 Running 0 1h
zk-2 1/1 Running 0 1h
NAME READY STATUS RESTARTS AGE
zk-0 0/1 Running 0 1h
zk-0 0/1 Running 1 1h
zk-0 1/1 Running 1 1h
Тестування готовності
Готовність не те саме, що і доступність. Якщо процес живий, він запланований і справний. Якщо процес готовий, він може обробляти вхідні дані. Доступність є необхідною, але не достатньою, умовою для готовності. Є випадки, особливо під час ініціалізації та завершення, коли процес може бути доступним, але не готовим.
Якщо ви вказуєте пробу на готовність, Kubernetes буде переконуватися, що процеси вашого застосунку не отримуватимуть мережевого трафіку до тих пір, поки їхні перевірки готовності не пройдуть.
Для сервера ZooKeeper, доступність означає готовність. Тому проба готовності з маніфесту zookeeper.yaml
ідентична пробі на доступність.
readinessProbe:
exec:
command:
- sh
- -c
- "zookeeper-ready 2181"
initialDelaySeconds: 15
timeoutSeconds: 5
Навіть якщо проби на доступність та готовність ідентичні, важливо вказати обидві. Це забезпечує, що мережевий трафік отримуватимуть лише справні сервери в ансамблі ZooKeeper.
Толерантність до відмов вузлів
Для успішного збереження змін даних ZooKeeper потрібен кворум серверів. Для ансамблю з трьох серверів, два сервери повинні бути справними, щоб записи вдалися. У системах на основі кворуму учасники розгорнені в областях відмов для забезпечення доступності. Щоб уникнути перебоїв через втрату окремої машини, найкращі практики виключають спільне розташування кількох екземплярів застосунку на одній машині.
Станадартно Kubernetes може розміщувати Podʼи в StatefulSet
на тому ж вузлі. Для створеного вами ансамблю з трьох серверів, якщо два сервери знаходяться на тому ж вузлі, і цей вузол виходить з ладу, клієнти вашої служби ZooKeeper зазнають перебою, поки хоча б один з Podʼів не буде перепланований.
Ви завжди повинні надавати додаткові можливості для перепланування процесів критичних систем у разі відмови вузла. Якщо ви це зробите, перерва буде тривати лише до тих пір, поки планувальник Kubernetes перепланує один з серверів ZooKeeper. Однак, якщо ви хочете, щоб ваша служба терпіла відмови вузлів без перебою роботи, вам слід встановити podAntiAffinity
.
Використовуйте наведену нижче команду, щоб отримати вузли для Podʼів у StatefulSet
zk
.
for i in 0 1 2; do kubectl get pod zk-$i --template={{.spec.nodeName}}; echo ""; done
Всі Podʼи у StatefulSet
zk
розгорнуті на різних вузлах.
kubernetes-node-cxpk
kubernetes-node-a5aq
kubernetes-node-2g2d
Це через те, що у Podʼах у StatefulSet
zk
вказано PodAntiAffinity
.
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: "app"
operator: In
values:
- zk
topologyKey: "kubernetes.io/hostname"
Поле requiredDuringSchedulingIgnoredDuringExecution
говорить планувальнику Kubernetes, що він ніколи не повинен розміщувати два Podʼи з міткою app
як zk
у домені, визначеному за допомогою topologyKey
. Ключ topologyKey
kubernetes.io/hostname
вказує, що домен — це окремий вузол. Використовуючи різні правила, мітки та селектори, ви можете розширити цей метод, щоб розподілити свій ансамбль на фізичні, мережеві та домени відмов.
Виживання під час обслуговування
У цьому розділі ви здійсните блокування та виведення вузлів для обслуговування. Якщо ви використовуєте цей посібник на спільному кластері, переконайтеся, що це не позначиться негативно на інших мешканцях.
Попередній розділ показав, як розподілити ваші Podʼи по вузлах, щоб вижити в разі непередбачуваних відмов вузлів, але вам також потрібно планувати тимчасові відмови вузлів, які виникають через заплановане обслуговування.
Використайте цю команду, щоб отримати вузли у вашому кластері.
kubectl get nodes
У цьому посібнику передбачається наявність кластера з щонайменше чотирма вузлами. Якщо в кластері є більше чотирьох вузлів, використовуйте kubectl cordon
, щоб заборонити доступ до всіх вузлів, окрім чотирьох. Обмеження до чотирьох вузлів гарантуватиме, що Kubernetes врахує обмеження подібності та PodDisruptionBudget при плануванні Podʼів zookeeper у наступній симуляції обслуговування.
kubectl cordon <імʼя-вузла>
Використайте цю команду, щоб отримати zk-pdb
PodDisruptionBudget
.
kubectl get pdb zk-pdb
Поле max-unavailable
показує Kubernetes, що в будь-який момент може бути недоступний найбільше один Pod з StatefulSet
zk
.
NAME MIN-AVAILABLE MAX-UNAVAILABLE ALLOWED-DISRUPTIONS AGE
zk-pdb N/A 1 1
У одному терміналі використовуйте цю команду, щоб переглядати Podʼи у StatefulSet
zk
.
kubectl get pods -w -l app=zk
У іншому терміналі використовуйте цю команду, щоб отримати вузли, на яких наразі заплановані Podʼи.
for i in 0 1 2; do kubectl get pod zk-$i --template {{.spec.nodeName}}; echo ""; done
Вихідна інформація подібна до наступної:
kubernetes-node-pb41
kubernetes-node-ixsl
kubernetes-node-i4c4
Використовуйте kubectl drain
, щоб заблокувати та вивести з використання вузол, на якому запланований Pod zk-0
.
kubectl drain $(kubectl get pod zk-0 --template {{.spec.nodeName}}) --ignore-daemonsets --force --delete-emptydir-data
Вихідна інформація подібна до наступної:
node "kubernetes-node-pb41" cordoned
WARNING: Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-node-pb41, kube-proxy-kubernetes-node-pb41; Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-o5elz
pod "zk-0" deleted
node "kubernetes-node-pb41" drained
Оскільки у вашому кластері є чотири вузла, команда kubectl drain
успішно виконується, і zk-0
перепланований на інший вузол.
NAME READY STATUS RESTARTS AGE
zk-0 1/1 Running 2 1h
zk-1 1/1 Running 0 1h
zk-2 1/1 Running 0 1h
NAME READY STATUS RESTARTS AGE
zk-0 1/1 Terminating 2 2h
zk-0 0/1 Terminating 2 2h
zk-0 0/1 Terminating 2 2h
zk-0 0/1 Terminating 2 2h
zk-0 0/1 Pending 0 0s
zk-0 0/1 Pending 0 0s
zk-0 0/1 ContainerCreating 0 0s
zk-0 0/1 Running 0 51s
zk-0 1/1 Running 0 1m
Продовжуйте слідкувати за Podʼами StatefulSet у першому терміналі і виведіть вузол, на якому запланований zk-1
.
kubectl drain $(kubectl get pod zk-1 --template {{.spec.nodeName}}) --ignore-daemonsets --force --delete-emptydir-data
Вихідна інформація подібна до наступної:
"kubernetes-node-ixsl" cordoned
WARNING: Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-node-ixsl, kube-proxy-kubernetes-node-ixsl; Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-voc74
pod "zk-1" deleted
node "kubernetes-node-ixsl" drained
Pod zk-1
не може бути перепланований, оскільки StatefulSet
zk
містить правило PodAntiAffinity
, яке запобігає спільному розташуванню Podʼів, і так як доступні тільки два вузла, Pod залишиться в стані очікування.
kubectl get pods -w -l app=zk
Вихідна інформація подібна до наступної:
NAME READY STATUS RESTARTS AGE
zk-0 1/1 Running 2 1h
zk-1 1/1 Running 0 1h
zk-2 1/1 Running 0 1h
NAME READY STATUS RESTARTS AGE
zk-0 1/1 Terminating 2 2h
zk-0 0/1 Terminating 2 2h
zk-0 0/1 Terminating 2 2h
zk-0 0/1 Terminating 2 2h
zk-0 0/1 Pending 0 0s
zk-0 0/1 Pending 0 0s
zk-0 0/1 ContainerCreating 0 0s
zk-0 0/1 Running 0 51s
zk-0 1/1 Running 0 1m
zk-1 1/1 Terminating 0 2h
zk-1 0/1 Terminating 0 2h
zk-1 0/1 Terminating 0 2h
zk-1 0/1 Terminating 0 2h
zk-1 0/1 Pending 0 0s
zk-1 0/1 Pending 0 0s
Продовжуйте слідкувати за Podʼами StatefulSet, і виведіть з використання вузол, на якому запланований zk-2
.
kubectl drain $(kubectl get pod zk-2 --template {{.spec.nodeName}}) --ignore-daemonsets --force --delete-emptydir-data
Вихідна інформація подібна до наступної:
node "kubernetes-node-i4c4" cordoned
WARNING: Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-node-i4c4, kube-proxy-kubernetes-node-i4c4; Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-dyrog
WARNING: Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-dyrog; Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-node-i4c4, kube-proxy-kubernetes-node-i4c4
There are pending pods when an error occurred: Cannot evict pod as it would violate the pod's disruption budget.
pod/zk-2
Використовуйте CTRL-C
для припинення роботи kubectl
.
Ви не можете вивести з роботи третій вузол, оскільки zk-2
буде порушувати zk-budget
. Однак, вузол залишатиметься заблокованим (cordoned).
Використовуйте zkCli.sh
для отримання значень введених впродовж перевірки адекватності zk-0
.
kubectl exec zk-0 zkCli.sh get /hello
Service все ще доступний, оскільки його PodDisruptionBudget
не порушено.
WatchedEvent state:SyncConnected type:None path:null
world
cZxid = 0x200000002
ctime = Wed Dec 07 00:08:59 UTC 2016
mZxid = 0x200000002
mtime = Wed Dec 07 00:08:59 UTC 2016
pZxid = 0x200000002
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
Використовуйте kubectl uncordon
, щоб розблокувати вузол.
kubectl uncordon kubernetes-node-pb41
Вивід буде подібний до наступного:
node "kubernetes-node-pb41" uncordoned
zk-1
переплановано на цей вузол. Зачекайте доки zk-1
не буде Running та Ready.
kubectl get pods -w -l app=zk
Вивід буде подібний до наступного:
NAME READY STATUS RESTARTS AGE
zk-0 1/1 Running 2 1h
zk-1 1/1 Running 0 1h
zk-2 1/1 Running 0 1h
NAME READY STATUS RESTARTS AGE
zk-0 1/1 Terminating 2 2h
zk-0 0/1 Terminating 2 2h
zk-0 0/1 Terminating 2 2h
zk-0 0/1 Terminating 2 2h
zk-0 0/1 Pending 0 0s
zk-0 0/1 Pending 0 0s
zk-0 0/1 ContainerCreating 0 0s
zk-0 0/1 Running 0 51s
zk-0 1/1 Running 0 1m
zk-1 1/1 Terminating 0 2h
zk-1 0/1 Terminating 0 2h
zk-1 0/1 Terminating 0 2h
zk-1 0/1 Terminating 0 2h
zk-1 0/1 Pending 0 0s
zk-1 0/1 Pending 0 0s
zk-1 0/1 Pending 0 12m
zk-1 0/1 ContainerCreating 0 12m
zk-1 0/1 Running 0 13m
zk-1 1/1 Running 0 13m
Спробуйте вивести вузол з обслуговування на якмоу запланований zk-2
.
kubectl drain $(kubectl get pod zk-2 --template {{.spec.nodeName}}) --ignore-daemonsets --force --delete-emptydir-data
Вивід буде подібний до наступного:
node "kubernetes-node-i4c4" already cordoned
WARNING: Deleting pods not managed by ReplicationController, ReplicaSet, Job, or DaemonSet: fluentd-cloud-logging-kubernetes-node-i4c4, kube-proxy-kubernetes-node-i4c4; Ignoring DaemonSet-managed pods: node-problem-detector-v0.1-dyrog
pod "heapster-v1.2.0-2604621511-wht1r" deleted
pod "zk-2" deleted
node "kubernetes-node-i4c4" drained
На цей раз kubectl drain
успішно виконується.
Розблокуйте другий вузол, щоб дозволити перепланування zk-2
.
kubectl uncordon kubernetes-node-ixsl
Вивід буде подібний до наступного:
node "kubernetes-node-ixsl" uncordoned
Ви можете використовувати kubectl drain
разом з PodDisruptionBudgets
, щоб забезпечити доступність ваших служб під час обслуговування. Якщо drain
використовується для блокування вузлів та видалення Podʼів до виключення вузла з експлуатації для обслуговування, служби, які виражають бюджет відмов, мають поважати цей бюджет . Ви завжди повинні виділяти додаткову потужність для критичних служб, щоб їх Podʼи могли негайно бути переплановані.
Очищення
- Використайте
kubectl uncordon
, щоб розблокувати всі вузли у вашому кластері. - Ви повинні видалити носії постійного носія для PersistentVolumes, використаних у цьому посібнику. Дотримуйтеся необхідних кроків, залежно від вашого середовища, конфігурації зберігання та методу надання послуг, щоб переконатися, що всі збережені дані вилучено.