Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Як організувати log management у Docker?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Три принципи:** 1. **App пише лише у stdout/stderr.** Без log-файлів всередині container. 2. **Конфігуруй log-driver** з rotation, щоб host не заповнювався. 3. **Ship-логи з host'а** у centralized-стек (Loki, ELK, CloudWatch). ```json // /etc/docker/daemon.json, host-wide default з rotation { "log-driver": "json-file", "log-opts": { "max-size": "100m", "max-file": "3", "compress": "true" } } ``` ```yaml # Per-service: ship у Loki-стек services: api: image: myorg/api logging: driver: "loki" options: loki-url: "http://loki:3100/loki/api/v1/push" loki-batch-size: "400" ``` **Structured (JSON) logи** не обговорюються для production: machine-parseable, searchable, queryable.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Log-management у Docker** це дисципліна capturing, rotating і centralizing того, що container'и пишуть. Дефолт (`json-file`) працює для dev, але приховує сюрпризи у production: chatty-container може заповнити disk за години, lookup через багато container'ів tedious, логи однієї node зникають, якщо node вмирає. Реальний production-стек: stdout-only-app + log-driver з rotation + shipper, що виносить логи off-host. ## Теорія ### TL;DR - **Twelve-Factor**: писати у stdout/stderr; нехай platform обробляє решту. - **Log-driver'и** (json-file, journald, syslog, fluentd, loki, awslogs, gcplogs, splunk тощо) визначають, що відбувається з цими stream. - **Default `json-file`** пише у `/var/lib/docker/containers/<id>/<id>-json.log`. Без rotation за замовчуванням. - **Set rotation** через `log-opts: max-size, max-file`. - **Для multi-host setup**, ship-логи off-node: Loki, ELK, CloudWatch, Datadog. - **Структуруй логи** (JSON), не plaintext. Майбутній ти подякує. ### Чому stdout-only Якщо твій app пише у file всередині container: - **Зупиняється на `docker logs`**, Docker бачить лише stdout/stderr. - **Зникає з container**, якщо не mount'нув volume, а management-volume per container це busywork. - **Перемагає log-driver'и**, driver'и оперують на stdout/stderr. - **Ламає orchestration**, Swarm/K8s припускають stdout/stderr-семантику. Twelve-factor-app пише у stdout. Platform (Docker, Compose, Swarm, K8s) направляє куди треба. ### Built-in log-driver'и | Driver | Що робить | Коли використовувати | |---|---|---| | `json-file` | Дефолт. Пише JSON на disk host. | Dev, малий ops. | | `local` | Як json-file, але binary, швидше, має rotation built-in. | Малий ops, нижчий overhead. | | `journald` | Шле у systemd-journal. | Linux-host'и, що використовують journald централізовано. | | `syslog` | Шле у syslog-daemon (локальний або remote). | Legacy, простий central-collection. | | `fluentd` | Шле у Fluentd/Fluent Bit-aggregator. | Зрілий, vendor-neutral central-pipeline. | | `loki` | Шле у Grafana Loki. | Сучасний, дешевий, інтегрується з Grafana. | | `awslogs` | Шле у AWS CloudWatch Logs. | AWS-деплої. | | `gcplogs` | Шле у GCP Cloud Logging. | GCP-деплої. | | `splunk` | Шле у Splunk HEC. | Splunk-shop'и. | | `none` | Дропає логи. | Тести, де байдуже. | ### Куди логи їдуть за замовчуванням ``` /var/lib/docker/containers/<id>/ ├── <id>-json.log # активний log-file ├── <id>-json.log.1 # rotated (якщо rotation set) └── <id>-json.log.2.gz # rotated, compressed ``` ## Приклади ### Ставити host-wide rotation (production must-have) ```json // /etc/docker/daemon.json { "log-driver": "json-file", "log-opts": { "max-size": "100m", "max-file": "5", "compress": "true" } } ``` ```bash sudo systemctl restart docker ``` Тепер кожен новий container cap'ить на 5 файлах по 100 MB = 500 MB worst case, зі старими файлами compressed. **Нота:** існуючі container'и тримають свій старий log-config до recreation. Якщо ти ставиш це на сервер, де вже крутяться container'и, ті container'и ще можуть заповнити disk. Recreate або per-container override. ### Per-container override ```bash docker run -d \ --log-driver=json-file \ --log-opt max-size=50m \ --log-opt max-file=3 \ --name=app \ myorg/app ``` Корисно, коли один chatty-сервіс потребує різних limit. ### Compose з loki-driver ```yaml services: loki: image: grafana/loki:2.9.0 ports: ["3100:3100"] grafana: image: grafana/grafana:10 ports: ["3000:3000"] depends_on: ["loki"] api: image: myorg/api:1.0 logging: driver: loki options: loki-url: "http://loki:3100/loki/api/v1/push" loki-retries: "5" loki-batch-size: "400" loki-external-labels: "job=api,env=prod" ``` `loki-external-labels` додає tag, щоб ти міг filter by service у Grafana. Цей driver потребує `docker-driver` plugin (one-time install): `docker plugin install grafana/loki-docker-driver:latest --alias loki --grant-all-permissions`. ### Structured-logging (JSON) App-side (Node.js example): ```js console.log(JSON.stringify({ level: 'info', msg: 'request handled', request_id: 'abc-123', user_id: 42, duration_ms: 87 })) ``` З `json-file`, Docker wrap'ить це: ```json {"log": "{\"level\":\"info\",\"msg\":\"request handled\",...}\n", "stream": "stdout", "time": "..."} ``` Зовнішній wrapper Docker'а; внутрішній JSON твій structured-log. Loki, ELK тощо парсять inner-JSON, і ти можеш query `{job="api"} |= "abc-123"` або filter by `user_id`. ### Fluentd / Fluent Bit-pipeline ```yaml services: fluentbit: image: fluent/fluent-bit:2.2 ports: ["24224:24224"] volumes: - ./fluent-bit.conf:/fluent-bit/etc/fluent-bit.conf:ro api: image: myorg/api:1.0 logging: driver: fluentd options: fluentd-address: localhost:24224 tag: api # Якщо fluent-bit unreachable, drop'ай логи, щоб не блокувати app: fluentd-async: "true" ``` Fluent Bit-config може route'ити ті ж логи у кілька destination: hot-search-index за останній день, cold-storage для compliance. ### Читання логів з Docker (і limit'и `docker logs`) ```bash docker logs -f --tail=100 api # tail і follow docker logs --since=10m api # останні 10 хвилин docker logs --until=2024-01-15T10:00:00 api # до timestamp ``` `docker logs` працює лише з `json-file`, `local` і `journald`-driver. З `fluentd`, `loki`, `syslog` тощо, логи не лишаються на disk, і `docker logs` повертає нічого, бери centralized-backend замість. ### CloudWatch на AWS ```bash docker run -d \ --log-driver=awslogs \ --log-opt awslogs-region=us-east-1 \ --log-opt awslogs-group=myapp \ --log-opt awslogs-stream=api-1 \ --log-opt awslogs-create-group=true \ myorg/api:1.0 ``` Docker-daemon потребує IAM-permission писати у CloudWatch (`logs:CreateLogStream`, `logs:PutLogEvents`). На EC2 з instance-role, додай policy; на Fargate, task-role обробляє. ### Kubernetes-style: kubelet + sidecar Docker driver-less варіант: лиши логи на disk через `json-file`, запусти per-host-shipper (Fluent Bit, Promtail, Vector), що читає `/var/lib/docker/containers/*/*-json.log` і ship'ить. Decouples app-deploy від log-destination. ## Реальне застосування - **Tiny / hobby**: `json-file` з rotation. Читай через `docker logs`. - **Single-host з monitoring**: Loki + Promtail + Grafana, читання `/var/lib/docker/containers`. - **Multi-host self-hosted**: Fluent Bit per host → Elasticsearch/OpenSearch + Kibana, або Loki + Grafana. - **Cloud**: native, `awslogs`, `gcplogs`, Datadog, Splunk. - **Compliance-environment**: довгострокове cold-storage (S3) + hot-index за останні 30 днів. ### Типові помилки **Писати у log-файли всередині container** Java-app з `log4j`, що пише у `/var/log/app.log`, перемагає `docker logs` і зникає з container. Конфігуруй log4j писати у `console` (stdout). **Без rotation на json-file** Дефолтний Docker без log-rotation. Chatty-app на default-config заповнить disk. Завжди ставь `max-size` і `max-file`. **Logging sensitive data** Пароль, JWT, PII з'являються в логах, бо хтось `console.log(req.body)`. Sanitize у джерелі. Centralized log-indexing робить leak легким для пошуку, і легким для scrape. **Бери `docker logs` у production для всього** Працює на single-host, але не масштабується. Centralize рано; retroactively додавати log-shipping посеред incident'у боляче. **Не ставити `compress: true`** Rotated `.log.N`-файли unrotated і uncompressed сидять на disk у full-розмірі. `compress: "true"` gzip'ить їх, часто 10x менше. ### Питання для поглиблення **Q:** Чому `docker logs` повертає нічого для деяких container? **A:** Цей container використовує non-disk-driver (fluentd, loki, awslogs). Логи shipping, не storing. Query destination замість. **Q:** Різниця між `json-file` і `local`? **A:** Обидва зберігають на disk на host. `local` використовує binary-format (менший, швидший), підтримує built-in rotation і рекомендується для нових setup. `json-file` старший і historical-default. Функціонально similar для `docker logs`. **Q:** JSON чи plaintext-логи? **A:** JSON. Сучасні log-backend парсять його natively, можеш filter by field (`level=error`, `user_id=42`). Plaintext OK для tiny-сервісів, але не масштабується. **Q:** (Senior) Які trade-off між `fluentd-async: true` і synchronous? **A:** Synchronous: якщо Fluent Bit down, Docker-daemon блокує на `write()`, що може hang container'и. Async: Docker буферизує (малий, in-memory) і продовжує; на overflow, логи drop. Для app-стабільності, async з generous-буфером безпечніший; для compliance, де log-loss unacceptable, запускай high-availability log-aggregator (failover, persistent-queue) і бери sync. **Q:** (Senior) Як обробляти multi-line stack-trace у логах? **A:** stdout пише кожну лінію як окрему подію. 30-лінійний Java stack-trace стає 30 unrelated log-entry. Два фікс: (1) Нехай app emit single JSON-event з повним trace як string-field. (2) Бери log-shipper (Fluent Bit, Filebeat, Vector) з multiline-парсинг-правилами, що join'ять лінії, починаючи з whitespace, у попередню подію. Source-side-фікс надійніший.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.