← Kubernetes: конфигурация
6 мин · средне · Урок 8 из 8

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    # проверить текущий шаг

Коротко

Прошли материал до конца? Отметьте урок пройденным.