Kubernetes 1.28 (Planternetes): разбираем нативные sidecar containers на тестовом кластере
Kubernetes 1.28 вышел с alpha sidecar containers. Обновляем тестовый кластер и смотрим, что нативные sidecar дают для логирующих агентов без хаков с initContainers.
Kubernetes 1.28 (Planternetes) выпущен с alpha sidecar containers и расширенным Job API, август 2023
Kubernetes 1.28 (Planternetes) выходит в августе, RC1 уже доступен - команда подхватила огородную тему и засадила KEP-ами весь огород. Нас в этом релизе интересовало одно: нативные sidecar containers получили статус alpha, и KEP-753 наконец перестал быть «когда-нибудь» и стал «уже есть, включай feature gate». Мы подняли тестовый кластер на RC1 и потратили несколько дней на то, чтобы понять, что это меняет практически.
Почему sidecar вообще был проблемой
Классический паттерн sidecar в Kubernetes - это Pod с несколькими контейнерами, один из которых основной, остальные вспомогательные: собирают логи, пишут метрики, проксируют трафик. Звучит просто. Проблема возникала в жизненном цикле.
До 1.28 все контейнеры в Pod стартуют «примерно одновременно» (с учётом readinessProbe, но не в строгом порядке). Если логирующий агент - например, Fluent Bit - должен быть готов раньше, чем основное приложение начнёт что-то писать в stdout, нужно было городить обходные пути. Самый распространённый - превращать агент в initContainer с restartPolicy: Always, который магически не завершался. Это работало, но выглядело как грязный хак и документировалось с пометкой «неофициально поддерживается».
Вторая беда - завершение. Когда Job заканчивает работу, Kubernetes не знал, что сначала надо дождаться, пока sidecar-агент дофлашит буфер и завершится корректно. В итоге логи за последние секунды работы Job просто терялись. Для batch-задач это боль: именно в конце обычно самое интересное - итоги, ошибки, результаты.
Что появилось в 1.28
Нативные sidecar containers в 1.28 - это новое поле restartPolicy: Always непосредственно внутри initContainers. Контейнер, помеченный так, ведёт себя иначе:
- Старт гарантированно раньше основных контейнеров. Kubernetes запустит такой initContainer и дождётся его готовности (readinessProbe или просто старта) перед тем, как поднять основные контейнеры. Это именно то, чего не хватало для логирующих агентов.
- Завершение гарантированно позже. Когда все основные контейнеры завершаются, Kubernetes сигналит sidecar-контейнерам и ждёт их. Для Job это означает, что агент успевает дофлашить всё накопленное.
- Перезапуск работает независимо. Если sidecar крашнется, он перезапустится не убивая основной контейнер - как и подразумевает
restartPolicy: Always.
Включается это через feature gate SidecarContainers=true на уровне API-сервера и kubelet. В 1.28 gate в alpha, то есть по умолчанию выключен.
Как мы это проверяли
На тестовом кластере 1.28 мы включили SidecarContainers=true через kube-apiserver и kubelet flags. Проверяли два сценария.
Сценарий первый - логирующий агент для долгоживущего деплоймента. Fluent Bit в виде нативного sidecar, основное приложение - простой HTTP-сервер. Раньше при старте пода бывало так: приложение уже пишет в stdout, а Fluent Bit ещё инициализируется и первые строки теряются. С нативным sidecar такого не было - агент стартовал и сигналил ready до того, как основной контейнер вообще получил запуск. Проверили десяток рестартов подов - пропусков не заметили.
Sценарий второй - batch Job с агентом. Это был главный тест. Job считает что-то, пишет в конце подробный лог итогов, завершается. Fluent Bit должен успеть забрать эти строки. До 1.28 на аналогичном кластере мы видели потери - последние 1-3 секунды stdout иногда не долетали. С нативным sidecar Job дожидается, пока агент завершится корректно. Логи приходили полные.
Манифест выглядит примерно так:
initContainers:
- name: log-agent
image: fluent/fluent-bit:2.1
restartPolicy: Always
# ... volume mounts, config
containers:
- name: app
image: myapp:latest
Разница с предыдущим хаком - раньше тот же Fluent Bit шёл как initContainer без restartPolicy: Always и мы писали кастомный entrypoint, который просто не давал контейнеру выйти. Теперь это ушло.
Что ещё интересного в 1.28
Расширенный Job API - второй большой блок этого релиза. backoffLimitPerIndex позволяет задавать лимит ошибок для каждого индекса indexed job отдельно, а не один на всю задачу. Для batch-пайплайнов, где часть индексов может легитимно падать, а остальные должны работать дальше, это полезно. Попробовали на синтетическом примере - работает как описано, большего пока сказать не можем.
Graduated to beta в 1.28: ReadWriteOncePod для PersistentVolumeClaim - блокировка тома на уровне одного пода (не узла). Если раньше не применяли - стоит изучить для workload-ов, где гонка записи критична.
Что нас сдерживает от prod
Alpha - это alpha. SidecarContainers=true в prod-кластерах мы не включаем. Не потому что функция выглядит сырой - KEP-753 висел с 2019 года и реализацию явно обдумывали долго. Просто alpha feature gate в production - это подписаться под возможными breaking changes при следующем minor-релизе без предупреждения. Мы не готовы на это для управляемых кластеров клиентов.
Тестовый кластер продолжает работать с включённым gate. Следим за тем, что войдёт в 1.29 - если feature gate двинется в beta, разговор о prod станет предметным. Пока - держим в голове, логирующие агенты в prod всё ещё ходят старым initContainer-хаком, который хотя бы предсказуем.