July 21

Мастер-класс по логированию в Linux: архитектура, инструменты и лучшие практики для DevOps и SRE

Это перевод оригинальной статьи Linux Logging Masterclass: Architecture, Tools, and Best Practices for DevOps and SRE.

Перевод сделан специально для телеграм-канала Мониторим ИТ. Подписывайтесь! Там еще больше полезных постов о мониторинге.

Глубокое погружение в принципы логирования Linux на производственном уровне для DevOps-инженеров, SRE-специалистов и инженеров платформ, работающих в Kubernetes, облаке и регулируемых средах.

1. Введение: почему логирование — это не опция, а необходимость

В любой боевой системе наблюдаемость определяется тремя основными элементами: метрики, трейсы и логи. Метрики указывают на наличие проблемы. Трейсы показывают, где именно в цепочке вызовов произошла ошибка. Логи объясняют причину.

Логирование — это старейшая и наиболее распространенная форма системной телеметрии, и тем не менее она остается одной из самых неправильно понимаемых. Инженеры перегружают систему логами, недогружают, логируют не то, что нужно, не централизуют их или полностью игнорируют ретеншн — до тех пор, пока инцидент в 3 часа ночи не заставит столкнуться с переполненным /var/log или отсутствием логов за нужный период.

В современных распределенных системах — кластерах Kubernetes, микросервисах, serverless-функциях — логирование стало значительно сложнее. Один пользовательский запрос может затрагивать десятки подов на нескольких нодах и в разных пространствах имен. Без структурированного, коррелированного и централизованного логирования восстановить последовательность событий практически невозможно.

В сфере финансовых технологий и регулируемых сред логирование — это не просто удобство для инженеров, а требование комплаенса. PCI-DSS, PSD2, GDPR и CESOP — все эти стандарты обязывают вести аудит-трейлы, логи доступа и соблюдать политику хранения данных. Последствия отсутствия логов при регуляторной проверке могут быть крайне серьёзными.

Это руководство охватывает полный стек логирования: от кольцевого буфера ядра и systemd-journald на нижнем уровне, через rsyslog и syslog-ng в середине, до Fluent Bit, Loki и Grafana на верхнем уровне. Каждый раздел ориентирован на использование в производственной среде.

2. Обзор архитектуры логирования Linux

Иерархия логирования

Система логирования в Linux представляет собой многоуровневую систему. Понимание каждого уровня помогает избежать неправильной диагностики во время инцидентов.

┌─────────────────────────────────────────┐
│         Central Logging Platform        │
│   (Loki / Elasticsearch / CloudWatch)   │
└──────────────────┬──────────────────────┘
                   │
┌──────────────────▼──────────────────────┐
│         Log Shipper / Aggregator        │
│      (Fluent Bit / Fluentd / Logstash)  │
└──────────────────┬──────────────────────┘
                   │
┌──────────────────▼──────────────────────┐
│        Syslog Daemon Layer              │
│        (rsyslog / syslog-ng)            │
└────────┬─────────────────────┬──────────┘
         │                     │
┌────────▼──────┐    ┌─────────▼──────────┐
│  systemd-     │    │   /var/log files    │
│  journald     │    │   (flat files)      │
└────────┬──────┘    └────────────────────┘
         │
┌────────▼──────────────────────────────────┐
│  Applications / Services / Kernel         │
│  (stdout/stderr, syslog(), /dev/kmsg)     │
└────────────────────────────────────────────┘

Логирование ядра

Ядро записывает сообщения в кольцевой буфер, доступный через /dev/kmsg. Команда dmesg читает этот буфер. Сообщения включают обнаружение оборудования, инициализацию драйверов, события OOM killer и ошибки файловой системы. В системах с systemd journald перехватывает сообщения ядра и делает их доступными через journalctl -k.

dmesg -T | grep -i error
dmesg -T | grep -i "oom"
journalctl -k --since "1 hour ago"

Логирование пользовательского пространства

Приложения логируют через несколько механизмов:

  • Системный вызов syslog() — классический интерфейс POSIX; в современных системах маршрутизация осуществляется через journald.
  • stdout/stderr — перехватываются менеджером сервисов (systemd или средой выполнения контейнеров).
  • Прямая запись в файл — приложения записывают напрямую в файл /var/log/app/.
  • Библиотеки структурированного логирования — запись JSON в stdout (распространено в микросервисах на Go, Python и Java).

Протокол syslog

Протокол syslog (RFC 5424) определяет facility (кто логирует: kern, auth, daemon, user и т.д.) и severity (уровень важности: emergency, alert, critical, error, warning, notice, info, debug). Эта комбинация формирует приоритет сообщения.

Уровни серьёзности и их значения:

  • emerg (0) — система неработоспособна
  • alert (1) — требуется немедленное действие
  • crit (2) — критические состояния
  • err (3) — ошибки
  • warning (4) — предупреждения
  • notice (5) — нормальные, но важные события
  • info (6) — информационные сообщения
  • debug (7) — отладочные сообщения

3. Подробный разбор systemd-journald

Как работает journald

systemd-journald — основной демон сбора логов в современных Linux-системах (RHEL 7+, Ubuntu 16.04+, Debian 8+). Он заменяет традиционный syslog как первый получатель логов.

journald собирает данные из:

  • /dev/kmsg — сообщения ядра
  • /dev/log — сокет syslog (устаревший, для совместимости)
  • stdout/ stderr сервисов, управляемых systemd
  • Нативный протокол журнала через /run/systemd/journal/
  • Подсистема аудита

Двоичный формат журнала

В отличие от текстовых syslog-файлов, journald хранит логи в двоичном структурированном формате:

  • Volatile (в режиме реального времени): /run/log/journal/ — исчезает после перезагрузки
  • Persistent /var/log/journal/: — сохраняется после перезагрузки

Каждый файл журнала включает в себя прямое и обратное запечатывание (с помощью --seal) для обнаружения попыток несанкционированного доступа и поддерживает индексированный поиск по юниту, идентификатору процесса, временной метке и идентификатору сообщения.

Для включения постоянного хранения создайте директорию:

mkdir -p /var/log/journal
systemd-tmpfiles --create --prefix /var/log/journal
systemctl restart systemd-journald

Основные команды journalctl

# Follow logs in real time (like tail -f)
journalctl -f
# Show all logs from the last hour
journalctl --since "1 hour ago"
# Show logs for a specific service
journalctl -u nginx.service
journalctl -u nginx.service --since "2024-01-15 10:00:00" --until "2024-01-15 11:00:00"
# Show kernel messages only
journalctl -k
# Show verbose context with explanations for errors
journalctl -xe
# Filter by priority (0=emerg to 7=debug)
journalctl -p err    # errors and above
journalctl -p warning..err
# Filter by PID
journalctl _PID=1234
# Output formats
journalctl -u nginx -o json-pretty
journalctl -u nginx -o short-iso
# Disk usage
journalctl --disk-usage
# Vacuum old logs
journalctl --vacuum-time=7d
journalctl --vacuum-size=500M

Конфигурация journald

/etc/systemd/journald.conf:

[Journal]
Storage=persistent
Compress=yes
SystemMaxUse=2G
SystemKeepFree=500M
SystemMaxFileSize=100M
MaxRetentionSec=30day
RateLimitInterval=30s
RateLimitBurst=10000
ForwardToSyslog=yes

Ключевые параметры настройки:

  • SystemMaxUse — жёсткий лимит на использование дискового пространства журналом
  • RateLimitBurst — максимальное количество сообщений за интервал RateLimitInterval для одного сервиса; критично для сервисов с большим количеством сообщений.
  • ForwardToSyslog=yes — передаёт (мостит) логи из journald в rsyslog для пересылки

4. rsyslog: глубокий разбор

Архитектура

rsyslog — это высокопроизводительный демон syslog, который обрабатывает маршрутизацию, фильтрацию, преобразование и пересылку логов. В большинстве дистрибутивов он работает совместно с journald, принимая перенаправленные сообщения и записывая их в файлы в /var/log/ или удаленные места назначения.

rsyslog обрабатывает сообщения через конвейер:

Input → Parser → Rulesets (filters + actions) → Output

Структура конфигурации

/etc/rsyslog.conf          # Main config
/etc/rsyslog.d/*.conf      # Drop-in configs (loaded alphabetically)

Типичный /etc/rsyslog.conf:

# Load modules
module(load="imuxsock")       # Local syslog socket
module(load="imjournal"       # Read from journald
       StateFile="imjournal.state")
# Global settings
$WorkDirectory /var/spool/rsyslog
$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat
# Local log files
auth,authpriv.*    /var/log/auth.log
*.*;auth,authpriv.none  /var/log/syslog
kern.*             /var/log/kern.log
*.emerg            :omusrmsg:*
# Include drop-ins
$IncludeConfig /etc/rsyslog.d/*.conf

Удалённая пересылка

# UDP forwarding (fire and forget, lower overhead)
*.* @logserver.example.com:514
# TCP forwarding (reliable, buffered)
*.* @@logserver.example.com:514
# TCP with TLS
*.* action(type="omfwd"
    target="logserver.example.com"
    port="6514"
    protocol="tcp"
    StreamDriver="gtls"
    StreamDriverMode="1"
    StreamDriverAuthMode="x509/name")

UDP против TCP для пересылки логов:

UDP (один @) имеет низкие накладные расходы, но может терять сообщения при высокой нагрузке или сбоях сети. TCP (двойной @@) буферизует и повторно передает данные, что делает его подходящим для обеспечения соответствия нормативным требованиям и ведения журналов аудита. Для высоконагруженных производственных систем используется очередь:

*.* action(type="omfwd"
    target="logserver.example.com"
    port="514"
    protocol="tcp"
    queue.type="LinkedList"
    queue.size="10000"
    queue.dequeueBatchSize="100"
    queue.filename="fwdRule"
    queue.maxDiskSpace="1g"
    queue.saveOnShutdown="on"
    action.resumeRetryCount="-1")

Структурированный вывод с использованием шаблонов

rsyslog может выводить данные в формате JSON для конечных потребителей:

template(name="JSONFormat" type="string"
    string="{\"time\":\"%timereported:::date-rfc3339%\",\"host\":\"%HOSTNAME%\",\"severity\":\"%syslogseverity-text%\",\"facility\":\"%syslogfacility-text%\",\"program\":\"%programname%\",\"pid\":\"%procid%\",\"message\":\"%msg:::json%\"}\n")
*.* action(type="omfile" file="/var/log/json/all.log" template="JSONFormat")

5. syslog-ng против rsyslog

syslog-ng придерживается иной философии проектирования: он использует декларативный язык конфигурации, основанный на источниках, фильтрах и получателях, а не гибридный подход, основанный на правилах, как в rsyslog.

Когда следует выбирать rsyslog:

  • В RHEL/CentOS, Ubuntu, Debian это стандартная практика, хорошо документированная.
  • Сложные правила маршрутизации с наборами правил
  • подходит для высокой нагрузки на одном узле
  • Интеграция с imjournal для обеспечения связи с journald

Когда следует выбирать syslog-ng:

  • Приоритетом является ясность конфигурации (читаемый синтаксис).
  • Активное использование парсеров (CSV, JSON, Apache и т. д.)
  • Сложная корреляция и переписывание сообщений
  • Премиум enterprise-функции (store-box и др.)

Аналог syslog-ng для удаленной пересылки:

source s_local {
    system();
    internal();
};
destination d_remote {
    network("logserver.example.com"
        port(514)
        transport("tcp"));
};
log {
    source(s_local);
    destination(d_remote);
};

Для большинства команд, управляющих Linux-инфраструктурой, rsyslog является практичным выбором по умолчанию, если только опыт команды или специфические требования к парсингу не склоняют в пользу syslog-ng.

6. Файлы логов и их расположение

Стандартные пути логов

Путь — Содержимое
/var/log/syslog — Общие системные сообщения (Debian/Ubuntu)
/var/log/messages — Общие системные сообщения (RHEL/CentOS)
/var/log/auth.log — События аутентификации (Debian/Ubuntu)
/var/log/secure — События аутентификации (RHEL/CentOS)
/var/log/kern.log — Сообщения ядра
/var/log/dmesg — Сообщения ядра при загрузке
/var/log/apt/ — Управление пакетами (Debian)
/var/log/yum.log — Управление пакетами (RHEL)
/var/log/nginx/ — Логи доступа и ошибок Nginx
/var/log/apache2/ — Логи доступа и ошибок Apache
/var/log/mysql/ — Лог ошибок MySQL
/var/log/postgresql/ — Логи PostgreSQL
/var/log/audit/audit.log — Демон аудита Linux

Практики логирования приложений

Современные приложения должны писать в stdout и stderr, а не в файлы. Это соответствует принципам Twelve-Factor App и именно его ожидают Kubernetes, Docker и systemd. Инфраструктура логирования (journald, среда выполнения контейнеров) занимается сбором, ротацией и пересылкой.

Когда прямая запись в файл неизбежна (устаревшие приложения, базы данных), необходимо обеспечить следующее:

  • Права доступа к директории логов ограничены (только пользователь приложения)
  • Настройка logrotate (см. раздел 7) (см. раздел 7).
  • Приложение обрабатывает сигнал SIGHUP для повторного открытия лог-файла (или используйте copytruncate).

7. Ротация логов с помощью logrotate

Без ротации логов каталог /var/log заполняет корневую файловую систему. На загруженном веб-сервере логи доступа могут разрастаться до гигабайт в день.

Конфигурация

Система logrotate запускается ежедневно с помощью cron или systemd timer в /etc/cron.daily/logrotate или logrotate.timer.

# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    sharedscripts
    postrotate
        nginx -s reopen
    endscript
}

Опции:

Option — Effect
daily/weekly/monthly — частота ротации
rotate N — хранить N ротированных файлов
compress — сжимать старые файлы (gzip)
delaycompress — пропустить сжатие самого последнего ротированного файла (позволяет приложениям завершить запись)
missingok — не выдавать ошибку, если лог отсутствует
notifempty — пропустить ротацию, если лог пустой
copytruncate — копировать и затем обрезать (для приложений, не поддерживающих SIGHUP)
postrotate — скрипт, выполняемый после ротации (например, перезапуск nginx)
dateext — использовать дату в имени ротированного файла вместо чисел

Тестирование logrotate

# Dry run
logrotate -d /etc/logrotate.d/nginx
# Force rotation now
logrotate -f /etc/logrotate.d/nginx

systemd-journald Rotation

journald имеет собственную встроенную ротацию, управляемую через journald.conf. Для сред, использующих только systemd и не имеющих rsyslog, убедитесь, что SystemMaxUse настроен, чтобы предотвратить неограниченный рост.

8. Централизованная архитектура логирования

Система логирования на одном хосте ненадежна и не масштабируема. Централизованное логирование решает следующие проблемы:

  • Единый интерфейс для отладки мультихостовых и мультисервисных систем
  • Корреляция между сервисами и узлами
  • Долгосрочное хранение независимо от жизненного цикла хоста
  • Требования комплаенса и аудита

Архитектурные паттерны

Паттерн 1: лёгкий (малый масштаб)

Linux hosts → rsyslog TCP → Central syslog server → Files

Паттерн 2: современный observability стек

Linux hosts → Fluent Bit → Loki → Grafana

Паттерн 3: стек Enterprise ELK

Linux hosts → Fluent Bit → Kafka → Logstash → Elasticsearch → Kibana

Паттерн 4: высоконагруженная регулируемая среда

Linux hosts → Fluent Bit (per node) → Kafka (durable buffer) → 
Logstash (parse/enrich) → Elasticsearch (hot-warm-cold) → Kibana
                                     ↓
                              Cold storage (S3/GCS)

В регулируемых средах Kafka как буфер между отправителями и индексатором имеет важное значение. Если Elasticsearch уходит на обслуживание и становится недоступным, Kafka сохраняет сообщения. Без него вы теряете данные логов во время простоя индексатора.

Fluent Bit

Fluent Bit — это легковесный инструмент для отправки логов, предпочитаемый в Kubernetes и средах с ограниченными ресурсами. Написан на языке C и потребляет примерно 450 килобайт оперативной памяти в состоянии простоя. Он работает как DaemonSet в Kubernetes или как systemd-сервис на Linux-хостах.

# /etc/fluent-bit/fluent-bit.conf
[SERVICE]
    Flush        5
    Daemon       Off
    Log_Level    info
    Parsers_File parsers.conf
[INPUT]
    Name           systemd
    Tag            host.*
    Systemd_Filter _SYSTEMD_UNIT=nginx.service
    DB             /var/log/flb_systemd.db
    Read_From_Tail On
[FILTER]
    Name   record_modifier
    Match  host.*
    Record hostname ${HOSTNAME}
    Record environment production
[OUTPUT]
    Name        loki
    Match       host.*
    Host        loki.monitoring.svc.cluster.local
    Port        3100
    Labels      job=fluentbit,host=${HOSTNAME}
    line_format json

Logstash — это мощный ETL-слой: выполняет парсинг, обогащение и маршрутизацию логов. Он используется тогда, когда необходимы сложные трансформации, такие как grok парсинг неструктурированных логов, GeoIP обогащение, нормализация полей или маршрутизация в несколько выходов.

input {
  kafka {
    bootstrap_servers => "kafka:9092"
    topics => ["logs"]
    codec => json
  }
}
filter {
  if [kubernetes][namespace] == "payments" {
    mutate {
      add_field => { "compliance_scope" => "pci" }
    }
  }
  
  if [message] =~ /ERROR/ {
    mutate { add_tag => ["error"] }
  }
}
output {
  elasticsearch {
    hosts => ["elasticsearch:9200"]
    index => "logs-%{+YYYY.MM.dd}"
  }
}

9. Логирование в Kubernetes

Модель логирования Kubernetes

В Kubernetes нет встроенной централизованной системы логирования. Официальная модель: контейнеры записывают данные в stdout/stderr, контейнерный runtime захватывает эти потоки, и дальнейшая обработка является вашей ответственностью.

На каждой ноде Kubernetes логи контейнеров находятся по пути:

/var/log/containers/<pod>_<namespace>_<container>-<id>.log

Это символические ссылки на:

/var/log/pods/<namespace>_<pod>_<uid>/<container>/<n>.log

Команды логирования kubectl

# Current logs
kubectl logs <pod> -n <namespace>
# Follow
kubectl logs -f <pod> -n <namespace>
# Previous container instance (after crash)
kubectl logs <pod> --previous -n <namespace>
# Multi-container pod
kubectl logs <pod> -c <container> -n <namespace>
# All pods matching a label
kubectl logs -l app=nginx -n production --prefix
# With timestamps
kubectl logs <pod> --timestamps=true

Архитектура логирования на уровне ноды

Container → container runtime (containerd/CRI-O) → /var/log/pods/...
                                                  ↓
                                          Fluent Bit DaemonSet
                                                  ↓
                                          Central log system

Интеграция journald зависит от runtime контейнеров. При использовании cgroups от systemd, containerd может перенаправлять запросы в journald:

journalctl -u containerd CONTAINER_NAME=nginx

Стек логирования Kubernetes: Fluent Bit + Loki

Рекомендуемый для использования в производственной среде лёгкий стек для Kubernetes:

# fluent-bit-daemonset.yaml (simplified)
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluent-bit
  namespace: logging
spec:
  selector:
    matchLabels:
      app: fluent-bit
  template:
    spec:
      serviceAccountName: fluent-bit
      containers:
      - name: fluent-bit
        image: cr.fluentbit.io/fluent/fluent-bit:3.0
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: config
          mountPath: /fluent-bit/etc
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: config
        configMap:
          name: fluent-bit-config

Loki — это горизонтально масштабируемая система агрегации логов, разработанная по принципу Prometheus, но для логов. Она индексирует только метки (а не полный текст), что делает её гораздо дешевле Elasticsearch для хранения исключительно логов.

# Loki labels strategy for Kubernetes
labels:
  namespace: "{{ .kubernetes.namespace }}"
  pod: "{{ .kubernetes.pod_name }}"
  container: "{{ .kubernetes.container_name }}"
  node: "{{ .kubernetes.host }}"

Критически важно: поддерживайте низкую кардинальность меток Loki.
Не используйте идентификаторы подов или запросов в качестве меток. Их необходимо хранить внутри логов. Высокая кардинальность меток ухудшает производительность Loki.

Проблемы логирования в Kubernetes

  • Ротация логов на узлах: kubelet по умолчанию ротирует контейнерные логи (10MB / 5 файлов). Fluent Bit DB отслеживает позицию и сохраняет результат ротации.
  • Жизненный цикл pod: когда pod удаляется, его логи удаляются с ноды. Логи должны быть отправлены до удаления pod.
  • CrashLoopBackOff: Используйте флаг --previous и отправляйте логи до завершения процесса, чтобы избежать потери контекста сбоя.
  • Контейнеры-сайдкары: Некоторые команды внедряют сайдкар, который считывает файлы логов приложения и записывает их в stdout. Это работает, но увеличивает нагрузку на ресурсы.

10. Облачное логирование

AWS CloudWatch Logs

CloudWatch Logs является нативным решением AWS. Агент CloudWatch собирает данные из следующих источников:

  • systemd journal
  • /var/log/* файлов
  • пользовательских логов приложений
// /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json
{
  "logs": {
    "logs_collected": {
      "files": {
        "collect_list": [
          {
            "file_path": "/var/log/nginx/access.log",
            "log_group_name": "/ec2/nginx/access",
            "log_stream_name": "{instance_id}",
            "timestamp_format": "%d/%b/%Y:%H:%M:%S %z"
          }
        ]
      },
      "journald": {
        "collect_list": [
          {
            "log_group_name": "/ec2/system",
            "log_stream_name": "{instance_id}/journald"
          }
        ]
      }
    }
  }
}

Для Kubernetes в EKS Fluent Bit отправляет данные напрямую в CloudWatch:

[OUTPUT]
    Name              cloudwatch_logs
    Match             kube.*
    region            eu-west-1
    log_group_name    /eks/cluster/application
    log_stream_prefix ${HOST_NAME}-
    auto_create_group true

GCP Cloud Logging

Google Cloud Logging (ранее Stackdriver) использует Ops Agent на GCE, а интеграция логирования в GKE выполняется автоматически. Для пользовательских конфигураций:

logging:
  receivers:
    nginx_access:
      type: files
      include_paths:
        - /var/log/nginx/access.log
  processors:
    nginx_parser:
      type: parse_nginx_combined
  pipelines:
    nginx_pipeline:
      receivers: [nginx_access]
      processors: [nginx_parser]

Azure Monitor

Azure Monitor Logs (Log Analytics) использует Azure Monitor Agent (AMA), который заменяет устаревший Log Analytics Agent (MMA). Настройка выполняется через Data Collection Rules (DCR):

{
  "dataSources": {
    "syslog": [
      {
        "streams": ["Microsoft-Syslog"],
        "facilityNames": ["auth", "authpriv", "daemon"],
        "logLevels": ["Warning", "Error", "Critical"],
        "name": "syslogSource"
      }
    ]
  }
}

11. Отладка и troubleshooting через логи

Диагностика сбоя сервиса

# Step 1: What's the service status?
systemctl status nginx
# Step 2: Get full journal context
journalctl -u nginx -xe --since "10 minutes ago"
# Step 3: Check last 100 lines
journalctl -u nginx -n 100 --no-pager
# Step 4: Check for dependency failures
journalctl -b -p err

Сбои аутентификации SSH

# Failed logins
journalctl -u ssh --since today | grep "Failed password"
# Successful logins
journalctl -u ssh --since today | grep "Accepted"
# On systems with auth.log:
grep "Failed password" /var/log/auth.log | awk '{print $11}' | sort | uniq -c | sort -rn | head -20

События OOM ядра

# OOM killer events
journalctl -k | grep -i "oom\|killed process\|out of memory"
# Memory pressure before OOM
dmesg -T | grep -A5 "oom_kill"

Отладка ошибок Nginx 502/503

# Combine nginx error log with upstream logs
journalctl -u nginx --since "15 minutes ago" -o json | \
  python3 -c "
import sys, json
for line in sys.stdin:
    e = json.loads(line)
    msg = e.get('MESSAGE','')
    if 'upstream' in msg or 'error' in msg.lower():
        print(e['__REALTIME_TIMESTAMP'], msg)
"

Корреляция логов между сервисами

В среде микросервисов используйте идентификатор корреляции (идентификатор трассировки), внедряемый на уровне API-шлюза и передаваемый через все вызовы сервисов. При отладке:

# Search for a specific request ID across all logs
grep "req-abc123" /var/log/*/access.log
# Or with Loki:
{namespace="production"} |= "req-abc123"

12. Security и compliance логирование

auditd: фреймворк аудита ядра

auditd перехватывает системные вызовы, значимые для безопасности, на уровне ядра — до любой фильтрации на уровне приложений. Это необходимо для соответствия требованиям PCI-DSS, SOX и CESOP.

# Install
apt install auditd audispd-plugins   # Debian/Ubuntu
dnf install audit                     # RHEL
# Enable
systemctl enable --now auditd

Правила аудита в /etc/audit/rules.d/:

# Monitor privileged command execution
-a always,exit -F arch=b64 -S execve -F euid=0 -k root_commands
# Monitor file access to sensitive files
-w /etc/passwd -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/sudoers -p wa -k sudoers
# Monitor network configuration changes
-a always,exit -F arch=b64 -S sethostname -S setdomainname -k system-locale
# Monitor successful/failed login attempts
-w /var/log/faillog -p wa -k logins
-w /var/log/lastlog -p wa -k logins
# Monitor sudo usage
-w /usr/bin/sudo -p x -k sudo_usage

Запрос логов аудита:

# All events for a specific user
ausearch -ua 1001 --start today
# All file writes to /etc
ausearch -f /etc --success yes
# Failed login attempts
ausearch -m USER_AUTH --success no --start today
# Generate a summary report
aureport --summary
aureport --failed

Анализ логов аутентификации

# Monitor for brute force attempts (>10 failures from same IP)
awk '/Failed password/{print $11}' /var/log/auth.log | \
  sort | uniq -c | sort -rn | awk '$1>10{print $2, $1, "attempts"}'
# Account lockouts
grep "pam_unix.*authentication failure" /var/log/auth.log
# sudo escalations
grep "sudo:" /var/log/auth.log | grep "COMMAND"

Специфика FinTech и Compliance

В регулируемых средах (PSD2, CESOP, PCI-DSS) требования к логированию включают:

  • Tamper-evident storage: использование запечатывания journald (--seal) или хранилищ с однократной записью (S3 Object Lock, WORM-диски)
  • Retention: PCI-DSS требует хранения минимум 1 год (3 месяца в онлайн-режиме, 9 месяцев в архиве).
  • Access logging: каждый доступ к данным держателей карт должен логироваться с указанием пользователя, времени, действия и исходного IP-адреса.
  • Privileged access monitoring: вся активность с правами root/sudo должна фиксироваться и анализироваться.
  • Log integrity verification: регулярная проверка хэшей архивных логов.
  • Separation of duties: данные логов должны быть недоступны пользователям, за которыми ведётся наблюдение.

Пример конфигурации rsyslog для финансовых систем, ориентированной на соблюдение нормативных требований:

# Auth events to tamper-evident remote store (TCP with TLS)
auth.*  action(type="omfwd"
    target="siem.compliance.internal"
    port="6514"
    protocol="tcp"
    StreamDriver="gtls"
    StreamDriverMode="1"
    queue.type="LinkedList"
    queue.filename="complianceFwd"
    queue.saveOnShutdown="on"
    action.resumeRetryCount="-1")
# Local copy for immediate access
auth.*  /var/log/auth.log

13. Производительность и оптимизация

Ограничение скорости (Rate Limiting)

Неправильно работающий сервис может генерировать миллионы строк логов в секунду, перегружая journald и заполняя диск. Ограничение скорости в journald:

# /etc/systemd/journald.conf
RateLimitInterval=30s
RateLimitBurst=10000

Если сервис превышает 10 000 сообщений за 30 секунд, journald отбрасывает дальнейшие сообщения до сброса интервала. В этом случае вы увидите следующее:

Suppressed N messages from unit nginx.service

Для rsyslog:

module(load="omprog")
if $programname == 'noisy-app' then {
    action(type="omfile" file="/dev/null")
    stop
}

Оптимизация дискового ввода-вывода

  • Асинхронная запись: rsyslog по умолчанию использует асинхронный ввод-вывод; не отключайте его.
  • Сжатие journald: оставьте Compress=yes; записи журнала хорошо сжимаются (снижение на 70-80%).
  • Отдельный раздел для логов: монтируйте /var/log на отдельный раздел или LVM-том, чтобы избежать переполнения корневой файловой системы
  • tmpfs для временных логов: Если вам нужны логи только для отладки в реальном времени (нет необходимости в постоянном хранении), используйте /run/log/journal/ (временный режим).

Настройка производительности journald

[Journal]
Storage=persistent
Compress=yes
SystemMaxUse=4G           # Absolute cap
SystemKeepFree=1G         # Always keep this free on disk
SystemMaxFileSize=200M    # Max size per journal file
MaxFileSec=1month         # Rotate files older than this

Проверьте текущую загрузку диска и выполните автоматическую очистку:

journalctl --disk-usage
journalctl --verify
journalctl --vacuum-size=2G
journalctl --vacuum-time=30d

Настройка буферизации Fluent Bit

Для высоконагруженных сред настройте буферизацию памяти и файловой системы Fluent Bit:

[SERVICE]
    Flush           1
    storage.path    /var/log/flb-storage/
    storage.sync    normal
    storage.checksum off
    storage.max_chunks_up 128
[INPUT]
    Name        tail
    storage.type filesystem   # Persist to disk if output unavailable
    Mem_Buf_Limit 50MB
    Buffer_Max_Size 5MB

14. Передовые методы

1. Лучшие практики

Без исключений. Анализ логов с одного хоста допустим для разработки, но никогда для производственной среды. У вас может возникнуть инцидент в 3 часа ночи, когда вам понадобятся логи с 12 хостов одновременно.

2. Используйте структурированное логирование (JSON).

Неструктурированные логи легко читаются человеком, но плохо обрабатываются машинами. JSON-логи позволяют фильтрацию по полям, агрегацию и настройку алертов.

Вывод приложения:

{"time":"2024-01-15T10:23:45Z","level":"ERROR","service":"payment-api","trace_id":"abc123","user_id":9821,"message":"Payment gateway timeout","duration_ms":5000,"gateway":"stripe"}

С помощью структурированных логов можно выполнять следующие запросы: {service="payment-api"} | json | duration_ms > 3000

3. Никогда не логируйте секреты

PAN-номера, пароли, API-ключи, токены, идентификаторы сессий — всё это не должно попадать в логи. Реализуйте очистку логов:

  • На уровне приложения (маскирование перед записью в лог)
  • На уровне отправителя ( Lua-фильтр Fluent Bit или mutate в Logstash)
  • Регулярно проводите аудит (поиск по шаблонам вроде номеров карт в образцах логов).
-- Fluent Bit Lua filter to redact card numbers
function redact_pci(tag, timestamp, record)
    if record["message"] then
        record["message"] = string.gsub(record["message"], "%d%d%d%d%s?%d%d%d%d%s?%d%d%d%d%s?%d%d%d%d", "****-****-****-****")
    end
    return 1, timestamp, record
end

4. Добавление согласованных метаданных

Каждая строка лога должна содержать: hostname, имя сервиса, окружение (production/staging), версию и correlation/trace ID. Это обязательное требование в распределенных системах.

5. Мониторинг объёма логов

Объем логов — это сигнал. Всплеск объема логов часто предшествует инциденту или сопровождает его. Настройте оповещения:

  • Loki: rate({job="myapp"}[5m]) > 1000 — если скорость логов превышает 1000 в минуту
  • Elasticsearch: мониторинг скорости индексации документов

6. Тестирование хранения и восстановления логов

Ежеквартально: необходимо подтверждать, что вы действительно можете получить логи возрастом 90 дней. Логи для комплаенса, которые невозможно восстановить, считаются нарушением требований комплаенса.

7. Разделение логов приложения и безопасности

Необходимо направлять события аутентификации, audit события и логи привилегированных команд в отдельное хранилище с более высоким сроком хранения и защитой от изменения. Не следует смешивать их с логами отладки приложений.

8. Документирование схемы логов

Для каждого сервиса необходимо поддерживать документ схемы логов.
Он должен описывать: какие поля отправляются, какие значения допустимы и что означает уровень severity в вашем контексте. Это значительно повышает эффективность во время инцидентов и при внедрении новых клиентов.

15. Современный стек логирования: journald + Fluent Bit + Loki + Grafana

Этот стек является практическим выбором для команд, использующих Kubernetes и уже имеющих инфраструктуру Grafana.
Он является open source, лёгким и глубоко интегрированным.

┌─────────────┐    ┌──────────────┐    ┌──────────┐    ┌─────────┐
│  systemd    │    │  Fluent Bit  │    │   Loki   │    │ Grafana │
│  journald   │───▶│  DaemonSet   │───▶│ (ingest) │───▶│(Explore)│
│             │    │              │    │          │    │         │
│  containers │    │  per node    │    │ S3/GCS   │    │ Alerts  │
└─────────────┘    └──────────────┘    └──────────┘    └─────────┘

Преимущества по сравнению с ELK

  • Стоимость: Loki индексирует только labels, полный текст ищется во время запроса. Хранение данных стоит в 10–20 раз дешевле, чем Elasticsearch при аналогичной ретенции.
  • Эксплуатационные издержки: отсутствие необходимости в настройке JVM tuning, shard management, сложной координации кластера.
  • Нативная интеграция с Grafana: один и тот же инструмент используется для метрик (Prometheus) и логов (Loki).
  • LogQL: Мощный язык запросов, аналогичный PromQL, знакомый любому пользователю Prometheus.

Примеры запросов Loki (LogQL):

# All errors from production payment service
{namespace="production", app="payment-api"} |= "ERROR"
# JSON parsing and field filtering
{namespace="production"} | json | level="error" | duration_ms > 1000
# Error rate over time
rate({namespace="production"} |= "ERROR" [5m])
# Top 10 slowest requests
{app="api"} | json | duration_ms > 0 | line_format "{{.duration_ms}}" | unwrap duration_ms | topk(10, sum by (path) (avg_over_time([1h])))

16. Практическая production конфигурация: один Linux сервер

Полный рабочий пример: один Linux сервер отправляет systemd journal в Loki через Fluent Bit.

Шаг 1: Установка Fluent Bit

curl https://raw.githubusercontent.com/fluent/fluent-bit/master/install.sh | sh
systemctl enable fluent-bit

Шаг 2: Настройка Fluent Bit

# /etc/fluent-bit/fluent-bit.conf
[SERVICE]
    Flush           5
    Daemon          Off
    Log_Level       info
    Parsers_File    parsers.conf
    storage.path    /var/log/flb-storage/
[INPUT]
    Name            systemd
    Tag             systemd.*
    DB              /var/log/flb_journal.db
    Read_From_Tail  On
    Strip_Underscores On
[INPUT]
    Name            tail
    Path            /var/log/nginx/access.log
    Tag             nginx.access
    DB              /var/log/flb_nginx.db
    Parser          nginx
[FILTER]
    Name            record_modifier
    Match           *
    Record          hostname ${HOSTNAME}
    Record          env production
[FILTER]
    Name            lua
    Match           *
    script          /etc/fluent-bit/redact.lua
    call            redact_pci
[OUTPUT]
    Name            loki
    Match           *
    Host            loki.internal
    Port            3100
    Labels          job=fluent-bit,host=${HOSTNAME}
    line_format     json
    auto_kubernetes_labels on

Шаг 3: Парсеры

# /etc/fluent-bit/parsers.conf
[PARSER]
    Name        nginx
    Format      regex
    Regex       ^(?<remote>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$
    Time_Key    time
    Time_Format %d/%b/%Y:%H:%M:%S %z

Шаг 4: Настройка Loki (минимальная)

# /etc/loki/loki.yaml
auth_enabled: false
server:
  http_listen_port: 3100
ingester:
  wal:
    dir: /var/loki/wal
  lifecycler:
    ring:
      replication_factor: 1
schema_config:
  configs:
    - from: 2024-01-01
      store: tsdb
      object_store: filesystem
      schema: v13
      index:
        prefix: index_
        period: 24h
storage_config:
  tsdb_shipper:
    active_index_directory: /var/loki/index
    cache_location: /var/loki/cache
  filesystem:
    directory: /var/loki/chunks
limits_config:
  retention_period: 30d

Запустите и проверьте:

systemctl start fluent-bit loki
# Check Fluent Bit is reading journal
journalctl -u fluent-bit -f
# Verify Loki is receiving
curl http://localhost:3100/ready
curl http://localhost:3100/loki/api/v1/labels

17. Частые проблемы и решения

Проблема: отсутствуют логи за временной интервал

Симптомы: Не удается найти логи за определенный период.

Диагноз:

journalctl --list-boots
journalctl --verify
journalctl --disk-usage

Причины и способы устранения:

  • Временное хранилище: journald в volatile режиме теряет логи после перезагрузки. Включите persistent storage: mkdir /var/log/journal && systemctl restart systemd-journald
  • Ограничение скорости: проверить сообщения suppression: journalctl | grep "Suppressed"
  • Fluent Bit position DB: если Fluent Bit упал, база может быть повреждена. Удалите его и перезапустите (возможен повторный сбор части логов).
  • Ротация логов удаляет файлы: вход tail в Fluent Bit с использованием DB сохраняет позицию при ротации; без этого ротация файлов может привести к пропуску строк.

Проблема: логи не ротируются

# Test config
logrotate -d /etc/logrotate.d/myapp
# Force rotation
logrotate -f /etc/logrotate.conf
# Check cron/timer
systemctl status logrotate.timer

Частая причина: неправильные права на файл или ошибка postrotate скрипта (проверьте код завершения).

Проблема: /var/log заполнен

# Find largest consumers
du -sh /var/log/* | sort -rh | head -20
# Check journald
journalctl --disk-usage
# Emergency cleanup
journalctl --vacuum-size=500M     # Trim journal to 500MB
journalctl --vacuum-time=3d       # Remove entries older than 3 days
# Find any rotated but uncompressed logs
find /var/log -name "*.log.*" ! -name "*.gz" -size +100M
gzip /var/log/bigapp/app.log.1

Профилактика: добавьте оповещение о заполнения диска при достижении 80% использования раздела /var/log.

Проблема: journald потребляет слишком много дискового пространства.

journalctl --disk-usage
# Expected: < configured SystemMaxUse value
# If over limit, journald should auto-vacuum; if not:
journalctl --vacuum-size=2G
# Permanently fix
cat >> /etc/systemd/journald.conf << EOF
SystemMaxUse=2G
SystemKeepFree=500M
EOF
systemctl restart systemd-journald

Проблема: Fluent Bit не отправляет логи в Loki

# Check Fluent Bit logs
journalctl -u fluent-bit -n 100
# Manually push a test log
curl -H "Content-Type: application/json" \
  -X POST http://loki.internal:3100/loki/api/v1/push \
  --data '{"streams":[{"stream":{"job":"test"},"values":[["'$(date +%s%N)'","test message"]]}]}'

Проблема: высокая кардинальность ломает Loki

Симптомы: производительность запросов в Loki снижается, приём данных замедляется, возникают ошибки ограничения потоков.

# Check number of unique streams
count(count by(__stream_shard__)(rate({job="myapp"}[5m])))

Исправление: Уменьшить разнообразие меток. Никогда не используйте идентификаторы запросов, идентификаторы пользователей или метки времени в качестве меток Loki. Они должны находиться в теле лога.

18. Заключение

Логирование в Linux — это не формальность, а операционная инфраструктура, столь же важная, как сеть или хранилище. Переход от локального journald к полностью централизованной, структурированной и коррелированной системе логирования — это разница между работой вслепую и реальной наблюдаемостью.

Современный стек логирования — journald, собирающий всё локально, Fluent Bit, эффективно передающий данные на уровне узла, Loki, экономично хранящий данные с индексированием по меткам, и Grafana, предоставляющая единые дашборды и оповещения — даёт наблюдаемость уровня production без тяжеловесности полноценного ELK-кластера.

Для FinTech и регулируемых сред логирование — это часть комплаенса. Потерянные логи — это нарушения аудита. Инвестиции в централизованное хранение, неизменяемые (tamper-evident) хранилища, корректные политики хранения и регулярную проверку восстановления логов — это не опция, а стоимость работы в регулируемой среде.

Обязательные требования:

  • Централизуйте или смиритесь с отсутствием мониторинга — распределенные логи, к которым нельзя обращаться с запросами, не являются наблюдаемостью.
  • Структурированное логирование — JSON повсюду, с самого начала; модернизация — мучительный процесс.
  • В логах никаких секретов: внедрите очистку на нескольких уровнях.
  • Проверьте срок хранения данных — логи, которые вы не можете восстановить, не учитываются комплаенсом.
  • Контролируйте объём логов — это сигнал; всплески предшествуют инцидентам.

Освоив эти основы и внедрив описанные практики, логирование превращается из проблемы в инструмент, дающий реальную управляемость системой.

Подписывайтесь на телеграм-канал Мониторим ИТ, там еще больше полезной информации о мониторинге!