Задача 11. Init-контейнеры и sidecar'ы

Job завершает работу, но под навсегда остаётся в Running из-за sidecar'а. Разбираем порядок запуска контейнеров, старую проблему завершения sidecar'ов и нативные sidecar-контейнеры.
Опубликовано:

Задача

Есть Job, который выгружает данные. Рядом с основным контейнером работает sidecar — прокси к базе данных:

YAML
apiVersion: batch/v1
kind: Job
metadata:
  name: export
spec:
  template:
    spec:
      restartPolicy: Never
      containers:
        - name: exporter
          image: exporter:1.0 # отработает и завершится
        - name: db-proxy
          image: cloud-sql-proxy # работает вечно
Нажмите, чтобы развернуть и увидеть больше

Основной контейнер успешно отрабатывает за пару минут и завершается. Job при этом никогда не переходит в состояние Completed — под висит в Running, пока его не убьют вручную.

Вопрос: почему и как это правильно решается?

Что кажется очевидным

«Kubernetes должен понять, что главный контейнер закончил, и остановить остальные».

Логично, но исторически Kubernetes так не делал — и понимание почему объясняет целый пласт костылей в чужих манифестах.

Как это работает на самом деле

В поде нет понятия «главный контейнер». Все контейнеры в containers равноправны, и под считается завершённым, только когда завершились все они.

Sidecar-прокси не рассчитан на завершение — он работает бесконечно. Основной контейнер вышел, sidecar продолжает жить, под остаётся Running, Job не завершается. Классическая проблема, из-за которой годами писали костыли: общий том с файлом-флагом, обращение к shared process namespace, вызов эндпоинта завершения у прокси.

Init-контейнеры: другой механизм

initContainers работают иначе:

Типичное применение — дождаться зависимости, применить миграции, подготовить файлы в общем томе.

YAML
initContainers:
  - name: wait-for-db
    image: busybox:1.36
    command: ["sh", "-c", "until nc -z db 5432; do sleep 2; done"]
Нажмите, чтобы развернуть и увидеть больше

Ограничение классических init-контейнеров: они не могут работать параллельно с основным. Для прокси, который нужен всё время жизни пода, это не подходило.

Нативные sidecar-контейнеры

Именно эту дыру закрыли нативные sidecar’ы. Механизм элегантный: sidecar объявляется как init-контейнер с restartPolicy: Always.

YAML
apiVersion: batch/v1
kind: Job
metadata:
  name: export
spec:
  template:
    spec:
      restartPolicy: Never
      initContainers:
        - name: db-proxy
          image: cloud-sql-proxy
          restartPolicy: Always # ← делает его sidecar'ом
      containers:
        - name: exporter
          image: exporter:1.0
Нажмите, чтобы развернуть и увидеть больше

Что это даёт:

Последний пункт решает и вторую известную боль: раньше при завершении пода прокси мог умереть раньше приложения, и приложение теряло связь с базой в момент shutdown.

Ответ

Под висит потому, что в поде нет главного контейнера — он завершается лишь когда завершились все контейнеры, а sidecar-прокси не завершается никогда.

Правильное решение — объявить прокси нативным sidecar’ом: init-контейнер с restartPolicy: Always. Тогда Kubernetes сам остановит его после отработки основного контейнера.

Что с этим делать


Дальше: Задача 12. Rolling update: maxSurge и maxUnavailable.

Начать поиск

Введите ключевые слова для поиска статей

↑↓
ESC
⌘K Горячая клавиша