MONITORING

راه‌اندازی مانیتورینگ با Prometheus، Grafana و Alertmanager با Docker Compose

یک استک مانیتورینگ کامل با Docker Compose: Prometheus، node_exporter، cAdvisor، Grafana و Alertmanager — همراه با فایل‌های پیکربندی و داشبورد اولیه.

Prometheus، Grafana و Alertmanager ترکیب استاندارد مانیتورینگ زیرساخت‌های مدرن هستند و بدون نیاز به لایسنس، روی یک سرور کوچک هم اجرا می‌شوند. در این راهنما یک استک کامل را با Docker Compose راه می‌اندازیم: Prometheus برای جمع‌آوری و ذخیره‌ی متریک، node_exporter برای متریک میزبان، cAdvisor برای متریک کانتینرها، Alertmanager برای هشدار و Grafana برای نمودار. همه‌ی فایل‌ها قابل‌کپی‌اند.

معماری در یک نگاه

جزءنقشپورت پیش‌فرض
Prometheusجمع‌آوری (pull) و ذخیره‌ی متریک؛ ارزیابی قواعد هشدار9090
node_exporterمتریک میزبان: CPU، حافظه، دیسک، شبکه9100
cAdvisorمتریک هر کانتینر Docker8080
Alertmanagerگروه‌بندی و ارسال هشدار (ایمیل، webhook، …)9093
Grafanaداشبورد و کاوش داده3000

جریان کار: exporterها متریک را روی یک آدرس HTTP (معمولاً /metrics) نشان می‌دهند، Prometheus هر ۱۵ ثانیه آن‌ها را می‌خواند، قواعد هشدار را ارزیابی می‌کند و هشدارهای فعال را به Alertmanager می‌فرستد، و Grafana از Prometheus کوئری می‌گیرد.

ساختار پوشه‌ها

monitoring/
├── docker-compose.yml
├── prometheus/
│   ├── prometheus.yml
│   └── rules.yml
├── alertmanager/
│   └── alertmanager.yml
└── grafana/
    └── provisioning/datasources/prometheus.yml

فایل docker-compose.yml

نسخه‌ها را ثابت کرده‌ایم (اصل ثابت‌بودن تگ در راهنمای اشتباه‌های رایج Compose توضیح داده شده)؛ پیش از استفاده با آخرین نسخه‌ی پایدار هر پروژه جایگزین کنید.

services:
  prometheus:
    image: prom/prometheus:v2.55.0
    restart: unless-stopped
    command:
      - --config.file=/etc/prometheus/prometheus.yml
      - --storage.tsdb.retention.time=15d
      - --web.enable-lifecycle
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - ./prometheus/rules.yml:/etc/prometheus/rules.yml:ro
      - prom_data:/prometheus
    ports:
      - "127.0.0.1:9090:9090"
    networks: [monitoring]

  node-exporter:
    image: prom/node-exporter:v1.8.2
    restart: unless-stopped
    command: ["--path.rootfs=/host"]
    pid: host
    volumes:
      - /:/host:ro,rslave
    networks: [monitoring]

  cadvisor:
    image: gcr.io/cadvisor/cadvisor:v0.49.1
    restart: unless-stopped
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:ro
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro
    networks: [monitoring]

  alertmanager:
    image: prom/alertmanager:v0.27.0
    restart: unless-stopped
    volumes:
      - ./alertmanager/alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro
    networks: [monitoring]

  grafana:
    image: grafana/grafana:11.3.0
    restart: unless-stopped
    environment:
      GF_SECURITY_ADMIN_PASSWORD__FILE: /run/secrets/grafana_admin
    secrets: [grafana_admin]
    volumes:
      - grafana_data:/var/lib/grafana
      - ./grafana/provisioning:/etc/grafana/provisioning:ro
    ports:
      - "127.0.0.1:3000:3000"
    networks: [monitoring]

secrets:
  grafana_admin:
    file: ./secrets/grafana_admin.txt

volumes:
  prom_data:
  grafana_data:

networks:
  monitoring:
YMLDocker Compose Validatorفایل Compose را پیش از اجرا بررسی کنید.
  • پورت‌های Prometheus و Grafana فقط روی 127.0.0.1 منتشر شده‌اند؛ Alertmanager، node_exporter و cAdvisor اصلاً منتشر نشده‌اند و فقط داخل شبکه‌ی monitoring در دسترس‌اند. برای دسترسی از بیرون، یک reverse proxy با HTTPS و احراز هویت جلوی Grafana بگذارید (راهنمای Nginx).
  • رمز ادمین Grafana از فایل خوانده می‌شود (پسوند __FILE در متغیرهای Grafana)؛ فایل secrets/grafana_admin.txt را در Git نگذارید.
  • cAdvisor دسترسی خواندنی گسترده به میزبان لازم دارد؛ به همین دلیل آن را فقط در شبکه‌ی داخلی نگه می‌داریم.

پیکربندی Prometheus

global:
  scrape_interval: 15s
  evaluation_interval: 15s

rule_files:
  - /etc/prometheus/rules.yml

alerting:
  alertmanagers:
    - static_configs:
        - targets: ["alertmanager:9093"]

scrape_configs:
  - job_name: prometheus
    static_configs:
      - targets: ["localhost:9090"]

  - job_name: node
    static_configs:
      - targets: ["node-exporter:9100"]

  - job_name: cadvisor
    static_configs:
      - targets: ["cadvisor:8080"]

نام‌های node-exporter و cadvisor همان نام سرویس‌های Compose‌اند که در شبکه‌ی مشترک resolve می‌شوند. برای افزودن سرورهای دیگر، آدرس IP:9100 هر یک را به targets job مربوط به node اضافه کنید.

YAMLYAML Validatorنحو فایل‌های YAML (prometheus.yml و rules.yml) را قبل از بارگذاری بررسی کنید.

قواعد هشدار اولیه

groups:
  - name: basics
    rules:
      - alert: InstanceDown
        expr: up == 0
        for: 2m
        labels: { severity: critical }
        annotations:
          summary: "{{ $labels.job }} on {{ $labels.instance }} is down"

      - alert: DiskSpaceLow
        expr: |
          node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"}
            / node_filesystem_size_bytes{fstype!~"tmpfs|overlay"} < 0.10
        for: 10m
        labels: { severity: warning }
        annotations:
          summary: "Less than 10% disk space left on {{ $labels.instance }} ({{ $labels.mountpoint }})"

      - alert: HighMemoryUsage
        expr: (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) > 0.90
        for: 10m
        labels: { severity: warning }
        annotations:
          summary: "Memory usage above 90% on {{ $labels.instance }}"

بخش for مهم است: هشدار فقط وقتی فعال می‌شود که شرط برای آن مدت پیوسته برقرار باشد؛ این کار جلوی هشدارهای کاذب از نوسان‌های کوتاه را می‌گیرد. اصول انتخاب و طراحی هشدار در راهنمای هشداردهی اصولی آمده است.

Alertmanager

Alertmanager هشدارها را گروه‌بندی می‌کند و به مقصد می‌فرستد. نمونه‌ی ساده با webhook (برای مثال یک ربات پیام‌رسان یا سرویس داخلی):

route:
  receiver: team
  group_by: [alertname, instance]
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h

receivers:
  - name: team
    webhook_configs:
      - url: http://notifier.internal:8080/alerts
        send_resolved: true

گزینه‌ی send_resolved پیام «برطرف شد» را هم می‌فرستد و repeat_interval فاصله‌ی یادآوری هشدار هنوز فعال را تعیین می‌کند؛ مقدار کم آن باعث نویز می‌شود.

اتصال Grafana به Prometheus

با provisioning، اتصال هنگام بالاآمدن Grafana خودکار ساخته می‌شود و نیازی به کلیک در رابط نیست (grafana/provisioning/datasources/prometheus.yml):

apiVersion: 1
datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    url: http://prometheus:9090
    isDefault: true

برای شروع سریع، یک داشبورد آماده وارد کنید: در Grafana مسیر Dashboards → New → Import و شناسه‌ی 1860 (Node Exporter Full) را وارد کنید. برای cAdvisor هم در grafana.com داشبوردهای آماده با عبارت «cadvisor» پیدا می‌شوند.

اجرا و راستی‌آزمایی

mkdir -p secrets && openssl rand -base64 24 > secrets/grafana_admin.txt
docker compose up -d
docker compose ps

curl -s localhost:9090/-/ready            # Prometheus آماده است؟
curl -s 'localhost:9090/api/v1/query?query=up'

خروجی کوئری up باید برای هر هدف مقدار 1 نشان دهد. نمای کامل هدف‌ها در رابط Prometheus، بخش Status → Targets، است. از راه دور با تونل SSH به رابط‌ها دسترسی بگیرید:

ssh -L 3000:localhost:3000 -L 9090:localhost:9090 user@server

قواعد را بدون ری‌استارت می‌توان بازخوانی کرد (به‌خاطر --web.enable-lifecycle): curl -X POST localhost:9090/-/reload. این endpoint نباید از بیرون در دسترس باشد.

نکته‌های بهره‌برداری

  • دیسک: رشد دیسک با تعداد سری‌های زمانی و مدت نگه‌داری تعیین می‌شود. متریک prometheus_tsdb_head_series را زیر نظر بگیرید و روی labelهایی که مقدارهای بی‌پایان دارند (شناسه‌ی کاربر، URL کامل) سری نسازید.
  • پشتیبان: اگر داده‌ی تاریخی مهم است، snapshot بگیرید یا از ذخیره‌سازی راه دور (remote write) استفاده کنید.
  • مانیتور خود مانیتورینگ: اگر خود Prometheus از کار بیفتد، هیچ هشداری ارسال نمی‌شود. یک بررسی مستقل بیرونی (heartbeat) برای آن بگذارید.
  • اگر روی Kubernetes هستید: به‌جای Compose معمولاً از kube-prometheus-stack (Helm) استفاده می‌شود، ولی مفاهیم و PromQL همان است.

Grafana و Docker را روی سرور ایران می‌توانید از مخزن داخلی نصب کنید؛ مراحل در راهنمای نصب از مخزن داخلی آمده است.

جمع‌بندی و مطالعه‌ی بعدی

با یک فایل Compose و سه فایل پیکربندی، مانیتورینگ پایه‌ی میزبان و کانتینرها آماده است. گام بعدی یادگرفتن زبان کوئری است: PromQL برای شروع کوئری‌های ضروری را با مثال نشان می‌دهد و هشداردهی اصولی کمک می‌کند هشدارهایی بنویسید که واقعاً ارزش بیدارشدن را داشته باشند.

پرسش‌های متداول

Prometheus به‌جای push، چرا متریک‌ها را pull می‌کند؟
در مدل pull، خود Prometheus فهرست هدف‌ها و فاصله‌ی جمع‌آوری را کنترل می‌کند و با یک نگاه می‌فهمد کدام هدف در دسترس نیست (متریک up). برای کارهای کوتاه‌مدت که اجازه‌ی scrape ندارند، Pushgateway وجود دارد، ولی استفاده‌ی عمومی از آن توصیه نمی‌شود.
داده‌ها چقدر نگه داشته می‌شوند و چقدر دیسک لازم است؟
پیش‌فرض نگه‌داری ۱۵ روز است (--storage.tsdb.retention.time). به‌طور تقریبی هر نمونه حدود ۱ تا ۲ بایت فشرده می‌شود؛ پس دیسک ≈ نمونه‌ی در ثانیه × بایت هر نمونه × ثانیه‌های نگه‌داری. با prometheus_tsdb_head_series تعداد سری‌های فعال را ببینید و بر اساس آن تخمین بزنید.
Grafana یا Prometheus: کدام برای هشدار بهتر است؟
ترجیحاً قواعد هشدار را در Prometheus و مسیریابی آن را در Alertmanager بنویسید؛ آن‌ها در کد (Git) نگه‌داری می‌شوند و مستقل از رابط کاربری کار می‌کنند. Grafana برای نمایش و بررسی است، هرچند خودش هم قابلیت هشدار دارد.
برای چند سرور چه کنم؟
روی هر سرور node_exporter اجرا کنید و آدرس همه را در targets یا با service discovery (فایل، DNS یا Kubernetes) به Prometheus بدهید. ارتباط را روی شبکه‌ی خصوصی یا VPN نگه دارید، نه اینترنت عمومی.