Multi-container: ambassador и adapter
Три роли для соседних контейнеров в одном Pod и признак, по которому между ними выбирают.
Multi-container паттерны
Pod - наименьшая единица, которую запускает Kubernetes. Внутри Pod может работать несколько контейнеров. Внутри одного приложения контейнеры делят роли, а не упаковывают два разных сервиса вместе.
Контейнеры одного Pod всегда живут на одном узле, делят сеть и могут делить тома (volume - общая папка внутри Pod). Если разнести их по двум Pod, между ними понадобится Service, сетевой запрос и отдельная настройка общего хранилища.
Есть три устоявшихся паттерна: sidecar, ambassador, adapter.
| Паттерн | Что делает | На каком уровне | Пример |
|---|---|---|---|
| sidecar | дополняет основной контейнер | общее название для всех трёх | сборщик логов, агент мониторинга |
| ambassador | прячет внешнюю сеть за 127.0.0.1 | сеть | nginx, envoy, haproxy |
| adapter | приводит данные к формату внешней системы | данные в общем томе | JSON от main в обычный текст |
Sidecar
Sidecar - контейнер рядом с основным, который его дополняет. Это
общее название. Sidecar не обязан слушать порт. Он может читать файл
командой tail -F или периодически проверять состояние основного
контейнера. Примеры: сборщик логов, агент мониторинга.
Начиная с 1.33 у sidecar есть отдельная форма записи: контейнер
кладут в initContainers и ставят ему restartPolicy: Always.
Такой контейнер стартует раньше основных, работает всё время жизни
Pod и гасится после них.
initContainers:
- name: log-shipper
image: busybox:1.36
restartPolicy: Always
command: ["sh", "-c", "tail -F /var/log/app/app.log"]
Запись через обычный containers тоже рабочая, её полно в старых
манифестах. Разница в гарантиях: порядок старта там не задан, и на
завершении Pod sidecar не узнает, что основной контейнер уже вышел.
Ambassador
Ambassador - это sidecar, который скрывает от основного контейнера
работу с внешней сетью. Основной контейнер обращается к
127.0.0.1:8080 и не знает, что находится снаружи. Ambassador
(обычно nginx, envoy или haproxy) принимает запрос на этом порту и
проксирует его на реальный Service.
Так из основного контейнера выносят всё общение с внешним миром. Сменился адрес Service, добавился TLS (шифрование канала), появилась логика повторов. Это правят в ambassador, основной контейнер остаётся прежним.
Поток запроса целиком:
main → 127.0.0.1:8080 (внутри Pod) → nginx-ambassador → Service auth-api
containers:
- name: main
image: my-app
# main ходит на localhost:8080
- name: ambassador
image: nginxinc/nginx-unprivileged:1.25
# nginx слушает 8080, проксирует на auth-api.default.svc:80
Adapter
Adapter - это sidecar, который приводит данные основного контейнера к формату, который ждёт внешняя система. Основной контейнер пишет в файл так, как ему удобно, например в JSON. Adapter читает тот же файл из общего тома и переводит его, например, в обычный текст.
Главное отличие от ambassador: ambassador работает на уровне сети, adapter переводит формат данных.
Что разделяют контейнеры Pod
Контейнеры Pod делят сеть. Адрес 127.0.0.1 у них общий, поэтому
порты должны быть разными. Они делят тома из spec.volumes: каждый
контейнер монтирует нужный том в свой путь.
Два контейнера слушают 8080, а 127.0.0.1 один на весь Pod. Второй
упадёт с ошибкой EADDRINUSE (адрес уже занят).
Свою файловую систему и свои переменные среды контейнеры не делят. У каждого они отдельные.
Связанные команды
kubectl logs <pod> -c <container>
kubectl exec <pod> -c <container> -- sh
kubectl describe pod <pod> # все контейнеры и их состояния
kubectl get pod <pod> -o jsonpath='{.spec.containers[*].name}'
kubectl get pod <pod> -o jsonpath='{.spec.volumes[*].name}'
kubectl explain pod.spec.shareProcessNamespace
Что часто ломается
Основной контейнер стартует раньше ambassador, который ещё не открыл
порт. Тогда основной получает connection refused. Помогает логика
повторов в его команде. Радикальнее лечит sidecar-форма: ambassador
в initContainers с restartPolicy: Always гарантированно
поднимется до основного контейнера.
Adapter не находит файл от основного контейнера. В начало команды
adapter добавляют ожидание until [ -f /path ]; do sleep 1; done.
Домашка
Теорию закрепляет задание на своём кластере: 3 шага с проверкой. Кластер поднимается по треку Kubernetes локально, задания лежат в репозитории k8s-practice.
./run.sh start k8s-multi-container-patterns # развернуть стартовое состояние
./run.sh verify k8s-multi-container-patterns # проверить текущий шаг
Коротко
- Несколько контейнеров в одном Pod делят узел, сеть и тома, но не файловую систему и не env.
- Sidecar это общее название для контейнера-помощника рядом с основным.
- Ambassador прячет внешнюю сеть за
127.0.0.1, основной контейнер ходит только на localhost. - Adapter переводит данные основного контейнера в формат, который ждёт внешняя система.
- Порты внутри Pod общие, поэтому два контейнера на одном порту конфликтуют.
Прошли материал до конца? Отметьте урок пройденным.
Прогресс сохранён в этом браузере
Почистишь историю или откроешь с телефона, и отметки пропадут. Оставь почту, они переедут в аккаунт. Пароль придумывать не нужно.
Готово. теперь в аккаунте: прогресс виден с любого устройства. Мой прогресс