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

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

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

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

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

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

Цілі

  • Розгорнути репліковану топологію 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, щоб скасувати перегляд.

Цей маніфест використовує різноманітні техніки для керування 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

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

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.

Що далі

Змінено June 20, 2024 at 12:44 PM PST: Sync changest from andygol/k8s-website (36d05bc8a1)