karawaci.kode

← Semua snippet

Bash Menengah Otomasi

Promtail + Loki log shipping setup

Setup Promtail ship log dari Docker / systemd ke Grafana Loki. Label discipline yang jangan kelebihan cardinality.

Dipublikasikan 28 Juni 2026

ELK keren tapi butuh CPU/RAM 10x lebih banyak dari Loki untuk volume sama. Untuk Indonesia startup yang hemat infra, Loki + Promtail combo lebih realistic. Snippet ini setup Promtail baca log Docker container + journald systemd, push ke Loki, dengan label discipline yang gak meledak.

Kode

# /etc/promtail/promtail.yaml
server:
  http_listen_port: 9080
  grpc_listen_port: 0

positions:
  filename: /var/lib/promtail/positions.yaml

clients:
  - url: http://loki.observability.svc.cluster.local:3100/loki/api/v1/push
    tenant_id: tokopedia-prod

# Limit untuk mencegah Promtail OOM
limits_config:
  readline_rate: 10000
  readline_burst: 20000

scrape_configs:
  # ==========================================
  # Docker container — auto-discover via label
  # ==========================================
  - job_name: docker
    docker_sd_configs:
      - host: unix:///var/run/docker.sock
        refresh_interval: 30s
        filters:
          - name: label
            values: ["promtail.enable=true"]

    relabel_configs:
      # Ambil nama container sebagai 'container' label
      - source_labels: ['__meta_docker_container_name']
        regex: '/(.+)'
        target_label: 'container'

      # Ambil service name dari label compose
      - source_labels: ['__meta_docker_container_label_com_docker_compose_service']
        target_label: 'service'

      # Ambil env (production/staging) dari label
      - source_labels: ['__meta_docker_container_label_env']
        target_label: 'env'

      # IMPORTANT: drop label yang high cardinality
      - regex: '__meta_docker_container_id'
        action: labeldrop

    pipeline_stages:
      # Parse JSON log dari Pino / structured logger
      - json:
          expressions:
            level: level
            msg: msg
            trace_id: trace_id

      # Promote 'level' jadi label (low cardinality: info/warn/error)
      - labels:
          level:

      # Output: format output ke "level=info msg=..." untuk grep
      - output:
          source: msg

  # ==========================================
  # Systemd journald — system service log
  # ==========================================
  - job_name: journal
    journal:
      max_age: 12h
      path: /var/log/journal
      labels:
        job: systemd

    relabel_configs:
      - source_labels: ['__journal__systemd_unit']
        target_label: 'unit'
      - source_labels: ['__journal__hostname']
        target_label: 'host'
      # Drop unit yang gak penting biar gak noise
      - source_labels: ['unit']
        regex: '(systemd-tmpfiles-setup|cron|networkd)\.service'
        action: drop

  # ==========================================
  # Nginx access log — parse jadi label efisien
  # ==========================================
  - job_name: nginx_access
    static_configs:
      - targets: [localhost]
        labels:
          job: nginx
          __path__: /var/log/nginx/access.log

    pipeline_stages:
      - regex:
          expression: '^(?P<remote>\S+) - (?P<user>\S+) \[(?P<timestamp>[^\]]+)\] "(?P<method>\S+) (?P<path>\S+) [^"]+" (?P<status>\d+) (?P<size>\d+)'

      # Method jadi label (8 value max)
      - labels:
          method:
          status:

      - timestamp:
          source: timestamp
          format: "02/Jan/2006:15:04:05 -0700"
#!/usr/bin/env bash
# install_promtail.sh — install Promtail di Ubuntu / Debian

set -euo pipefail

readonly VERSION="${PROMTAIL_VERSION:-2.9.7}"
readonly ARCH="$(dpkg --print-architecture)"

# Download binary
cd /tmp
curl -fsSL "https://github.com/grafana/loki/releases/download/v${VERSION}/promtail-linux-${ARCH}.zip" -o promtail.zip
unzip -o promtail.zip
sudo mv "promtail-linux-${ARCH}" /usr/local/bin/promtail
sudo chmod +x /usr/local/bin/promtail

# Permission: butuh akses ke Docker socket dan journald
sudo usermod -a -G docker promtail 2>/dev/null || true
sudo usermod -a -G systemd-journal promtail 2>/dev/null || true

# Direktori state
sudo mkdir -p /var/lib/promtail
sudo chown promtail:promtail /var/lib/promtail

# Systemd unit
sudo tee /etc/systemd/system/promtail.service > /dev/null <<'EOF'
[Unit]
Description=Promtail log shipper
After=network-online.target

[Service]
Type=simple
User=promtail
Group=promtail
ExecStart=/usr/local/bin/promtail -config.file=/etc/promtail/promtail.yaml
Restart=on-failure
RestartSec=10s

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now promtail

echo "Promtail running. Cek status:"
sudo systemctl status promtail --no-pager

Pemakaian

# Validasi config sebelum apply
promtail -config.file=/etc/promtail/promtail.yaml -dry-run

# Cek log discovery — Docker container apa yang ke-scrape
curl -s http://localhost:9080/api/v1/targets | jq '.data.active[] | {labels, lastError}'

# Tail Promtail sendiri (kalau ada error config)
sudo journalctl -u promtail -f
# docker-compose.yml — label container supaya ke-scrape Promtail
services:
  api-tokopedia:
    image: ghcr.io/tokopedia/api:v2
    labels:
      promtail.enable: "true"
      env: "production"
    logging:
      driver: json-file
      options:
        max-size: "100m"
        max-file: "5"
# Query LogQL di Grafana Explore
# 1. Cari error semua API service
{env="production", level="error"} |~ "panic|fatal"

# 2. Hitung error rate per service
sum by (service) (rate({env="production", level="error"}[5m]))

# 3. Cari log specific trace
{env="production"} |~ "trace_id=5f3b8a2c1d9e4f6a7b8c9d0e"

Kapan dipakai

  • Replace ELK / Splunk untuk cost reduction.
  • Multi-tenant logging cluster (per project, per env).
  • Pair dengan Grafana untuk single pane of glass (log + metric + trace).
  • Kubernetes cluster yang sudah deploy Promtail DaemonSet.

Catatan

  • Label cardinality — total unique combinasi label hindari di atas 100rb per stream. Trace_id, request_id, user_id JANGAN jadi label. Taruh di body log.
  • labeldrop untuk drop high cardinality label yang Docker auto-attach (container_id 64-char hex).
  • Pipeline stages order matter: json → labels → timestamp. Salah urutan, parse gagal.
  • clients[].tenant_id untuk multi-tenant Loki. Tanpa ini, log nyampur di default tenant.
  • positions file persist offset baca. Jangan hapus saat troubleshoot — bisa re-ship semua log dari awal.
  • Limit readline_rate mencegah Promtail consume CPU 100% kalau ada app spam log.

Loki bukan untuk full-text search dokumen. Body log boleh besar, tapi query LogQL pakai filter expression linear scan. Untuk search free-text dengan throughput tinggi, tetap pakai Elasticsearch.

# tags

lokipromtaillogginggrafanaobservability

Ditulis oleh Asti Larasati · 28 Juni 2026