Модуль 4.2: PersistentVolumes та PersistentVolumeClaims
Складність:
[MEDIUM]— Основна абстракція зберіганняЧас на виконання: 40–50 хвилин
Передумови: Модуль 4.1 (Томи), Модуль 1.2 (CSI)
Що ви зможете робити
Розділ «Що ви зможете робити»Після цього модуля ви зможете:
- Створити PersistentVolumes та PersistentVolumeClaims з відповідними режимами доступу та класами сховища
- Пояснити життєвий цикл PV (Available → Bound → Released → Deleted) та політики повернення
- Дебажити PVC у стані Pending, перевіряючи StorageClass, ємність, режими доступу та node affinity
- Імплементувати статичне виділення для локального сховища та динамічне виділення за допомогою StorageClasses
Чому цей модуль важливий
Розділ «Чому цей модуль важливий»Персистентні томи (PV) та PersistentVolumeClaims (PVC) — це основа постійного зберігання в Kubernetes. Вони розділяють надання сховища та його споживання, дозволяючи адміністраторам керувати сховищем незалежно від розробників, які його використовують. На іспиті CKA активно перевіряють створення PV/PVC, прив’язку та усунення несправностей.
Аналогія з орендою квартири
Уявіть сховище як оренду квартири. Персистентний том (PV) — це сама квартира — вона існує незалежно від того, живе там хтось чи ні. PersistentVolumeClaim — це заява орендаря, в якій вказані його потреби: «Мені потрібні 2 спальні, центральне розташування, паркомісце». Керуючий будинком (Kubernetes) зіставляє заяви з доступними квартирами. Орендарю (Поду) не потрібно знати, яку саме квартиру він отримав — лише що вона відповідає його вимогам.
Що ви дізнаєтесь
Розділ «Що ви дізнаєтесь»Після завершення цього модуля ви зможете:
- Створювати PersistentVolumes вручну
- Створювати PersistentVolumeClaims для запиту сховища
- Розуміти процес прив’язки між PV та PVC
- Налаштовувати режими доступу та політики повернення
- Використовувати PVC у Подах
- Усувати типові проблеми PV/PVC
Чи знали ви?
Розділ «Чи знали ви?»- PV мають кластерний масштаб: На відміну від більшості ресурсів, Персистентні томи не належать жодному простору імен — вони доступні в усьому кластері
- Прив’язка є постійною: Після прив’язки PVC до PV ця прив’язка є ексклюзивною, поки PVC не буде видалено (або PV не буде повернуто)
- Розмір має значення по-іншому: PVC, що запитує 5Gi, може прив’язатися до PV на 100Gi, якщо ближчого збігу немає — додатковий простір зарезервований, але потенційно витрачений даремно
Частина 1: Розуміння моделі PV/PVC
Розділ «Частина 1: Розуміння моделі PV/PVC»1.1 Абстракція зберігання
Розділ «1.1 Абстракція зберігання»┌──────────────────────────────────────────────────────────────────────┐│ Модель абстракції PV/PVC ││ ││ Адміністратор кластера Розробник ││ ┌─────────────┐ ┌─────────────┐ ││ │ Надає │ │ Запитує │ ││ │ сховище │ │ сховище │ ││ └──────┬──────┘ └──────┬──────┘ ││ │ │ ││ ▼ ▼ ││ ┌─────────────┐ Прив'язка ┌─────────────┐ ││ │ Persistent │◄───────────────────►│ Persistent │ ││ │ Volume (PV) │ │ VolumeClaim │ ││ │ │ │ (PVC) │ ││ │ 100Gi NFS │ │ 50Gi RWO │ ││ └──────┬──────┘ └──────┬──────┘ ││ │ │ ││ │ Фізичне сховище │ Монтування в Под ││ ▼ ▼ ││ ┌─────────────┐ ┌─────────────┐ ││ │ NFS │ │ Под │ ││ │ Сервер │─────────────────────►│ /data │ ││ └─────────────┘ └─────────────┘ │└──────────────────────────────────────────────────────────────────────┘1.2 Навіщо таке розділення?
Розділ «1.2 Навіщо таке розділення?»| Питання | Хто відповідає | Ресурс |
|---|---|---|
| Яке сховище доступне? | Адміністратор | PersistentVolume |
| Скільки сховища потрібно? | Розробник | PersistentVolumeClaim |
| Куди монтувати? | Розробник | Специфікація Пода |
| Деталі бекенду сховища | Адміністратор | PV + StorageClass |
1.3 Життєвий цикл PV/PVC
Розділ «1.3 Життєвий цикл PV/PVC»┌─────────────────────────────────────────────────────────────────────┐│ Життєвий цикл PV/PVC ││ ││ PV створено ──► Available ──► Bound ──► Released ──► [Повернення] ││ │ │ │ │ │ ││ │ │ │ │ │ ││ │ PVC створено PVC PVC Retain/ ││ │ та знайдено існує видалено Delete/ ││ │ збіг Recycle ││ ││ Фази PV: ││ • Available: Готовий до прив'язки ││ • Bound: Пов'язаний з PVC ││ • Released: PVC видалено, очікує повернення ││ • Failed: Автоматичне повернення не вдалось │└─────────────────────────────────────────────────────────────────────┘Частина 2: Створення PersistentVolumes
Розділ «Частина 2: Створення PersistentVolumes»2.1 Специфікація PV
Розділ «2.1 Специфікація PV»apiVersion: v1kind: PersistentVolumemetadata: name: pv-nfs-data labels: type: nfs environment: productionspec: capacity: storage: 100Gi # Розмір тому volumeMode: Filesystem # Filesystem або Block accessModes: - ReadWriteMany # Може бути змонтований кількома вузлами persistentVolumeReclaimPolicy: Retain # Що відбувається при звільненні storageClassName: manual # Має збігатися з PVC (або порожній) mountOptions: - hard - nfsvers=4.1 nfs: # Конфігурація, специфічна для бекенду path: /exports/data server: nfs-server.example.com2.2 Режими доступу
Розділ «2.2 Режими доступу»| Режим | Скорочення | Опис |
|---|---|---|
| ReadWriteOnce | RWO | Читання-запис на одному вузлі |
| ReadOnlyMany | ROX | Тільки читання на кількох вузлах |
| ReadWriteMany | RWX | Читання-запис на кількох вузлах |
| ReadWriteOncePod | RWOP | Читання-запис для одного Пода (K8s 1.22+) |
Підтримка залежить від бекенду:
- NFS: RWO, ROX, RWX
- AWS EBS: лише RWO
- GCE PD: RWO, ROX
- Azure Disk: лише RWO
- Local: лише RWO
2.3 Політики повернення
Розділ «2.3 Політики повернення»| Політика | Поведінка | Випадок використання |
|---|---|---|
| Retain | PV зберігається після видалення PVC | Продакшен-дані, ручне очищення |
| Delete | PV та базове сховище видаляються | Динамічне надання, dev/test |
| Recycle | Базове очищення (rm -rf /data/*) | Застаріло — не використовуйте |
2.4 Режими тому
Розділ «2.4 Режими тому»spec: volumeMode: Filesystem # За замовчуванням — монтується як каталог # АБО volumeMode: Block # Необроблений блоковий пристрій (для баз даних)2.5 Поширені типи PV
Розділ «2.5 Поширені типи PV»hostPath PV (лише для тестування):
apiVersion: v1kind: PersistentVolumemetadata: name: pv-hostpathspec: capacity: storage: 10Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Delete storageClassName: manual hostPath: path: /mnt/data type: DirectoryOrCreateNFS PV:
apiVersion: v1kind: PersistentVolumemetadata: name: pv-nfsspec: capacity: storage: 50Gi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain storageClassName: nfs nfs: server: 192.168.1.100 path: /exports/shareLocal PV (специфічний для вузла):
apiVersion: v1kind: PersistentVolumemetadata: name: pv-localspec: capacity: storage: 200Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Retain storageClassName: local-storage local: path: /mnt/disks/ssd1 nodeAffinity: # Обов'язково для локальних томів! required: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/hostname operator: In values: - worker-node-1Частина 3: Створення PersistentVolumeClaims
Розділ «Частина 3: Створення PersistentVolumeClaims»3.1 Специфікація PVC
Розділ «3.1 Специфікація PVC»apiVersion: v1kind: PersistentVolumeClaimmetadata: name: data-claim namespace: production # PVC належать до namespace!spec: accessModes: - ReadWriteOnce # Має збігатися або бути підмножиною PV volumeMode: Filesystem resources: requests: storage: 50Gi # Мінімальний необхідний розмір storageClassName: manual # Збігається зі storageClassName PV selector: # Опціонально: обрати конкретні PV matchLabels: type: nfs environment: production3.2 Правила прив’язки
Розділ «3.2 Правила прив’язки»PVC прив’язується до PV, коли:
- storageClassName збігається (або обидва порожні)
- accessModes, що запитуються, доступні в PV
- resources.requests.storage <= ємність PV
- selector (якщо вказано) збігається з мітками PV
┌─────────────────────────────────────────────────────────────────────┐│ Рішення про прив'язку ││ ││ Запит PVC Доступний PV Збіг? ││ ───────────── ──────────── ────── ││ 50Gi RWO 100Gi RWO ✓ Розмір OK, режим OK ││ 50Gi RWX 100Gi RWO ✗ Невідповідність режиму ││ 50Gi RWO manual 100Gi RWO fast ✗ Невідповідність SC ││ 50Gi RWO 30Gi RWO ✗ Розмір замалий ││ ││ Примітка: PVC може прив'язатися до більшого PV, але не меншого │└─────────────────────────────────────────────────────────────────────┘3.3 Створення PVC через kubectl
Розділ «3.3 Створення PVC через kubectl»# Швидкий спосіб створення PVC (обмежені параметри)cat <<EOF | k apply -f -apiVersion: v1kind: PersistentVolumeClaimmetadata: name: my-claimspec: accessModes: - ReadWriteOnce resources: requests: storage: 10Gi storageClassName: standardEOF3.4 Перевірка статусу PVC
Розділ «3.4 Перевірка статусу PVC»# Список PVCk get pvc# NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS# my-claim Bound pv-001 10Gi RWO standard
# Детальний переглядk describe pvc my-claim
# Перевірити, до якого PV прив'язаноk get pvc my-claim -o jsonpath='{.spec.volumeName}'Частина 4: Використання PVC у Подах
Розділ «Частина 4: Використання PVC у Подах»4.1 Базовий Под з PVC
Розділ «4.1 Базовий Под з PVC»apiVersion: v1kind: Podmetadata: name: app-with-storagespec: containers: - name: app image: nginx:1.25 volumeMounts: - name: data mountPath: /usr/share/nginx/html volumes: - name: data persistentVolumeClaim: claimName: my-claim # Посилання на ім'я PVC4.2 PVC у Deployments
Розділ «4.2 PVC у Deployments»apiVersion: apps/v1kind: Deploymentmetadata: name: web-appspec: replicas: 3 selector: matchLabels: app: web template: metadata: labels: app: web spec: containers: - name: web image: nginx:1.25 volumeMounts: - name: shared-data mountPath: /data volumes: - name: shared-data persistentVolumeClaim: claimName: shared-pvc # Має бути RWX для кількох реплікВажливо: Для Deployments з кількома репліками вам потрібно:
- PVC з режимом доступу
ReadWriteMany, АБО - StatefulSet з volumeClaimTemplates (кожна репліка отримує власний PVC)
4.3 Монтування PVC тільки для читання
Розділ «4.3 Монтування PVC тільки для читання»volumes:- name: data persistentVolumeClaim: claimName: my-claim readOnly: true # Монтувати тільки для читанняЧастина 5: Зіставлення PV/PVC за допомогою селекторів
Розділ «Частина 5: Зіставлення PV/PVC за допомогою селекторів»5.1 Вибір на основі міток
Розділ «5.1 Вибір на основі міток»# PV з міткамиapiVersion: v1kind: PersistentVolumemetadata: name: pv-fast-ssd labels: type: ssd speed: fast region: us-eastspec: capacity: storage: 100Gi accessModes: - ReadWriteOnce storageClassName: "" # Порожній для ручної прив'язки hostPath: path: /mnt/ssd---# PVC, що обирає конкретний PVapiVersion: v1kind: PersistentVolumeClaimmetadata: name: fast-storage-claimspec: accessModes: - ReadWriteOnce resources: requests: storage: 50Gi storageClassName: "" # Має збігатися з PV selector: matchLabels: type: ssd speed: fast matchExpressions: - key: region operator: In values: - us-east - us-west5.2 Прямий вибір тому
Розділ «5.2 Прямий вибір тому»Примусова прив’язка PVC до конкретного PV за іменем:
apiVersion: v1kind: PersistentVolumeClaimmetadata: name: specific-pv-claimspec: accessModes: - ReadWriteOnce resources: requests: storage: 10Gi storageClassName: "" volumeName: pv-fast-ssd # Прив'язатися до цього конкретного PVЧастина 6: Звільнення та очищення PV
Розділ «Частина 6: Звільнення та очищення PV»6.1 Розуміння стану Released
Розділ «6.1 Розуміння стану Released»Коли PVC видаляється:
PVC видалено ──► Статус PV змінюється на "Released" │ ├── Retain: Дані збережені, PV не можна повторно використати │ Адміністратор повинен вручну очистити │ └── Delete: PV та сховище видаляються автоматично6.2 Повернення звільненого PV
Розділ «6.2 Повернення звільненого PV»# Перевірити статус PVk get pv pv-data# NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM# pv-data 100Gi RWO Retain Released default/old-claim
# Видалити посилання на claim, щоб зробити PV доступним зновуk patch pv pv-data -p '{"spec":{"claimRef": null}}'
# Перевірити, що він Availablek get pv pv-data# STATUS: Available6.3 Ручне видалення даних
Розділ «6.3 Ручне видалення даних»Для політики Retain дані залишаються на сховищі. Кроки очищення:
- Створіть резервну копію даних за потреби
- Видаліть дані з базового сховища
- Видаліть claimRef (як вище) або видаліть/перестворіть PV
Типові помилки
Розділ «Типові помилки»| Помилка | Проблема | Рішення |
|---|---|---|
| PVC застряг у Pending | Немає відповідного PV | Перевірте storageClassName, розмір, режими доступу |
| Невідповідність режиму доступу | PVC запитує RWX, PV має лише RWO | Використовуйте сумісні режими доступу |
| Невідповідність StorageClass | PVC та PV мають різний storageClassName | Вирівняйте storageClassName або використовуйте "" для обох |
| Видалили PVC, втратили дані | Політика повернення була Delete | Використовуйте Retain для важливих даних |
| Не можна повторно використати Released PV | claimRef все ще встановлений | Патч PV для видалення claimRef |
| Local PV без nodeAffinity | Под не може знайти том | Додайте обов’язкову секцію nodeAffinity |
| PVC у неправильному namespace | Под не може посилатися на нього | PVC повинні бути в тому самому namespace, що й Под |
Тест
Розділ «Тест»Q1: Область видимості PV
Розділ «Q1: Область видимості PV»PersistentVolumes належать до namespace чи мають кластерний масштаб?
Відповідь
Область видимості кластера. PersistentVolumes не належать жодному простору імен — вони доступні в усьому кластері. PersistentVolumeClaims належать до namespace і можуть використовуватися лише Подами в тому самому namespace.
Q2: Розмір при прив’язці
Розділ «Q2: Розмір при прив’язці»PVC запитує 20Gi. Доступні PV: 10Gi, 50Gi та 100Gi. Який прив’яжеться?
Відповідь
50Gi — Kubernetes обирає найменший PV, який задовольняє запит. 10Gi замалий. Між 50Gi та 100Gi кращим збігом є 50Gi для мінімізації витраченого простору.
Q3: Сумісність режимів доступу
Розділ «Q3: Сумісність режимів доступу»Чи може PVC, що запитує ReadWriteOnce, прив’язатися до PV з ReadWriteMany?
Відповідь
Так. Запитувані режими доступу PVC повинні бути підмножиною того, що пропонує PV. RWX включає можливості RWO, тому запит RWO може бути задоволений PV з RWX.
Q4: Звільнений PV
Розділ «Q4: Звільнений PV»PV показує статус “Released”. Що це означає і що відбувається далі?
Відповідь
“Released” означає, що прив’язаний PVC було видалено, але PV все ще має claimRef. З політикою Retain PV залишається Released, поки адміністратор вручну не очистить claimRef. З політикою Delete PV та базове сховище автоматично видаляються.
Q5: Порожній рядок StorageClass
Розділ «Q5: Порожній рядок StorageClass»Яка різниця між storageClassName: "" та відсутністю storageClassName?
Відповідь
storageClassName: ""(порожній рядок): Прив’язуватися лише до PV без storageClassName, вимкнути динамічне надання- Не вказано: Використовувати StorageClass кластера за замовчуванням (якщо існує), може запустити динамічне надання
Для ручної прив’язки PV до PVC явно встановіть storageClassName: "" на обох.
Q6: Вимоги до Local PV
Розділ «Q6: Вимоги до Local PV»Яка спеціальна конфігурація потрібна для локального PersistentVolume?
Відповідь
Локальні PV потребують nodeAffinity для вказівки, на якому вузлі знаходиться сховище. Без цього Поди можуть бути заплановані на вузли без доступу до локального сховища.
nodeAffinity: required: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/hostname operator: In values: - specific-node-nameПрактична вправа: Статичне надання PV
Розділ «Практична вправа: Статичне надання PV»Сценарій
Розділ «Сценарій»Створіть PV та PVC, потім використайте сховище в Поді. Перевірте, що дані зберігаються після видалення Пода.
Налаштування
Розділ «Налаштування»# Створити namespacek create ns pv-labЗавдання 1: Створити PersistentVolume
Розділ «Завдання 1: Створити PersistentVolume»cat <<EOF | k apply -f -apiVersion: v1kind: PersistentVolumemetadata: name: lab-pv labels: lab: storagespec: capacity: storage: 1Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Retain storageClassName: manual hostPath: path: /tmp/lab-pv-data type: DirectoryOrCreateEOFПеревірка:
k get pv lab-pv# STATUS має бути "Available"Завдання 2: Створити PersistentVolumeClaim
Розділ «Завдання 2: Створити PersistentVolumeClaim»cat <<EOF | k apply -f -apiVersion: v1kind: PersistentVolumeClaimmetadata: name: lab-pvc namespace: pv-labspec: accessModes: - ReadWriteOnce resources: requests: storage: 500Mi storageClassName: manual selector: matchLabels: lab: storageEOFПеревірка прив’язки:
k get pvc -n pv-lab# STATUS має бути "Bound"
k get pv lab-pv# CLAIM має показувати "pv-lab/lab-pvc"Завдання 3: Використати PVC у Поді
Розділ «Завдання 3: Використати PVC у Поді»cat <<EOF | k apply -f -apiVersion: v1kind: Podmetadata: name: storage-pod namespace: pv-labspec: containers: - name: writer image: busybox:1.36 command: ['sh', '-c', 'echo "Data written at \$(date)" > /data/timestamp.txt; sleep 3600'] volumeMounts: - name: storage mountPath: /data volumes: - name: storage persistentVolumeClaim: claimName: lab-pvcEOFЗавдання 4: Перевірити збереження даних
Розділ «Завдання 4: Перевірити збереження даних»# Перевірити записані даніk exec -n pv-lab storage-pod -- cat /data/timestamp.txt
# Видалити Подk delete pod -n pv-lab storage-pod
# Перестворити Подcat <<EOF | k apply -f -apiVersion: v1kind: Podmetadata: name: storage-pod-v2 namespace: pv-labspec: containers: - name: reader image: busybox:1.36 command: ['sh', '-c', 'cat /data/timestamp.txt; sleep 3600'] volumeMounts: - name: storage mountPath: /data volumes: - name: storage persistentVolumeClaim: claimName: lab-pvcEOF
# Перевірити, що дані збереглисяk logs -n pv-lab storage-pod-v2# Має показати оригінальну мітку часуЗавдання 5: Перевірити стан Released
Розділ «Завдання 5: Перевірити стан Released»# Видалити PVC (Под треба видалити спочатку)k delete pod -n pv-lab storage-pod-v2k delete pvc -n pv-lab lab-pvc
# Перевірити статус PVk get pv lab-pv# STATUS має бути "Released" (через політику Retain)
# Зробити PV доступним зновуk patch pv lab-pv -p '{"spec":{"claimRef": null}}'
k get pv lab-pv# STATUS має бути "Available"Критерії успіху
Розділ «Критерії успіху»- PV створено та показує “Available”
- PVC створено та прив’язано до PV
- Под може записувати дані на змонтований том
- Дані зберігаються після видалення Пода
- PV показує “Released” після видалення PVC
- PV можна знову зробити “Available”
Очищення
Розділ «Очищення»k delete ns pv-labk delete pv lab-pvПрактичні вправи
Розділ «Практичні вправи»Вправа 1: Створити PV (2 хв)
Розділ «Вправа 1: Створити PV (2 хв)»# Завдання: Створити PV на 5Gi з доступом RWO, політикою Retain, storageClassName "slow"# Бекенд: hostPath /mnt/dataВправа 2: Створити PVC (1 хв)
Розділ «Вправа 2: Створити PVC (1 хв)»# Завдання: Створити PVC, що запитує 2Gi з RWO, storageClassName "slow"Вправа 3: Перевірити прив’язку (1 хв)
Розділ «Вправа 3: Перевірити прив’язку (1 хв)»# Завдання: Перевірити, що PVC прив'язаний до правильного PV# Команди: k get pvc, k get pv, перевірте стовпець CLAIMВправа 4: Селектор PVC (2 хв)
Розділ «Вправа 4: Селектор PVC (2 хв)»# Завдання: Створити PVC, який прив'язується лише до PV з міткою "tier: gold"# Використовуйте selector.matchLabelsВправа 5: Под з PVC (2 хв)
Розділ «Вправа 5: Под з PVC (2 хв)»# Завдання: Створити Под, що монтує PVC "data-pvc" в /app/data# Образ: nginxВправа 6: Усунення несправності Pending PVC (2 хв)
Розділ «Вправа 6: Усунення несправності Pending PVC (2 хв)»# Дано: PVC застряг у стані Pending# Завдання: Визначити, чому він не прив'язується# Перевірте: k describe pvc, подивіться на EventsВправа 7: Повернення Released PV (1 хв)
Розділ «Вправа 7: Повернення Released PV (1 хв)»# Завдання: Зробити "Released" PV знову доступним# Команда: k patch pv <name> -p '{"spec":{"claimRef": null}}'Вправа 8: Local PV з nodeAffinity (3 хв)
Розділ «Вправа 8: Local PV з nodeAffinity (3 хв)»# Завдання: Створити локальний PV, який працює лише на вузлі "worker-1"# Включіть обов'язкову секцію nodeAffinityНаступний модуль
Розділ «Наступний модуль»Переходьте до Модуль 4.3: StorageClasses та динамічне надання, щоб дізнатися про автоматичне створення PV.