Перейти до вмісту

Модуль 1.4: Томи для розробників

Hands-On Lab Available
K8s Cluster intermediate 30 min
Launch Lab ↗

Opens in Killercoda in a new tab

Складність: [MEDIUM] — Необхідний для застосунків зі збереженням стану

Час на виконання: 40–50 хвилин

Передумови: Модуль 1.3 (Піди з кількома контейнерами)


Що ви зможете робити

Розділ «Що ви зможете робити»

Після завершення цього модуля ви зможете:

  • Створити Піди з томами emptyDir, hostPath та PersistentVolumeClaim
  • Налаштувати монтування томів для обміну даними між контейнерами в одному Поді
  • Діагностувати помилки монтування томів, включно з проблемами прав доступу та відсутніми PVC
  • Пояснити різницю між ефемерними та постійними томами та коли використовувати кожен

Чому цей модуль важливий

Розділ «Чому цей модуль важливий»

Контейнери ефемерні — при перезапуску всі дані втрачаються. Для реальних застосунків потрібне постійне зберігання: бази даних потребують надійних даних, застосунки потребують спільних файлів, а контейнерам потрібні способи обміну даними.

CKAD тестує практичне використання томів: монтування ConfigMaps, обмін даними між контейнерами та використання постійного зберігання. Ви не будете керувати StorageClasses (це CKA), але будете використовувати PersistentVolumeClaims.

Аналогія зі шухлядою стола

Файлова система контейнера — як дошка для записів — корисна, поки ви поруч, але стирається, коли ви йдете. Том emptyDir — як спільний стіл у кімнаті для нарад — усі учасники наради можуть ним користуватися, але він очищується, коли нарада закінчується. PersistentVolume — як ваша шухляда стола — вона ваша, зберігається між робочими днями і містить ваші важливі файли.


Типи томів для розробників

Розділ «Типи томів для розробників»
Тип томуЗбереженняСпільний доступСценарій використання
emptyDirЧас життя ПідaМіж контейнерамиТимчасовий простір, кеші
hostPathЧас життя вузлаНіДоступ до вузла (лише для розробки)
configMapЧас життя кластераТільки читанняКонфігураційні файли
secretЧас життя кластераТільки читанняЧутливі дані
persistentVolumeClaimПоза ПідомЗалежитьБази даних, застосунки зі збереженням стану
projectedРізнеТільки читанняОб’єднання кількох джерел

emptyDir: Тимчасове спільне сховище

Розділ «emptyDir: Тимчасове спільне сховище»

emptyDir створюється при запуску Підa і видаляється при видаленні Підa. Ідеальний для:

  • Обміну файлами між контейнерами
  • Тимчасового простору для обчислень
  • Кешів
apiVersion: v1
kind: Pod
metadata:
name: emptydir-demo
spec:
containers:
- name: writer
image: busybox
command: ["sh", "-c", "echo 'Hello' > /data/message && sleep 3600"]
volumeMounts:
- name: shared
mountPath: /data
- name: reader
image: busybox
command: ["sh", "-c", "cat /data/message && sleep 3600"]
volumeMounts:
- name: shared
mountPath: /data
volumes:
- name: shared
emptyDir: {}

emptyDir в оперативній пам’яті

Розділ «emptyDir в оперативній пам’яті»

Для швидкого тимчасового простору:

volumes:
- name: cache
emptyDir:
medium: Memory # Використовує RAM замість диска
sizeLimit: 100Mi # Обмеження використання пам'яті

Монтуйте ConfigMaps як файли. Кожен ключ стає файлом.

Terminal window
# З літералів
k create configmap app-config \
--from-literal=log_level=debug \
--from-literal=api_url=http://api.example.com
# З файлу
k create configmap nginx-config --from-file=nginx.conf
apiVersion: v1
kind: Pod
metadata:
name: config-demo
spec:
containers:
- name: app
image: busybox
command: ["sh", "-c", "cat /config/log_level && sleep 3600"]
volumeMounts:
- name: config
mountPath: /config
volumes:
- name: config
configMap:
name: app-config

Результат:

/config/
├── log_level # Містить "debug"
└── api_url # Містить "http://api.example.com"

Монтування конкретних ключів

Розділ «Монтування конкретних ключів»
volumes:
- name: config
configMap:
name: app-config
items:
- key: log_level
path: logging/level.txt # Власний шлях

SubPath: Монтування одного файлу без перезапису

Розділ «SubPath: Монтування одного файлу без перезапису»
volumeMounts:
- name: config
mountPath: /etc/app/config.yaml # Конкретний файл
subPath: config.yaml # Ключ з ConfigMap

Як ConfigMaps, але для чутливих даних. Змонтовані файли зберігаються в tmpfs (в оперативній пам’яті).

Terminal window
k create secret generic db-creds \
--from-literal=username=admin \
--from-literal=password=secret123
apiVersion: v1
kind: Pod
metadata:
name: secret-demo
spec:
containers:
- name: app
image: busybox
command: ["sh", "-c", "cat /secrets/password && sleep 3600"]
volumeMounts:
- name: db-secrets
mountPath: /secrets
readOnly: true
volumes:
- name: db-secrets
secret:
secretName: db-creds

Права доступу до файлів

Розділ «Права доступу до файлів»
volumes:
- name: db-secrets
secret:
secretName: db-creds
defaultMode: 0400 # Тільки читання для власника

Для даних, що переживають перезапуски Підів. Як розробник, ви запитуєте сховище через PVC; кластер його забезпечує.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-pvc
spec:
accessModes:
- ReadWriteOnce # RWO, ROX, RWX
resources:
requests:
storage: 1Gi
# storageClassName: fast # Необов'язково: конкретний клас
РежимСкороченняОпис
ReadWriteOnceRWOОдин вузол може монтувати для читання-запису
ReadOnlyManyROXБагато вузлів можуть монтувати тільки для читання
ReadWriteManyRWXБагато вузлів можуть монтувати для читання-запису

Використання PVC у Піді

Розділ «Використання PVC у Піді»
apiVersion: v1
kind: Pod
metadata:
name: pvc-demo
spec:
containers:
- name: app
image: nginx
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: data-pvc

Імперативне створення PVC

Розділ «Імперативне створення PVC»
Terminal window
# Прямої імперативної команди немає, але YAML пишеться швидко
cat << 'EOF' | k apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
EOF

Об’єднують кілька джерел в одну точку монтування.

apiVersion: v1
kind: Pod
metadata:
name: projected-demo
spec:
containers:
- name: app
image: busybox
command: ["sh", "-c", "ls -la /projected && sleep 3600"]
volumeMounts:
- name: all-config
mountPath: /projected
volumes:
- name: all-config
projected:
sources:
- configMap:
name: app-config
- secret:
name: app-secrets
- downwardAPI:
items:
- path: "labels"
fieldRef:
fieldPath: metadata.labels

Типові паттерни томів

Розділ «Типові паттерни томів»

Паттерн 1: Спільний тимчасовий простір

Розділ «Паттерн 1: Спільний тимчасовий простір»
spec:
containers:
- name: processor
image: processor
volumeMounts:
- name: scratch
mountPath: /tmp/work
- name: uploader
image: uploader
volumeMounts:
- name: scratch
mountPath: /data
volumes:
- name: scratch
emptyDir: {}

Паттерн 2: Конфігурація + Секрети

Розділ «Паттерн 2: Конфігурація + Секрети»
spec:
containers:
- name: app
image: myapp
volumeMounts:
- name: config
mountPath: /etc/app
- name: secrets
mountPath: /etc/secrets
readOnly: true
volumes:
- name: config
configMap:
name: app-config
- name: secrets
secret:
secretName: app-secrets

Паттерн 3: Init-контейнер готує дані

Розділ «Паттерн 3: Init-контейнер готує дані»
spec:
initContainers:
- name: download
image: curlimages/curl
command: ["curl", "-o", "/data/app.tar", "http://example.com/app.tar"]
volumeMounts:
- name: app-data
mountPath: /data
containers:
- name: app
image: myapp
volumeMounts:
- name: app-data
mountPath: /app
volumes:
- name: app-data
emptyDir: {}

Усунення проблем з томами

Розділ «Усунення проблем з томами»

Перевірка статусу тому

Розділ «Перевірка статусу тому»
Terminal window
# Томи Підa
k describe pod myapp | grep -A10 Volumes
# Статус PVC
k get pvc
# Деталі PVC
k describe pvc data-pvc
СимптомПричинаРішення
Під застряг у PendingPVC не прив’язаноПеревірте наявність PV
Відмова у доступіНеправильний режим/користувачВстановіть securityContext.fsGroup
Файл не знайденоНеправильний mountPathПеревірте відповідність шляхів
ConfigMap не оновлюєтьсяЗмонтовані файли кешуютьсяПерезапустіть Під або обережно використовуйте subPath

Виправлення проблем з правами доступу

Розділ «Виправлення проблем з правами доступу»
spec:
securityContext:
fsGroup: 1000 # ID групи для файлів тому
containers:
- name: app
image: myapp
securityContext:
runAsUser: 1000

  • ConfigMaps та Secrets є зрештою узгодженими. Коли ви їх оновлюєте, Піди бачать зміни протягом хвилини — але НЕ якщо ви використали монтування через subPath. Монтування subPath — це знімки, що не оновлюються автоматично.

  • emptyDir за замовчуванням використовує диск вузла, але може використовувати RAM (medium: Memory). Томи в оперативній пам’яті швидші, але враховуються в лімітах пам’яті контейнера.

  • Видалення PVC блокується, якщо Під його використовує. Спочатку видаліть Під, потім PVC. Встановіть persistentVolumeReclaimPolicy: Delete для автоматичного видалення базового сховища при видаленні PVC.


ПомилкаЧому це шкодитьРішення
Забули volumeMountsТом визначено, але не змонтованоДодайте mount до контейнера
Неправильний mountPathФайли з’являються у неочікуваному місціРетельно перевірте шляхи
Використання subPath для оновлень у реальному часіОновлення не поширюютьсяУникайте subPath або перезапустіть Під
PVC з неправильним режимом доступуБагатовузлові застосунки не працюютьВикористовуйте RWX для спільного доступу
Відсутнє визначення томуПід не запускаєтьсяВизначте том у spec.volumes

  1. Що відбувається з даними в emptyDir, коли Під видаляється?

    Відповідь Дані видаляються. Сховище emptyDir прив'язане до життєвого циклу Підa — воно створюється при запуску Підa і видаляється при його видаленні.
  2. Як змонтувати лише один ключ з ConfigMap як конкретний файл?

    Відповідь Використовуйте поле `items` з `path`: ```yaml volumes: - name: config configMap: name: my-config items: - key: app.conf path: application.conf ```
  3. Який режим доступу дозволяє кільком вузлам монтувати том для читання-запису?

    Відповідь `ReadWriteMany` (RWX). Це необхідно для спільного сховища між Підами на різних вузлах, наприклад NFS або хмарні файлові сховища.
  4. Як контейнери в одному Піді обмінюються файлами?

    Відповідь Через спільний том. Визначте `emptyDir` (або інший тип тому) і змонтуйте його в обох контейнерах. Вони зможуть читати/писати ті самі файли.

Завдання: Створити повний застосунок з кількома типами томів.

Сценарій: Побудуйте застосунок, що:

  1. Використовує ConfigMap для конфігурації
  2. Використовує Secret для облікових даних
  3. Використовує emptyDir для спільного кешу між контейнерами
  4. Використовує PVC для постійних даних
apiVersion: v1
kind: ConfigMap
metadata:
name: app-settings
data:
config.json: |
{"logLevel": "info", "cacheEnabled": true}
---
apiVersion: v1
kind: Secret
metadata:
name: app-creds
type: Opaque
stringData:
api-key: super-secret-key
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: app-data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 100Mi
---
apiVersion: v1
kind: Pod
metadata:
name: volumes-app
spec:
containers:
- name: app
image: nginx
volumeMounts:
- name: config
mountPath: /etc/app
- name: secrets
mountPath: /etc/secrets
readOnly: true
- name: cache
mountPath: /tmp/cache
- name: data
mountPath: /data
- name: cache-warmer
image: busybox
command: ["sh", "-c", "while true; do echo 'Cache data' > /cache/warm; sleep 30; done"]
volumeMounts:
- name: cache
mountPath: /cache
volumes:
- name: config
configMap:
name: app-settings
- name: secrets
secret:
secretName: app-creds
- name: cache
emptyDir: {}
- name: data
persistentVolumeClaim:
claimName: app-data

Перевірка:

Terminal window
# Застосувати всі ресурси
k apply -f volumes-app.yaml
# Перевірити, що Під працює
k get pod volumes-app
# Перевірити монтування
k exec volumes-app -c app -- ls -la /etc/app
k exec volumes-app -c app -- ls -la /etc/secrets
k exec volumes-app -c app -- ls -la /tmp/cache
k exec volumes-app -c app -- ls -la /data
# Перевірити, що PVC прив'язано
k get pvc app-data
# Очищення
k delete pod volumes-app
k delete pvc app-data
k delete configmap app-settings
k delete secret app-creds

Вправа 1: Спільний emptyDir (Ціль: 3 хвилини)

Розділ «Вправа 1: Спільний emptyDir (Ціль: 3 хвилини)»
Terminal window
# Створити Під зі спільним emptyDir
cat << 'EOF' | k apply -f -
apiVersion: v1
kind: Pod
metadata:
name: shared-pod
spec:
containers:
- name: writer
image: busybox
command: ["sh", "-c", "echo hello > /shared/msg && sleep 3600"]
volumeMounts:
- name: shared
mountPath: /shared
- name: reader
image: busybox
command: ["sh", "-c", "sleep 5 && cat /shared/msg && sleep 3600"]
volumeMounts:
- name: shared
mountPath: /shared
volumes:
- name: shared
emptyDir: {}
EOF
# Перевірити, що спільний доступ працює
k logs shared-pod -c reader
# Очищення
k delete pod shared-pod

Вправа 2: Том ConfigMap (Ціль: 3 хвилини)

Розділ «Вправа 2: Том ConfigMap (Ціль: 3 хвилини)»
Terminal window
# Створити ConfigMap
k create configmap web-config --from-literal=index.html="Welcome to CKAD!"
# Створити Під з ConfigMap
cat << 'EOF' | k apply -f -
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
configMap:
name: web-config
EOF
# Перевірити вміст
k exec web -- cat /usr/share/nginx/html/index.html
# Очищення
k delete pod web
k delete cm web-config

Вправа 3: Том Secret (Ціль: 3 хвилини)

Розділ «Вправа 3: Том Secret (Ціль: 3 хвилини)»
Terminal window
# Створити Secret
k create secret generic db-pass --from-literal=password=mysecret
# Змонтувати у Під
cat << 'EOF' | k apply -f -
apiVersion: v1
kind: Pod
metadata:
name: secret-pod
spec:
containers:
- name: app
image: busybox
command: ["sh", "-c", "cat /secrets/password && sleep 3600"]
volumeMounts:
- name: creds
mountPath: /secrets
readOnly: true
volumes:
- name: creds
secret:
secretName: db-pass
EOF
# Перевірити, що secret змонтовано
k logs secret-pod
# Очищення
k delete pod secret-pod
k delete secret db-pass

Вправа 4: Використання PVC (Ціль: 4 хвилини)

Розділ «Вправа 4: Використання PVC (Ціль: 4 хвилини)»
Terminal window
# Створити PVC
cat << 'EOF' | k apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-pvc
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 50Mi
EOF
# Використати у Піді
cat << 'EOF' | k apply -f -
apiVersion: v1
kind: Pod
metadata:
name: pvc-pod
spec:
containers:
- name: app
image: nginx
volumeMounts:
- name: storage
mountPath: /data
volumes:
- name: storage
persistentVolumeClaim:
claimName: test-pvc
EOF
# Перевірити, що PVC прив'язано
k get pvc test-pvc
# Записати дані
k exec pvc-pod -- sh -c "echo 'Persistent!' > /data/test.txt"
k exec pvc-pod -- cat /data/test.txt
# Очищення
k delete pod pvc-pod
k delete pvc test-pvc

Вправа 5: Projected-том (Ціль: 4 хвилини)

Розділ «Вправа 5: Projected-том (Ціль: 4 хвилини)»
Terminal window
# Створити джерела
k create cm proj-config --from-literal=config=value
k create secret generic proj-secret --from-literal=secret=hidden
# Створити Під з projected-томом
cat << 'EOF' | k apply -f -
apiVersion: v1
kind: Pod
metadata:
name: proj-pod
labels:
app: projected
spec:
containers:
- name: app
image: busybox
command: ["sh", "-c", "ls -la /projected && sleep 3600"]
volumeMounts:
- name: combined
mountPath: /projected
volumes:
- name: combined
projected:
sources:
- configMap:
name: proj-config
- secret:
name: proj-secret
EOF
# Перевірити об'єднані файли
k exec proj-pod -- ls /projected
# Очищення
k delete pod proj-pod
k delete cm proj-config
k delete secret proj-secret

Вправа 6: Повне завдання з томами (Ціль: 6 хвилин)

Розділ «Вправа 6: Повне завдання з томами (Ціль: 6 хвилин)»

Створіть з пам’яті — без підказок:

Створіть Під data-processor, що:

  1. Init-контейнер завантажує “дані” (симуляція через echo)
  2. Основний контейнер обробляє дані (nginx)
  3. Sidecar логує статус обробки
  4. Використовує emptyDir для спільних даних
  5. Монтує ConfigMap з налаштуваннями обробки
Відповідь
Terminal window
# Створити ConfigMap
k create cm processing-config --from-literal=mode=fast
# Створити Під
cat << 'EOF' | k apply -f -
apiVersion: v1
kind: Pod
metadata:
name: data-processor
spec:
initContainers:
- name: downloader
image: busybox
command: ["sh", "-c", "echo 'Downloaded data' > /data/input.txt"]
volumeMounts:
- name: data
mountPath: /data
containers:
- name: processor
image: nginx
volumeMounts:
- name: data
mountPath: /data
- name: config
mountPath: /etc/config
- name: logger
image: busybox
command: ["sh", "-c", "while true; do echo Processing $(cat /data/input.txt); sleep 5; done"]
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
emptyDir: {}
- name: config
configMap:
name: processing-config
EOF
# Перевірка
k get pod data-processor
k logs data-processor -c logger
k exec data-processor -c processor -- cat /etc/config/mode
# Очищення
k delete pod data-processor
k delete cm processing-config

Підсумковий тест Частини 1 — Перевірте свої знання з проєктування та збірки застосунків.