ADG Оставить заявку
Блог DevOps 5 мин чтения

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-хаком, который хотя бы предсказуем.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.