1 - Запуск застосунку без збереження стану за допомогою Deployment

Ця сторінка показує, як запустити застосунок за допомогою обʼєкта Deployment в Kubernetes.

Цілі

  • Створити розгортання nginx.
  • Використання kubectl для виведення інформації про розгортання.
  • Оновити розгортання.

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

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

Версія вашого Kubernetes сервера має бути не старішою ніж v1.9.

Для перевірки версії введіть kubectl version.

Створення та дослідження розгортання nginx

Ви можете запустити застосунок, створивши обʼєкт Kubernetes Deployment, і ви можете описати Deployment у файлі YAML. Наприклад, цей файл YAML описує Deployment, який використовує образ Docker nginx:1.14.2:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2 # вказує Deployment запустити 2 Podʼи, що відповідають шаблону
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80
  1. Створіть Deployment на основі файлу YAML:

    kubectl apply -f https://k8s.io/examples/application/deployment.yaml
    
  2. Виведіть інформацію про Deployment:

    kubectl describe deployment nginx-deployment
    

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

    Name:     nginx-deployment
    Namespace:    default
    CreationTimestamp:  Tue, 30 Aug 2016 18:11:37 -0700
    Labels:     app=nginx
    Annotations:    deployment.kubernetes.io/revision=1
    Selector:   app=nginx
    Replicas:   2 desired | 2 updated | 2 total | 2 available | 0 unavailable
    StrategyType:   RollingUpdate
    MinReadySeconds:  0
    RollingUpdateStrategy:  1 max unavailable, 1 max surge
    Pod Template:
      Labels:       app=nginx
      Containers:
        nginx:
        Image:              nginx:1.14.2
        Port:               80/TCP
        Environment:        <none>
        Mounts:             <none>
      Volumes:              <none>
    Conditions:
      Type          Status  Reason
      ----          ------  ------
      Available     True    MinimumReplicasAvailable
      Progressing   True    NewReplicaSetAvailable
    OldReplicaSets:   <none>
    NewReplicaSet:    nginx-deployment-1771418926 (2/2 replicas created)
    No events.
    
  3. Перегляньте Podʼи, створені розгортанням:

    kubectl get pods -l app=nginx
    

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

    NAME                                READY     STATUS    RESTARTS   AGE
    nginx-deployment-1771418926-7o5ns   1/1       Running   0          16h
    nginx-deployment-1771418926-r18az   1/1       Running   0          16h
    
  4. Виведіть інформацію про Pod:

    kubectl describe pod <pod-name>
    

    де <pod-name> — це імʼя одного з ваших Podʼів.

Оновлення розгортання

Ви можете оновити розгортання, застосувавши новий файл YAML. Цей файл YAML вказує, що розгортання повинне бути оновлене до nginx 1.16.1.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.16.1 # Оновлення версії nginx з 1.14.2 до 1.16.1
        ports:
        - containerPort: 80
  1. Застосуйте новий файл YAML:

    kubectl apply -f https://k8s.io/examples/application/deployment-update.yaml
    
  2. Спостерігайте, як розгортання створює Podʼи з новими іменами і видаляє старі Podʼи:

    kubectl get pods -l app=nginx
    

Масштабування застосунку шляхом збільшення кількості реплік

Ви можете збільшити кількість Podʼів у вашому Deployment, застосувавши новий YAML файл. Цей файл YAML встановлює replicas на 4, що вказує, що Deployment повиннен мати чотири Podʼи:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 4 # Update the replicas from 2 to 4
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.16.1
        ports:
        - containerPort: 80
  1. Застосуйте новий файл YAML:

    kubectl apply -f https://k8s.io/examples/application/deployment-scale.yaml
    
  2. Перевірте, що Deployment має чотири Podʼи:

    kubectl get pods -l app=nginx
    

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

    NAME                               READY     STATUS    RESTARTS   AGE
    nginx-deployment-148880595-4zdqq   1/1       Running   0          25s
    nginx-deployment-148880595-6zgi1   1/1       Running   0          25s
    nginx-deployment-148880595-fxcez   1/1       Running   0          2m
    nginx-deployment-148880595-rwovn   1/1       Running   0          2m
    

Видалення Deployment

Видаліть Deployment за іменем:

kubectl delete deployment nginx-deployment

ReplicationControllers — Старий спосіб

Перевагою створення реплікованих застосунків є використання Deployment, який своєю чергою використовує ReplicaSet. До того, як в Kubernetes були додані Deployment та ReplicaSet, репліковані застосунки конфігурувалися за допомогою ReplicationController.

Що далі

2 - Запуск одноекземплярного застосунку зі збереженням стану

На цій сторінці показано, як запустити одноекземплярний застосунок зі збереженням стану (Stateful) в Kubernetes, використовуючи PersistentVolume та Deployment. Застосунок — MySQL.

Цілі

  • Створити PersistentVolume, посилаючись на диск у вашому середовищі.
  • Створити Deployment MySQL.
  • Використання MySQL для інших Podʼів в кластері за відомим DNS-імʼям.

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

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

    Для перевірки версії введіть kubectl version.

  • Вам потрібно мати або динамічний провізор PersistentVolume з типовим StorageClass, або статично надавати PersistentVolume самостійно, щоб задовольнити PersistentVolumeClaims, що використовується тут.

Розгортання MySQL

Ви можете запустити stateful застосунок, створивши Deployment Kubernetes та підʼєднавши його до наявного PersistentVolume за допомогою PersistentVolumeClaim. Наприклад, цей файл YAML описує Deployment, що запускає MySQL та посилається на PersistentVolumeClaim. Файл визначає монтування тому для /var/lib/mysql, а потім створює PersistentVolumeClaim, який шукає том 20G. Ця заявка виконується будь-яким наявним томом, який відповідає вимогам, або за допомогою динамічного провізора.

Примітка: пароль визначений в файлі конфігурації yaml, і це не є безпечним. Див. Kubernetes Secrets для безпечнішого рішення.

apiVersion: v1
kind: Service
metadata:
  name: mysql
spec:
  ports:
  - port: 3306
  selector:
    app: mysql
  clusterIP: None
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - image: mysql:9
        name: mysql
        env:
          # Використання secret у реальному використанні
        - name: MYSQL_ROOT_PASSWORD
          value: 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
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mysql-pv-volume
  labels:
    type: local
spec:
  storageClassName: manual
  capacity:
    storage: 20Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/mnt/data"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pv-claim
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
  1. Розгорніть PV та PVC з файлу YAML:

    kubectl apply -f https://k8s.io/examples/application/mysql/mysql-pv.yaml
    
  2. Розгорніть вміст файлу YAML:

    kubectl apply -f https://k8s.io/examples/application/mysql/mysql-deployment.yaml
    
  3. Показати інформацію про Deployment:

    kubectl describe deployment mysql
    

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

    Name:                 mysql
    Namespace:            default
    CreationTimestamp:    Tue, 01 Nov 2016 11:18:45 -0700
    Labels:               app=mysql
    Annotations:          deployment.kubernetes.io/revision=1
    Selector:             app=mysql
    Replicas:             1 desired | 1 updated | 1 total | 0 available | 1 unavailable
    StrategyType:         Recreate
    MinReadySeconds:      0
    Pod Template:
      Labels:       app=mysql
      Containers:
        mysql:
        Image:      mysql:9
        Port:       3306/TCP
        Environment:
          MYSQL_ROOT_PASSWORD:      password
        Mounts:
          /var/lib/mysql from mysql-persistent-storage (rw)
      Volumes:
        mysql-persistent-storage:
        Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
        ClaimName:  mysql-pv-claim
        ReadOnly:   false
    Conditions:
      Type          Status  Reason
      ----          ------  ------
      Available     False   MinimumReplicasUnavailable
      Progressing   True    ReplicaSetUpdated
    OldReplicaSets:       <none>
    NewReplicaSet:        mysql-63082529 (1/1 replicas created)
    Events:
      FirstSeen    LastSeen    Count    From                SubobjectPath    Type        Reason            Message
      ---------    --------    -----    ----                -------------    --------    ------            -------
      33s          33s         1        {deployment-controller }             Normal      ScalingReplicaSet Scaled up replica set mysql-63082529 to 1
    
  4. Перегляньте Podʼи, створені Deployment:

    kubectl get pods -l app=mysql
    

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

    NAME                   READY     STATUS    RESTARTS   AGE
    mysql-63082529-2z3ki   1/1       Running   0          3m
    
  5. Перевірка PersistentVolumeClaim:

    kubectl describe pvc mysql-pv-claim
    

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

    Name:         mysql-pv-claim
    Namespace:    default
    StorageClass:
    Status:       Bound
    Volume:       mysql-pv-volume
    Labels:       <none>
    Annotations:    pv.kubernetes.io/bind-completed=yes
                    pv.kubernetes.io/bound-by-controller=yes
    Capacity:     20Gi
    Access Modes: RWO
    Events:       <none>
    

Доступ до екземпляра MySQL

Попередній YAML-файл створює Service, який дозволяє іншим Podʼам в кластері отримувати доступ до бази даних. Опція Service clusterIP: None дозволяє імені DNS Serviceʼа безпосередньо посилатись на IP-адресу Podʼа. Це оптимально, коли у вас є лише один Pod за Service і ви не маєте наміру збільшувати кількість Podʼів.

Запустіть клієнта MySQL, щоб підʼєднатися до сервера:

kubectl run -it --rm --image=mysql:9 --restart=Never mysql-client -- mysql -h mysql -ppassword

Ця команда створює новий Pod у кластері, що виконує клієнта MySQL та підʼєднується до сервера через Service. Якщо підʼєднання вдалося, ви знаєте, що ваша stateful база даних MySQL працює.

Waiting for pod default/mysql-client-274442439-zyp6i to be running, status is Pending, pod ready: false
If you don't see a command prompt, try pressing enter.

mysql>

Оновлення

Образ або будь-яку іншу частину Deployment можна оновити як зазвичай за допомогою команди kubectl apply. Ось деякі заходи обережності, які специфічні для застосунків зі збереженням стану:

  • Не масштабуйте застосунок. Цей варіант призначений лише для застосунків з одним екземпляром. Основний PersistentVolume можна примонтувати лише до одного Podʼа. Для кластеризованих застосунків зі збереженням стану див. документацію StatefulSet.
  • Використовуйте strategy: type: Recreate в файлі конфігурації YAML Deployment. Це вказує Kubernetes не використовувати постійні (rolling) оновлення. Такі оновлення не працюватимуть, оскільки ви не можете мати більше одного Podʼа, що працює одночасно. Стратегія Recreate зупинить перший Pod перед створенням нового з оновленою конфігурацією.

Видалення Deployment

Видаліть розгорнуті обʼєкти за іменем:

kubectl delete deployment,svc mysql
kubectl delete pvc mysql-pv-claim
kubectl delete pv mysql-pv-volume

Якщо ви вручну вказували PersistentVolume, вам також потрібно вручну видалити його, а також звільнити основний ресурс. Якщо ви використовували динамічного провізора, він автоматично видаляє PersistentVolume, коли бачить, що ви видалили PersistentVolumeClaim. Деякі динамічні провізори (наприклад, ті, що стосуються EBS та PD) також звільняють основний ресурс після видалення PersistentVolume.

Що далі

3 - Запуск реплікованого застосунку зі збереженням стану

Ця сторінка показує, як запустити реплікований застосунок зі збереженням стану (StatefulSet). Цей застосунок — реплікована база даних MySQL. У топології цього прикладу є один головний сервер і кілька реплік, що використовують асинхронну реплікацію на основі рядків.

Примітка:

Ця конфігурація не для операційної експлуатації. Налаштування MySQL залишаються на небезпечних стандартних значеннях, щоб зосередитися на загальних патернах запуску застосунків зі збереженням стану в Kubernetes.

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

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

  • Вам потрібно мати або динамічний провізор PersistentVolume з типовим StorageClass, або статично надавати PersistentVolume самостійно, щоб задовольнити PersistentVolumeClaims, що використовується тут.

  • Це завдання передбачає, що ви знаєте про Постійні томи та StatefulSets, а також інші основні поняття, такі як Pod, Service та ConfigMap.
  • Трохи знань MySQL корисні, але цей посібник має на меті представити загальні патерни, які можуть бути корисними для інших систем.
  • Ви використовуєте простір імен default або інший простір імен, в якому відсутні обʼєкти, що конфліктують.
  • У вас має бути AMD64-сумісний процесор.

Цілі

  • Розгорнути репліковану топологію MySQL за допомогою StatefulSet.
  • Направити трафік від клієнта MySQL.
  • Спостерігати стійкість до перерв у роботі.
  • Масштабувати StatefulSet вгору та вниз.

Розгортання MySQL

Приклад розгортання складається з ConfigMap, двох Service та StatefulSet.

Створення ConfigMap

Створіть ConfigMap із наступного файлу конфігурації YAML:

apiVersion: v1
kind: ConfigMap
metadata:
  name: mysql
  labels:
    app: mysql
    app.kubernetes.io/name: mysql
data:
  primary.cnf: |
    # Застосовує цю конфігурацію до primary.
    [mysqld]
    log-bin
  replica.cnf: |
    # Застосовує цю конфігурацію до реплік.
    [mysqld]
    super-read-only

kubectl apply -f https://k8s.io/examples/application/mysql/mysql-configmap.yaml

Цей ConfigMap надає перевизначення для my.cnf, що дозволяє вам незалежно керувати конфігурацією на головному сервері MySQL та його репліках. У цьому випадку ви хочете, щоб головний сервер міг обслуговувати логи реплікацій реплік, а репліки відхиляли будь-які записи, які надходять не через реплікацію.

Немає нічого особливого у самому ConfigMap, що зумовлює застосування різних частин до різних Podʼів. Кожен Pod вирішує, яку частину дивитися при ініціалізації, на основі інформації, що надає контролер StatefulSet.

Створення Service

Створіть Service із наступного файлу конфігурації YAML:

# Service headless для стабільних записів DNS членів StatefulSet.
apiVersion: v1
kind: Service
metadata:
  name: mysql
  labels:
    app: mysql
    app.kubernetes.io/name: mysql
spec:
  ports:
  - name: mysql
    port: 3306
  clusterIP: None
  selector:
    app: mysql
---
# Service клієнт для підʼєднання до екземпляру MySQL для читання.
# Для запису ви повинні підключитися до основного: mysql-0.mysql.
apiVersion: v1
kind: Service
metadata:
  name: mysql-read
  labels:
    app: mysql
    app.kubernetes.io/name: mysql
    readonly: "true"
spec:
  ports:
  - name: mysql
    port: 3306
  selector:
    app: mysql
kubectl apply -f https://k8s.io/examples/application/mysql/mysql-services.yaml

Service headless надає місце для DNS-записів, які контролери StatefulSet створюють для кожного Podʼа, що є частиною набору. Оскільки headless Service називається mysql, Podʼи доступні за допомогою зіставлення <pod-name>.mysql з будь-якого іншого Podʼа в тому ж Kubernetes кластері та просторі імен.

Клієнтський Service, з назвою mysql-read, є звичайним Service з власним кластерним IP, який розподіляє підключення між всіма Podʼами MySQL, які повідомляють про готовність. Набір потенційних точок доступу включає головний сервер MySQL та всі репліки.

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

Створення StatefulSet

Наостанок, створіть StatefulSet із наступного файлу конфігурації YAML:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
      app.kubernetes.io/name: mysql
  serviceName: mysql
  replicas: 3
  template:
    metadata:
      labels:
        app: mysql
        app.kubernetes.io/name: mysql
    spec:
      initContainers:
      - name: init-mysql
        image: mysql:5.7
        command:
        - bash
        - "-c"
        - |
          set -ex
          # Generate mysql server-id from pod ordinal index.
          [[ $HOSTNAME =~ -([0-9]+)$ ]] || exit 1
          ordinal=${BASH_REMATCH[1]}
          echo [mysqld] > /mnt/conf.d/server-id.cnf
          # Add an offset to avoid reserved server-id=0 value.
          echo server-id=$((100 + $ordinal)) >> /mnt/conf.d/server-id.cnf
          # Copy appropriate conf.d files from config-map to emptyDir.
          if [[ $ordinal -eq 0 ]]; then
            cp /mnt/config-map/primary.cnf /mnt/conf.d/
          else
            cp /mnt/config-map/replica.cnf /mnt/conf.d/
          fi
        volumeMounts:
        - name: conf
          mountPath: /mnt/conf.d
        - name: config-map
          mountPath: /mnt/config-map
      - name: clone-mysql
        image: gcr.io/google-samples/xtrabackup:1.0
        command:
        - bash
        - "-c"
        - |
          set -ex
          # Skip the clone if data already exists.
          [[ -d /var/lib/mysql/mysql ]] && exit 0
          # Skip the clone on primary (ordinal index 0).
          [[ `hostname` =~ -([0-9]+)$ ]] || exit 1
          ordinal=${BASH_REMATCH[1]}
          [[ $ordinal -eq 0 ]] && exit 0
          # Clone data from previous peer.
          ncat --recv-only mysql-$(($ordinal-1)).mysql 3307 | xbstream -x -C /var/lib/mysql
          # Prepare the backup.
          xtrabackup --prepare --target-dir=/var/lib/mysql
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
          subPath: mysql
        - name: conf
          mountPath: /etc/mysql/conf.d
      containers:
      - name: mysql
        image: mysql:5.7
        env:
        - name: MYSQL_ALLOW_EMPTY_PASSWORD
          value: "1"
        ports:
        - name: mysql
          containerPort: 3306
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
          subPath: mysql
        - name: conf
          mountPath: /etc/mysql/conf.d
        resources:
          requests:
            cpu: 500m
            memory: 1Gi
        livenessProbe:
          exec:
            command: ["mysqladmin", "ping"]
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 5
        readinessProbe:
          exec:
            # Перевіряє, чи можемо ми виконувати запити через TCP (skip-networking вимкнено).
            command: ["mysql", "-h", "127.0.0.1", "-e", "SELECT 1"]
          initialDelaySeconds: 5
          periodSeconds: 2
          timeoutSeconds: 1
      - name: xtrabackup
        image: gcr.io/google-samples/xtrabackup:1.0
        ports:
        - name: xtrabackup
          containerPort: 3307
        command:
        - bash
        - "-c"
        - |
          set -ex
          cd /var/lib/mysql

          # Determine binlog position of cloned data, if any.
          if [[ -f xtrabackup_slave_info && "x$(<xtrabackup_slave_info)" != "x" ]]; then
            # XtraBackup already generated a partial "CHANGE MASTER TO" query
            # because we're cloning from an existing replica. (Need to remove the tailing semicolon!)
            cat xtrabackup_slave_info | sed -E 's/;$//g' > change_master_to.sql.in
            # Ignore xtrabackup_binlog_info in this case (it's useless).
            rm -f xtrabackup_slave_info xtrabackup_binlog_info
          elif [[ -f xtrabackup_binlog_info ]]; then
            # We're cloning directly from primary. Parse binlog position.
            [[ `cat xtrabackup_binlog_info` =~ ^(.*?)[[:space:]]+(.*?)$ ]] || exit 1
            rm -f xtrabackup_binlog_info xtrabackup_slave_info
            echo "CHANGE MASTER TO MASTER_LOG_FILE='${BASH_REMATCH[1]}',\
                  MASTER_LOG_POS=${BASH_REMATCH[2]}" > change_master_to.sql.in
          fi

          # Check if we need to complete a clone by starting replication.
          if [[ -f change_master_to.sql.in ]]; then
            echo "Waiting for mysqld to be ready (accepting connections)"
            until mysql -h 127.0.0.1 -e "SELECT 1"; do sleep 1; done

            echo "Initializing replication from clone position"
            mysql -h 127.0.0.1 \
                  -e "$(<change_master_to.sql.in), \
                          MASTER_HOST='mysql-0.mysql', \
                          MASTER_USER='root', \
                          MASTER_PASSWORD='', \
                          MASTER_CONNECT_RETRY=10; \
                        START SLAVE;" || exit 1
            # In case of container restart, attempt this at-most-once.
            mv change_master_to.sql.in change_master_to.sql.orig
          fi

          # Start a server to send backups when requested by peers.
          exec ncat --listen --keep-open --send-only --max-conns=1 3307 -c \
            "xtrabackup --backup --slave-info --stream=xbstream --host=127.0.0.1 --user=root"
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
          subPath: mysql
        - name: conf
          mountPath: /etc/mysql/conf.d
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
      volumes:
      - name: conf
        emptyDir: {}
      - name: config-map
        configMap:
          name: mysql
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 10Gi
kubectl apply -f https://k8s.io/examples/application/mysql/mysql-statefulset.yaml

Ви можете спостерігати за процесом запуску, виконавши:

kubectl get pods -l app=mysql --watch

Через деякий час ви повинні побачити, що всі 3 Podʼи стануть Running:

NAME      READY     STATUS    RESTARTS   AGE
mysql-0   2/2       Running   0          2m
mysql-1   2/2       Running   0          1m
mysql-2   2/2       Running   0          1m

Натисніть Ctrl+C, щоб скасувати перегляд.

Примітка:

Якщо ви не бачите жодних змін, переконайтеся, що у вас увімкнений динамічний PersistentVolume провізор, як зазначено у передумовах.

Цей маніфест використовує різноманітні техніки для керування Podʼами як частиною StatefulSet. У наступному розділі підкреслено деякі з цих технік, щоб пояснити, що відбувається під час створення Podʼів StatefulSet.

Розуміння ініціалізації Podʼа зі збереженням стану

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

Крім того, контролер призначає кожному Podʼу унікальне, стабільне імʼя у формі <statefulset-name>-<ordinal-index>, в результаті отримуємо Podʼи з іменами mysql-0, mysql-1 та mysql-2.

Шаблон Podʼа у вищенаведеному маніфесті StatefulSet використовує ці властивості, щоб здійснити впорядкований запуск реплікації MySQL.

Створення конфігурації

Перед запуском будь-яких контейнерів у специфікації Podʼа, спочатку Pod запускає будь-які контейнери ініціалізації у визначеному порядку.

Перший контейнер ініціалізації, init-mysql, генерує спеціальні конфігураційні файли MySQL на основі порядкового індексу.

Скрипт визначає свій власний порядковий індекс, витягуючи його з кінця імені Podʼа, яке повертається командою hostname. Потім він зберігає порядковий індекс (з числовим зміщенням для уникнення зарезервованих значень) у файлі з назвою server-id.cnf в теці conf.d MySQL. Це перетворює унікальний, стабільний ідентифікатор, наданий StatefulSet, у домен ідентифікаторів серверів MySQL, які вимагають таких же властивостей.

Скрипт у контейнері init-mysql також застосовує або primary.cnf, або replica.cnf з ConfigMap, копіюючи вміст у conf.d. Оскільки у топології цього прикладу є лише один головний сервер MySQL та будь-яка кількість реплік, скрипт призначає порядковий індекс 0 головному серверу, а всі інші — репліками. Разом з гарантією контролера StatefulSet щодо порядку розгортання, це забезпечує готовність головного сервера MySQL перед створенням реплік, щоб вони могли почати реплікацію.

Клонування наявних даних

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

Другий контейнер ініціалізації, з назвою clone-mysql, виконує операцію клонування на реплікаційному Podʼі першого разу, коли він запускається на порожньому PersistentVolume. Це означає, що він копіює всі наявні дані з іншого працюючого Podʼа, таким чином, його локальний стан є достатньо консистентним для початку реплікації з головного сервера.

MySQL сам по собі не надає механізму для цього, тому приклад використовує популярний відкритий інструмент — Percona XtraBackup. Під час клонування MySQL сервер може мати знижену продуктивність. Щоб мінімізувати вплив на головний сервер MySQL, скрипт вказує кожному Podʼу отримувати дані з Podʼа, чий порядковий індекс на одиницю менший. Це працює через те, що контролер StatefulSet завжди гарантує, що Pod N є готовим перед запуском Podʼа N+1.

Початок реплікації

Після успішного завершення контейнерів ініціалізації запускаються звичайні контейнери. Podʼи MySQL складаються з контейнера mysql, в якому працює фактичний сервер mysqld, та контейнера xtrabackup, який діє як sidecar.

Sidecar xtrabackup переглядає клоновані файли даних і визначає, чи необхідно ініціалізувати реплікацію MySQL на репліці. У цьому випадку він чекає, доки mysqld буде готовий, а потім виконує команди CHANGE MASTER TO та START SLAVE з параметрами реплікації, отриманими з клонованих файлів XtraBackup.

Після того як репліка починає реплікацію, вона запамʼятовує свій головний сервер MySQL і автоматично підʼєднується, якщо сервер перезавантажується або зʼєднання втрачається. Також, оскільки репліки шукають головний сервер за його стабільним DNS-імʼям (mysql-0.mysql), вони автоматично знаходять головний сервер навіть якщо він отримує новий IP Podʼа через перепланування.

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

Надсилання клієнтського трафіку

Ви можете надіслати тестові запити до головного сервера MySQL (хост mysql-0.mysql) запустивши тимчасовий контейнер з образом mysql:5.7 і використовуючи бінарний файл клієнта mysql.

kubectl run mysql-client --image=mysql:5.7 -i --rm --restart=Never --\
  mysql -h mysql-0.mysql <<EOF
CREATE DATABASE test;
CREATE TABLE test.messages (message VARCHAR(250));
INSERT INTO test.messages VALUES ('hello');
EOF

Використовуйте хост mysql-read, щоб надіслати тестові запити до будь-якого сервера, який повідомляє про готовність:

kubectl run mysql-client --image=mysql:5.7 -i -t --rm --restart=Never --\
  mysql -h mysql-read -e "SELECT * FROM test.messages"

Ви повинні отримати вивід схожий на цей:

Waiting for pod default/mysql-client to be running, status is Pending, pod ready: false
+---------+
| message |
+---------+
| hello   |
+---------+
pod "mysql-client" deleted

Щоб продемонструвати, що Service mysql-read розподіляє підключення між серверами, ви можете запустити SELECT @@server_id у циклі:

kubectl run mysql-client-loop --image=mysql:5.7 -i -t --rm --restart=Never --\
  bash -ic "while sleep 1; do mysql -h mysql-read -e 'SELECT @@server_id,NOW()'; done"

Ви повинні бачити, що @@server_id змінюється випадковим чином, оскільки може бути вибрана інша точка доступу при кожній спробі підключення:

+-------------+---------------------+
| @@server_id | NOW()               |
+-------------+---------------------+
|         100 | 2006-01-02 15:04:05 |
+-------------+---------------------+
+-------------+---------------------+
| @@server_id | NOW()               |
+-------------+---------------------+
|         102 | 2006-01-02 15:04:06 |
+-------------+---------------------+
+-------------+---------------------+
| @@server_id | NOW()               |
+-------------+---------------------+
|         101 | 2006-01-02 15:04:07 |
+-------------+---------------------+

Ви можете натиснути Ctrl+C, коли захочете зупинити цикл, але цікаво тримати його запущеним в іншому вікні, щоб бачити ефект від наступних кроків.

Симуляція відмови Podʼа та Вузла

Щоб продемонструвати підвищену доступність читання з пулу реплік замість одного сервера, залиште цикл SELECT @@server_id, запущений вище, активним, тоді як ви змушуєте Pod вийти зі стану Ready.

Збій проби готовності

Проба готовності для контейнера mysql виконує команду mysql -h 127.0.0.1 -e 'SELECT 1', щоб переконатися, що сервер запущений і може виконувати запити.

Одним зі способів змусити цю пробу готовності збоїти — це пошкодити цю команду:

kubectl exec mysql-2 -c mysql -- mv /usr/bin/mysql /usr/bin/mysql.off

Ця дія втручається у файлову систему контейнера Podʼа mysql-2 і перейменовує команду mysql, щоб проба готовності не могла її знайти. Через кілька секунд Pod повинен повідомити про один зі своїх контейнерів як неготовий, що можна перевірити за допомогою:

kubectl get pod mysql-2

Перевірте значення 1/2 у стовпці READY:

NAME      READY     STATUS    RESTARTS   AGE
mysql-2   1/2       Running   0          3m

На цьому етапі ви повинні бачити продовження роботи вашого циклу SELECT @@server_id, хоча він більше не повідомляє про 102. Нагадаємо, що скрипт init-mysql визначив server-id як 100 + $ordinal, тож ідентифікатор сервера 102 відповідає Поду mysql-2.

Тепер відновіть Pod і він повинен знову зʼявитися у виводі циклу через кілька секунд:

kubectl exec mysql-2 -c mysql -- mv /usr/bin/mysql.off /usr/bin/mysql

Видалення Podʼів

Контролер StatefulSet також створює Podʼи знову, якщо вони видалені, подібно до того, як це робить ReplicaSet для Podʼів без збереження стану.

kubectl delete pod mysql-2

Контролер StatefulSet помічає, що Podʼа mysql-2 більше не існує, і створює новий з тією ж назвою та повʼязаний з тим самим PersistentVolumeClaim. Ви повинні побачити, що ідентифікатор сервера 102 зникне з виводу циклу протягом певного часу і потім повернеться самостійно.

Виведення вузла з експлуатації

Якщо у вашому кластері Kubernetes є кілька вузлів, ви можете симулювати відмову вузла(наприклад, під час оновлення вузлів) за допомогою команди drain.

Спочатку визначте, на якому вузлі знаходиться один із Podʼів MySQL:

kubectl get pod mysql-2 -o wide

Назва вузла повинна зʼявитися у останньому стовпчику:

NAME      READY     STATUS    RESTARTS   AGE       IP            NODE
mysql-2   2/2       Running   0          15m       10.244.5.27   kubernetes-node-9l2t

Потім виведіть вузол з експлуатації, виконавши наступну команду, яка забороняє новим Podʼам запускатися на цьому вузлі та видаляє будь-які існуючі Podʼи. Замість <node-name> підставте назву вузла, яку ви знайшли на попередньому кроці.

Увага:

Виведення вузла з експлуатації може вплинути на інші завдання та застосунки, що працюють на тому ж вузлі. Виконуйте наступний крок тільки на тестовому кластері.
# Дивіться вище поради щодо впливу на інші завдання
kubectl drain <node-name> --force --delete-emptydir-data --ignore-daemonsets

Тепер ви можете спостерігати, як Pod переплановується на іншому вузлі:

kubectl get pod mysql-2 -o wide --watch

Це має виглядати приблизно так:

NAME      READY   STATUS          RESTARTS   AGE       IP            NODE
mysql-2   2/2     Terminating     0          15m       10.244.1.56   kubernetes-node-9l2t
[...]
mysql-2   0/2     Pending         0          0s        <none>        kubernetes-node-fjlm
mysql-2   0/2     Init:0/2        0          0s        <none>        kubernetes-node-fjlm
mysql-2   0/2     Init:1/2        0          20s       10.244.5.32   kubernetes-node-fjlm
mysql-2   0/2     PodInitializing 0          21s       10.244.5.32   kubernetes-node-fjlm
mysql-2   1/2     Running         0          22s       10.244.5.32   kubernetes-node-fjlm
mysql-2   2/2     Running         0          30s       10.244.5.32   kubernetes-node-fjlm

І знову, ви повинні побачити, що ідентифікатор сервера 102 зник з виводу циклу SELECT @@server_id протягом певного часу, а потім повернувся.

Тепер знову дозвольте вузлу приймати навантаження:

kubectl uncordon <node-name>

Масштабування кількості реплік

Коли ви використовуєте реплікацію MySQL, ви можете збільшувати кількість запитів на читання, додаючи репліки. Для StatefulSet це можна зробити однією командою:

kubectl scale statefulset mysql --replicas=5

Спостерігайте, як нові Podʼи запускаються, виконавши:

kubectl get pods -l app=mysql --watch

Коли вони будуть готові, ви побачите, що ідентифікатори серверів 103 та 104 починають зʼявлятися у виводі циклу SELECT @@server_id.

Ви також можете перевірити, що ці нові сервери мають дані, які ви додали до них до того, як вони існували:

kubectl run mysql-client --image=mysql:5.7 -i -t --rm --restart=Never --\
  mysql -h mysql-3.mysql -e "SELECT * FROM test.messages"
Waiting for pod default/mysql-client to be running, status is Pending, pod ready: false
+---------+
| message |
+---------+
| hello   |
+---------+
pod "mysql-client" deleted

Зменшити кількість реплік також можна однією командою:

kubectl scale statefulset mysql --replicas=3

Примітка:

Хоча збільшення створює нові PersistentVolumeClaims автоматично, зменшення автоматично не видаляє ці PVC.

Це дає вам можливість залишити ці ініціалізовані PVC для швидшого збільшення, або вилучити дані перед їх видаленням.

Ви можете перевірити це, виконавши:

kubectl get pvc -l app=mysql

Це покаже, що всі 5 PVC все ще існують, попри те, що StatefulSet був зменшений до 3:

NAME           STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE
data-mysql-0   Bound     pvc-8acbf5dc-b103-11e6-93fa-42010a800002   10Gi       RWO           20m
data-mysql-1   Bound     pvc-8ad39820-b103-11e6-93fa-42010a800002   10Gi       RWO           20m
data-mysql-2   Bound     pvc-8ad69a6d-b103-11e6-93fa-42010a800002   10Gi       RWO           20m
data-mysql-3   Bound     pvc-50043c45-b1c5-11e6-93fa-42010a800002   10Gi       RWO           2m
data-mysql-4   Bound     pvc-500a9957-b1c5-11e6-93fa-42010a800002   10Gi       RWO           2m

Якщо ви не збираєтеся повторно використовувати додаткові PVC, ви можете їх видалити:

kubectl delete pvc data-mysql-3
kubectl delete pvc data-mysql-4

Очищення

  1. Припинить цикл SELECT @@server_id, натиснувши Ctrl+C в його терміналі або виконавши наступне з іншого терміналу:

    kubectl delete pod mysql-client-loop --now
    
  2. Видаліть StatefulSet. Це також розпочне завершення Podʼів.

    kubectl delete statefulset mysql
    
  3. Перевірте, що Podʼи зникають. Вони можуть зайняти деякий час для завершення роботи.

    kubectl get pods -l app=mysql
    

    Ви будете знати, що Podʼи завершилися, коли вищезазначена команда поверне:

    No resources found.
    
  4. Видаліть ConfigMap, Services та PersistentVolumeClaims.

    kubectl delete configmap,service,pvc -l app=mysql
    
  5. Якщо ви вручну створювали PersistentVolumes, вам також потрібно вручну видалити їх, а також звільнити відповідні ресурси. Якщо ви використовували динамічний провізор, він автоматично видаляє PersistentVolumes, коли бачить, що ви видалили PersistentVolumeClaims. Деякі динамічні провізори (такі як ті, що стосуються EBS та PD) також звільняють відповідні ресурси при видаленні PersistentVolumes.

Що далі

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

Це завдання показує, як масштабувати StatefulSet. Масштабування StatefulSet означає збільшення або зменшення кількості реплік.

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

  • StatefulSets доступні тільки в версії Kubernetes 1.5 або пізніше. Щоб перевірити вашу версію Kubernetes, виконайте kubectl version.

  • Не всі застосунки зі збереженням стану гарно масштабуються. Якщо ви не впевнені, чи масштабувати ваші StatefulSets, див. Концепції StatefulSet або Посібник StatefulSet для отримання додаткової інформації.

  • Ви повинні виконувати масштабування лише тоді, коли ви впевнені, що ваш кластер застосунку зі збереженням стану є повністю справним.

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

Використання kubectl для масштабування StatefulSets

Спочатку знайдіть StatefulSet, який ви хочете масштабувати.

kubectl get statefulsets <назва-stateful-set>

Змініть кількість реплік вашого StatefulSet:

kubectl scale statefulsets <назва-stateful-set> --replicas=<нові-репліки>

Виконання оновлень на місці для вашого StatefulSets

Альтернативно, ви можете виконати оновлення на місці для ваших StatefulSets.

Якщо ваш StatefulSet спочатку був створений за допомогою kubectl apply, оновіть .spec.replicas маніфестів StatefulSet, а потім виконайте kubectl apply:

kubectl apply -f <stateful-set-file-updated>

Інакше, відредагуйте це поле за допомогою kubectl edit:

kubectl edit statefulsets <stateful-set-name>

Або використовуйте kubectl patch:

kubectl patch statefulsets <назва-stateful-set> -p '{"spec":{"replicas":<нові-репліки>}}'

Усунення несправностей

Масштабування вниз не працює правильно

Ви не можете зменшити масштаб StatefulSet, коли будь-який з Stateful Podʼів, яким він керує, є нездоровим. Масштабування відбувається лише після того, як ці Stateful Podʼи стають запущеними та готовими.

Якщо spec.replicas > 1, Kubernetes не може визначити причину несправності Podʼа. Це може бути наслідком постійного збою або тимчасового збою. Тимчасовий збій може бути викликаний перезапуском, необхідним для оновлення або обслуговування.

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

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

Що далі

5 - Видалення StatefulSet

Це завдання показує, як видалити StatefulSet.

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

  • Це завдання передбачає, що у вас є застосунок, який працює на вашому кластері та представлений StatefulSet.

Видалення StatefulSet

Ви можете видалити StatefulSet так само як і інші ресурси в Kubernetes: використовуйте команду kubectl delete та вкажіть StatefulSet або файлом, або імʼям.

kubectl delete -f <file.yaml>
kubectl delete statefulsets <statefulset-name>

Після видалення StatefulSet може знадобитися окремо видалити повʼязаний headless service.

kubectl delete service <імʼя-сервісу>

Під час видалення StatefulSet через kubectl, StatefulSet масштабується до 0. Всі Podʼи, які є частиною цього робочого навантаження, також видаляються. Якщо ви хочете видалити лише StatefulSet і не Podʼи, використовуйте --cascade=orphan. Наприклад:

kubectl delete -f <файл.yaml> --cascade=orphan

Передачею --cascade=orphan до kubectl delete Podʼи, що керуються StatefulSet залишаються після того, як обʼєкт StatefulSet сам по собі буде видалений. Якщо Podʼи мають мітку app.kubernetes.io/name=MyApp, ви можете видалити їх наступним чином:

kubectl delete pods -l app.kubernetes.io/name=MyApp

Постійні томи

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

Примітка:

Будьте обережні при видаленні PVC, оскільки це може призвести до втрати даних.

Повне видалення StatefulSet

Щоб видалити все у StatefulSet, включаючи повʼязані Podʼи, ви можете виконати серію команд, схожих на наступні:

grace=$(kubectl get pods <под-stateful-set> --template '{{.spec.terminationGracePeriodSeconds}}')
kubectl delete statefulset -l app.kubernetes.io/name=MyApp
sleep $grace
kubectl delete pvc -l app.kubernetes.io/name=MyApp

У вище наведеному прикладі Podʼи мають мітку app.kubernetes.io/name=MyApp; підставте вашу власну мітку за потреби.

Примусове видалення Podʼів StatefulSet

Якщо ви помітите, що деякі Podʼи у вашому StatefulSet застрягли у стані 'Terminating' або 'Unknown' протягом тривалого періоду часу, вам може знадобитися втрутитися вручну, щоб примусово видалити Podʼи з apiserverʼа. Це потенційно небезпечне завдання. Див. Примусове видалення Podʼів StatefulSet для отримання детальної інформації.

Що далі

Дізнайтеся більше про примусове видалення Podʼів StatefulSet.

6 - Примусове видалення Podʼів StatefulSet

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

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

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

Міркування про StatefulSet

При нормальному функціонуванні StatefulSet ніколи немає потреби у примусовому видаленні Podʼів StatefulSet. Контролер StatefulSet відповідає за створення, масштабування та видалення членів StatefulSet. Він намагається забезпечити, щоб зазначена кількість Podʼів від ordinal 0 до N-1 були справними та готовими. StatefulSet забезпечує те, що в будь-який момент часу в кластері працює не більше одного Podʼа з заданою ідентичністю. Це називається семантикою як максимум один, яку забезпечує StatefulSet.

Примусове видалення слід виконувати з обережністю, оскільки воно може порушити семантику "як максимум один", притаманну StatefulSet. StatefulSet можуть використовуватися для запуску розподілених і кластерних застосунків, які потребують стабільної мережевої ідентичності та стабільного сховища. Ці застосунки часто мають конфігурацію, яка ґрунтується на ансамблі фіксованої кількості членів з фіксованими ідентичностями. Наявність декількох членів із тією самою ідентичністю може бути руйнівною і може призвести до втрати даних (наприклад, у випадку "розщеплення мозку" в системах на основі кворуму).

Видалення Podʼів

Ви можете виконати коректне видалення Podʼа за допомогою наступної команди:

kubectl delete pods <pod>

Щоб таке видалення призвело до коректного завершення, необхідно вказати pod.Spec.TerminationGracePeriodSeconds більше 0. Практика встановлення pod.Spec.TerminationGracePeriodSeconds на 0 секунд є небезпечною та категорично не рекомендується для Podʼів StatefulSet. Коректне видалення є безпечним і забезпечить, що Pod коректно завершить роботу перед тим, як kubelet видалить імʼя з apiserver.

Pod не видаляється автоматично, коли вузол недоступний. Podʼи, які працюють на недоступному вузлі, потрапляють у стан 'Terminating' або 'Unknown' після тайм-ауту. Podʼи також можуть потрапляти в ці стани, коли користувач спробує коректне видалення Podʼа на недоступному вузлі. Єдині способи, якими Pod у такому стані може бути видалено з apiserver, наступні:

  • Обʼєкт Node видаляється (або вами, або Контролером Вузлів).
  • kubelet на недоступному Вузлі починає відповідати, припиняє роботу Pod та видаляє запис з apiserver.
  • Примусове видалення Podʼа користувачем.

Рекомендована практика — використовувати перший або другий підхід. Якщо вузол підтверджено є несправним (наприклад, постійно відключений від мережі, вимкнений тощо), то видаліть обʼєкт Node. Якщо вузол страждає від розділення мережі, то спробуйте вирішити це або зачекайте на його вирішення. Коли розділення мережі виправляється, kubelet завершить видалення Podʼа та звільнить його імʼя в apiserver.

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

Примусове видалення

Примусові видалення не чекають підтвердження від kubelet про те, що Pod було завершено. Незалежно від того, чи успішно примусове завершення Podʼа припиняє його роботу чи ні, воно негайно звільняє імʼя з apiserverʼа. Це дозволить контролеру StatefulSet створити новий Pod з тією самою ідентичністю; це може призвести до дублювання все ще працюючого Podʼа, і якщо цей Pod все ще може спілкуватися з іншими членами StatefulSet, це порушить семантику "як максимум один", яку StatefulSet має гарантувати.

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

Якщо ви хочете примусово видалити Pod за допомогою kubectl версії >= 1.5, виконайте наступне:

kubectl delete pods <pod> --grace-period=0 --force

Якщо ви використовуєте будь-яку версію kubectl <= 1.4, ви повинні пропустити параметр --force і використовувати:

kubectl delete pods <pod> --grace-period=0

Якщо навіть після цих команд Pod застряг у стані Unknown, використайте наступну команду для видалення Podʼа з кластера:

kubectl patch pod <pod> -p '{"metadata":{"finalizers":null}}'

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

Що далі

Дізнайтеся більше про налагодження StatefulSet.

7 - Покрокове керівництво HorizontalPodAutoscaler

HorizontalPodAutoscaler (HPA) автоматично оновлює ресурс робочого навантаження (наприклад, Deployment або StatefulSet), з метою автоматичного масштабування робочого навантаження, щоб відповідати попиту.

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

Якщо навантаження зменшується, а кількість Podʼів перевищує налаштований мінімум, HorizontalPodAutoscaler інструктує ресурс робочого навантаження (Deployment, StatefulSet або інший схожий ресурс) зменшити масштаб.

Цей документ детально розглядає приклад увімкнення HorizontalPodAutoscaler для автоматичного управління масштабуванням для прикладу вебзастосунку. Це приклад навантаження — Apache httpd, що виконує деякий код PHP.

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

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

Версія вашого Kubernetes сервера має бути не старішою ніж 1.23.

Для перевірки версії введіть kubectl version.

Якщо ви використовуєте старі версії Kubernetes, зверніться до документації для цієї версії (див. доступні версії документації).

Для виконання рекомендацій цього посібника, вам також потрібно використовувати кластер, в якому розгорнутий і налаштований Metrics Server. Сервер метрик Kubernetes збирає метрики ресурсів з kubelets у вашому кластері та використовує ці метрики через Kubernetes API, використовуючи APIService, щоб додати нові види ресурсів, які представляють метричні показники.

Щоб дізнатися, як розгорнути Metrics Server, див. документацію metrics-server.

Якщо ви використовуєте Minikube, виконайте наступну команду для ввімкнення metrics-server:

minikube addons enable metrics-server

Запустіть та надайте доступ до сервера php-apache

Для демонстрації HorizontalPodAutoscaler ви спочатку запустите Deployment, який запускає контейнер за допомогою образу hpa-example, та експонуєте його як Service за допомогою наступного маніфесту:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: php-apache
spec:
  selector:
    matchLabels:
      run: php-apache
  template:
    metadata:
      labels:
        run: php-apache
    spec:
      containers:
      - name: php-apache
        image: registry.k8s.io/hpa-example
        ports:
        - containerPort: 80
        resources:
          limits:
            cpu: 500m
          requests:
            cpu: 200m
---
apiVersion: v1
kind: Service
metadata:
  name: php-apache
  labels:
    run: php-apache
spec:
  ports:
  - port: 80
  selector:
    run: php-apache

Для цього виконайте наступну команду:

kubectl apply -f https://k8s.io/examples/application/php-apache.yaml
deployment.apps/php-apache створено
service/php-apache створено

Створіть HorizontalPodAutoscaler

Тепер, коли сервер працює, створіть автомасштабувальник за допомогою kubectl. Підкоманда kubectl autoscale, частина kubectl, допоможе зробити це.

За мить ви виконаєте команду, яка створить HorizontalPodAutoscaler, що підтримує від 1 до 10 реплік контрольованих Podʼів за допомогою Deployment php-apache, який ви створили на першому етапі цих інструкцій.

Грубо кажучи, контролер контролер HPA збільшує або зменшує кількість реплік (шляхом оновлення Deployment) для підтримки середнього використання процесора на рівні 50% по всіх Podʼах. Deployment потім оновлює ReplicaSet — це частина всіх Deployment у Kubernetes — і потім ReplicaSet додає або видаляє Podʼи на основі змін у його .spec.

Оскільки кожен Pod запитує 200 мілі-ядер за допомогою kubectl run, це означає середнє використання процесора 100 мілі-ядер. Див. Деталі алгоритму для отримання додаткової інформації щодо алгоритму.

Створіть HorizontalPodAutoscaler:

kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10
horizontalpodautoscaler.autoscaling/php-apache масштабовано

Ви можете перевірити поточний стан нового HorizontalPodAutoscaler, виконавши:

# Ви можете використовувати "hpa" або "horizontalpodautoscaler"; обидва імені працюють добре.
kubectl get hpa

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

NAME         REFERENCE                     TARGET    MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache/scale   0% / 50%  1         10        1          18s

(якщо ви бачите інші HorizontalPodAutoscalers з різними іменами, це означає, що вони вже існували, і це зазвичай не проблема).

Зверніть увагу, що поточне використання ЦП становить 0%, оскільки немає клієнтів, що відправляють запити на сервер (стовпець «TARGET» показує середнє значення по всіх Podʼах, що контролюються відповідним розгортанням).

Збільшення навантаження

Далі перегляньте, як автомасштабувальник реагує на збільшене навантаження. Для цього ви запустите інший Pod, який буде виступати в ролі клієнта. Контейнер всередині Podʼа клієнта працює у нескінченному циклі, надсилаючи запити до служби php-apache.

# Виконайте це в окремому терміналі,
# щоб навантаження продовжувалося, і ви могли продовжити роботу з рештою кроків
kubectl run -i --tty load-generator --rm --image=busybox:1.28 --restart=Never -- /bin/sh -c "while sleep 0.01; do wget -q -O- http://php-apache; done"

Тепер виконайте:

# натисніть Ctrl+C, щоб припинити перегляд, коли будете готові
kubectl get hpa php-apache --watch

Протягом хвилини ви повинні побачити вище навантаження процесора; наприклад:

NAME         REFERENCE                     TARGET      MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache/scale   305% / 50%  1         10        1          3m

а потім більше реплік. Наприклад:

NAME         REFERENCE                     TARGET      MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache/scale   305% / 50%  1         10        7          3m

Тут споживання ЦП збільшилося до 305% від запиту. В результаті Deployment був змінений до 7 реплік:

kubectl get deployment php-apache

Ви повинні побачити, що кількість реплік відповідає цифрі з HorizontalPodAutoscaler

NAME         READY   UP-TO-DATE   AVAILABLE   AGE
php-apache   7/7      7           7           19m

Примітка:

Може знадобитися кілька хвилин, щоб стабілізувалася кількість реплік. Оскільки обсяг навантаження не контролюється, може статися так, що кінцева кількість реплік буде відрізнятися від цього прикладу.

Зупиніть генерування навантаження

Для завершення прикладу припиніть надсилання навантаження.

У терміналі, де ви створили Pod, який працює з образом busybox, зупиніть генерування навантаження, натиснувши <Ctrl> + C.

Потім перевірте результат (через хвилину чи так):

# натисніть Ctrl+C, щоб припинити перегляд, коли будете готові
kubectl get hpa php-apache --watch

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

NAME         REFERENCE                     TARGET       MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache/scale   0% / 50%     1         10        1          11m

і Deployment також показує, що він зменшився:

kubectl get deployment php-apache
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
php-apache   1/1     1            1           27m

Як тільки використання CPU знизилося до 0, HPA автоматично зменшив кількість реплік до 1.

Автоматичне масштабування реплік може зайняти кілька хвилин.

Автомасштабування за допомогою кількох метрик та власних метрик

Ви можете вводити додаткові метрики для використання при автомасштабуванні Deployment php-apache, використовуючи версію API autoscaling/v2.

Спочатку отримайте YAML вашого HorizontalPodAutoscaler у формі autoscaling/v2:

kubectl get hpa php-apache -o yaml > /tmp/hpa-v2.yaml

Відкрийте файл /tmp/hpa-v2.yaml у редакторі, і ви побачите YAML, що виглядає наступним чином:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50
status:
  observedGeneration: 1
  lastScaleTime: <some-time>
  currentReplicas: 1
  desiredReplicas: 1
  currentMetrics:
  - type: Resource
    resource:
      name: cpu
    current:
      averageUtilization: 0
      averageValue: 0

Зверніть увагу, що поле targetCPUUtilizationPercentage було замінено масивом під назвою metrics. Метрика використання ЦП є ресурсною метрикою, оскільки вона представлена як відсоток ресурсу, вказаного в контейнерах Podʼа. Зверніть увагу, що ви можете вказати інші ресурсні метрики крім ЦП. Стандартно, єдиним іншим підтримуваним типом ресурсної метрики є memory. Ці ресурси не змінюють назви з кластера на кластер і завжди повинні бути доступними, поки API metrics.k8s.io доступне.

Ви також можете вказати ресурсні метрики у вигляді безпосередніх значень, а не як відсотки від запитаного значення, використовуючи target.type AverageValue замість Utilization, і встановлюючи відповідне поле target.averageValue замість target.averageUtilization.

  metrics:
  - type: Resource
    resource:
      name: memory
      target:
        type: AverageValue
        averageValue: 500Mi

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

Перший з цих альтернативних типів метрик — це метрики Podʼів. Ці метрики описують Podʼи і вони усереднюються разом по Podʼах і порівнюються з цільовим значенням для визначення кількості реплік. Вони працюють так само як ресурсні метрики, за винятком того, що вони тільки підтримують тип target AverageValue.

Метрики Podʼів вказуються за допомогою блоку метрики, подібного до цього:

type: Pods
pods:
  metric:
    name: packets-per-second
  target:
    type: AverageValue
    averageValue: 1k

Другий альтернативний тип метрики — це метрики обʼєктів. Ці метрики описують інший обʼєкт в тому ж просторі імен, замість опису Podʼів. Метрики не обовʼязково отримуються з обʼєкта; вони лише описують його. Метрики обʼєктів підтримують типи target як Value і AverageValue. З Value ціль порівнюється безпосередньо з метрикою отриманою з API. З AverageValue значення, повернене з API власних метрик, ділиться на кількість Podʼів перед порівнянням з цільовим значенням. Ось приклад YAML представлення метрики requests-per-second.

type: Object
object:
  metric:
    name: requests-per-second
  describedObject:
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    name: main-route
  target:
    type: Value
    value: 2k

Якщо ви надаєте кілька таких блоків метрик, HorizontalPodAutoscaler буде розглядати кожну метрику по черзі. HorizontalPodAutoscaler розраховуватиме запропоновані кількості реплік для кожної метрики, а потім вибере той, який має найбільшу кількість реплік.

Наприклад, якщо ваша система моніторингу збирає метрики про мережевий трафік, ви можете оновити визначення вище за допомогою kubectl edit так, щоб воно виглядало наступним чином:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50
  - type: Pods
    pods:
      metric:
        name: packets-per-second
      target:
        type: AverageValue
        averageValue: 1k
  - type: Object
    object:
      metric:
        name: requests-per-second
      describedObject:
        apiVersion: networking.k8s.io/v1
        kind: Ingress
        name: main-route
      target:
        type: Value
        value: 10k
status:
  observedGeneration: 1
  lastScaleTime: <some-time>
  currentReplicas: 1
  desiredReplicas: 1
  currentMetrics:
  - type: Resource
    resource:
      name: cpu
    current:
      averageUtilization: 0
      averageValue: 0
  - type: Object
    object:
      metric:
        name: requests-per-second
      describedObject:
        apiVersion: networking.k8s.io/v1
        kind: Ingress
        name: main-route
      current:
        value: 10k

Тоді ваш HorizontalPodAutoscaler намагатиметься забезпечити, щоб кожен Pod витрачав приблизно 50% від своєї запитаної CPU, обслуговував 1000 пакетів за секунду та всі Podʼи за головним маршрутом Ingress обслуговували в загальному 10000 запитів за секунду.

Автомасштабування за більш конкретними метриками

Багато систем метрик дозволяють описувати метрики або за назвою, або за набором додаткових описів, які називаються мітками (labels). Для всіх типів метрик, крім ресурсних (Podʼів, обʼєктів і зовнішніх, описаних нижче), ви можете вказати додатковий селектор міток, який передається вашій системі метрик. Наприклад, якщо ви збираєте метрику http_requests з міткою verb, ви можете вказати наступний блок метрики, щоб масштабувати тільки на запити GET:

type: Object
object:
  metric:
    name: http_requests
    selector: {matchLabels: {verb: GET}}

Цей селектор використовує такий же синтаксис, як і повні селектори міток Kubernetes. Система моніторингу визначає, як зведені кілька серій в одне значення, якщо імʼя та селектор відповідають кільком серіям. Селектор є додатковим, і не може вибирати метрики, що описують обʼєкти, які не є цільовим обʼєктом (цільові Podʼи у випадку типу Pods, та описаний обʼєкт у випадку типу Object).

Застосунки, які працюють на Kubernetes, можуть потребувати автоматичного масштабування на основі метрик, які не мають очевидного стосунку до будь-якого обʼєкта в кластері Kubernetes, наприклад, метрики, що описують хостову службу з жодною прямою кореляцією з просторами імен Kubernetes. У Kubernetes 1.10 і пізніших версіях ви можете вирішити цей випадок використовуючи зовнішні метрики.

Для використання зовнішніх метрик потрібно знання вашої системи моніторингу; налаштування аналогічно необхідному при використанні власних метрик. Зовнішні метрики дозволяють автоматично масштабувати ваш кластер на основі будь-якої метрики, доступної в вашій системі моніторингу. Надайте блок metric з name та selector, як вище, і використовуйте тип метрики External замість Object. Якщо кілька часових рядів відповідають metricSelector, то сума їх значень використовується HorizontalPodAutoscaler. Зовнішні метрики підтримують як типи цілей Value, так і AverageValue, які працюють точно так само, як коли ви використовуєте тип Object.

Наприклад, якщо ваш додаток обробляє завдання з хостової служби черги, ви можете додати наступний розділ до вашого маніфесту HorizontalPodAutoscaler, щоб вказати, що вам потрібен один робочий процес на кожні 30 невиконаних завдань.

- type: External
  external:
    metric:
      name: queue_messages_ready
      selector:
        matchLabels:
          queue: "worker_tasks"
    target:
      type: AverageValue
      averageValue: 30

Коли це можливо, краще використовувати типи цілей власних метрик замість зовнішніх метрик, оскільки для адміністраторів кластера легше захистити API власних метрик. Зовнішнє API метрик потенційно дозволяє доступ до будь-якої метрики, тому адміністратори кластера повинні бути обережні при його використанні.

Додаток: Умови стану горизонтального автомасштабування Podʼів

При використанні форми autoscaling/v2 HorizontalPodAutoscaler ви зможете бачити умови стану, встановлені Kubernetes на HorizontalPodAutoscaler. Ці умови стану вказують, чи може або не може HorizontalPodAutoscaler масштабуватися, а також чи є в цей час будь-які обмеження.

Умови зʼявляються у полі status.conditions. Щоб побачити умови, які впливають на HorizontalPodAutoscaler, ми можемо використати kubectl describe hpa:

kubectl describe hpa cm-test
Name:                           cm-test
Namespace:                      prom
Labels:                         <none>
Annotations:                    <none>
CreationTimestamp:              Fri, 16 Jun 2017 18:09:22 +0000
Reference:                      ReplicationController/cm-test
Metrics:                        ( current / target )
  "http_requests" on pods:      66m / 500m
Min replicas:                   1
Max replicas:                   4
ReplicationController pods:     1 current / 1 desired
Conditions:
  Type                  Status  Reason                  Message
  ----                  ------  ------                  -------
  AbleToScale           True    ReadyForNewScale        the last scale time was sufficiently old as to warrant a new scale
  ScalingActive         True    ValidMetricFound        the HPA was able to successfully calculate a replica count from pods metric http_requests
  ScalingLimited        False   DesiredWithinRange      the desired replica count is within the acceptable range
Events:

Для цього HorizontalPodAutoscaler ви можете побачити кілька умов у справному стані. Перше, AbleToScale, вказує, чи може або не може HPA отримувати та оновлювати масштаби, а також чи будь-які умови затримки повʼязані з масштабуванням. Друге, ScalingActive, вказує, чи увімкнений HPA (тобто кількість реплік цілі не дорівнює нулю) та чи може розраховувати потрібні масштаби. Якщо це False, це, як правило, вказує на проблеми з отриманням метрик. Нарешті, остання умова, ScalingLimited, вказує на те, що потрібний масштаб був обмежений максимальним або мінімальним значенням HorizontalPodAutoscaler. Це свідчить про те, що ви можливо захочете збільшити або зменшити мінімальну або максимальну кількість реплік на вашому HorizontalPodAutoscaler.

Кількості

Усі метрики в HorizontalPodAutoscaler та API метрик вказуються за спеціальною цільною числовою нотацією, відомою в Kubernetes як кількість. Наприклад, кількість 10500m буде записана як 10.5 у десятковій нотації. API метрик повертатимуть цілі числа без суфікса, якщо це можливо, і зазвичай повертають кількості в міліодиницях у протилежному випадку. Це означає, що ви можете бачити зміну вашого значення метрики між 1 і 1500m, або між 1 і 1.5, коли воно записане в десятковій нотації.

Інші можливі сценарії

Створення автомасштабування декларативно

Замість використання команди kubectl autoscale для створення HorizontalPodAutoscaler імперативно, ми можемо використати наступний маніфест, щоб створити його декларативно:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50

Потім створіть автомасштабування, виконавши наступну команду:

kubectl create -f https://k8s.io/examples/application/hpa/php-apache.yaml
horizontalpodautoscaler.autoscaling/php-apache created

8 - Вказання бюджету розладів для вашого застосунку

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.21 [stable]

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

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

Версія вашого Kubernetes сервера має бути не старішою ніж v1.21.

Для перевірки версії введіть kubectl version.

Захист застосунку за допомогою PodDisruptionBudget

  1. Визначте, який застосунок ви хочете захистити за допомогою PodDisruptionBudget (PDB).
  2. Подумайте про те, як ваш застосунок реагує на розлади.
  3. Створіть визначення PDB у вигляді файлу YAML.
  4. Створіть обʼєкт PDB з файлу YAML.

Визначення застосунку для захисту

Найбільш поширене використання полягає в захисті застосунку, який визначено одним із вбудованих контролерів Kubernetes:

  • Deployment (Розгортання)
  • ReplicationController (Контролер Реплікації)
  • ReplicaSet (Набір Реплік)
  • StatefulSet (Набір зі збереженням стану)

У цьому випадку обовʼязково відзначте .spec.selector контролера; той самий селектор використовується в .spec.selector PDBs.

Починаючи з версії 1.15, PDB підтримують власні контролери, де включено субресурс масштабування.

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

Подумайте про реакцію вашого застосунку на розлади

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

  • Фронтенди без збереження стану:
    • Застереження: не зменшуйте потужність обслуговування більш ніж на 10%.
      • Рішення: використовуйте PDB з minAvailable 90%, наприклад.
  • Одноекземплярний застосунок зі збереженням стану:
    • Застереження: не закривайте цей застосунок без спілкування зі мною.
      • Можливе рішення 1: Не використовуйте PDB і толеруйте випадковий простій.
      • Можливе рішення 2: Встановіть PDB з maxUnavailable=0. Маючи розуміння (поза Kubernetes), що оператор кластера повинен звʼязатися з вами перед закриттям. Коли оператор кластера звертається до вас, готуйтеся до простою, а потім видаліть PDB, щоб показати готовність до розладу. Після цього створіть новий.
  • Багатоекземплярний застосунок зі збереженням стану, такий як Consul, ZooKeeper або etcd:
    • Застереження: не зменшуйте кількість екземплярів нижче кворуму, в іншому випадку записи будуть невдалі.
      • Можливе рішення 1: встановіть maxUnavailable на рівень 1 (працює з різними масштабами застосунку).
      • Можливе рішення 2: встановіть minAvailable рівним розміру кворуму (наприклад, 3 при масштабуванні 5). (Дозволяє більше розладів одночасно).
  • Пакетне завдання (Job) з перезапуском:
    • Застереження: завдання повинно завершитися у разі добровільного розладу.
      • Можливе рішення: Не створюйте PDB. Контролер завдань створить Pod на заміну.

Логіка округлення при вказанні відсотків

Значення для minAvailable або maxUnavailable можуть бути виражені як цілі числа або як відсотки.

  • Коли ви вказуєте ціле число, воно представляє кількість Podʼів. Наприклад, якщо ви встановите minAvailable на 10, то завжди повинно бути доступно 10 Podʼів, навіть під час розладу.
  • Коли ви вказуєте відсоток, встановивши значення як рядкове представлення відсотка (наприклад, "50%"), воно представляє відсоток від загальної кількості Podʼів. Наприклад, якщо ви встановите minAvailable на "50%", то принаймні 50% Podʼів залишаться доступними під час розладу.

Коли ви вказуєте значення як відсоток, це може не відповідати точній кількості Podʼів. Наприклад, якщо у вас є 7 Podʼів і ви встановите minAvailable на "50%", то не зразу очевидно, чи має це означати, що повинно бути доступно 3 або 4 Podʼи. Kubernetes округлює до найближчого цілого числа, тому в цьому випадку повинно бути доступно 4 Podʼи. Коли ви вказуєте значення maxUnavailable як відсоток, Kubernetes округлює кількість Podʼів, які можуть мати розлади. Таким чином, розлад може перевищувати ваш визначений відсоток maxUnavailable. Ви можете ознайомитися з кодом, який керує такою поведінкою.

Вказання PodDisruptionBudget

PodDisruptionBudget має три поля:

  • Селектор міток .spec.selector, щоб вказати набір Podʼів, до яких він застосовується. Це поле обовʼязкове.
  • .spec.minAvailable — це опис кількості Podʼів з цього набору, які повинні залишитися доступними після виселення, навіть у відсутності виселеного Podʼа. minAvailable може бути абсолютним числом або відсотком.
  • .spec.maxUnavailable (доступний у Kubernetes 1.7 та вище) — це опис кількості Podʼів з цього набору, які можуть бути недоступними після виселення. Це також може бути абсолютним числом або відсотком.

Примітка:

Поведінка для порожнього селектора відрізняється між policy/v1beta1 та policy/v1 API для PodDisruptionBudgets. Для policy/v1beta1 порожній селектор відповідає нулю Podʼів, тоді як для policy/v1 порожній селектор відповідає кожному Podʼа у просторі імен.

Ви можете вказати лише один із maxUnavailable або minAvailable в одному PodDisruptionBudget. maxUnavailable може бути використаний лише для контролю виселення Podʼів, які всі мають той самий повʼязаний контролер, який керує ними. У наведених нижче прикладах, "бажані репліки" — це scale контролера, що керує Podʼами, які вибрані PodDisruptionBudget.

Приклад 1: З minAvailable 5, виселення дозволяються, поки залишаються 5 або більше справних Podʼів серед тих, які вибрані селектором PodDisruptionBudget.

Приклад 2: З minAvailable 30%, виселення дозволяються, якщо принаймні 30% від кількості бажаних реплік є справними.

Приклад 3: З maxUnavailable 5, виселення дозволяються, поки є не більше 5 несправних реплік серед загальної кількості бажаних реплік.

Приклад 4: З maxUnavailable 30%, виселення дозволяються, якщо кількість несправних реплік не перевищує 30% від загальної кількості бажаних реплік, округленої до найближчого цілого числа. Якщо загальна кількість бажаних реплік — лише одна, ця єдина репліка все ще допускається до виселення, що призводить до ефективної недоступності на 100%.

У типовому використанні один бюджет використовуватиметься для збірки Podʼів, керованих контролером — наприклад, Podʼів у одному ReplicaSet або StatefulSet.

Примітка:

Бюджет розладів фактично не гарантує, що вказана кількість/відсоток Podʼів завжди буде активними. Наприклад, вузол, на якому розміщено Pod зі збірки, може вийти з ладу, коли збірка досягне мінімального розміру, вказаного в бюджеті, що призведе до зменшення кількості доступних Podʼів зі збірки нижче вказаного розміру. Бюджет може захищати лише від добровільних виселень, а не від всіх причин недоступності.

Якщо ви встановите maxUnavailable на 0% або 0, або ви встановите minAvailable на 100% або рівну кількості реплік, ви вимагаєте нульових добровільних виселень. Коли ви встановлюєте нульові добровільні виселення для робочого навантаження, такого як ReplicaSet, тоді ви не зможете успішно вивести з експлуатації вузол, на якому працює один з цих Podʼів. Якщо ви намагаєтеся вивести з експлуатації вузол, де працює невиселяємий Pod, відключення ніколи не завершиться. Це допускається згідно з семантикою PodDisruptionBudget.

Ви можете знайти приклади визначення бюджетів розладів Podʼів нижче. Вони відповідають Podʼам з міткою app: zookeeper.

Приклад PDB з використанням minAvailable:

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: zk-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: zookeeper

Приклад PDB з використанням maxUnavailable:

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: zk-pdb
spec:
  maxUnavailable: 1
  selector:
    matchLabels:
      app: zookeeper

Наприклад, якщо вищезгаданий обʼєкт zk-pdb вибирає Podʼи з StatefulSet розміром 3, обидві специфікації мають точно таке ж значення. Рекомендується використання maxUnavailable, оскільки він автоматично реагує на зміни кількості реплік відповідного контролера.

Створення обʼєкта PodDisruptionBudget

Для створення або оновлення обʼєкта PDB використовуйте наступну команду kubectl:

kubectl apply -f mypdb.yaml

Перевірка статусу обʼєкта PodDisruptionBudget

Щоб перевірити статус обʼєкта PDB, скористайтеся наступною командою kubectl.

Якщо в вашому просторі імен відсутні Podʼи, що відповідають мітці app: zookeeper, ви побачите щось подібне:

kubectl get poddisruptionbudgets
NAME     MIN AVAILABLE   MAX UNAVAILABLE   ALLOWED DISRUPTIONS   AGE
zk-pdb   2               N/A               0                     7s

Якщо є Podʼи, які відповідають умовам (скажімо, 3), то ви побачите щось на кшталт:

kubectl get poddisruptionbudgets
NAME     MIN AVAILABLE   MAX UNAVAILABLE   ALLOWED DISRUPTIONS   AGE
zk-pdb   2               N/A               1                     7s

Ненульове значення для ALLOWED DISRUPTIONS означає, що контролер розладів Podʼів бачив Podʼи, порахував відповідні Podʼи і оновив статус PDB.

Ви можете отримати більше інформації про статус PDB за допомогою цієї команди:

kubectl get poddisruptionbudgets zk-pdb -o yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  annotations:

  creationTimestamp: "2020-03-04T04:22:56Z"
  generation: 1
  name: zk-pdb

status:
  currentHealthy: 3
  desiredHealthy: 2
  disruptionsAllowed: 1
  expectedPods: 3
  observedGeneration: 1

Справність Pod

Поточна реалізація вважає Podʼи справними, якщо вони мають елемент .status.conditions з type="Ready" і status="True". Ці Podʼи відстежуються через поле .status.currentHealthy в статусі PDB.

Політика виселення несправних Podʼів

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.31 [stable](стандартно увімкнено)

PodDisruptionBudget, який охороняє застосунок, забезпечує, що кількість Podʼів зі статусом .status.currentHealthy не опуститься нижче, ніж кількість, вказана у .status.desiredHealthy, не дозволяючи виселення справних Podʼів. Використовуючи .spec.unhealthyPodEvictionPolicy, ви також можете визначити критерії, коли несправні Podʼи повинні розглядатися для виселення. Стандартна поведінка, коли політика не вказана, відповідає політиці IfHealthyBudget.

Політики:

IfHealthyBudget
Запущені Podʼи (.status.phase="Running"), але ще не справні, можуть бути виселені тільки якщо охоронюваний застосунок не перебуває в розладі (.status.currentHealthy щонайменше дорівнює .status.desiredHealthy).

Ця політика забезпечує, що запущені Podʼи застосунку, що перебуває в стані розлажу, мають кращі шанси стати справними. Це має негативні наслідки для виведення вузлів з експлуатації, які можуть бути заблоковані неправильно працюючими застосунками, які охороняються PDB. Зокрема, застосунки з Podʼами у стані CrashLoopBackOff (через помилку або неправильну конфігурацію), або Podʼи, яким просто не вдається повідомити стан Ready.

AlwaysAllow
Запущені Podʼи (.status.phase="Running"), але ще не справні вважаються такими що перебувають в стані розладу та можуть бути виселені незалежно від того, чи виконуються критерії в PDB.

Це означає, що майбутні запущені Podʼи застосунку, що перебуває в стані розладу, можуть не мати можливості стати справними. Використовуючи цю політику, менеджери кластера можуть легко вивести з експлуатації неправильно працюючі застосунки, які охороняються PDB. Зокрема, застосунки з Podʼами у стані CrashLoopBackOff (через помилку або неправильну конфігурацію), або Podʼи, яким просто не вдається повідомити стан Ready.

Примітка:

Podʼи у фазах Pending, Succeeded або Failed завжди вважаються кандидатами на виселення.

Довільні робочі навантаження та селектори

Ви можете пропустити цей розділ, якщо ви використовуєте PDB лише з вбудованими ресурсами навантаження (Deployment, ReplicaSet, StatefulSet та ReplicationController) або з власними ресурсами, які реалізують субресурс scale, і де селектор PDB точно відповідає селектору власного ресурсу Podʼа.

Ви можете використовувати PDB з підпроцесами, керованими іншим ресурсом, "оператором" або чистими подами, але з такими обмеженнями:

  • можна використовувати лише .spec.minAvailable, а не .spec.maxUnavailable.
  • можна використовувати лише ціле значення з .spec.minAvailable, а не відсоток.

Неможливо використовувати інші конфігурації доступності, оскільки Kubernetes не може вивести загальну кількість Podʼів без підтримуваного власного ресурсу.

Ви можете використовувати селектор, який вибирає підмножину або надмножину Podʼів, що належать ресурсу навантаження. Eviction API не дозволить виселення будь-якого Podʼа, покритого кількома PDB, тому більшість користувачів захочуть уникати перетинаючих селекторів. Одним розумним використанням перетинаючих PDB є перехід Podʼів з одного PDB до іншого.

9 - Отримання доступу до API Kubernetes з Pod

Цей посібник демонструє, як отримати доступ до API Kubernetes з середини Pod.

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

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

Отримання доступу до API з середини Pod

При доступі до API з середини Pod, пошук та автентифікація до сервера API відрізняються трохи від зовнішнього клієнта.

Найпростіший спосіб використовувати API Kubernetes з Pod — використовувати одну з офіційних клієнтських бібліотек. Ці бібліотеки можуть автоматично визначати сервер API та автентифікувати.

Використання офіційних клієнтських бібліотек

З середини Pod рекомендовані способи підключення до API Kubernetes:

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

Прямий доступ до REST API

Під час роботи в Pod ваш контейнер може створити HTTPS URL для сервера API Kubernetes, отримавши змінні середовища KUBERNETES_SERVICE_HOST та KUBERNETES_SERVICE_PORT_HTTPS. Внутрішня адреса сервера API також публікується для Service з іменем kubernetes в просторі імен default, щоб Pod можна було використовувати kubernetes.default.svc як DNS-імʼя для локального сервера API.

Примітка:

Kubernetes не гарантує, що сервер API має дійсний сертифікат для імені хосту kubernetes.default.svc; однак очікується, що панель управління представить дійсний сертифікат для імені хоста або IP-адреси, яку представляє $KUBERNETES_SERVICE_HOST.

Рекомендований спосіб автентифікації на сервері API — за допомогою службового облікового запису. Стандартно, Pod повʼязаний з службовим обліковим записом, і обліковий запис (токен) для цього службового облікового запису розміщується в дереві файлової системи кожного контейнера в цьому Pod, у /var/run/secrets/kubernetes.io/serviceaccount/token.

Якщо доступно, пакет сертифікатів розміщується в дереві файлової системи кожного контейнера за адресою /var/run/secrets/kubernetes.io/serviceaccount/ca.crt, і його слід використовувати для перевірки сертифіката сервера API.

Нарешті, простір імен default для операцій з просторовими іменами API розміщується в файлі у /var/run/secrets/kubernetes.io/serviceaccount/namespace в кожному контейнері.

Використання kubectl proxy

Якщо ви хочете запитувати API без офіційної клієнтської бібліотеки, ви можете запустити kubectl proxy як команду нового контейнера sidecar в Pod. Таким чином, kubectl proxy буде автентифікуватися до API та викладати його на інтерфейс localhost Pod, щоб інші контейнери в Pod могли використовувати його безпосередньо.

Без використання проксі

Можливо уникнути використання kubectl proxy, передаючи токен автентифікації прямо на сервер API. Внутрішній сертифікат забезпечує зʼєднання.

# Вказати імʼя хоста внутрішнього API-сервера
APISERVER=https://kubernetes.default.svc

# Шлях до токена ServiceAccount
SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount

# Прочитати простір імен цього Pod
NAMESPACE=$(cat ${SERVICEACCOUNT}/namespace)

# Прочитати токен облікового службового запису
TOKEN=$(cat ${SERVICEACCOUNT}/token)

# Звертатися до внутрішнього центру сертифікації (CA)
CACERT=${SERVICEACCOUNT}/ca.crt

# Досліджувати API за допомогою TOKEN
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api

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

{
  "kind": "APIVersions",
  "versions": ["v1"],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "10.0.1.149:443"
    }
  ]
}