Sidecar pattern: helper container in same pod (Envoy mTLS, Fluentd log shipping, Vault Agent secret injection, PID1 zombie anti-pattern)
Sidecar нужен, когда у многих сервисов появляется одинаковая инфраструктурная обязанность: mTLS, логирование, метрики, secret rotation, TLS termination, protocol adaptation, egress policy. Если вшивать это в каждое приложение, команды начинают писать один и тот же код на Go, Java, Python, Node и Ruby. Rollout новой версии security logic требует пересборки всех сервисов, а несовпадение библиотек приводит к разному поведению в одинаковых ситуациях.
Паттерн Sidecar выносит такую обязанность в отдельный контейнер рядом с основным приложением. В Kubernetes это обычно контейнер в том же pod: общий network namespace, общий lifecycle на уровне pod, optional shared volume. Main app продолжает делать бизнес-логику, sidecar берет на себя cross-cutting concern. Поэтому service mesh, log shippers и Vault Agent так часто выглядят как sidecar.
Знание паттерна важно не только для Kubernetes. Sidecar учит мыслить composition-ом процессов: один процесс не обязан быть универсальным. Но это не бесплатная декомпозиция. Каждый sidecar добавляет CPU, memory, startup dependency, shutdown coordination и новую точку debug. На больших кластерах Envoy в каждом pod может стоить дороже, чем само приложение.
::concept{slug="containers"} ::concept{slug="service-mesh"}
Pod можно представить как маленькую машину, где живут несколько процессов. Main container это бизнес-приложение. Sidecar это помощник в той же машине: проксирует трафик, читает log file, обновляет secrets, перехватывает outbound calls или держит локальный cache. Они близко друг к другу не логически, а физически: localhost общий, volumes можно разделить, scheduling общий.
Ключевой mental model: sidecar не является отдельным микросервисом. Он не должен жить независимо от приложения и обслуживать много unrelated clients. Он сопровождает конкретный main app и масштабируется вместе с ним. Если replicas приложения стало 50, sidecar тоже стало 50. Это удобно для locality, но дорого по ресурсам.
ADR-style framing: sidecar покупает polyglot reuse и независимый rollout инфраструктурной функции, но продает простоту runtime. Если concern можно решить библиотекой без duplication и с хорошим governance, библиотека проще. Если concern должен быть одинаковым для всех языков и enforced платформой, sidecar часто выигрывает.
Диаграмма показывает один Kubernetes Pod с Main app, Envoy proxy, Log shipper и Vault Agent. Внешняя группа содержит service-b, Loki logs и Vault server. Ребра показывают не абстрактную архитектуру, а реальные каналы: app говорит с Envoy через localhost, Envoy ходит к service-b по mTLS, app пишет logs в shared volume, Fluentd/Vector отправляет их в Loki, Vault Agent получает secrets и кладет их в volume для app.
Важно, что app не знает деталей mTLS, формата Loki или Vault API. Для него это локальный HTTP endpoint, файл логов и файл secrets. Это и есть сильная сторона sidecar: приложение остается проще, а platform team может менять инфраструктурный слой.
Диаграмма также показывает anti-pattern про PID 1 zombie reaping. Это не sidecar как таковой, а типичная container-runtime ошибка: если процесс в контейнере запущен как PID 1 и не reap-ит child processes, zombies могут накапливаться. Sidecar-heavy pods часто запускают вспомогательные процессы, поэтому lifecycle hygiene становится важнее.
mtls-via-envoy показывает service mesh path. Приложение отправляет обычный HTTP на localhost, Envoy перехватывает или принимает трафик, добавляет сертификат и передает запрос к service-b по mTLS. Урок: security policy можно enforced на proxy layer без переписывания бизнес-кода. Но app теперь зависит от готовности sidecar: если Envoy не поднялся, outbound traffic может не работать.
log-shipping показывает decoupling от logging backend. App пишет /var/log/app.log, log sidecar tail-ит файл, парсит JSON, добавляет Kubernetes metadata и отправляет в Loki. Урок: backend логирования можно заменить без релиза приложения. Цена: нужно следить за backpressure. Если Loki недоступен, sidecar должен буферизовать, drop-ать или блокировать, и это решение влияет на pod disk и latency.
vault-secret-injection показывает secrets lifecycle. Vault Agent аутентифицируется service account-ом pod, получает dynamic credentials, пишет их в shared volume и обновляет lease до expiration. App читает файл и не требует Vault SDK. Урок: secret rotation можно сделать прозрачным. Цена: приложение должно уметь перечитывать file или выдерживать graceful reconnect.
pid1-zombie-issue показывает container hygiene. Если app является PID 1 без init wrapper, child process deaths могут оставлять zombies. Урок: sidecar pattern не отменяет базовую дисциплину контейнеров: tini, dumb-init, корректный signal handling, preStop hooks и termination grace period.
ADR-001: sidecar против shared library. Shared library дешевле по runtime: нет второго процесса, меньше memory, проще tracing внутри одного address space. Но library надо поддерживать для каждого языка и framework. Sidecar дороже по ресурсам, зато дает единое поведение и независимый lifecycle. Для mTLS, traffic policy и secret injection sidecar часто оправдан; для простого JSON logging library обычно достаточно.
ADR-002: sidecar против platform gateway. Gateway централизует concern и уменьшает overhead per pod. Но он не видит локальные детали процесса и может стать bottleneck. Sidecar дает locality: он рядом с app, может общаться через localhost, читать volume, применять policy per workload. Цена линейная: 1000 pods значит 1000 proxies.
ADR-003: operational independence против lifecycle coupling. Sidecar можно обновить отдельно от app image, но Kubernetes pod все равно scheduling unit. Crash sidecar может перезапустить весь pod или сделать его неготовым. Поэтому readiness gates и startup probes должны отражать реальную зависимость: app не должен принимать трафик, пока proxy или secrets sidecar не готовы.
ADR-004: transparency против debuggability. Чем прозрачнее sidecar, тем меньше app code. Но при инциденте сложнее понять, где ошибка: app, proxy, iptables redirect, certificate rotation, DNS, policy или log pipeline. Нужны отдельные dashboards и correlation IDs через оба контейнера.
Istio и Linkerd используют sidecar proxy для traffic management, mTLS, retries, telemetry и policy. Новые mesh-подходы иногда уходят к ambient/node-level proxy, потому что per-pod sidecar overhead стал заметной ценой.
Vault Agent Injector добавляет sidecar или init container, чтобы выдавать secrets без прямой интеграции приложения с Vault. Это особенно полезно в polyglot fleet и legacy apps.
Fluentd, Fluent Bit и Vector могут работать как DaemonSet или sidecar. Sidecar удобен, когда нужно читать локальный формат или volume конкретного pod, но DaemonSet дешевле по ресурсам для стандартного stdout logging.
AWS App Mesh использует Envoy sidecar для service-to-service communication. Datadog и другие observability agents могут работать sidecar-ом, когда нужна per-workload instrumentation или локальная агрегация.
Heavy sidecar тяжелее main app. Если proxy, log shipper и agent съедают большую часть memory, autoscaling приложения перестает отражать реальную стоимость workload.
Слишком много sidecars в одном pod. Три-четыре помощника создают сложный startup graph, спорят за CPU и делают incident response медленным.
Dependency cycle на старте. App ждет sidecar, sidecar ждет app endpoint, readiness никогда не становится passing. Такие циклы надо разрывать startupProbe, init container или явным degraded mode.
Sidecar без resource limits. Всплеск логов или retry storm в proxy может вытеснить main app по CPU/memory и вызвать OOM.
Неправильный shutdown. Kubernetes посылает SIGTERM, app завершилась, а sidecar все еще держит connection; или наоборот proxy умер первым и app не может flush-нуть запросы. Нужны preStop hooks, drain и terminationGracePeriod.
Секреты пишутся в world-readable volume. Sidecar упростил delivery, но не отменил file permissions, rotation и audit.
Не используй sidecar для маленького сервиса, где concern покрывается одной зрелой library и нет polyglot pain. Лишний контейнер усложнит deploy без выигрыша.
Не используй sidecar, если требование централизуется на ingress или gateway. Например, public TLS termination часто лучше делать на edge, а не в каждом pod.
Не используй sidecar для batch jobs с секундным runtime, если startup sidecar занимает больше времени, чем полезная работа. В таких случаях init container, library или platform feature проще.
Не используй sidecar как способ спрятать плохой контракт. Если app не умеет корректно reload secrets, sidecar не спасет от stale credentials. Если app не отдает structured logs, log sidecar не превратит хаос в observability.
Дальше логично читать service-mesh, потому что Envoy sidecar это самый известный production use case. Перед этим полезно закрыть containers и Kubernetes pod lifecycle: startupProbe, readinessProbe, livenessProbe, terminationGracePeriod. Для resilience связка с timeout-deadline важна: proxy retries без deadline легко создают retry storm.
Связанные CloudArch материалы: ::concept{slug="service-discovery"} ::concept{slug="timeout-deadline"} ::concept{slug="load-balancing"}
Внешние источники: Brendan Burns, Designing Distributed Systems; Kubernetes docs по Sidecar Containers и Pod lifecycle; Istio docs по sidecar injection; Vault Agent Injector documentation; Envoy architecture overview.