Ответ
В распределенных системах, таких как Kubernetes, где приложение работает в нескольких репликах (подах), централизованный сбор логов — обязательная практика. Я настраиваю его следующим образом:
Основной подход: Агент-сборщик логов (Logging Agent). В каждый узел (ноду) Kubernetes устанавливается легковесный агент (например, Fluentd, Fluent Bit или Filebeat), который:
- Отслеживает логи из всех контейнеров на узле (из директории
/var/log/containers/). - Обогащает записи метаданными (имя пода, контейнера, неймспейс, метки).
- Отправляет их в централизованное хранилище.
Архитектура (EFK/ELK Stack):
[Pods (Replica 1, 2, 3)]
| (пишут в stdout/stderr)
v
[Docker/Container Runtime] -> /var/log/containers/*.log
|
v
[Fluent Bit DaemonSet на каждой Node]
| (обогащает + пересылает)
v
[Central Logging Backend: Elasticsearch]
|
v
[Kibana / Grafana Loki UI] <-- Для визуализации и поиска
Конфигурация Fluent Bit (упрощенный DaemonSet манифест):
# fluent-bit-daemonset.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluent-bit
spec:
selector:
matchLabels:
app: fluent-bit
template:
metadata:
labels:
app: fluent-bit
spec:
containers:
- name: fluent-bit
image: fluent/fluent-bit:latest
volumeMounts:
- name: varlog
mountPath: /var/log
- name: config
mountPath: /fluent-bit/etc/fluent-bit.conf
subPath: fluent-bit.conf
volumes:
- name: varlog
hostPath:
path: /var/log
- name: config
configMap:
name: fluent-bit-config
---
# Конфигурация Fluent Bit (ConfigMap)
apiVersion: v1
kind: ConfigMap
metadata:
name: fluent-bit-config
data:
fluent-bit.conf: |
[SERVICE]
Parsers_File parsers.conf
[INPUT]
Name tail
Path /var/log/containers/*.log
Parser docker
Tag kube.*
Mem_Buf_Limit 5MB
# Добавляем метаданные Kubernetes
DB /var/log/flb_kube.db
[FILTER]
Name kubernetes
Match kube.*
Merge_Log On
K8S-Logging.Parser On
K8S-Logging.Exclude On
[OUTPUT]
Name es # Elasticsearch
Match *
Host elasticsearch-logging.monitoring.svc.cluster.local
Port 9200
Logstash_Format On
Logstash_Prefix fluent-bit
Retry_Limit False
Ключевые практики, которые я применяю:
- Структурированное логирование (JSON): Логирую в JSON, чтобы агент мог легко парсить и индексировать поля. Использую библиотеки типа
structlogдля Python или аналоги.import structlog logger = structlog.get_logger() logger.info("request_processed", path="/api/users", duration_ms=45, replica_id=os.getenv("HOSTNAME")) # Выход: {"event": "request_processed", "path": "/api/users", "duration_ms": 45, "replica_id": "app-7cbbd6f5d-abc12", ...} - Контекст и трассировка: Включаю
correlation_idилиtrace_idв каждую запись лога, чтобы собрать все логи по одному запросу из разных сервисов и реплик. - Уровни логирования и семплирование: Настраиваю разные уровни детализации (DEBUG для dev, INFO/WARNING для prod) и семплирование для высоконагруженных эндпоинтов, чтобы не перегружать систему логирования.
- Ротация и хранение: Настраиваю политики индексов в Elasticsearch (ILM — Index Lifecycle Management) для автоматического удаления или архивации старых логов.