Модуль 9: Мониторинг и наблюдаемость в DevOps
📊 Зачем нужен мониторинг?
Мониторинг — это процесс непрерывного наблюдения за состоянием системы, сбора метрик и генерации предупреждений. Без мониторинга вы узнаете о проблемах только когда пользователи начнут жаловаться. Это как водить машину с закрытыми глазами — опасно и безответственно.
По данным исследований 2025 года, 87% компаний с зрелыми практиками мониторинга обнаруживают проблемы до того, как они повлияют на пользователей1. Prometheus и Grafana стали стандартом де-факто для мониторинга в DevOps — как нож и вилка, вместе они делают мониторинг инфраструктуры и приложений простым и наглядным1.
🎯 Четыре золотых сигнала мониторинга
Google SRE определила четыре ключевые метрики, которые должен отслеживать любой сервис:
1. Latency (Задержка)
Время ответа на запросы. Критично для пользовательского опыта.
2. Traffic (Трафик)
Количество запросов в секунду. Показывает нагрузку на систему.
3. Errors (Ошибки)
Процент неуспешных запросов. Индикатор качества сервиса.
4. Saturation (Насыщенность)
Использование ресурсов (CPU, память, диск). Предупреждает о приближении к лимитам.
🔥 Prometheus — сердце мониторинга
Prometheus — open-source система мониторинга, которая собирает метрики, хранит их в виде временных рядов и предоставляет мощный язык запросов PromQL1. Это движок мониторинга, который сам опрашивает сервисы, собирает данные и сохраняет их в своей базе1.
Архитектура Prometheus
Prometheus Server — основной компонент, который:
- Собирает метрики с целевых систем (pull-модель)
- Хранит данные в формате временных рядов
- Предоставляет HTTP API для запросов
- Оценивает правила алертов
Exporters — программы, которые собирают метрики:
- Node Exporter — метрики ОС Linux
- cAdvisor — метрики Docker контейнеров
- MySQL Exporter — метрики базы данных
- Blackbox Exporter — проверка доступности сервисов
Alertmanager — компонент для управления алертами и уведомлениями
⚙️ Установка стека мониторинга
Docker Compose для полного стека
# docker-compose.yml
version: '3.8'
services:
prometheus:
image: prom/prometheus:latest
container_name: prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
- ./prometheus/rules:/etc/prometheus/rules
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
- '--storage.tsdb.retention.time=200h'
- '--web.enable-lifecycle'
- '--web.enable-admin-api'
restart: unless-stopped
grafana:
image: grafana/grafana:latest
container_name: grafana
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin123
- GF_USERS_ALLOW_SIGN_UP=false
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning
restart: unless-stopped
node-exporter:
image: prom/node-exporter:latest
container_name: node-exporter
ports:
- "9100:9100"
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.rootfs=/rootfs'
- '--path.sysfs=/host/sys'
- '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($|/)'
restart: unless-stopped
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
container_name: cadvisor
ports:
- "8080:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:rw
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
- /dev/disk/:/dev/disk:ro
privileged: true
restart: unless-stopped
alertmanager:
image: prom/alertmanager:latest
container_name: alertmanager
ports:
- "9093:9093"
volumes:
- ./alertmanager/alertmanager.yml:/etc/alertmanager/alertmanager.yml
restart: unless-stopped
volumes:
prometheus_data:
grafana_data:
Конфигурация Prometheus
# prometheus/prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- "rules/*.yml"
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'node-exporter'
static_configs:
- targets: ['node-exporter:9100']
- job_name: 'cadvisor'
static_configs:
- targets: ['cadvisor:8080']
- job_name: 'my-app'
static_configs:
- targets: ['my-app:3000']
metrics_path: '/metrics'
scrape_interval: 5s
alerting:
alertmanagers:
- static_configs:
- targets:
- alertmanager:9093
📈 Основы PromQL
PromQL — язык запросов Prometheus для анализа метрик:
Базовые запросы
# Текущее значение метрики
up
# Фильтрация по лейблам
up{job="node-exporter"}
# Арифметические операции
node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes
# Процент использования памяти
(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100
# Загрузка CPU за последние 5 минут
100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
Функции времени
# Количество запросов в секунду rate(http_requests_total[5m]) # Увеличение за период increase(http_requests_total[1h]) # 95-й процентиль времени ответа histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) # Среднее значение за период avg_over_time(cpu_usage[10m])
🛠️ Практические задания
Задание 9.1: Запуск стека мониторинга
# 1. Создание структуры проекта mkdir monitoring-stack cd monitoring-stack mkdir -p prometheus/rules grafana/provisioning alertmanager # 2. Создание docker-compose.yml (используйте пример выше) # 3. Запуск стека docker-compose up -d # 4. Проверка сервисов curl http://localhost:9090/targets # Prometheus targets curl http://localhost:3000 # Grafana (admin/admin123) curl http://localhost:9100/metrics # Node Exporter metrics
Задание 9.2: Мониторинг приложения
// app.js - Node.js приложение с метриками
const express = require('express');
const promClient = require('prom-client');
const app = express();
const port = process.env.PORT || 3000;
// Создание реестра метрик
const register = new promClient.Registry();
// Счетчик запросов
const httpRequestsTotal = new promClient.Counter({
name: 'http_requests_total',
help: 'Total number of HTTP requests',
labelNames: ['method', 'route', 'status_code'],
registers: [register]
});
// Гистограмма времени ответа
const httpRequestDuration = new promClient.Histogram({
name: 'http_request_duration_seconds',
help: 'Duration of HTTP requests in seconds',
labelNames: ['method', 'route', 'status_code'],
buckets: [0.1, 0.3, 0.5, 0.7, 1, 3, 5, 7, 10],
registers: [register]
});
// Gauge для активных подключений
const activeConnections = new promClient.Gauge({
name: 'active_connections',
help: 'Number of active connections',
registers: [register]
});
// Middleware для сбора метрик
app.use((req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = (Date.now() - start) / 1000;
const route = req.route?.path || req.path;
httpRequestsTotal
.labels(req.method, route, res.statusCode.toString())
.inc();
httpRequestDuration
.labels(req.method, route, res.statusCode.toString())
.observe(duration);
});
next();
});
// Эндпоинт для метрик
app.get('/metrics', async (req, res) => {
res.set('Content-Type', register.contentType);
res.end(await register.metrics());
});
// API эндпоинты
app.get('/', (req, res) => {
res.json({
message: 'Hello Monitoring!',
timestamp: new Date().toISOString()
});
});
app.get('/health', (req, res) => {
res.json({ status: 'healthy' });
});
app.get('/api/users', (req, res) => {
// Симуляция времени обработки
setTimeout(() => {
res.json({ users: ['Alice', 'Bob', 'Charlie'] });
}, Math.random() * 100);
});
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
Задание 9.3: Настройка алертов
# prometheus/rules/alerts.yml
groups:
- name: system.rules
rules:
- alert: InstanceDown
expr: up == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Instance {{ $labels.instance }} down"
description: "{{ $labels.instance }} has been down for more than 1 minute."
- alert: HighCPUUsage
expr: 100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
for: 2m
labels:
severity: warning
annotations:
summary: "High CPU usage on {{ $labels.instance }}"
description: "CPU usage is above 80% for more than 2 minutes."
- alert: HighMemoryUsage
expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 90
for: 2m
labels:
severity: critical
annotations:
summary: "High memory usage on {{ $labels.instance }}"
description: "Memory usage is above 90% for more than 2 minutes."
- alert: DiskSpaceLow
expr: (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) * 100 < 10
for: 1m
labels:
severity: warning
annotations:
summary: "Low disk space on {{ $labels.instance }}"
description: "Disk space is below 10% on {{ $labels.instance }}."
- alert: HighErrorRate
expr: rate(http_requests_total{status_code=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.1
for: 5m
labels:
severity: critical
annotations:
summary: "High error rate detected"
description: "Error rate is above 10% for more than 5 minutes."
Конфигурация Alertmanager
# alertmanager/alertmanager.yml
global:
smtp_smarthost: 'localhost:587'
smtp_from: 'alerts@company.com'
route:
group_by: ['alertname']
group_wait: 10s
group_interval: 10s
repeat_interval: 1h
receiver: 'web.hook'
receivers:
- name: 'web.hook'
email_configs:
- to: 'admin@company.com'
subject: 'Alert: {{ .GroupLabels.alertname }}'
body: |
{{ range .Alerts }}
Alert: {{ .Annotations.summary }}
Description: {{ .Annotations.description }}
{{ end }}
slack_configs:
- api_url: 'YOUR_SLACK_WEBHOOK_URL'
channel: '#alerts'
title: 'Alert: {{ .GroupLabels.alertname }}'
text: |
{{ range .Alerts }}
{{ .Annotations.summary }}
{{ .Annotations.description }}
{{ end }}
inhibit_rules:
- source_match:
severity: 'critical'
target_match:
severity: 'warning'
equal: ['alertname', 'dev', 'instance']
📊 Grafana — визуализация данных
Создание дашборда
json
# grafana/provisioning/dashboards/system-overview.json
{
"dashboard": {
"title": "System Overview",
"tags": ["system", "monitoring"],
"panels": [
{
"title": "CPU Usage",
"type": "stat",
"targets": [
{
"expr": "100 - (avg(irate(node_cpu_seconds_total{mode=\"idle\"}[5m])) * 100)",
"legendFormat": "CPU Usage %"
}
],
"fieldConfig": {
"defaults": {
"unit": "percent",
"thresholds": {
"steps": [
{"color": "green", "value": null},
{"color": "yellow", "value": 70},
{"color": "red", "value": 90}
]
}
}
}
},
{
"title": "Memory Usage",
"type": "timeseries",
"targets": [
{
"expr": "(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100",
"legendFormat": "Memory Usage %"
}
]
},
{
"title": "HTTP Requests Rate",
"type": "timeseries",
"targets": [
{
"expr": "rate(http_requests_total[5m])",
"legendFormat": "{{ method }} {{ route }}"
}
]
}
]
}
}
🔍 Логирование с ELK Stack
Docker Compose для ELK
# elk-stack.yml
version: '3.8'
services:
elasticsearch:
image: elasticsearch:8.11.0
environment:
- discovery.type=single-node
- xpack.security.enabled=false
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
ports:
- "9200:9200"
volumes:
- elasticsearch_data:/usr/share/elasticsearch/data
kibana:
image: kibana:8.11.0
ports:
- "5601:5601"
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
depends_on:
- elasticsearch
logstash:
image: logstash:8.11.0
volumes:
- ./logstash/pipeline:/usr/share/logstash/pipeline
ports:
- "5000:5000"
depends_on:
- elasticsearch
volumes:
elasticsearch_data:
Конфигурация Logstash
ruby
# logstash/pipeline/logstash.conf
input {
beats {
port => 5044
}
tcp {
port => 5000
codec => json
}
}
filter {
if [fields][service] == "nginx" {
grok {
match => { "message" => "%{NGINXACCESS}" }
}
date {
match => [ "timestamp", "dd/MMM/yyyy:HH:mm:ss Z" ]
}
}
}
output {
elasticsearch {
hosts => ["elasticsearch:9200"]
index => "logs-%{+YYYY.MM.dd}"
}
stdout {
codec => rubydebug
}
}
📖 Полезные ресурсы
- Prometheus Documentation — официальная документация
- Grafana Documentation — руководство по Grafana
- PromQL Tutorial — изучение языка запросов1
- YouTube: "Мониторинг в DevOps с Prometheus и Grafana" — практический курс
- The Four Golden Signals — принципы мониторинга от Google SRE
✅ Чек-лист модуля
- Понимаю важность мониторинга в DevOps
- Знаю четыре золотых сигнала мониторинга
- Установил и настроил Prometheus
- Освоил основы PromQL для запросов метрик
- Настроил сбор метрик с Node Exporter и cAdvisor
- Создал дашборды в Grafana
- Настроил алерты в Prometheus и Alertmanager
- Добавил метрики в собственное приложение
- Изучил основы логирования с ELK Stack
- Понимаю разницу между мониторингом и наблюдаемостью
🚀 Что дальше?
После освоения мониторинга переходите к Модулю 10: Облачные платформы для изучения современных облачных решений и их интеграции с системами мониторинга.