Sidecar для логов
Второй контейнер рядом с основным: общий emptyDir, tail -F и разница между легаси-формой и native sidecar.
Что такое sidecar
Sidecar это второй (или третий) контейнер в одном Pod, который расширяет основной. Не отдельный Pod через Service, а именно рядом, в той же сетевой и storage-капсуле. Цели бывают разные: собрать логи, прокинуть мутуальный TLS, скачать конфиг, шифровать трафик.
Ключевое свойство sidecar: контейнеры Pod делят сеть и могут шарить
тома. Sidecar читает тот же файл, в который пишет main. Видит тот
же 127.0.0.1 что и main. Без Service, без DNS, без сетевых hops.
Классический кейс - log shipping: nginx пишет access-логи в файл,
sidecar log-shipper через tail -F забирает их и отправляет
наружу. Реализуется через busybox в 5 строк или через fluent-bit,
filebeat, vector в проде.
Где живут общие данные
Между контейнерами Pod общую файловую систему даёт volume. Чаще
всего emptyDir: пустая папка, которая создаётся при старте Pod и
исчезает вместе с ним. Оба контейнера монтируют этот volume в
свои mountPath, и пишут/читают друг у друга.
spec:
volumes:
- name: logs
emptyDir: {}
containers:
- name: nginx
volumeMounts:
- name: logs
mountPath: /var/log/nginx
- name: log-shipper
volumeMounts:
- name: logs
mountPath: /var/log/nginx
emptyDir живёт ровно столько, сколько живёт Pod. Pod пересоздался,
данные ушли. Для логов это нормально: они должны уехать наружу,
прежде чем Pod умрёт.
Sidecar и порядок запуска
В обычном containers все контейнеры стартуют параллельно. kubelet
не гарантирует, что nginx запустится раньше log-shipper. Поэтому в
sidecar обычно пишут tail -F (большая F), а не tail -f.
Маленькая f падает, если файла ещё нет в момент старта. Большая
ждёт пока файл появится, и потом подхватывает.
С tail -f файла на старте нет, sidecar падает, Pod уходит в
CrashLoopBackOff. Основной контейнер при этом работает, и причину ищут
долго.
Native sidecar - это initContainer с restartPolicy: Always. Он
стартует раньше основных контейнеров, продолжает работать всё время
жизни Pod и останавливается после того, как main завершился. Поле
доступно с 1.29 и достигло GA в 1.33, так что на актуальном кластере
это основной способ описать sidecar.
spec:
initContainers:
- name: log-shipper
image: busybox:1.36
restartPolicy: Always
command: ["sh", "-c", "tail -F /var/log/nginx/access.log"]
Тот же sidecar в обычном containers тоже работает, и старые
манифесты полны такой записи. Но порядок старта там не задан, а на
завершении Pod sidecar не узнает, что main уже вышел. Эта форма
считается легаси: новое пишут через initContainers с restartPolicy.
в containers (легаси) | в initContainers с restartPolicy: Always | |
|---|---|---|
| Порядок старта | не задан, параллельно с основными | стартует раньше основных |
| Когда main вышел | не узнаёт об этом | kubelet гасит после основных |
| Статус формы | полно в старых манифестах | основной способ, GA с 1.33 |
Связанные команды
kubectl logs <pod> -c <container> # лог конкретного контейнера
kubectl logs <pod> --all-containers # всё подряд
kubectl exec <pod> -c <container> -- sh
kubectl describe pod <pod> # видно оба контейнера и их статус
kubectl get pod <name> -o jsonpath='{.spec.containers[*].name}'
Почему нельзя добавить контейнер в существующий Pod
Спека Pod в большинстве полей immutable. spec.containers после
создания менять нельзя: можно только подменить image и ещё пару
полей. Добавление контейнера требует delete и create. В Deployment
эту магию прячет контроллер: создаёт новые Pod со spec и убирает
старые. Сам Pod остаётся неизменяемым объектом. Проверяется это
быстро: kubectl edit pod не даст вставить второй контейнер.
Что часто ломается
- Забыли
-cвkubectl logsна Pod с двумя контейнерами. Ошибка «a container name must be specified». Лечится-c log-shipperили--all-containers. - nginx из официального образа стартует под root, и
runAsNonRoot: trueне даёт ему подняться. Подойдётnginxinc/nginx-unprivileged, он слушает 8080 сразу под uid 101. - Sidecar в
containersне получает SIGTERM, когда main вышел. Остаётся висеть доterminationGracePeriodSeconds. Native sidecar через initContainers это чинит: kubelet гасит его после основных контейнеров.
Домашка
Теорию закрепляет задание на своём кластере: 2 шага с проверкой. Кластер поднимается по треку Kubernetes локально, задания лежат в репозитории k8s-practice.
./run.sh start k8s-sidecar-logger # развернуть стартовое состояние
./run.sh verify k8s-sidecar-logger # проверить текущий шаг
Коротко
- Sidecar это дополнительный контейнер в том же Pod, с общей сетью и общими томами.
- Обмен файлами между контейнерами делают через
emptyDir, который живёт столько же, сколько Pod. tail -Fпереживает отсутствие файла на старте,tail -fнет.- Native sidecar пишут как initContainer с
restartPolicy: Always, GA с 1.33. - Состав контейнеров у существующего Pod поменять нельзя, нужен delete и create либо rollout через Deployment.
Прошли материал до конца? Отметьте урок пройденным.
Прогресс сохранён в этом браузере
Почистишь историю или откроешь с телефона, и отметки пропадут. Оставь почту, они переедут в аккаунт. Пароль придумывать не нужно.
Готово. теперь в аккаунте: прогресс виден с любого устройства. Мой прогресс