Grafana + Loki + Alloy + Prometheus로 모니터링 시스템 구축하기

김팔복 2026. 5. 26. 오전 2:02:25
조회 11 추천 0 댓글 0

  • Alloy : 로그·메트릭·트레이스를 수집해 Loki·Prometheus 등으로 전달하는 통합 에이전트
  • Loki : Grafana에서 만든 로그 수집·저장 시스템
  • Grafana : 로그와 메트릭을 대시보드로 시각화하는 모니터링 UI
  • Prometheus : 서버와 애플리케이션 메트릭을 수집·저장하는 모니터링 시스템

Mosquitto 로그 설정

sudo vi /etc/mosquitto/conf.d/mosquitto_log.conf
log_timestamp true
log_timestamp_format %Y-%m-%d %H:%M:%S
# log_type all
log_type error
log_type warning
log_type notice
log_type information
# timezone 확인
timedatectl

# timezone 설정
sudo timedatectl set-timezone Asia/Seoul

Mosquitto 로그 파일 권한

sudo nano /etc/logrotate.d/mosquitto
/var/log/mosquitto/mosquitto.log {
	... 생략
    create 640 mosquitto adm
    # nocreate  <<<<< 이 설정이 있다면 삭제
}

"create 640 mosquitto adm" 를 추가합니다.

이러면 logrotate가 새 빈 파일을 640 권한 + adm 그룹으로 만들어주고, Mosquitto는 reload 받아서 그 파일에 이어서 씁니다.

# mosquitto logrotate 설정 문법 검사
sudo logrotate -d /etc/logrotate.d/mosquitto
# 재시작
sudo systemctl restart mosquitto

# 상태 확인
sudo systemctl status mosquitto
# 현재 mosquittolog 권한도 수동으로 한 번은 맞춰줍니다.

sudo chgrp adm /var/log/mosquitto/mosquitto.log
sudo chmod 640 /var/log/mosquitto/mosquitto.log
sudo usermod -aG adm alloy
# alloy가 mosquitto 로그 파일을 읽을 수 있는지 확인 (Permission denied 에러 발생 하는지 확인)
sudo -u alloy cat /var/log/mosquitto/mosquitto.log | head

Grafana Loki + Grafana 설치 (with Docker compose)

https://grafana.com/oss/loki/

디렉토리구조

~/monitoring/
├── docker-compose.yml
├── loki/
│   └── loki-config.yaml
└── grafana/
    └── provisioning/
        └── datasources/
            └── loki.yaml

docker-compose.yml

services:
  loki:
    image: grafana/loki:3.6.0
    container_name: loki
    restart: unless-stopped
    user: "0:0"
    ports:
      - "3100:3100"
    volumes:
      - ./loki/loki-config.yaml:/etc/loki/local-config.yaml
      - loki-data:/loki
    command: -config.file=/etc/loki/local-config.yaml
    networks:
      - monitoring

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    restart: unless-stopped
    ports:
      - "3000:3000"
    volumes:
      - grafana-data:/var/lib/grafana
      - ./grafana/provisioning:/etc/grafana/provisioning
    environment:
      - GF_SECURITY_ADMIN_USER=admin
      - GF_SECURITY_ADMIN_PASSWORD=admin
    networks:
      - monitoring
    depends_on:
      - loki

volumes:
  loki-data:
  grafana-data:

networks:
  monitoring:
    driver: bridge

포인트 몇 가지:

  • volumes로 loki-data, grafana-data를 외부에 잡아둬서 컨테이너 지워도 데이터 유지됨 (튜토리얼 예제는 이게 빠져있음)
  • 두 컨테이너가 같은 monitoring 네트워크에 있어서 Grafana가 http://loki:3100로 Loki를 찾을 수 있음
  • restart: unless-stopped 로 서버 재부팅 시 자동 복구
  • Grafana 비밀번호는 영상에서는 admin/admin로 두고, 실서버 운영할 땐 바꾸세요

loki/loki-config.yaml

auth_enabled: false
server:
  http_listen_port: 3100
  grpc_listen_port: 9096
  log_level: info

common:
  instance_addr: 127.0.0.1
  path_prefix: /loki
  storage:
    filesystem:
      chunks_directory: /loki/chunks
      rules_directory: /loki/rules
  replication_factor: 1
  ring:
    kvstore:
      store: inmemory

schema_config:
  configs:
    - from: 2024-01-01
      store: tsdb
      object_store: filesystem
      schema: v13
      index:
        prefix: index_
        period: 24h

limits_config:
  allow_structured_metadata: true
  volume_enabled: true
  retention_period: 360h   # 15일 보관

compactor:
  working_directory: /loki/retention
  delete_request_store: filesystem
  retention_enabled: true

grafana/provisioning/datasources/loki.yaml

apiVersion: 1

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

Docker 설치 참고: https://docs.docker.com/engine/install/ubuntu/

실행

cd ~/monitoring
docker compose up -d
docker ps             # 둘 다 running 인지 확인
docker logs -f loki   # 에러 없는지 확인 (Ctrl+C로 빠져나옴)

헬스체크 3가지:

확인 | URL | 기대 결과
Loki 살아있나 | http://<모니터링서버IP>:3100/ready | ready 텍스트
Loki 메트릭 | http://<모니터링서버IP>:3100/metrics |Prometheus 포맷 메트릭들
Grafana | http://<모니터링서버IP>:3000 | 로그인 페이지 (admin/admin)

Grafana 로그인 후 좌측 메뉴 → Connections → Data sources에 "Loki"가 자동으로 들어가 있으면 성공입니다. 클릭해서 맨 아래 Save & test 눌러보면 "Data source successfully connected" 떠야 해요.


Alloy 설치

https://grafana.com/docs/alloy/latest/set-up/install/linux/

loki 네트워크 테스트

curl http://<모니터링서버IP>:3100/ready

ready 가 떨어지면 OK.

Mosquitto 로그 위치 확인

  • /var/log/mosquitto/mosquitto.log

시작하기 전에

일부 데비안 기반 클라우드 가상 머신에는 GPG가 기본적으로 설치되어 있지 않습니다. Linux 가상 머신에 GPG를 설치하려면 터미널 창에서 다음 명령을 실행하십시오.

sudo apt install gpg

설치

Linux에 Alloy를 설치하려면 터미널 창에서 다음 명령어를 실행하세요.

  1. GPG 키를 가져오고 Grafana 패키지 저장소를 추가하세요.
sudo mkdir -p /etc/apt/keyrings
sudo wget -O /etc/apt/keyrings/grafana.asc https://apt.grafana.com/gpg-full.key
sudo chmod 644 /etc/apt/keyrings/grafana.asc
echo "deb [signed-by=/etc/apt/keyrings/grafana.asc] https://apt.grafana.com stable main" | sudo tee /etc/apt/sources.list.d/grafana.list
  1. 저장소를 업데이트하세요.
sudo apt update
  1. Alloy를 설치하세요.
sudo apt install alloy

apt로 설치하면 자동으로 생성됩니다:

  • 설정 파일: /etc/alloy/config.alloy
  • 환경변수: /etc/default/alloy (포트, 로그 레벨 등 서비스 옵션)
  • systemd 서비스: alloy.service

config.alloy 작성

https://grafana.com/docs/alloy/latest/reference/components/

  1. 원본 설정 파일 복사
sudo cp /etc/alloy/config.alloy /etc/alloy/config.alloy.bak.$(date +%Y%m%d)
  1. 설정 파일 새로 작성:
sudo nano /etc/alloy/config.alloy
// ===========================================
// Mosquitto 로그 수집 → Loki 전송
// ===========================================

logging {
  level = "info"
}

// 1. 수집할 로그 파일 지정
local.file_match "mosquitto" {
    path_targets = [
        {
            __path__ = "/var/log/mosquitto/mosquitto.log",
            job      = "S_1_MOSQUITTO",
            host     = constants.hostname,
        },
    ]
}

// 2. 파일 tail (실시간으로 새 줄 읽기)
loki.source.file "mosquitto" {
    targets    = local.file_match.mosquitto.targets
    forward_to = [loki.process.mosquitto.receiver]
}

// 3. 정적 라벨 추가
loki.process "mosquitto" {
    stage.static_labels {
        values = {
            env     = "production",
            service = "S_1_MOSQUITTO",
        }
    }
    forward_to = [loki.write.default.receiver]
}

// 4. Loki로 전송
loki.write "default" {
    endpoint {
        url = "http://<모니터링서버사설IP>:3100/loki/api/v1/push"
    }
}

⚠️ <모니터링서버사설IP> 부분을 실제 IP로 교체하세요.

Alloy ui 설정

sudo nano /etc/default/alloy
... 생략
CUSTOM_ARGS="--server.http.listen-addr=0.0.0.0:12345"

Alloy RUN

# alloy 실행
sudo systemctl start alloy

# alloy 상태 확인
sudo systemctl status alloy

# 부팅시 자동 실행
sudo systemctl enable alloy.service

Alloy ui
→ 브라우저 → http://{{alloy 서버 IP}}:12345

Grafana 에서 Logs 확인

  • 좌측 메뉴 → Drilldown → Logs
  • 좌측 메뉴 → explore → Label filters 선택 후 “Run query”

Prometheus 설치

Prometheus가 메트릭을 수집하는 방법은 두 가지가 있습니다.
방법1. Prometheus가 직접 데이터를 가져오는 pull (scrape) 방식
Prometheus가 alloy에서 pull을 합니다.
방법2. alloy 같은 에이전트가 Prometheus로 데이터를 보내주는 remote_write (push) 방식입니다.
사실 pull 이 Prometheus의 전통적인 기본 방식인데, 저는 remote_write를 사용하겠습니다.
이미 앞에서 alloy로 로그를 push 하는 구조를 만들었으니,
개인적으로는 메트릭도 같은 방식으로 통일하는 게 일관성 있어서 좋을 것 같습니다.

전체 그림

[어플리케이션 서버]                          [모니터링 서버]
                                        ┌──────────────────┐
                                        │   Grafana :3000  │
Mosquitto                               │      ↓ 조회       │
   (로그)                                │                  │
   ↓                                    │   Loki :3100 ←───┼── 로그 수신
                                        │                  │
mosquitto_exporter ──┐                  │   Prometheus     │
                     ├─ Alloy ──push───→│      :9090   ←───┼── 메트릭 수신
node metrics  ───────┘                  │                  │
                                        └──────────────────┘

Alloy 하나가 로그(Loki로) + 메트릭(Prometheus로) 둘 다 보냅니다.

docker-compose.yml 수정

기존의 Loki + Grafana가 있었습니다. 여기에 prometheus

cd ~/monitoring
mkdir -p prometheus/data
sudo chown -R 65534:65534 prometheus/data

65534:65534? Prometheus 도커 이미지의 nobody 유저 UID/GID입니다. 1편에서 Loki가 10001이었던 거랑 같은 원리입니다.

prometheus 설정 파일

nano ~/monitoring/prometheus/prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s

# 이 scrape_configs 섹션은 모스키토 메트릭 수집과는 무관합니다.
# Prometheus가 자기 자신(:9090)을 scrape해서, Prometheus의 동작 메트릭을 수집하는 설정입니다.
# (예: 메모리 사용량, 처리한 쿼리 수, scrape 시간 등)
# 모스키토 메트릭은 Alloy가 remote_write로 push할 예정이라 여기에 추가하지 않습니다.

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

docker-compose.yml에 Prometheus 추가

nano ~/monitoring/docker-compose.yml
services:
  loki:
    # ... 기존 그대로 ...

  grafana:
    # ... 기존 그대로 ...

  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    restart: unless-stopped
    user: "65534:65534"
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
      - ./prometheus/data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--web.enable-remote-write-receiver'
      - '--storage.tsdb.retention.time=15d'
    networks:
      - monitoring

volumes:
  # ... 기존 그대로 ...

networks:
  # ... 기존 그대로 ...
  • --config.file=/etc/prometheus/prometheus.yml
    • 설정파일 위치
  • --storage.tsdb.path=/prometheus
    • 수집한 메트릭스 저장 위치
  • --web.enable-remote-write-receiver
    • 외부에서 push 받을 수 있게 설정
  • --storage.tsdb.retention.time=15d
    • 메트릭스 저장 기간

docker

# 실행 중이라면 docker 중지
docker compose down

# docker 실행
docker compose up -d prometheus

prometheus 실행 되는지 확인

  1. 브라우저에서 : http://<<서버IP>>:9090
  2. remote_write receiver가 활성화됐는지 확인
    a. 로컬에서 : curl -X POST -i http://localhost:9090/api/v1/write
    • 실제 데이터를 보낸게 아니라 415 또는 400 에러 발생 함 (정상)
    • 404 에러인 경우 문제 (실패)
      b. 외부서버에서 : curl -X POST -i http://<<서버IP>>:9090/api/v1/write

Grafana에 Prometheus 데이터소스 추가

nano ~/monitoring/grafana/provisioning/datasources/prometheus.yaml
apiVersion: 1
datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    orgId: 1
    url: http://prometheus:9090
    isDefault: false
    version: 1
    editable: true

재시작

docker compose restart grafana

점검

  • Grafana 에서 Prometheus 데이터소스 연결 확인
  • Prometheus 웹 ui : http://<<서버IP>>:9090

지금 보이는 120개 메트릭은 Prometheus 컨테이너 내부의 정보입니다.
모니터링 서버 호스트의 CPU, 메모리 같은 정보는 아직 없어요.
컨테이너는 격리되어 있어서, 컨테이너 안의 프로세스가 호스트 정보를 직접 알 수 없거든요.
호스트 정보를 보려면 호스트 측에 별도의 exporter나 에이전트를 두고, 거기서 노출되는 걸 Prometheus가 가져가야 합니다.


Alloy 에서 메트릭스 보내기

https://grafana.com/docs/alloy/latest/reference/components/

[어플리케이션 서버]                             [모니터링 서버]

Alloy (메트릭 컴포넌트 추가)                  Prometheus :9090
 ├─ prometheus.exporter.unix                 ↑
 │   (호스트 메트릭 노출)                  │ remote_write
 ├─ prometheus.scrape ───────────────> │
 │   (위에서 긁어옴)
 ├ prometheus.relabel
 │   (host/env/service 라벨 추가)
 └─ prometheus.remote_write
     (모니터링 서버로 push)

config.alloy 수정

sudo nano /etc/alloy/config.alloy

기존 내용은 그대로 두고, 맨 아래에 추가합니다:

// ===========================================
// 호스트 메트릭 수집 → Prometheus 전송
// ===========================================

// 1. 호스트 OS 메트릭 노출 (node_exporter 역할)
prometheus.exporter.unix "default" {
  include_exporter_metrics = true
  disable_collectors       = ["mdadm"]
}

// 2. 메트릭에 공통 label 추가
prometheus.relabel "metrics_labels" {
  forward_to = [prometheus.remote_write.default.receiver]
  rule {
    target_label = "host"
    replacement  = constants.hostname
  }
  rule {
    target_label = "env"
    replacement  = "production"
  }
  rule {
    target_label = "service"
    replacement  = "S_1_MOSQUITTO"
  }
}

// 3. exporter 메트릭 scrape
prometheus.scrape "default" {
  targets = array.concat(
    prometheus.exporter.unix.default.targets,
    [{
      job         = "alloy",
      __address__ = "127.0.0.1:12345",
    }],
  )
  scrape_interval = "15s"
  forward_to = [prometheus.relabel.metrics_labels.receiver]
}

// 4. Prometheus로 메트릭 push
prometheus.remote_write "default" {
  endpoint {
    url = "http://<모니터링서버사설IP>:9090/api/v1/write"
  }
}

문법 체크 후 재시작

# 문법 체크 (선택이지만 권장)
sudo alloy fmt /etc/alloy/config.alloy

# Alloy 재시작
sudo systemctl restart alloy

# 상태확인
sudo systemctl status alloy

# 실시간 로그 모니터링
sudo journalctl -u alloy -f

검증

  • Alloy UI 접속
http://<Mosquitto서버IP>:12345

Graph 페이지에서 새 컴포넌트들이 보이고 화살표로 연결되어야 합니다:

  • Prometheus 에서 메트릭 들어오나 확인
http://<모니터링서버IP>:9090
  • grafana 에서 확인
    Drilldown > Metrics 에서 확인

댓글 0