Kubernetes 1.27: sidecar containers как alpha API - тестируем в dev-кластере
Kubernetes 1.27 вводит sidecar containers как alpha API. Разбираем что это меняет для init-контейнеров логирования и mTLS, и когда это можно двигать в прод.
Kubernetes 1.27 GA (апрель 2023) - sidecar containers alpha API, ускорение StatefulSet rolling update и другие изменения релиза
Kubernetes 1.27 RC2 вышел в конце марта, и пока все обсуждают цифру «релизов в год» и ребрендинг etcd-шного проекта, нас зацепила одна конкретная фича: sidecar containers как официальный alpha API. Не потому что мы любим альфы сами по себе - а потому что эта штука меняет кое-что фундаментальное в том, как init-контейнеры соседствуют с основными workload.
Развернули 1.27 в dev-кластере, включили feature gate и провели несколько часов с новым механизмом. Вот что получилось.
Чего не хватало в старой модели
Классическая схема с init-контейнерами - это последовательный запуск до основного контейнера. Хорошо подходит для миграций базы или генерации конфигов. Но для sidecar-ов - агентов логирования, Envoy при mTLS, агентов трейсинга - это не то что нужно.
Типичная ситуация до 1.27: мы деплоим Fluentd или Vector как sidecar для сбора логов. Нужно чтобы он жил рядом с основным контейнером весь жизненный цикл пода. Технически это работало через обычный контейнер в spec.containers, не init. Но в этой схеме два неприятных момента:
- При завершении пода init-контейнеры и обычные контейнеры завершаются в произвольном порядке. Агент логирования может умереть раньше основного приложения и потерять последние строки лога - как раз те, что вылетают при краше или graceful shutdown.
- StatefulSet rolling update ждёт готовности каждого пода перед переходом к следующему. Если sidecar медленно поднимается, весь роллаут затормаживается. До 1.27 это решалось таймаутами и readinessProbe-хаками, что выглядело грязновато.
В Istio-среде с автоматическим inject-ом Envoy проблема знакома ещё острее: при завершении пода Envoy иногда умирал раньше чем основное приложение успевало ответить на health check или дослать в полёте запросы.
Что даёт новый API
В 1.27 появилось поле restartPolicy: Always для init-контейнеров. Именно это поле превращает init-контейнер в «официальный sidecar»: он стартует в фазе init, но в отличие от обычного init-контейнера - не завершается, а живёт параллельно с основными контейнерами. И главное: Kubernetes теперь гарантирует порядок завершения: sidecar-ы с restartPolicy: Always останавливаются после основных контейнеров пода.
Выглядит в манифесте так:
initContainers:
- name: log-agent
image: fluent/fluent-bit:2.0
restartPolicy: Always
volumeMounts:
- name: varlog
mountPath: /var/log
Семантика при этом меняется значительно. Такой контейнер:
- Стартует раньше основных контейнеров (сохраняется init-семантика запуска).
- Живёт параллельно с основными - не завершается при их старте.
- Завершается позже основных - Kubernetes явно ждёт остановки sidecar-ов после того как основные контейнеры завершились.
- Перезапускается при падении, как обычный контейнер с
restartPolicy: Always.
Для Envoy в Istio это потенциально решает старую проблему с гонкой при shutdown. Для агентов логирования - гарантия доставки последних событий перед смертью пода.
Что мы пощупали в dev
Включить фичу: добавить --feature-gates=SidecarContainers=true в kube-apiserver и kubelet. В managed-кластере это не всегда просто - зависит от того, насколько control plane открыт для кастомизации. В нашем dev-стенде прошло без проблем.
Тестировали два сценария.
Первый - агент логирования. Взяли тестовое приложение, которое пишет в stdout построчно с задержкой, и добавили Fluent Bit как sidecar. Имитировали краш основного контейнера через kill -9. В старой схеме (обычный контейнер) агент иногда не успевал смыть буфер - в зависимости от timing-а теряли 2-5 последних строк. С новым API sidecar явно получил SIGTERM после основного контейнера, буфер ушёл, потерь не было. Несколько прогонов - стабильно.
Второй - StatefulSet rolling update. Добавили sidecar в StatefulSet с тремя репликами и запустили роллаут. По ощущениям стало чуть быстрее, чем в аналогичной конфигурации на 1.26 с обычным sidecar-контейнером, но это именно «по ощущениям» на dev-стенде. На продакшн-нагрузках разница может быть ощутимее - там, где sidecar-ы тяжёлые и readiness probe чувствительная.
Когда двигать в прод
Пока - не двигаем. Причин несколько.
Alpha это alpha. API может поменяться до beta, и feature gate придётся перевключать вручную при каждом обновлении. Для dev - нормально. Для продакшн-кластера, где апгрейды планируются на полгода вперёд - нет.
Совместимость с операторами. Если в кластере есть операторы, которые генерируют PodSpec программно (например, Istio, cert-manager, различные job-контроллеры), они пока не умеют в новое поле restartPolicy на init-контейнерах. Часть из них при виде незнакомого поля может повести себя непредсказуемо - в зависимости от того, как написан валидационный код.
Istio в частности. Именно тот случай, где новый API максимально полезен - и именно он пока не поддерживает SidecarContainers. Istio inject-ит Envoy как обычный контейнер, и это не изменится быстро. Пока feature gate включён и Istio не готов, есть риск конфликтов при webhook-инжекции.
Наш вывод по 1.27 в целом: апгрейд с 1.26 в плане сложности - стандартный, без сюрпризов уровня «убрали PSP». Sidecar containers - самое интересное, что появилось, но именно как alpha: смотреть, тестировать в dev, следить за beta-треком. На продакшн-кластерах в рамках managed-сопровождения включаем feature gate только на стендах клиентов, которые явно хотят участвовать в обкатке.
Бета, судя по темпу разработки, появится в одном из следующих релизов. Вот тогда разговор о продакшне станет предметным.