← Kubernetes: конфигурация
6 мин · средне · Урок 7 из 8

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 nginx mountPath: /var/log/nginx log-shipper mountPath: /var/log/nginx пишет tail -F читает emptyDir logs
Общий том даёт обмен файлами без сети: один пишет, второй читает тот же путь.

Между контейнерами 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 не даст вставить второй контейнер.

Что часто ломается

Домашка

Теорию закрепляет задание на своём кластере: 2 шага с проверкой. Кластер поднимается по треку Kubernetes локально, задания лежат в репозитории k8s-practice.

./run.sh start k8s-sidecar-logger     # развернуть стартовое состояние
./run.sh verify k8s-sidecar-logger    # проверить текущий шаг

Коротко

Прошли материал до конца? Отметьте урок пройденным.