← กลับ
PrometheusMonitoringService DiscoveryKubernetes

Prometheus Scrape หลาย Service แบบไม่ต้อง Hardcode

เพิ่ม service ทีต้องแก้ prometheus.yml ทีก็เหนื่อย ใช้ service discovery ให้ Prometheus หาเอง รองรับ Docker, Kubernetes, Consul

2026-01-30อ่าน 6 นาทีใหม่

ปัญหา

prometheus.yml แบบ hardcode:

scrape_configs:
  - job_name: 'app1'
    static_configs:
      - targets: ['app1:9090']

  - job_name: 'app2'
    static_configs:
      - targets: ['app2:9090']

  - job_name: 'app3'
    static_configs:
      - targets: ['app3:9090']

มี service ใหม่ — ต้องแก้ไฟล์ → reload Prometheus → ทุกครั้ง

ถ้ามี 50 service → จัดการไม่ไหว

Service Discovery

Prometheus รองรับ SD จากหลายแหล่ง:

  • docker_sd_configs — auto discover container ใน Docker
  • kubernetes_sd_configs — discover pod / service ใน K8s
  • consul_sd_configs — Consul registry
  • file_sd_configs — ไฟล์ JSON ที่ tool อื่น generate
  • dns_sd_configs — DNS SRV record
  • ec2_sd_configs — AWS EC2 instance

หลักการ: Prometheus query API ของแหล่งข้อมูล → ได้รายการ target → scrape ตาม label/annotation ที่กำหนด

ใช้ Docker SD

prometheus.yml:

scrape_configs:
  - job_name: 'docker'
    docker_sd_configs:
      - host: unix:///var/run/docker.sock
        refresh_interval: 30s

    relabel_configs:
      # filter — scrape เฉพาะ container ที่มี label "prometheus.scrape=true"
      - source_labels: [__meta_docker_container_label_prometheus_scrape]
        regex: 'true'
        action: keep

      # ใช้ port จาก label "prometheus.port"
      - source_labels: [__meta_docker_container_label_prometheus_port]
        target_label: __address__
        replacement: ${1}:${2}
        regex: '(.+)'

      # job name = container name
      - source_labels: [__meta_docker_container_name]
        regex: '/(.*)'
        target_label: job

Docker compose ที่ container "opt-in" ให้ scrape:

services:
  api:
    image: myapi
    labels:
      prometheus.scrape: "true"
      prometheus.port: "9090"
    ports:
      - "9090"

  worker:
    image: myworker
    labels:
      prometheus.scrape: "true"
      prometheus.port: "9091"

  prometheus:
    image: prom/prometheus:latest
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
      - /var/run/docker.sock:/var/run/docker.sock:ro
    ports:
      - "9090:9090"

ทุก container ที่มี label prometheus.scrape: "true" → Prometheus scrape ให้อัตโนมัติ container ใหม่ — ใส่ label = appear ใน Prometheus ภายใน 30 วินาที

ใช้ Kubernetes SD

วิธีมาตรฐานในการ scrape pod ใน K8s:

scrape_configs:
  - job_name: 'kubernetes-pods'
    kubernetes_sd_configs:
      - role: pod

    relabel_configs:
      # scrape เฉพาะ pod ที่มี annotation prometheus.io/scrape=true
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
        action: keep
        regex: true

      # ใช้ path จาก annotation
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
        action: replace
        target_label: __metrics_path__
        regex: (.+)

      # ใช้ port จาก annotation
      - source_labels:
          [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
        action: replace
        regex: ([^:]+)(?::\d+)?;(\d+)
        replacement: $1:$2
        target_label: __address__

      # ดึง namespace + pod name มาเป็น label
      - action: labelmap
        regex: __meta_kubernetes_pod_label_(.+)

      - source_labels: [__meta_kubernetes_namespace]
        target_label: namespace

      - source_labels: [__meta_kubernetes_pod_name]
        target_label: pod

deployment ที่ opt-in:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  template:
    metadata:
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "9090"
        prometheus.io/path: "/metrics"
    spec:
      containers:
        - name: app
          image: myapp:1.0
          ports:
            - name: metrics
              containerPort: 9090

Prometheus Operator + ServiceMonitor

ถ้าใช้ kube-prometheus-stack (Helm chart) — มี ServiceMonitor CRD ที่ดีกว่า:

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: myapp
  labels:
    release: kube-prometheus-stack    # ต้อง match label ของ Prometheus instance
spec:
  selector:
    matchLabels:
      app: myapp
  endpoints:
    - port: metrics
      interval: 30s
      path: /metrics

แค่นี้ Prometheus Operator จะ generate scrape config ให้อัตโนมัติ

ดี: declarative, manage ผ่าน manifest ปกติ, version control ได้

File-based Service Discovery

สำหรับ infrastructure ที่ไม่มี API native — สร้างไฟล์ JSON ให้ Prometheus อ่าน:

scrape_configs:
  - job_name: 'custom'
    file_sd_configs:
      - files:
          - /etc/prometheus/targets/*.json
        refresh_interval: 30s

/etc/prometheus/targets/databases.json:

[
  {
    "targets": ["db1.internal:9187", "db2.internal:9187"],
    "labels": {
      "service": "postgres",
      "env": "production"
    }
  },
  {
    "targets": ["redis1.internal:9121", "redis2.internal:9121"],
    "labels": {
      "service": "redis",
      "env": "production"
    }
  }
]

ใช้กับ tool อื่น generate ไฟล์นี้ — เช่น script ที่ดึงรายการจาก Terraform state

Consul Service Discovery

ถ้ามี Consul registry อยู่:

scrape_configs:
  - job_name: 'consul'
    consul_sd_configs:
      - server: 'consul.internal:8500'

    relabel_configs:
      - source_labels: [__meta_consul_tags]
        regex: '.*,prometheus,.*'
        action: keep

      - source_labels: [__meta_consul_service]
        target_label: job

service ที่ register ใน Consul ด้วย tag prometheus → Prometheus ดึงมา scrape

Multi-cluster / Multi-region

ใช้ Prometheus federation หรือ Thanos เพื่อ aggregate:

scrape_configs:
  - job_name: 'federate'
    scrape_interval: 30s
    honor_labels: true
    metrics_path: '/federate'
    params:
      'match[]':
        - '{job=~".+"}'
    static_configs:
      - targets:
          - prometheus-region-1:9090
          - prometheus-region-2:9090

หรือใช้ VictoriaMetrics ที่ scale ดีกว่า + เก็บ data นานกว่า

Reload config ไม่ต้อง restart

# ผ่าน HTTP API (ต้องเปิด --web.enable-lifecycle)
curl -X POST http://prometheus:9090/-/reload

# หรือส่ง SIGHUP
docker exec prometheus kill -HUP 1

ตั้ง config ใน Prometheus image:

command:
  - '--config.file=/etc/prometheus/prometheus.yml'
  - '--web.enable-lifecycle'
  - '--storage.tsdb.retention.time=30d'

Verify ว่า target ถูก discover

UI Prometheus: http://prometheus:9090/targets

หรือ API:

curl http://prometheus:9090/api/v1/targets | jq '.data.activeTargets[] | {job: .labels.job, health: .health, url: .scrapeUrl}'

ปัญหาที่เจอบ่อย

Target down (UNREACHABLE) → Prometheus เข้า service URL ไม่ได้

  • เช็ค network: curl <target> จาก pod ของ Prometheus
  • เช็ค port ตรงมั้ย
  • เช็ค NetworkPolicy ใน K8s

Target ไม่โผล่ → SD ไม่ match label/annotation

  • ดู Service Discovery page ใน Prometheus UI ดู target ที่ถูก drop กับเหตุผล

Cardinality ระเบิด → label ที่มี value ไม่ซ้ำเยอะ (เช่น user_id, request_id) — Prometheus เก็บเป็น series คนละตัว

  • หลีกเลี่ยง label ที่ unbounded
  • ใช้ histogram bucket แทน raw value

Best Practice

  1. Convention over configuration — ใช้ annotation/label มาตรฐานเดียว ทุก service follow
  2. Filter ที่ relabel ก่อน — อย่า scrape ทุก pod ใน cluster ใหญ่ (กิน resource)
  3. Consistent labelingservice, env, region ใส่ทุก target
  4. Recording rules สำหรับ query ที่ซับซ้อน — query ตอน dashboard เร็วขึ้น
  5. Alert rules แยกไฟล์rules/*.yml modular

สรุป

Service discovery เปลี่ยน Prometheus จาก "config ที่ต้องแก้ทุกครั้ง" → "ระบบที่ตั้งครั้งเดียวขยายเอง"

หลักของทุก SD เหมือนกัน: Prometheus ถาม source → ได้ list → relabel → scrape

เริ่มจาก Docker SD หรือ K8s SD ตามที่ infra ใช้อยู่ — แล้วขยายไป Consul / file SD ตามต้องการ

← ดูบทความอื่น