[Self-Hosted] 로그와 메트릭 모니터링 Grafana + Alloy + Prometheus + Loki를 설치해보자 (feat. Caddy)

[Self-Hosted] 로그와 메트릭 모니터링 Grafana + Alloy + Prometheus + Loki를 설치해보자 (feat. Caddy)

Caddy의 메트릭과 액세스 로그를 모니터링하려고 설치했다.

처음에는 ELK Stack을 설치하려고 했는데... SECCOMP 문제로 실패했다.

새롭게 찾은 대안이 Grafana + Alloy + Prometheus + Loki이다.

서비스
설명
Caddy 리버스 프록시
Grafana 데이터 시각화
Alloy 데이터 처리
Prometheus 메트릭 수집 및 쿼리
Loki 로그 수집 및 쿼리

설치 환경

  • Synology DS220+ (DSM 7.2.2-72806 Update 3)
  • Docker Engine 24.0.2
  • Docker Compose 2.29.6
💡
모든 도커 컨테이너는 같은 도커 네트워크에 연결되어 있어야 한다.

🔧 Caddyfile 수정하기

{
  # Global options
  admin 0.0.0.0:2019 # networks: host가 아닌 도커 컨테이너를 사용 중이라면 필요함

  metrics {
    # per_host # 호스트별로 메트릭 수집
  }

  log access-json { # 이름 수정 가능함
    include http.log.access.reverse_proxy # reverse_proxy 로거를 수집함
    output file /var/log/caddy/access.log { # 경로 수정 가능함
      roll_size 1Gib
      roll_keep 5
      roll_keep_for 90d
    }
    format json
    level INFO
  }
}

# reverse_proxy options
example.com {
  log reverse_proxy # 로거 이름 수정 가능함

  reverse_proxy example:80
}

Caddyfile 예시

Prometheus에서 사용할 metrics API와 Alloy에서 사용할 log 파일이 필요하다.

Caddy에서 metrics API와 logging은 기본적으로 비활성화 되어 있다.

  • 할 일
    • Admin API 활성화
    • log 파일 생성

metrics 옵션을 설정하고 나면 Admin API를 사용할 수 있다.

log 옵션을 설정하고 나면 log 파일이 생성된다.


🔧 Caddy의 compose.yml 수정하기

services:
  caddy:
    image: 'caddy:latest'
    container_name: caddy
    hostname: caddy
    restart: unless-stopped
    ports:
      - 2019:2019 # 포트 개방이 필요함
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - ./srv:/srv
      - ./data:/data
      - ./config:/config
      - /path/to:/var/log/caddy // 원하는 경로로 수정하기 (Alloy가 사용할 log 파일)
    environment:
      - TZ=Asia/Seoul

caddy/compose.yml 예시

Caddy의 기본 Admin API 주소는 localhost:2019로 되어 있다.

도커 컨테이너로 운영 중일 때는 localhost의 주소가 컨테이너마다 다르기 때문에 Prometheus에서 Caddy의 localhost:2019로 API 요청을 할 수가 없다. 그래서 Admin API 주소를 0.0.0.0:2019로 바꾸는 것이다. 그럼 컨테이너의 2019 포트를 개방해야 한다.

Alloy에 사용할 Caddy의 log 파일을 로컬 볼륨에 마운트한다. 이 경로는 Alloy의 compose.yml 파일에서도 동일하게 사용한다.

Caddyfile과 compose.yml 파일 수정이 모두 끝났으면

curl http://내부아이피:2019/metrics

브라우저에서 접속하거나 curl 명령어를 이용해서 metrics이 정상적으로 출력되는지 확인한다.

Monitoring Caddy with Prometheus metrics - Caddy Documentation
Caddy is a powerful, enterprise-ready, open source web server with automatic HTTPS written in Go

Monitoring Caddy with Prometheus metrics

log (Caddyfile directive) - Caddy Documentation
Caddy is a powerful, enterprise-ready, open source web server with automatic HTTPS written in Go

log


🚨 Caddy 오류 해결

⚠️
curl: (56) Recv failure: Connection reset by peer

만약 위와 같은 오류가 뜬다면, 포트 개방이 안 되어 있다는 뜻이다.

Caddy 도커 컨테이너의 2019 포트를 개방한 다음에 다시 시도한다.

⚠️
HTTP 403: {"error":"host not allowed: 0.0.0.0:2019"}

caddy reload 명령어를 실행하고 위의 오류가 뜬다면 --address localhost:2019 옵션을 커맨드에 같이 입력한다.

Caddy 도커 컨테이너의 포트가 개방되어 있는지 확인하고, admin 0.0.0.0:2019 옵션이 적용되었는지 확인한다.


⚙️ 설정 파일 구성하기

폴더 구조

📦 grafana-stack
├─ config
│  ├─ alloy
│  │  └─ config.alloy
│  ├─ grafana
│  │  ├─ datasources
│  │  │  └─ datasource.yml
│  │  └─ grafana.ini
│  └─ prometheus
│     └─ prometheus.yml
└─ compose.yml

폴더 구조

config.alloy

loki.source.file "caddy_access_logs" {
  targets = [{
    __path__ = "/var/log/access.log",
  }]

  forward_to = [loki.process.access_logs.receiver]
}

loki.process "access_logs" {
  forward_to = [loki.write.loki.receiver]
}

loki.write "loki" {
  endpoint {
    url = "http://grafana-loki:3100/loki/api/v1/push"
  }
  external_labels = {
    app = "caddy",
  }
}

config.alloy 예시

loki | Grafana Alloy documentation
Learn about the loki components in Grafana Alloy

loki components

prometheus | Grafana Alloy documentation
Learn about the prometheus components in Grafana Alloy

prometheus components

datasource.yml

apiVersion: 1

datasources:
  - name: Loki
    type: loki
    access: proxy
    orgId: 1
    url: http://grafana-loki:3100
    basicAuth: false
    isDefault: true
    version: 1
    editable: true

  - name: Prometheus
    type: prometheus
    url: http://grafana-prometheus:9090
    isDefault: false
    access: proxy
    editable: true

datasource.yml 예시

grafana.ini

[analytics]
reporting_enabled = false

[auth.anonymous]
enabled = true
org_role = Admin

[explore]
enabled = true

[users]
default_theme = dark

grafana.ini 예시

Configure Grafana | Grafana documentation
Learn how to configure Grafana and understand configuration options.

Configure Grafana

prometheus.yml

global:
  scrape_interval: 15s
  scrape_timeout: 10s
  evaluation_interval: 15s

alerting:
  alertmanagers:
    - static_configs:
        - targets:
            - localhost:9093
      scheme: http
      timeout: 10s
      api_version: v1

scrape_configs:
  - job_name: prometheus
    honor_timestamps: true
    scrape_interval: 15s
    scrape_timeout: 10s
    metrics_path: /metrics
    scheme: http
    static_configs:
      - targets:
          - localhost:9090

  - job_name: caddy
    static_configs:
      - targets:
          - caddy:2019

prometheus.yml 예시


⚙️ compose.yml 작성하기

volumes:
  grafana_data:
    name: grafana_data
  grafana_prometheus_data:
    name: grafana_prometheus_data

services:
  grafana-loki:
    image: 'grafana/loki:latest'
    container_name: grafana-loki
    hostname: grafana-loki
    restart: unless-stopped
    command:
      - -config.file=/etc/loki/local-config.yaml
    ports:
      - 3100:3100
    environment:
      - TZ=Asia/Seoul

  grafana-alloy:
    image: 'grafana/alloy:latest'
    container_name: grafana-alloy
    hostname: grafana-alloy
    restart: unless-stopped
    command:
      - run
      - /etc/alloy/config.alloy
      - --storage.path=/var/lib/alloy/data
      - --server.http.listen-addr=0.0.0.0:12345
    ports:
      - 12345:12345
    volumes:
      - ./config/alloy:/etc/alloy
      - /path/to:/var/log // caddy/compose.yml에서 설정한 로그 경로
    environment:
      - TZ=Asia/Seoul
    depends_on:
      - grafana-loki

  grafana-prometheus:
    image: 'prom/prometheus:latest'
    container_name: grafana-prometheus
    hostname: grafana-prometheus
    restart: unless-stopped
    command:
      - --config.file=/etc/prometheus/prometheus.yml
      - --storage.tsdb.path=/prometheus
      - --web.enable-lifecycle
    ports:
      - 9091:9090
    volumes:
      - ./config/prometheus:/etc/prometheus
      - grafana_prometheus_data:/prometheus
    environment:
      - TZ=Asia/Seoul

  grafana:
    image: 'grafana/grafana:latest'
    container_name: grafana
    hostname: grafana
    restart: unless-stopped
    command:
      - --config=/etc/grafana-config/grafana.ini
    ports:
      - 3000:3000
    volumes:
      - grafana_data:/var/lib/grafana
      - ./config/grafana:/etc/grafana-config
      - ./config/grafana/datasources:/etc/grafana/provisioning/datasources
    environment:
      - TZ=Asia/Seoul
    healthcheck:
      test: ['CMD', 'curl', '-f', 'http://localhost:3000/healthz']
      interval: 1s
      timeout: 10s
      retries: 5

grafana-stack/compose.yml 예시

alloy/example at main · grafana/alloy
OpenTelemetry Collector distribution with programmable pipelines - grafana/alloy

Docker Compose example

실행하기

docker compose up -d

http://내부아이피:3000으로 접속해서 대시보드가 잘 뜨는지 확인한다.


✅ Alloy 확인하기

http://내부아이피:12345로 접속해서 각 작업의 상태와 종속성을 확인할 수 있다.


✅ Prometheus 확인하기

http://내부아이피:9090으로 접속해서 metrics API 연결이 잘 되고 있는지 확인할 수 있다.


🚨 Prometheus 오류 해결

⚠️
dial tcp: lookup ... on 127.0.0.11:53: no such host
  • 주소 설정이 잘 되어 있는지 확인한다.
  • 같은 도커 네트워크에 연결되어 있는지 확인한다.
  • Caddy Admin API 주소가 0.0.0.0:2019로 설정되어 있는지 확인한다.
  • Caddy 도커 컨테이너의 2019 포트가 개방되어 있는지 확인한다.

✅ Grafana 데이터 소스 확인하기

알려준 예시 설정대로 했다면 위에 사진처럼 나온다.

각 데이터 소스를 눌러서 맨 아래로 내리면 Save & test가 있다.

눌러서 잘 되는지 확인한다.

왼쪽 메뉴에서 Drilldown > Metrics, Logs을 눌러서 메트릭과 로그가 잘 뜨는지 확인한다.


🚨 Grafana 오류 해결

⚠️
Post "http://IP:PORT/api/v1/query": dial tcp IP:PORT: connect: connection refused - There was an error returned querying the Prometheus API.
  • 주소 설정이 잘 되어 있는지 확인한다.
  • 같은 도커 네트워크에 연결되어 있는지 확인한다.

⚙️ 대시보드 추가하기

Create dashboard을 누르고 Import dashboard를 누른다.

Caddy Monitoring | Grafana Labs

위 사이트에 접속해서 대시보드를 다운로드한 다음에 업로드한다.

정상적인 모습

위 사진처럼 나온다면 잘 적용된 것이다.

만약, Caddyfile에서 per_host 옵션을 활성화했다면 각 host가 전부 떠버려서 이상하게 보인다.

이상한 모습

이렇게 이상하게 뜬다면 각 패널을 직접 수정해야 한다.

Edit을 누르면 쿼리 수정 화면이 나온다.

sum(caddy_http_requests_total{instance=~"$host", handler=~"$handler"})

sum() 함수를 이용해서 전부 더하면 된다.

다른 패널도 마찬가지로 sum() 함수를 사용하면 된다.

GitHub - Malfhas/caddy-grafana: Monitoring Caddy Server with Grafana (Prometheus + Loki) on Debian
Monitoring Caddy Server with Grafana (Prometheus + Loki) on Debian - Malfhas/caddy-grafana

Monitoring Caddy Server with Grafana (Prometheus + Loki) on Debian