Модуль 1.3: Піди з кількома контейнерами
Складність:
[MEDIUM]— Ключова навичка CKAD, що вимагає розпізнавання паттернівЧас на виконання: 50–60 хвилин
Передумови: Модуль 1.1 (Образи контейнерів), Модуль 1.2 (Jobs та CronJobs)
Що ви зможете робити
Розділ «Що ви зможете робити»Після завершення цього модуля ви зможете:
- Створити Піди з кількома контейнерами, використовуючи паттерни sidecar, init-контейнер та ambassador
- Пояснити коли використовувати кожен паттерн з кількома контейнерами та їх компроміси
- Діагностувати збої init-контейнерів та проблеми комунікації через спільні томи між контейнерами
- Реалізувати паттерн sidecar для логування, що відправляє логи з основного контейнера застосунку
Чому цей модуль важливий
Розділ «Чому цей модуль важливий»Більшість застосунків потребують більше одного контейнера. Вебсерверу потрібен відправник логів. API потребує проксі. Обробнику даних потрібен ініціалізатор. Піди з кількома контейнерами — це спосіб компонувати ці частини.
Це ключова тема CKAD. Очікуйте 2–4 запитання саме про паттерни з кількома контейнерами. Вам потрібно розпізнавати, коли використовувати кожен паттерн, і реалізовувати їх швидко.
Аналогія з фуд-траком
Під — як фуд-трак. Основний контейнер — це шеф-кухар — він готує їжу. Але успішному фуд-траку потрібно більше: хтось для прийому замовлень (sidecar), хтось для підготовки перед відкриттям (init), і, можливо, каса, повернута до клієнтів іншим боком (ambassador). Вони всі ділять один трак (Під), ділять робочий простір (файлову систему) і працюють разом — але кожен має окрему роль.
Паттерни з кількома контейнерами
Розділ «Паттерни з кількома контейнерами»Три паттерни, які ви мусите знати
Розділ «Три паттерни, які ви мусите знати»┌─────────────────────────────────────────────────────────────────┐│ Паттерни з кількома контейнерами │├─────────────────────────────────────────────────────────────────┤│ ││ INIT-КОНТЕЙНЕР ││ ┌─────────────┐ ┌─────────────┐ ││ │ Init- │───▶│ Основний │ ││ │ контейнер │ │ контейнер │ ││ │(запуск першим)│ │(запуск після)│ ││ └─────────────┘ └─────────────┘ ││ • Ініціалізація даних, очікування залежностей, налаштування ││ ││ SIDECAR ││ ┌─────────────┐ ┌─────────────┐ ││ │ Основний │◀──▶│ Sidecar │ ││ │ контейнер │ │ контейнер │ ││ │(логіка додатку)│ │ (помічник) │ ││ └─────────────┘ └─────────────┘ ││ • Відправка логів, моніторинг, синхронізація конфігурації ││ ││ AMBASSADOR ││ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ││ │ Основний │───▶│ Ambassador │───▶│ Зовнішній │ ││ │ контейнер │ │ (проксі) │ │ сервіс │ ││ └─────────────┘ └─────────────┘ └─────────────┘ ││ • Проксіювання з'єднань, обробка TLS, обмеження швидкості ││ │└─────────────────────────────────────────────────────────────────┘Init-контейнери
Розділ «Init-контейнери»Init-контейнери запускаються перед стартом контейнерів застосунку. Вони виконуються послідовно, кожен має завершитися успішно перед стартом наступного.
Сценарії використання
Розділ «Сценарії використання»- Очікування готовності сервісу
- Клонування git-репозиторію або завантаження файлів
- Генерація конфігураційних файлів
- Запуск міграцій бази даних
- Очікування встановлення дозволів
YAML для Init-контейнера
Розділ «YAML для Init-контейнера»apiVersion: v1kind: Podmetadata: name: init-demospec: initContainers: - name: init-wait image: busybox command: ['sh', '-c', 'until nslookup myservice; do echo waiting; sleep 2; done'] - name: init-setup image: busybox command: ['sh', '-c', 'echo "Setup complete" > /data/ready'] volumeMounts: - name: shared mountPath: /data containers: - name: main image: nginx volumeMounts: - name: shared mountPath: /usr/share/nginx/html volumes: - name: shared emptyDir: {}Ключові властивості
Розділ «Ключові властивості»| Властивість | Поведінка |
|---|---|
| Порядок запуску | Послідовний (init1, потім init2, потім основний) |
| Невдача | Під перезапускається, якщо будь-який init-контейнер не вдається |
| Політика перезапуску | Завжди перезапускати з першого init при перезапуску Підa |
| Ресурси | Можуть мати інші ліміти ресурсів, ніж контейнери застосунку |
| Проби | Немає liveness/readiness проб (їм просто потрібно завершитися з кодом 0) |
Статус Init-контейнера
Розділ «Статус Init-контейнера»# Перевірити статус init-контейнераk get pod init-demo
# Детальний статусk describe pod init-demo | grep -A10 "Init Containers"
# Логи init-контейнераk logs init-demo -c init-waitSidecar-контейнери
Розділ «Sidecar-контейнери»Sidecar-контейнери працюють поруч з основним контейнером протягом усього життя Підa. Вони розширюють функціональність без модифікації основного застосунку.
Сценарії використання
Розділ «Сценарії використання»- Агрегація логів (відправка логів до центральної системи)
- Агенти моніторингу (збір метрик)
- Синхронізація конфігурації (відстеження змін конфігурації)
- Проксі сервісних меш (Istio, Linkerd)
- Наповнення кешу
YAML для Sidecar
Розділ «YAML для Sidecar»apiVersion: v1kind: Podmetadata: name: sidecar-demospec: containers: - name: main image: nginx volumeMounts: - name: logs mountPath: /var/log/nginx - name: log-shipper image: busybox command: ['sh', '-c', 'tail -F /var/log/nginx/access.log'] volumeMounts: - name: logs mountPath: /var/log/nginx volumes: - name: logs emptyDir: {}Спільний доступ до даних між контейнерами
Розділ «Спільний доступ до даних між контейнерами»Контейнери у Піді можуть ділити:
- Томи (найчастіше)
volumes:- name: shared emptyDir: {}- Мережу (один localhost)
# Основний контейнер відкриває :8080# Sidecar може звертатися до localhost:8080- Простір процесів (рідко)
spec: shareProcessNamespace: trueПаттерн Ambassador
Розділ «Паттерн Ambassador»Ambassador-контейнери проксіюють з’єднання до зовнішніх сервісів, абстрагуючи складність від основного контейнера.
Сценарії використання
Розділ «Сценарії використання»- Пулінг з’єднань до бази даних
- Термінація TLS
- Виявлення сервісів
- Обмеження швидкості
- Трансляція протоколів
YAML для Ambassador
Розділ «YAML для Ambassador»apiVersion: v1kind: Podmetadata: name: ambassador-demospec: containers: - name: main image: myapp env: - name: DB_HOST value: "localhost" # Ambassador обробляє реальне з'єднання - name: DB_PORT value: "5432" - name: db-proxy image: ambassador-proxy env: - name: REAL_DB_HOST value: "db.production.svc" - name: REAL_DB_PORT value: "5432" ports: - containerPort: 5432 # Слухає на localhost:5432 для основногоРозпізнавання паттернів
Розділ «Розпізнавання паттернів»Коли використовувати кожен паттерн?
| Сценарій | Паттерн | Чому |
|---|---|---|
| Очікування бази даних перед стартом | Init | Одноразова перевірка залежності |
| Відправка логів до Elasticsearch | Sidecar | Безперервна операція |
| Завантаження конфігурації перед стартом додатку | Init | Завдання налаштування |
| Відстеження змін конфігураційного файлу | Sidecar | Безперервна операція |
| Проксіювання з’єднань до бази даних | Ambassador | Шар абстракції |
| Запуск міграцій бази даних | Init | Одноразова операція |
| Додавання TLS до додатку без TLS | Ambassador | Обробка протоколу |
| Збір метрик Prometheus | Sidecar | Безперервна операція |
Швидке створення Підів з кількома контейнерами
Розділ «Швидке створення Підів з кількома контейнерами»Ви не можете створити Піди з кількома контейнерами імперативно. Використовуйте паттерн “згенерувати-і-відредагувати”:
Крок 1: Згенерувати базу
Розділ «Крок 1: Згенерувати базу»k run multi --image=nginx --dry-run=client -o yaml > multi.yamlКрок 2: Додати контейнери
Розділ «Крок 2: Додати контейнери»Відредагуйте multi.yaml:
apiVersion: v1kind: Podmetadata: name: multispec: containers: - name: nginx image: nginx - name: sidecar # ДОДАЙТЕ ЦЕ image: busybox # ДОДАЙТЕ ЦЕ command: ["sleep", "3600"] # ДОДАЙТЕ ЦЕКрок 3: Додати Init-контейнери (за потреби)
Розділ «Крок 3: Додати Init-контейнери (за потреби)»apiVersion: v1kind: Podmetadata: name: multispec: initContainers: # ДОДАЙТЕ ЦЮ СЕКЦІЮ - name: init image: busybox command: ["sh", "-c", "echo init done"] containers: - name: nginx image: nginx - name: sidecar image: busybox command: ["sleep", "3600"]Налагодження Підів з кількома контейнерами
Розділ «Налагодження Підів з кількома контейнерами»Вказання контейнера
Розділ «Вказання контейнера»# Логи конкретного контейнераk logs multi -c sidecar
# Exec у конкретний контейнерk exec -it multi -c sidecar -- sh
# Describe показує всі контейнериk describe pod multiПеревірка статусу контейнера
Розділ «Перевірка статусу контейнера»# Статуси всіх контейнерівk get pod multi -o jsonpath='{.status.containerStatuses[*].name}'
# Перевірка готовностіk get pod multi -o jsonpath='{range .status.containerStatuses[*]}{.name}{"\t"}{.ready}{"\n"}{end}'Типові проблеми
Розділ «Типові проблеми»| Симптом | Причина | Рішення |
|---|---|---|
Під застряг у Init:0/1 | Init-контейнер не завершується | Перевірте логи init-контейнера |
Один контейнер у CrashLoopBackOff | Команда контейнера завершується | Виправте команду або додайте sleep |
| Контейнери не можуть ділити дані | Немає спільного тому | Додайте том emptyDir |
| Основний не може досягти sidecar | Неправильна конфігурація мережі | Використовуйте localhost:port |
Чи знали ви?
Розділ «Чи знали ви?»-
Init-контейнери можуть мати інші образи, ніж контейнери застосунку. Використовуйте спеціалізовані інструменти (як
git, клієнти баз даних) в init-контейнерах без роздування вашого образу застосунку. -
Sidecar-контейнери традиційно перезапускаються разом з Підом, але Kubernetes 1.28+ додав нативну підтримку sidecar з
restartPolicy: Alwaysдля init-контейнерів, роблячи їх справжніми sidecar-контейнерами, що перезапускаються незалежно. -
Паттерн ambassador з’явився раніше за сервісні меші. До Istio та Linkerd розробники використовували ambassador-контейнери для обробки крос-функціональних задач. Тепер сервісні меші автоматизують ін’єкцію sidecar.
Типові помилки
Розділ «Типові помилки»| Помилка | Чому це шкодить | Рішення |
|---|---|---|
Забули -c container | Логи не того контейнера | Завжди вказуйте контейнер |
Init-контейнер зі sleep | Під ніколи не запуститься | Init має завершитися з кодом 0 |
| Немає спільного тому | Контейнери не можуть обмінюватися файлами | Додайте emptyDir |
| Sidecar завершується негайно | Під постійно перезапускається | Додайте sleep infinity або реальний сервіс |
| Неправильний порт у localhost | Відмова з’єднання | Перевірте відповідність портів |
Тест
Розділ «Тест»-
У якому порядку запускаються init-контейнери?
Відповідь
Послідовно, один за одним. Кожен має завершитися успішно (вийти з кодом 0) перед стартом наступного. Контейнери застосунку запускаються лише після завершення всіх init-контейнерів. -
Як контейнери в одному Піді обмінюються файлами?
Відповідь
Через спільні томи. Визначте том (наприклад, `emptyDir`) і змонтуйте його в обох контейнерах. Вони зможуть читати/писати за однаковим шляхом. -
Який паттерн ви використаєте, щоб дочекатися готовності бази даних перед стартом вашого застосунку?
Відповідь
Init-контейнер. Він запускається перед основним контейнером і може циклічно перевіряти доступність бази даних, а потім успішно завершитися. -
Як переглянути логи конкретного контейнера у Піді з кількома контейнерами?
Відповідь
`kubectl logs pod-name -c container-name`. Прапор `-c` вказує, логи якого контейнера отримати.
Практична вправа
Розділ «Практична вправа»Завдання: Побудувати Під з init-, sidecar- та основним контейнерами.
Сценарій: Створіть застосунок, що:
- Init: Завантажує конфігурацію з URL (симуляція)
- Основний: Запускає nginx, що обслуговує конфігурацію
- Sidecar: Моніторить та логує зміни
apiVersion: v1kind: Podmetadata: name: full-patternspec: initContainers: - name: config-init image: busybox command: ['sh', '-c', 'echo "Welcome to CKAD!" > /data/index.html'] volumeMounts: - name: html mountPath: /data containers: - name: nginx image: nginx volumeMounts: - name: html mountPath: /usr/share/nginx/html ports: - containerPort: 80 - name: monitor image: busybox command: ['sh', '-c', 'while true; do echo "Checking..."; cat /data/index.html; sleep 10; done'] volumeMounts: - name: html mountPath: /data volumes: - name: html emptyDir: {}Перевірка:
# Застосуватиk apply -f full-pattern.yaml
# Дочекатися готовностіk get pod full-pattern -w
# Перевірити завершення initk describe pod full-pattern | grep -A5 "Init Containers"
# Перевірити, що nginx обслуговує контентk exec full-pattern -c nginx -- curl localhost
# Перевірити логи монітораk logs full-pattern -c monitor
# Очищенняk delete pod full-patternПрактичні вправи
Розділ «Практичні вправи»Вправа 1: Базовий Init-контейнер (Ціль: 3 хвилини)
Розділ «Вправа 1: Базовий Init-контейнер (Ціль: 3 хвилини)»# Створити Під з init-контейнеромcat << 'EOF' | k apply -f -apiVersion: v1kind: Podmetadata: name: init-podspec: initContainers: - name: init image: busybox command: ["sh", "-c", "echo 'Init complete' && sleep 3"] containers: - name: main image: nginxEOF
# Спостерігати за стартом Підak get pod init-pod -w
# Перевірити логи initk logs init-pod -c init
# Очищенняk delete pod init-podВправа 2: Базовий Sidecar (Ціль: 3 хвилини)
Розділ «Вправа 2: Базовий Sidecar (Ціль: 3 хвилини)»# Створити Під із sidecarcat << 'EOF' | k apply -f -apiVersion: v1kind: Podmetadata: name: sidecar-podspec: containers: - name: main image: nginx - name: sidecar image: busybox command: ["sh", "-c", "while true; do echo 'Sidecar running'; sleep 5; done"]EOF
# Перевірити обидва контейнериk get pod sidecar-pod -o jsonpath='{.spec.containers[*].name}'
# Перевірити логи sidecark logs sidecar-pod -c sidecar
# Очищенняk delete pod sidecar-podВправа 3: Спільний том (Ціль: 4 хвилини)
Розділ «Вправа 3: Спільний том (Ціль: 4 хвилини)»# Створити Під зі спільним томомcat << 'EOF' | k apply -f -apiVersion: v1kind: Podmetadata: name: shared-volspec: containers: - name: writer image: busybox command: ["sh", "-c", "while true; do date >> /shared/log.txt; sleep 5; done"] volumeMounts: - name: shared mountPath: /shared - name: reader image: busybox command: ["sh", "-c", "tail -f /shared/log.txt"] volumeMounts: - name: shared mountPath: /shared volumes: - name: shared emptyDir: {}EOF
# Перевірити, що reader бачить дані writerk logs shared-vol -c reader
# Очищенняk delete pod shared-volВправа 4: Init, що очікує на сервіс (Ціль: 5 хвилин)
Розділ «Вправа 4: Init, що очікує на сервіс (Ціль: 5 хвилин)»# Спочатку створити сервісk create svc clusterip wait-svc --tcp=80:80
# Створити Під, що очікує на сервісcat << 'EOF' | k apply -f -apiVersion: v1kind: Podmetadata: name: wait-podspec: initContainers: - name: wait image: busybox command: ['sh', '-c', 'until nslookup wait-svc; do echo waiting; sleep 2; done'] containers: - name: main image: nginxEOF
# Перевірити статус initk describe pod wait-pod | grep -A3 "Init Containers"
# Очищенняk delete pod wait-podk delete svc wait-svcВправа 5: Паттерн Ambassador (Ціль: 5 хвилин)
Розділ «Вправа 5: Паттерн Ambassador (Ціль: 5 хвилин)»# Створити Під з ambassador-проксіcat << 'EOF' | k apply -f -apiVersion: v1kind: Podmetadata: name: ambassador-podspec: containers: - name: main image: busybox command: ["sh", "-c", "while true; do wget -qO- localhost:8080; sleep 10; done"] - name: proxy image: nginx ports: - containerPort: 8080EOF
# Основний контейнер звертається до проксі через localhostk logs ambassador-pod -c main
# Очищенняk delete pod ambassador-podВправа 6: Повне завдання з кількома контейнерами (Ціль: 8 хвилин)
Розділ «Вправа 6: Повне завдання з кількома контейнерами (Ціль: 8 хвилин)»Без підказок — створіть з пам’яті:
Створіть Під з іменем app-complete з:
- Init-контейнер: Створює
/data/config.txtз текстом “Config loaded” - Основний контейнер (nginx): Обслуговує директорію
/data - Sidecar: Моніторить
/data/config.txtкожні 5 секунд
Після створення перевірте:
- Під працює (Running)
- Init завершився успішно
- Sidecar показує вміст конфігурації
Відповідь
apiVersion: v1kind: Podmetadata: name: app-completespec: initContainers: - name: init image: busybox command: ["sh", "-c", "echo 'Config loaded' > /data/config.txt"] volumeMounts: - name: data mountPath: /data containers: - name: nginx image: nginx volumeMounts: - name: data mountPath: /usr/share/nginx/html - name: monitor image: busybox command: ["sh", "-c", "while true; do cat /data/config.txt; sleep 5; done"] volumeMounts: - name: data mountPath: /data volumes: - name: data emptyDir: {}k apply -f app-complete.yamlk get pod app-completek logs app-complete -c initk logs app-complete -c monitork delete pod app-completeНаступний модуль
Розділ «Наступний модуль»Модуль 1.4: Томи для розробників — Паттерни постійного та ефемерного зберігання.