Модуль 4.1: Томи (Volumes)
Складність:
[MEDIUM]— Основа для всіх концепцій зберігання данихЧас на виконання: 35–45 хвилин
Передумови: Модуль 2.1 (Поди), Модуль 2.7 (ConfigMaps та Secrets)
Що ви зможете робити
Розділ «Що ви зможете робити»Після цього модуля ви зможете:
- Налаштувати emptyDir, hostPath та projected volumes і пояснити, коли використовувати кожен
- Змонтувати томи в контейнери за конкретними шляхами з доступом тільки для читання або читання-запису
- Пояснити життєвий цикл томів (прив’язаний до пода, не до контейнера) та гарантії збереження даних
- Дебажити збої монтування томів, перевіряючи події, шляхи та дозволи
Чому цей модуль важливий
Розділ «Чому цей модуль важливий»Контейнери є ефемерними — коли вони перезапускаються, всі дані втрачаються. Томи вирішують цю проблему, надаючи постійне або спільне сховище, яке переживає перезапуски контейнерів. На іспиті CKA вам потрібно буде налаштовувати різні типи томів для обміну даними між контейнерами, кешування тимчасових файлів та впровадження конфігурації.
Аналогія з картотечною шафою
Уявіть контейнер як робочий стіл зі шухлядами, які спорожнюються щоразу, коли ви йдете з роботи. Том — це як картотечна шафа в кутку — вона зберігає ваші файли навіть коли вас немає. Деякі шафи спільні між столами (emptyDir), деякі — це загальне сховище будівлі (PV), а деякі — просто дзеркала корпоративного довідника (configMap/secret projected volumes).
Що ви дізнаєтесь
Розділ «Що ви дізнаєтесь»Після завершення цього модуля ви зможете:
- Розуміти, навіщо потрібні томи
- Використовувати emptyDir для тимчасового спільного сховища
- Використовувати hostPath для сховища на рівні вузла (та знати його ризики)
- Використовувати projected volumes для впровадження конфігурації
- Розуміти життєвий цикл томів та коли дані зберігаються
Чи знали ви?
Розділ «Чи знали ви?»- emptyDir у пам’яті: Ви можете підтримувати emptyDir оперативною пам’яттю замість диска за допомогою
medium: Memory— чудово для конфіденційних тимчасових файлів, які ніколи не повинні потрапляти на диск - Projected volumes поєднують 4 джерела: configMap, secret, downwardAPI та serviceAccountToken — усе це можна проєктувати в один каталог
- hostPath заборонений у продакшені: Більшість політик безпеки (включно з Pod Security Standards) блокують hostPath, оскільки він відкриває файлову систему вузла для подів
Частина 1: Розуміння томів
Розділ «Частина 1: Розуміння томів»1.1 Проблема зберігання даних у контейнерах
Розділ «1.1 Проблема зберігання даних у контейнерах»Контейнери мають ізольовану файлову систему. Коли контейнер падає і перезапускається:
┌──────────────────────────────────────────────────────────────┐│ Перезапуск контейнера = Втрата даних ││ ││ Контейнер v1 Контейнер v2 (після перезапуску) ││ ┌─────────────────┐ ┌─────────────────┐ ││ │ /app │ │ /app │ ││ │ ├── config.yml │ ──→ │ ├── config.yml │ (з образу) ││ │ └── data/ │ │ └── data/ │ ││ │ └── cache │ │ └── (пусто!)│ ││ └─────────────────┘ └─────────────────┘ ││ ││ Дані часу виконання ЗНИКЛИ після перезапуску │└──────────────────────────────────────────────────────────────┘1.2 Як томи вирішують цю проблему
Розділ «1.2 Як томи вирішують цю проблему»Томи надають сховище, яке існує поза файловою системою контейнера:
┌──────────────────────────────────────────────────────────────┐│ З томами = Дані зберігаються ││ ││ Контейнер v1 Контейнер v2 (після перезапуску) ││ ┌─────────────────┐ ┌─────────────────┐ ││ │ /app │ │ /app │ ││ │ ├── config.yml │ │ ├── config.yml │ ││ │ └── data/ ──────┼───┐ │ └── data/ ──────┼───┐ ││ └─────────────────┘ │ └─────────────────┘ │ ││ │ │ ││ ▼ ▼ ││ ┌────────────────────────────────┐ ││ │ Том (спільний) │ ││ │ └── cache (все ще тут!) │ ││ └────────────────────────────────┘ │└──────────────────────────────────────────────────────────────┘1.3 Огляд типів томів
Розділ «1.3 Огляд типів томів»| Тип тому | Час життя | Використання | Збереження даних |
|---|---|---|---|
| emptyDir | Час життя Пода | Тимчасове сховище, обмін між контейнерами | Втрачається при видаленні Пода |
| hostPath | Час життя вузла | Дані рівня вузла, DaemonSets | Зберігається на вузлі |
| configMap | Час життя ConfigMap | Файли конфігурації | Керується ConfigMap |
| secret | Час життя Secret | Облікові дані | Керується Secret |
| projected | Час життя джерела | Кілька джерел в одному монтуванні | Залежить від джерел |
| persistentVolumeClaim | Час життя PV | Постійні дані | Переживає видалення Пода |
| image | Час життя образу | Вміст OCI-образу як том тільки для читання (K8s 1.35+) | Тільки для читання, завантажується з реєстру |
Нове в K8s 1.35: Image Volumes (GA)
Image volumes дозволяють завантажити OCI-образ з реєстру та змонтувати його вміст безпосередньо як том тільки для читання. Жодних init-контейнерів або bootstrap-скриптів не потрібно. Ідеально для ML-моделей, пакетів конфігурації або статичних ресурсів:
volumes:- name: model-dataimage:reference: registry.example.com/ml-models/bert:v2pullPolicy: IfNotPresent
Частина 2: Томи emptyDir
Розділ «Частина 2: Томи emptyDir»2.1 Що таке emptyDir?
Розділ «2.1 Що таке emptyDir?»Том emptyDir створюється, коли Под призначається на вузол, і існує доки Под працює на цьому вузлі. Він починається порожнім — звідси й назва.
Ключові характеристики:
- Створюється при запуску Пода на вузлі
- Видаляється, коли Под видаляється з вузла (з будь-якої причини)
- Спільний для всіх контейнерів у Поді
- Може бути підтриманий диском або пам’яттю (tmpfs)
2.2 Базове використання emptyDir
Розділ «2.2 Базове використання emptyDir»apiVersion: v1kind: Podmetadata: name: shared-dataspec: containers: - name: writer image: busybox command: ['sh', '-c', 'echo "Hello from writer" > /data/message; sleep 3600'] volumeMounts: - name: shared-storage mountPath: /data - name: reader image: busybox command: ['sh', '-c', 'sleep 5; cat /data/message; sleep 3600'] volumeMounts: - name: shared-storage mountPath: /data volumes: - name: shared-storage emptyDir: {}2.3 emptyDir з підтримкою пам’яттю
Розділ «2.3 emptyDir з підтримкою пам’яттю»Для конфіденційних тимчасових даних або швидшого введення-виведення:
apiVersion: v1kind: Podmetadata: name: memory-backedspec: containers: - name: app image: busybox command: ['sh', '-c', 'sleep 3600'] volumeMounts: - name: tmpfs-volume mountPath: /cache volumes: - name: tmpfs-volume emptyDir: medium: Memory # Використовує RAM замість диска sizeLimit: 100Mi # Важливо! Обмежте використання пам'ятіКоли використовувати emptyDir з підтримкою пам’яттю:
- Тимчасові облікові дані, які не повинні потрапляти на диск
- Високошвидкісне кешування
- Тимчасовий простір для обчислень
Увага: Томи з підтримкою пам’яттю враховуються в ліміті пам’яті контейнера!
2.4 Обмеження розміру emptyDir
Розділ «2.4 Обмеження розміру emptyDir»volumes:- name: cache emptyDir: sizeLimit: 500Mi # Обмежити використання дискаЯкщо Под перевищить обмеження розміру, його буде витіснено.
Частина 3: Томи hostPath
Розділ «Частина 3: Томи hostPath»3.1 Що таке hostPath?
Розділ «3.1 Що таке hostPath?»hostPath монтує файл або каталог з файлової системи хост-вузла до Пода.
┌─────────────────────────────────────────────────────────────┐│ Вузол ││ ││ Файлова система вузла Под ││ ┌─────────────────┐ ┌─────────────────────────┐ ││ │ /var/log/ │ │ Контейнер │ ││ │ └── pods/ │◄──────│ /host-logs/ ◄────┐ │ ││ │ └── *.log │ │ │ │ ││ └─────────────────┘ │ volumeMounts: │ │ ││ │ mountPath: │ │ ││ /data/ │ /host-logs ──┘ │ ││ └── myapp/ ◄───────│ │ ││ └── config │ └─────────────────────────┘ ││ │└─────────────────────────────────────────────────────────────┘3.2 Конфігурація hostPath
Розділ «3.2 Конфігурація hostPath»apiVersion: v1kind: Podmetadata: name: hostpath-examplespec: containers: - name: app image: busybox command: ['sh', '-c', 'ls -la /host-data; sleep 3600'] volumeMounts: - name: host-volume mountPath: /host-data readOnly: true # Хороша практика для безпеки volumes: - name: host-volume hostPath: path: /var/log # Шлях на вузлі type: Directory # Має бути каталогом3.3 Типи hostPath
Розділ «3.3 Типи hostPath»| Тип | Поведінка |
|---|---|
"" (порожній) | Жодних перевірок перед монтуванням |
DirectoryOrCreate | Створити каталог, якщо відсутній |
Directory | Має існувати, має бути каталогом |
FileOrCreate | Створити файл, якщо відсутній |
File | Має існувати, має бути файлом |
Socket | Має існувати, має бути UNIX-сокетом |
CharDevice | Має існувати, має бути символьним пристроєм |
BlockDevice | Має існувати, має бути блоковим пристроєм |
3.4 Ризики безпеки hostPath
Розділ «3.4 Ризики безпеки hostPath»Чому hostPath є небезпечним:
# НЕБЕЗПЕЧНО — Ніколи не робіть цього у продакшені!volumes:- name: root-access hostPath: path: / # Доступ до всієї файлової системи вузла! type: DirectoryРизики:
- Втеча контейнера на вузол
- Читання конфіденційних файлів вузла (/etc/shadow, облікові дані kubelet)
- Запис шкідливих файлів на вузол
- Зміна конфігурації вузла
Безпечне використання hostPath:
- DaemonSets, яким потрібен доступ до вузла (збирачі логів, агенти моніторингу)
- Налагодження на рівні вузла (тимчасово)
- Доступ до Docker-сокета для CI/CD (використовуйте з крайньою обережністю)
3.5 hostPath у DaemonSets
Розділ «3.5 hostPath у DaemonSets»Легітимне використання hostPath — збір логів:
apiVersion: apps/v1kind: DaemonSetmetadata: name: log-collectorspec: selector: matchLabels: name: log-collector template: metadata: labels: name: log-collector spec: containers: - name: collector image: fluentd:latest volumeMounts: - name: varlog mountPath: /var/log readOnly: true # Тільки для читання заради безпеки - name: containers mountPath: /var/lib/docker/containers readOnly: true volumes: - name: varlog hostPath: path: /var/log type: Directory - name: containers hostPath: path: /var/lib/docker/containers type: DirectoryЧастина 4: Projected Volumes
Розділ «Частина 4: Projected Volumes»4.1 Що таке Projected Volumes?
Розділ «4.1 Що таке Projected Volumes?»Projected volumes поєднують кілька джерел томів в один каталог. Це корисно, коли вам потрібні configMaps, secrets та дані downwardAPI в одному місці.
┌──────────────────────────────────────────────────────────────┐│ Projected Volume ││ ││ Джерела: Точка монтування: ││ ┌─────────────┐ /etc/config/ ││ │ ConfigMap A │─────┐ ├── app.conf (з A) ││ └─────────────┘ │ ├── db.conf (з A) ││ ┌─────────────┐ │ ├── password (з B) ││ │ Secret B │─────┼────────→ ├── api-key (з B) ││ └─────────────┘ │ ├── labels (з C) ││ ┌─────────────┐ │ └── annotations (з C) ││ │ DownwardAPI │─────┘ ││ └─────────────┘ │└──────────────────────────────────────────────────────────────┘4.2 Конфігурація Projected Volume
Розділ «4.2 Конфігурація Projected Volume»apiVersion: v1kind: Podmetadata: name: projected-volume-demo labels: app: demo version: v1spec: containers: - name: app image: busybox command: ['sh', '-c', 'ls -la /etc/config; sleep 3600'] volumeMounts: - name: all-config mountPath: /etc/config readOnly: true volumes: - name: all-config projected: sources: # Джерело 1: ConfigMap - configMap: name: app-config items: - key: app.properties path: app.conf # Джерело 2: Secret - secret: name: app-secrets items: - key: password path: credentials/password # Джерело 3: Downward API - downwardAPI: items: - path: labels fieldRef: fieldPath: metadata.labels - path: cpu-limit resourceFieldRef: containerName: app resource: limits.cpu4.3 Проєктування токена Service Account
Розділ «4.3 Проєктування токена Service Account»Сучасний Kubernetes використовує проєктовані токени service account замість застарілих токенів:
apiVersion: v1kind: Podmetadata: name: service-account-projectionspec: serviceAccountName: my-service-account containers: - name: app image: myapp:latest volumeMounts: - name: token mountPath: /var/run/secrets/tokens readOnly: true volumes: - name: token projected: sources: - serviceAccountToken: path: token expirationSeconds: 3600 # Короткоживучий токен audience: api # Цільова аудиторія4.4 Випадки використання Projected Volume
Розділ «4.4 Випадки використання Projected Volume»| Випадок використання | Поєднані джерела |
|---|---|
| Пакет конфігурації додатка | configMap + secret |
| Ідентифікація Пода | serviceAccountToken + downwardAPI |
| Повне впровадження конфігурації | configMap + secret + downwardAPI |
| Конфігурація sidecar | Кілька configMaps |
Частина 5: Томи ConfigMap та Secret
Розділ «Частина 5: Томи ConfigMap та Secret»5.1 ConfigMap як том
Розділ «5.1 ConfigMap як том»apiVersion: v1kind: ConfigMapmetadata: name: nginx-configdata: nginx.conf: | server { listen 80; location / { root /usr/share/nginx/html; } }---apiVersion: v1kind: Podmetadata: name: nginxspec: containers: - name: nginx image: nginx:1.25 volumeMounts: - name: config mountPath: /etc/nginx/conf.d readOnly: true volumes: - name: config configMap: name: nginx-config # Опціонально: вибрати конкретні ключі items: - key: nginx.conf path: default.conf # Перейменувати файл5.2 Secret як том
Розділ «5.2 Secret як том»apiVersion: v1kind: Secretmetadata: name: tls-certstype: kubernetes.io/tlsdata: tls.crt: <base64-encoded-cert> tls.key: <base64-encoded-key>---apiVersion: v1kind: Podmetadata: name: tls-appspec: containers: - name: app image: nginx:1.25 volumeMounts: - name: certs mountPath: /etc/tls readOnly: true volumes: - name: certs secret: secretName: tls-certs defaultMode: 0400 # Обмежені дозволи5.3 Автооновлення для томів ConfigMap/Secret
Розділ «5.3 Автооновлення для томів ConfigMap/Secret»Коли змонтовані як томи (не змінні середовища), ConfigMaps та Secrets оновлюються автоматично:
┌──────────────────────────────────────────────────────────────┐│ Поведінка оновлення ││ ││ ConfigMap/Secret оновлено ───→ Том оновлено (протягом ~1 хв) ││ ││ ⚠️ Застереження: ││ • Використовує атомарну заміну через symlink ││ • subPath монтування НЕ оновлюються автоматично ││ • Додаток повинен виявити зміни та перезавантажитись ││ • Період синхронізації kubelet впливає на затримку │└──────────────────────────────────────────────────────────────┘5.4 Монтування subPath (без автооновлення)
Розділ «5.4 Монтування subPath (без автооновлення)»Коли потрібно змонтувати один файл у існуючий каталог:
volumeMounts:- name: config mountPath: /etc/nginx/nginx.conf # Один файл subPath: nginx.conf # Ключ із ConfigMap readOnly: trueУвага: Монтування subPath не отримують оновлень при зміні ConfigMap/Secret!
Типові помилки
Розділ «Типові помилки»| Помилка | Проблема | Рішення |
|---|---|---|
| emptyDir для постійних даних | Дані втрачаються при видаленні Пода | Використовуйте PVC |
| hostPath у продакшені | Вразливість безпеки | Використовуйте PVC або уникайте повністю |
| Немає sizeLimit на emptyDir | Под може заповнити диск вузла | Завжди встановлюйте sizeLimit |
| subPath з очікуванням оновлень | Зміни конфігурації не відображаються | Використовуйте повне монтування або перезапустіть Под |
| Memory emptyDir без ліміту | OOM kills | Встановіть sizeLimit, враховуйте в ліміті пам’яті |
| hostPath type: "" | Жодної валідації, тихі збої | Використовуйте явний тип, наприклад Directory |
Тест
Розділ «Тест»Q1: Час життя тому
Розділ «Q1: Час життя тому»Що станеться з даними emptyDir, коли контейнер падає, але Под продовжує працювати?
Відповідь
Дані emptyDir зберігаються. Час життя emptyDir прив’язаний до Пода, а не до контейнера. Лише коли Под видаляється або витісняється з вузла, emptyDir видаляється.
Q2: emptyDir з підтримкою пам’яттю
Розділ «Q2: emptyDir з підтримкою пам’яттю»Що є ключовим фактором при використанні emptyDir.medium: Memory?
Відповідь
emptyDir з підтримкою пам’яттю враховується в ліміті пам’яті контейнера. Необхідно встановити sizeLimit, щоб запобігти проблемам з OOM, і загальний обсяг повинен вміщуватись у виділену пам’ять Пода.
Q3: Безпека hostPath
Розділ «Q3: Безпека hostPath»Чому тип hostPath "" (порожній рядок) є ризикованим?
Відповідь
Тип "" не виконує жодних перевірок перед монтуванням. Шлях може не існувати, може бути неправильного типу (файл замість каталогу) або може бути символічним посиланням на неочікуване місце. Використовуйте явні типи, такі як Directory або File, для валідації.
Q4: Джерела Projected Volume
Розділ «Q4: Джерела Projected Volume»Назвіть чотири типи джерел, які можна поєднати в projected volume.
Відповідь
- configMap — дані ConfigMap
- secret — дані Secret
- downwardAPI — метадані Пода та інформація про ресурси
- serviceAccountToken — проєктовані токени service account
Q5: Автооновлення ConfigMap
Розділ «Q5: Автооновлення ConfigMap»ConfigMap змонтований як том. Ви оновлюєте ConfigMap. Що повинен зробити додаток?
Відповідь
Додаток повинен виявити зміни та перезавантажити конфігурацію. Хоча Kubernetes автоматично оновлює файли в змонтованому томі (протягом ~1 хвилини), додаток повинен відстежувати зміни та перезавантажуватись — це не відбувається автоматично. Як альтернатива — перезапустіть Под для застосування змін.
Q6: Поведінка subPath
Розділ «Q6: Поведінка subPath»Навіщо використовувати subPath і який компроміс?
Відповідь
Використовуйте subPath для монтування одного файлу з ConfigMap/Secret у існуючий каталог без перезапису інших файлів. Компроміс полягає в тому, що монтування subPath не оновлюються автоматично при зміні джерела — потрібно перезапустити Под.
Практична вправа: Обмін даними між контейнерами через томи
Розділ «Практична вправа: Обмін даними між контейнерами через томи»Сценарій
Розділ «Сценарій»Створіть Под з двома контейнерами, які обмінюються даними через томи. Один контейнер записує логи, інший їх обробляє.
Налаштування
Розділ «Налаштування»# Створити namespacek create ns volume-lab
# Створити мультиконтейнерний Подcat <<EOF | k apply -f -apiVersion: v1kind: Podmetadata: name: log-processor namespace: volume-labspec: containers: # Контейнер-записувач — генерує логи - name: writer image: busybox:1.36 command: - sh - -c - | i=0 while true; do echo "\$(date): Log entry \$i" >> /logs/app.log i=\$((i+1)) sleep 5 done volumeMounts: - name: log-volume mountPath: /logs # Контейнер-читач — обробляє логи - name: reader image: busybox:1.36 command: - sh - -c - | echo "Waiting for logs..." sleep 10 tail -f /logs/app.log volumeMounts: - name: log-volume mountPath: /logs readOnly: true volumes: - name: log-volume emptyDir: sizeLimit: 50MiEOFЗавдання
Розділ «Завдання»- Перевірте, що Под працює:
k get pod log-processor -n volume-lab- Перевірте, що записувач створює логи:
k exec -n volume-lab log-processor -c writer -- cat /logs/app.log- Перевірте, що читач бачить логи:
k logs -n volume-lab log-processor -c reader- Перевірте спільний доступ до тому:
# Записати від записувачаk exec -n volume-lab log-processor -c writer -- sh -c 'echo "TEST MESSAGE" >> /logs/app.log'
# Прочитати від читачаk exec -n volume-lab log-processor -c reader -- tail -1 /logs/app.log- Перевірте розташування emptyDir на вузлі (для розуміння):
k get pod log-processor -n volume-lab -o jsonpath='{.status.hostIP}'# emptyDir знаходиться в /var/lib/kubelet/pods/<pod-uid>/volumes/kubernetes.io~empty-dir/log-volumeКритерії успіху
Розділ «Критерії успіху»- Под працює з обома контейнерами
- Записувач створює записи логів кожні 5 секунд
- Читач бачить логи, записані записувачем
- Дані зберігаються після перезапуску контейнера (перевірте за допомогою
k exec ... -- kill 1)
Додатковий виклик
Розділ «Додатковий виклик»Додайте третій контейнер, який моніторить використання диска спільного тому та записує попередження, коли використання перевищує 80%.
Очищення
Розділ «Очищення»k delete ns volume-labПрактичні вправи
Розділ «Практичні вправи»Практикуйте ці сценарії для готовності до іспиту:
Вправа 1: Створити Под з emptyDir (2 хв)
Розділ «Вправа 1: Створити Под з emptyDir (2 хв)»# Завдання: Створити Под з томом emptyDir, змонтованим у /cache# Підказка: k run cache-pod --image=busybox --dry-run=client -o yaml > pod.yaml# Потім додайте секцію volumesВправа 2: emptyDir з підтримкою пам’яттю (2 хв)
Розділ «Вправа 2: emptyDir з підтримкою пам’яттю (2 хв)»# Завдання: Створити emptyDir з підтримкою RAM та лімітом 64Mi# Ключові поля: emptyDir.medium: Memory, emptyDir.sizeLimit: 64MiВправа 3: hostPath тільки для читання (2 хв)
Розділ «Вправа 3: hostPath тільки для читання (2 хв)»# Завдання: Змонтувати /var/log з хоста як том тільки для читання# Важливо: Завжди використовуйте readOnly: true для hostPath, коли можливоВправа 4: Projected Volume (3 хв)
Розділ «Вправа 4: Projected Volume (3 хв)»# Завдання: Створити projected volume, що поєднує:# - ConfigMap "app-config"# - Secret "app-secrets"# Змонтувати у /etc/appВправа 5: Том ConfigMap з Items (2 хв)
Розділ «Вправа 5: Том ConfigMap з Items (2 хв)»# Завдання: Змонтувати лише ключ "app.conf" з ConfigMap як "config.yaml"# Використовуйте configMap.items для вибору та перейменуванняВправа 6: Монтування subPath (2 хв)
Розділ «Вправа 6: Монтування subPath (2 хв)»# Завдання: Змонтувати один файл з ConfigMap у /etc/myapp/config.yaml# Без перезапису інших файлів у /etc/myappВправа 7: Обмін томами між контейнерами (3 хв)
Розділ «Вправа 7: Обмін томами між контейнерами (3 хв)»# Завдання: Створити Под з 2 контейнерами, що спільно використовують emptyDir# Контейнер 1 записує у /shared/data.txt# Контейнер 2 читає з /shared/data.txtВправа 8: Налагодження проблем монтування томів (2 хв)
Розділ «Вправа 8: Налагодження проблем монтування томів (2 хв)»# Дано: Под застряг у стані ContainerCreating# Завдання: Визначити, чи це проблема монтування тому# Команди: k describe pod, перевірте секцію EventsНаступний модуль
Розділ «Наступний модуль»Переходьте до Модуль 4.2: PersistentVolumes та PersistentVolumeClaims, щоб дізнатися про постійне сховище, яке переживає видалення Пода.