Adapter pattern: transform output для внешних систем
Adapter — это контейнер, задача которого преобразовывать output main приложения в формат, который ожидает внешняя система. Канонические сценарии: app пишет логи в proprietary формате → adapter трансформирует в JSON; app expose внутренние метрики через файл/socket → adapter конвертирует в Prometheus exposition format на HTTP endpoint. Adapter не добавляет новой функциональности — он «адаптирует» существующий output. Современно adapter реализуется как native sidecar (initContainer + restartPolicy: Always, GA v1.33), но концептуально это отдельный паттерн.
Monitoring Stack: JMX exporter + Prometheus + Grafana
Зачем нужен adapter
Сценарий: у вас есть vendor binary (closed-source приложение, legacy продукт, custom binary без исходников), который пишет в stdout custom формат логов:
2026-01-15 10:30:01 [INFO] (component=worker) [tid=42] Job 12345 completed in 234ms
Ваш централизованный logging stack (Loki, ELK, Datadog) ожидает JSON:
{"ts":"2026-01-15T10:30:01Z","level":"info","component":"worker","tid":42,"msg":"Job 12345 completed","duration_ms":234}
Вы не можете поменять источник логов (vendor binary). Вариантов два:
- Парсить на стороне log collector (Logstash filter, Fluent Bit lua parser) — но это сложно поддерживать и распределяет логику.
- Adapter container в том же Pod — читает raw output, делает transform, выдаёт в нужном формате. Логика парсинга живёт рядом с app.
YAML пример: adapter для метрик
Самый частый реальный adapter — Prometheus exporter для приложения, которое не умеет говорить prometheus exposition format напрямую.
apiVersion: v1
kind: Pod
metadata:
name: app-with-prometheus-adapter
spec:
initContainers:
- name: metrics-adapter # native sidecar, реализует adapter паттерн
image: prom/redis_exporter:v1.55
restartPolicy: Always
args: ['--redis.addr=localhost:6379']
ports:
- name: metrics
containerPort: 9121
containers:
- name: redis
image: redis:7
ports:
- name: redis
containerPort: 6379
Что происходит:
redisexposes свой бинарный protocol наlocalhost:6379;metrics-adapter(redis_exporter) подключается кlocalhost:6379, опрашивает Redis через его native protocol (INFO,CONFIG GET);- adapter выдаёт результат в Prometheus exposition format на
localhost:9121/metrics; - Prometheus scrape
Pod_IP:9121/metrics— и получает метрики Redis в стандартном формате.
Redis сам не знает про Prometheus, не имеет prometheus endpoint. Adapter эту интеграцию делает.
Канонические adapter use cases
| Сценарий | Main app даёт | Adapter преобразует в |
|---|---|---|
| Prometheus metrics | proprietary metrics endpoint, log file, native protocol | exposition format на /metrics |
| JSON logs | custom text format в stdout/file | structured JSON |
| Healthcheck | TCP check, custom protocol | HTTP /healthz для probe |
| Tracing | application-specific spans | OTLP формат |
| Audit events | local file | syslog или Kafka |
Adapter vs Sidecar: где граница
Часто их путают, и часто их грань размыта. Разница концептуальная:
- Sidecar — добавляет функциональность, которой у main app нет. Log shipper «забирает логи и отправляет наружу» — это новая функция (доставка).
- Adapter — преобразует существующий output в новый формат, не добавляя новой смысловой функциональности. Prometheus exporter «берёт метрики, которые app уже отдаёт, и переводит в format» — никакой новой информации.
На практике один контейнер часто делает оба: fluent-bit и забирает логи (sidecar), и парсит из text в JSON (adapter). Поэтому в YAML вы видите только initContainers + restartPolicy: Always — название паттерна в манифесте отсутствует.
Деление на adapter/sidecar — методологическое, для проектирования. В Kubernetes API оба механизма — один и тот же native sidecar (initContainer + restartPolicy: Always, GA v1.33). Имя паттерна влияет только на то, как вы думаете о проблеме, не на YAML.
Когда adapter — правильный выбор
- Vendor binary без возможности модификации. Closed-source, у вас только бинарь.
- Legacy app, которое нельзя релизить часто. Изменение adapter — это отдельный rolling update без релиза основного app.
- Полифонные приложения: app пишет логи в десяти форматах для разных consumers. Один adapter под каждый формат лучше, чем добавлять 10 endpoints в app.
- Адаптация под стандарт мониторинга, который app не поддерживает: prometheus, OpenTelemetry, syslog RFC5424.
Когда adapter — антипаттерн
Если вы можете изменить main app code — почти всегда лучше изменить там:
- Добавьте Prometheus client library и
/metricsendpoint в app. Никакого adapter не нужно. - Включите structured JSON logging в app config. Никакого парсинга не нужно.
- Добавьте OTLP exporter в app. Не нужен sidecar collector.
Adapter — это escape hatch для случаев, когда модификация app невозможна или дорога. Если код в ваших руках — модифицируйте app.
Anti-pattern: добавить sidecar adapter, потому что «парсить логи в Python проще, чем добавлять JSON logger в Go-приложение». Это создаёт два процесса там, где был один; добавляет CPU/memory; усложняет debug (логи в Pod в двух местах: app stdout и adapter stdout). Если можно модифицировать app — это всегда правильнее.
Killer момент: shared volume для files-based adapter
Часть adapter паттернов работает через shared file, а не через localhost socket. Например, app пишет метрики в файл (vendor binary сделал так), adapter этот файл читает и выдаёт на HTTP.
spec:
initContainers:
- name: file-to-prometheus
image: my-org/file-metrics-adapter:v1
restartPolicy: Always
args: ['--path=/var/metrics/app.json', '--port=9090']
volumeMounts:
- mountPath: /var/metrics
name: metrics
ports:
- containerPort: 9090
containers:
- name: app
image: vendor/legacy-app:v1
volumeMounts:
- mountPath: /var/metrics
name: metrics # app пишет сюда
volumes:
- name: metrics
emptyDir: {} # обмен через локальный emptyDir
Adapter tail -f файл, парсит, expose на /metrics. Это типовая схема для legacy интеграций.
CKAD: что знать про adapter
На CKAD adapter спрашивают концептуально, не глубоко:
- понимать, что это transform output, не добавление функциональности;
- уметь привести пример (Prometheus exporter, log JSON adapter);
- знать, что современно реализуется как native sidecar (initContainer +
restartPolicy: Always, GA v1.33); - понимать, что adapter — это для случаев, когда app модифицировать нельзя.
Чисто практически в CKAD задачах adapter появляется редко — гораздо чаще sidecar и init.