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

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

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

Opens in Killercoda in a new tab

Складність: [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-контейнери запускаються перед стартом контейнерів застосунку. Вони виконуються послідовно, кожен має завершитися успішно перед стартом наступного.

Сценарії використання

Розділ «Сценарії використання»
  • Очікування готовності сервісу
  • Клонування git-репозиторію або завантаження файлів
  • Генерація конфігураційних файлів
  • Запуск міграцій бази даних
  • Очікування встановлення дозволів

YAML для Init-контейнера

Розділ «YAML для Init-контейнера»
apiVersion: v1
kind: Pod
metadata:
name: init-demo
spec:
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-контейнера»
Terminal window
# Перевірити статус init-контейнера
k get pod init-demo
# Детальний статус
k describe pod init-demo | grep -A10 "Init Containers"
# Логи init-контейнера
k logs init-demo -c init-wait

Sidecar-контейнери працюють поруч з основним контейнером протягом усього життя Підa. Вони розширюють функціональність без модифікації основного застосунку.

Сценарії використання

Розділ «Сценарії використання»
  • Агрегація логів (відправка логів до центральної системи)
  • Агенти моніторингу (збір метрик)
  • Синхронізація конфігурації (відстеження змін конфігурації)
  • Проксі сервісних меш (Istio, Linkerd)
  • Наповнення кешу
apiVersion: v1
kind: Pod
metadata:
name: sidecar-demo
spec:
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: {}

Спільний доступ до даних між контейнерами

Розділ «Спільний доступ до даних між контейнерами»

Контейнери у Піді можуть ділити:

  1. Томи (найчастіше)
volumes:
- name: shared
emptyDir: {}
  1. Мережу (один localhost)
# Основний контейнер відкриває :8080
# Sidecar може звертатися до localhost:8080
  1. Простір процесів (рідко)
spec:
shareProcessNamespace: true

Ambassador-контейнери проксіюють з’єднання до зовнішніх сервісів, абстрагуючи складність від основного контейнера.

Сценарії використання

Розділ «Сценарії використання»
  • Пулінг з’єднань до бази даних
  • Термінація TLS
  • Виявлення сервісів
  • Обмеження швидкості
  • Трансляція протоколів
apiVersion: v1
kind: Pod
metadata:
name: ambassador-demo
spec:
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Одноразова перевірка залежності
Відправка логів до ElasticsearchSidecarБезперервна операція
Завантаження конфігурації перед стартом додаткуInitЗавдання налаштування
Відстеження змін конфігураційного файлуSidecarБезперервна операція
Проксіювання з’єднань до бази данихAmbassadorШар абстракції
Запуск міграцій бази данихInitОдноразова операція
Додавання TLS до додатку без TLSAmbassadorОбробка протоколу
Збір метрик PrometheusSidecarБезперервна операція

Швидке створення Підів з кількома контейнерами

Розділ «Швидке створення Підів з кількома контейнерами»

Ви не можете створити Піди з кількома контейнерами імперативно. Використовуйте паттерн “згенерувати-і-відредагувати”:

Крок 1: Згенерувати базу

Розділ «Крок 1: Згенерувати базу»
Terminal window
k run multi --image=nginx --dry-run=client -o yaml > multi.yaml

Крок 2: Додати контейнери

Розділ «Крок 2: Додати контейнери»

Відредагуйте multi.yaml:

apiVersion: v1
kind: Pod
metadata:
name: multi
spec:
containers:
- name: nginx
image: nginx
- name: sidecar # ДОДАЙТЕ ЦЕ
image: busybox # ДОДАЙТЕ ЦЕ
command: ["sleep", "3600"] # ДОДАЙТЕ ЦЕ

Крок 3: Додати Init-контейнери (за потреби)

Розділ «Крок 3: Додати Init-контейнери (за потреби)»
apiVersion: v1
kind: Pod
metadata:
name: multi
spec:
initContainers: # ДОДАЙТЕ ЦЮ СЕКЦІЮ
- name: init
image: busybox
command: ["sh", "-c", "echo init done"]
containers:
- name: nginx
image: nginx
- name: sidecar
image: busybox
command: ["sleep", "3600"]

Налагодження Підів з кількома контейнерами

Розділ «Налагодження Підів з кількома контейнерами»

Вказання контейнера

Розділ «Вказання контейнера»
Terminal window
# Логи конкретного контейнера
k logs multi -c sidecar
# Exec у конкретний контейнер
k exec -it multi -c sidecar -- sh
# Describe показує всі контейнери
k describe pod multi

Перевірка статусу контейнера

Розділ «Перевірка статусу контейнера»
Terminal window
# Статуси всіх контейнерів
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/1Init-контейнер не завершуєтьсяПеревірте логи 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Відмова з’єднанняПеревірте відповідність портів

  1. У якому порядку запускаються init-контейнери?

    Відповідь Послідовно, один за одним. Кожен має завершитися успішно (вийти з кодом 0) перед стартом наступного. Контейнери застосунку запускаються лише після завершення всіх init-контейнерів.
  2. Як контейнери в одному Піді обмінюються файлами?

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

    Відповідь Init-контейнер. Він запускається перед основним контейнером і може циклічно перевіряти доступність бази даних, а потім успішно завершитися.
  4. Як переглянути логи конкретного контейнера у Піді з кількома контейнерами?

    Відповідь `kubectl logs pod-name -c container-name`. Прапор `-c` вказує, логи якого контейнера отримати.

Завдання: Побудувати Під з init-, sidecar- та основним контейнерами.

Сценарій: Створіть застосунок, що:

  1. Init: Завантажує конфігурацію з URL (симуляція)
  2. Основний: Запускає nginx, що обслуговує конфігурацію
  3. Sidecar: Моніторить та логує зміни
apiVersion: v1
kind: Pod
metadata:
name: full-pattern
spec:
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: {}

Перевірка:

Terminal window
# Застосувати
k apply -f full-pattern.yaml
# Дочекатися готовності
k get pod full-pattern -w
# Перевірити завершення init
k 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 хвилини)»
Terminal window
# Створити Під з init-контейнером
cat << 'EOF' | k apply -f -
apiVersion: v1
kind: Pod
metadata:
name: init-pod
spec:
initContainers:
- name: init
image: busybox
command: ["sh", "-c", "echo 'Init complete' && sleep 3"]
containers:
- name: main
image: nginx
EOF
# Спостерігати за стартом Підa
k get pod init-pod -w
# Перевірити логи init
k logs init-pod -c init
# Очищення
k delete pod init-pod

Вправа 2: Базовий Sidecar (Ціль: 3 хвилини)

Розділ «Вправа 2: Базовий Sidecar (Ціль: 3 хвилини)»
Terminal window
# Створити Під із sidecar
cat << 'EOF' | k apply -f -
apiVersion: v1
kind: Pod
metadata:
name: sidecar-pod
spec:
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}'
# Перевірити логи sidecar
k logs sidecar-pod -c sidecar
# Очищення
k delete pod sidecar-pod

Вправа 3: Спільний том (Ціль: 4 хвилини)

Розділ «Вправа 3: Спільний том (Ціль: 4 хвилини)»
Terminal window
# Створити Під зі спільним томом
cat << 'EOF' | k apply -f -
apiVersion: v1
kind: Pod
metadata:
name: shared-vol
spec:
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 бачить дані writer
k logs shared-vol -c reader
# Очищення
k delete pod shared-vol

Вправа 4: Init, що очікує на сервіс (Ціль: 5 хвилин)

Розділ «Вправа 4: Init, що очікує на сервіс (Ціль: 5 хвилин)»
Terminal window
# Спочатку створити сервіс
k create svc clusterip wait-svc --tcp=80:80
# Створити Під, що очікує на сервіс
cat << 'EOF' | k apply -f -
apiVersion: v1
kind: Pod
metadata:
name: wait-pod
spec:
initContainers:
- name: wait
image: busybox
command: ['sh', '-c', 'until nslookup wait-svc; do echo waiting; sleep 2; done']
containers:
- name: main
image: nginx
EOF
# Перевірити статус init
k describe pod wait-pod | grep -A3 "Init Containers"
# Очищення
k delete pod wait-pod
k delete svc wait-svc

Вправа 5: Паттерн Ambassador (Ціль: 5 хвилин)

Розділ «Вправа 5: Паттерн Ambassador (Ціль: 5 хвилин)»
Terminal window
# Створити Під з ambassador-проксі
cat << 'EOF' | k apply -f -
apiVersion: v1
kind: Pod
metadata:
name: ambassador-pod
spec:
containers:
- name: main
image: busybox
command: ["sh", "-c", "while true; do wget -qO- localhost:8080; sleep 10; done"]
- name: proxy
image: nginx
ports:
- containerPort: 8080
EOF
# Основний контейнер звертається до проксі через localhost
k logs ambassador-pod -c main
# Очищення
k delete pod ambassador-pod

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

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

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

Створіть Під з іменем app-complete з:

  1. Init-контейнер: Створює /data/config.txt з текстом “Config loaded”
  2. Основний контейнер (nginx): Обслуговує директорію /data
  3. Sidecar: Моніторить /data/config.txt кожні 5 секунд

Після створення перевірте:

  • Під працює (Running)
  • Init завершився успішно
  • Sidecar показує вміст конфігурації
Відповідь
apiVersion: v1
kind: Pod
metadata:
name: app-complete
spec:
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: {}
Terminal window
k apply -f app-complete.yaml
k get pod app-complete
k logs app-complete -c init
k logs app-complete -c monitor
k delete pod app-complete

Модуль 1.4: Томи для розробників — Паттерни постійного та ефемерного зберігання.