1. Grafana Alloy 개요
Grafana Agent에 대해 알아보려던 중, 공식 문서에서 경고 문구를 발견하였다. Grafana Labs에서 Grafana Agent를 대체하는 새로운 솔루션인 Grafana Alloy를 발표했다는 내용이었다. 이에 따라 자연스럽게 Grafana Alloy에 대해 알아보기로 하였다.
Grafana Alloy는 다음과 같은 특징을 가지고 있다고 한다.
- Grafana Agent의 후속 제품으로, 기존 Agent의 기능을 개선하고 확장
- Istio와 같은 서비스에서 발생하는 방대한 커스텀 메트릭을 효율적으로 필터링하여 필요한 메트릭만 수집할 수 있다.
- 기존 Grafana Agent에서 Alloy로의 마이그레이션 방법을 공식적으로 제공하고 있어 기존 사용자들의 전환이 용이하다.
그렇다면 Grafana Alloy에 대해 기본 사용법과 개념에 대해 조금 더 알아보자.
2. Grafana Alloy란?
Grafana Alloy는 애플리케이션과 원격 백엔드 사이에서 텔레메트리 데이터(메트릭, 로그, 트레이스, 프로파일)를 수집하고 전달하는 경량 에이전트이다. Alloy는 다양한 데이터 소스로부터 정보를 수집하고, 필요에 따라 처리한 후 Grafana 생태계의 백엔드 시스템(prometheus, loki 등)으로 전송하는 통합 솔루션을 제공한다.
주요 특징은 다음과 같다.
- 통합 텔레메트리 수집: OTel, Prometheus, Pyroscope, Loki 등 다양한 메트릭, 로그, 트레이스 및 프로파일링 도구에 대한 기본 파이프라인 제공
- 호환성: OTel collector, Prometheus 에이전트, Promtail과 완벽하게 호환
- 유연한 배포: 온프레미스, 클라우드 또는 하이브리드 환경 어디에나 배포 가능
- 리소스 효율성: 프로메테우스 대비 약 1/10 수준의 리소스로 메트릭 수집 가능
- Grafana LGTM 스택 지원: Loki(로그), Grafana(시각화), Tempo(트레이스), Mimir(메트릭) 스택과 원활하게 연동
3. Grafana Alloy 설치
alloy는 대부분의 환경에서 설치를 지원한다. 이번 실습은 쿠버네티스 환경에서 alloy를 설치하는 방법에 대해 다룬다.
Kubernetes(Helm Chart) 설치
용이한 설정 추가(values 파일 수정)를 위해 차트를 pull 받아 사용한다.
# helm repo 추가
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update
# helm pull
helm pull grafana/alloy --untar # 압축해제 옵션
# helm install
cd alloy
helm install grafana-alloy .
다른 agent와 비슷하게 alloy 역시, 데몬셋으로 모든 노드의 배포가 된다. 기본 포트는 12345번이다.
# 데몬셋 확인
kubectl get daemonsets.apps DESIRED CURRENT READY UP-TO-DATE AVAILABL
grafana-alloy 1 1 1 1 1
# 파드 확인
kubectl get pod
NAME READY STATUS RESTARTS AGE
grafana-alloy-qq2d9 2/2 Running 0 5h21m
# 서비스 포트 확인
kubectl get svc
NAME TYPE CLUSTER-IP PORT(S)
grafana-alloy NodePort 10.96.155.47 12345:30003/TCP
4. Grafana Alloy 기본 사용
설정 파일 문법(Configuration syntax)
기존의 Grafana Agent에서는 아래 두 가지 방식의 모드를 제공하였지만, Alloy로 넘어오면서 이제는 권장하던 HCL 문법으로 통합이 된 것으로 보인다.
static mode
- YAML 기반 구성 파일 사용
- Prometheus, Loki, Tempo 설정 구조와 유사한 전통적인 방식
flow mode
- River 언어(Hashicorp의 테라폼 HCL 영감) 기반 구성
컴포넌트
Grafana Alloy는 컴포넌트 기반 아키텍처를 채택하여, 추가적인 플러그인이나 모듈을 별도로 설치할 필요 없이 설정 파일 내에서 다양한 컴포넌트를 정의하고 구성할 수 있다. Grafana는 공식 문서를 통해 다양한 컴포넌트 목록과 각각의 사용 방법을 제공하고 있어, 사용자가 자신의 환경에 맞는 구성을 쉽게 찾을 수 있다.
블록(Block)
- 블록을 사용하여 컴포넌트 및 속성의 그룹을 구성할 수 있음
- 각 블록은 여러 개의 속성 또는 중첩된 블록을 표현할 수 있음
# Prometheus의 remote_write 기능을 사용하여 데이터를 전송할 URL을 설정하는 블록을 정의
prometheus.remote_write "my_block_name" {
# 하위 블록
endpoint {
url = "http://localhost:9009/api/prom/push" # 속성 정의
}
}
속성(Attributes)
- 개별 설정을 구성하는 데 사용
- 속성은 항상 ATTRIBUTE_NAME = ATTRIBUTE_VALUE 형식을 따름
# 로그레벨 설정
log_level = "debug"
표현식 (Expressions)
- 속성의 값을 계산하는 데 사용됩니다. 간단한 상수 값부터 복잡한 수학 연산, 함수 호출까지 지원
local.file.password_file.content
Config-Reloader 사용
Grafana Alloy의 파드를 확인해 보면 컨테이너가 2개로 구성이 되어있는데 config-reloader
라는 컨테이너를 통해 파드를 재시작하지 않고도 런타임에서 동적으로 설정을 적용할 수 있다(actuator refresh와 비슷). 플래그의 설정된 webhook-url을 통해 동작하는 것 같다.
사용방법은 해당 URL로 POST 요청을 보내면 된다. 기본 포트는 12345이며, 필자의 경우는 클러스터(kind)로 구축하여 로컬에서 진입하기 위해 노드 포트를 사용하였다.
# 형식
curl -X POST http://<ALLOY_SERVICE_ENDPOINT>/-/reload
# 예시
curl -X POST http://localhost:12345/-/reload # 로컬
curl -X POST http://localhost:30003/-/reload # 노드포트
# 결과
config reloaded
대시보드(UI)
Grafana Alloy는 UI 대시보드를 제공한다. 컴포넌트 간의 종속성 관계를 그래프를 통해 시각화해서 보여주고, 각 컴포넌트들의 세부 구성정보나 데이터 흐름 등 복잡한 텔레메트리 파이프라인을 쉽게 이해할 수 있도록 도와준다.
로깅 및 라이브디버깅 설정
라이브디버깅 활성화
해당 설정을 통해 UI에서 라이브 디버깅을 사용할 수 있다(기본 비활성화)
livedebugging {
enabled = true
}
아래는 실시간으로 필터링된 로그를 라이브 디버깅한 화면이다. 하지만 아직 지원하지 않는 컴포넌트들이 많은 것 같긴 하다.
Alloy Log Level 설정
Alloy 애플리케이션 로그 레벨과 포맷을 설정할 수 있다
- log level
- error
- debug
- info
- warn
- log format
- logfmt
- json
logging {
level = "debug"
format = "logfmt"
}
5. 실습: Grafana Alloy를 활용한 메트릭 수집 및 Prometheus 연동
Grafana Alloy를 사용하여 시스템 메트릭을 수집하고, 이를 Prometheus로 전송하는 방법에 대해 실습해 보자.
먼저 Prometheus를 설치하고, Alloy를 구성하여 메트릭을 수집한 다음, 이 데이터를 Prometheus로 전송하는 전체 파이프라인을 구축
각 단계별로 필요한 설정과 명령어를 자세히 살펴보고, 실제 구현 과정에서 발생할 수 있는 주의사항도 함께 알아보자.
프로메테우스 설치(kube-prometheus-stack)
kube-prometheus-stack
헬름차트의 operator 기반 프로메테우스를 설치한다.
# repo 추가
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
# 파라미터 파일 생성
cat <<EOT > monitor-values.yaml
prometheus:
service:
type: NodePort
nodePort: 30001
prometheusSpec:
scrapeInterval: "15s"
evaluationInterval: "15s"
podMonitorSelectorNilUsesHelmValues: false
serviceMonitorSelectorNilUsesHelmValues: false
retention: 5d
retentionSize: "10GiB"
grafana:
defaultDashboardsTimezone: Asia/Seoul
adminPassword: prom-operator
service:
type: NodePort
nodePort: 30002
defaultRules:
create: false
prometheus-windows-exporter:
prometheus:
monitor:
enabled: false
alertmanager:
enabled: false
EOT
# 배포
helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack --version 69.3.1 \ -f monitor-values.yaml --create-namespace --namespace monitoring
# 확인
kubectl get prometheus,servicemonitors -n monitoring
kubectl get crd | grep monitoring
프로메테우스 버전 확인
최근 출시된 Prometheus 3.0.0 버전은 CPU와 메모리 사용량이 이전 버전에 비해 크게 감소했다고 한다.
Alloy 설정파일 작성(Configure Alloy)
이제 Alloy를 구성하여 메트릭을 수집하고 Prometheus로 전송하는 파이프라인을 설정해 보겠다. 이 설정은 세 가지 주요 컴포넌트로 구성된다.
컴포넌트 1: 스크랩핑(Scraping)
이 컴포넌트는 시스템 메트릭을 수집하는 역할을 한다.
// Unix 시스템 메트릭을 수집하는 exporter 정의
// Node-exporter와 유사한 시스템 메트릭(CPU, 메모리, 디스크 등)을 수집합니다
prometheus.exporter.unix "local_system" { }
// 위에서 정의한 exporter로부터 메트릭을 스크래핑하는 설정
prometheus.scrape "scrape_metrics" {
targets = prometheus.exporter.unix.local_system.targets // 스크래핑할 대상 지정
forward_to = [prometheus.relabel.filter_metrics.receiver] // 수집된 메트릭을 전달할 다음 컴포넌트
scrape_interval = "10s" // 10초마다 메트릭 수집
}
컴포넌트 2: 메트릭 필터링(Filter metrics)
수집된 메트릭 중 특정 조건에 맞는 데이터만 선별하는 역할을 한다.
// env 라벨을 통해 특정 환경의 메트릭만 필터링 할 경우 유용
prometheus.relabel "filter_metrics" {
rule {
action = "drop" // 조건에 맞는 메트릭을 제외(drop)
source_labels = ["env"] // 'env' 라벨을 기준으로 필터링
regex = "dev" // 'env' 값이 'dev'인 메트릭은 제외
}
// 필터링된 메트릭을 remote_write 컴포넌트로 전달
forward_to = [prometheus.remote_write.metrics_service.receiver]
}
컴포넌트 3: Prometheus로 메트릭 전송
필터링된 메트릭을 Prometheus로 전송하는 역할을 한다.
prometheus.remote_write "metrics_service" {
endpoint {
url = "http://localhost:9090/api/v1/write" // Prometheus의 remote_write API 엔드포인트
// 인증이 필요한 경우 아래 주석을 해제하고 사용
// basic_auth {
// username = "admin"
// password = "admin"
// }
}
}
이제 전체 컴포넌트 설정을 Alloy 헬름 차트 values.yaml 파일에 작성하고 Alloy를 재배포한다.
# values.yaml 파일 수정
alloy:
configMap:
# -- Create a new ConfigMap for the config file.
create: true
# -- Content to assign to the new ConfigMap. This is passed into `tpl` allowing for templating from values.
content: |
prometheus.exporter.unix "local_system" { }
prometheus.scrape "scrape_metrics" {
targets = prometheus.exporter.unix.local_system.targets
forward_to = [prometheus.relabel.filter_metrics.receiver]
scrape_interval = "10s"
}
prometheus.relabel "filter_metrics" {
rule {
action = "drop"
source_labels = ["env"]
regex = "dev"
}
forward_to = [prometheus.remote_write.metrics_service.receiver]
}
prometheus.remote_write "metrics_service" {
endpoint {
url = "http://localhost:9090/api/v1/write"
// basic_auth {
// username = "admin"
// password = "admin"
// }
}
}
# helm upgrade
helm upgrade grafana-alloy .
Alloy 설정을 다시 로드하여 변경사항을 적용한다.
curl -X POST http://localhost:30003/-/reload
Reload the configuration
Prometheus Remote-Write 활성화
Prometheus의 Remote Write 기능은 외부 시스템(이 경우 Alloy)에서 수집한 데이터를 Prometheus로 전송할 수 있게 해주는 중요한 기능이다. 하지만 보안 및 리소스 관리 이유로 이 기능은 기본적으로 비활성화되어 있다.
remote write의 URL로 직접 API 요청을 보내면 비활성화되어 있음을 확인할 수 있다.
# 프로메테우스로 remote write api 요청
curl -XPOST localhost:30000/api/v1/write
remote write receiver needs to be enabled with --web.enable-remote-write-receiver
이 기능을 활성화하기 위해서는 Prometheus 설정에서 enableRemoteWriteReceiver
옵션을 활성화해야 합니다. kube-prometheus-stack을 사용하는 경우, values 파일에서 다음과 같이 설정을 변경합니다. 따라서, 차트 values 파일에 enableRemoteWriteReceiver
필드를 true로 변경하여 재배포해준다.
# monitor-values.yaml
prometheus:
prometheusSpec:
...
enableRemoteWriteReceiver: true # 해당 필드를 true로 변경
helm upgrade kube-prometheus-stack prometheus-community/kube-prometheus-stack --version 69.3.1 \ -f monitor-values.yaml --create-namespace --namespace monitoring
여담이지만, Alloy를 개발한 메인테이너가 이 Remote Write 기능을 Prometheus 오픈소스 프로젝트에 직접 기여했다는 이야기가 있다.
Alloy 메트릭 확인
Alloy가 성공적으로 배포되고 메트릭을 수집하기 시작하면, 이제 Prometheus에서 이러한 메트릭을 확인할 수 있다. Alloy가 배포된 노드(현재는 컨테이너)에 대한 특정 메트릭을 관찰할 수 있다. 예를 들어, Alloy가 배포된 노드에는 /etc/alloy 파일 시스템 경로가 존재하는 것을 메트릭을 통해 확인할 수 있다.
기존 프로메테우스에는 /etc/alloy
라는 파일 시스템을 가진 노드의 메트릭이 없는 것을 확인할 수 있다.
반면에, Alloy가 메트릭을 수집한 이후에는 /etc/alloy
파일 시스템 경로가 존재하는 것을 메트릭을 통해 확인할 수 있다. 이를 통해 Alloy의 배포 상태를 확인할 수 있으며, 또한 Alloy가 제대로 메트릭을 수집하고 있는지 검증할 수 있다.
Alloy의 Unix 시스템 exporter가 수집한 메트릭 엔드포인트로 요청을 보내면, Alloy가 수집하는 메트릭을 직접 확인할 수 있다. alloy라는 단어를 필터링해서 보면, 프로메테우스에서 방금 조회한 시스템 메트릭이 출력된다.
curl http://localhost:30003/api/v0/component/prometheus.exporter.unix.local_system/metrics | grep alloy
...
node_filesystem_files{device="/dev/vdb1",fstype="btrfs",mountpoint="/etc/alloy"} 0
node_filesystem_files_free{device="/dev/vdb1",fstype="btrfs",mountpoint="/etc/alloy"} 0
이러한 메트릭은 Alloy가 정상적으로 작동하고 있으며, 시스템 메트릭을 성공적으로 수집하고 있음을 보여준다.
6. 실습: Grafana Alloy를 활용한 로그 수집 및 Loki 연동
이번 섹션에서는 조금 다르게, Grafana Alloy를 Linux VM에 설치하고 Loki와 연동하여 로그 수집 파이프라인을 구축하는 방법에 대해 실습해 보자.
로키 설치(loki-stack)
먼저 Kubernetes 클러스터에 Loki를 설치하겠습니다. 이를 위해 Grafana에서 제공하는 loki-stack Helm 차트를 사용합니다:
cat << EOF >> override-values.yaml
loki:
enabled: true
isDefault: true
url: http://{{(include "loki.serviceName" .)}}:{{ .Values.loki.service.port }}
readinessProbe:
httpGet:
path: /ready
port: http-metrics
initialDelaySeconds: 45
livenessProbe:
httpGet:
path: /ready
port: http-metrics
initialDelaySeconds: 45
datasource:
jsonData: "{}"
uid: ""
persistence:
enabled: true
storageClassName: nfs-client
size: 5Gi
promtail:
enabled: true
config:
logLevel: info
serverPort: 3101
clients:
- url: http://{{ .Release.Name }}:3100/loki/api/v1/push
grafana:
enabled: true
sidecar:
datasources:
label: ""
labelValue: ""
enabled: true
maxLines: 1000
image:
tag: 10.3.3
EOF
helm install loki-stack grafana/loki-stack --values override-values.yaml
helm upgrade loki-stack grafana/loki-stack --values override-values.yaml
helm uninstall loki-stack
helm install loki-stack .
helm upgrade loki-stack .
Grafana Datasource Loki 연결 에러
그라파나의 Grafana > Connect > Data sources
메뉴에서 Loki를 데이터 소스로 연결 시에, 다음과 같은 에러가 발생할 수 있다.
Loki의 파드 에러 로그는 다음과 같다.
...
lokiHost=10.96.178.226:3100 lokiPath=/loki/api/v1/query status=error error="parse error at line 1, col 1: syntax error: unexpected IDENTIFIER"
이 오류는 주로 Grafana와 Loki 간의 버전 호환성 문제로 발생한다. 특히 최신 버전의 Grafana와 이전 버전의 Loki를 함께 사용할 때 자주 발생하는 문제라고 한다. 이 문제를 해결하기 위해서는 Grafana 또는 Loki의 버전을 조정해야 하는데. 일반적으로 Loki의 이미지 태그를 변경하는 것이 더 간단한 해결책인 것 같다.
위의 설정에서 Grafana 이미지 태그를 2.9.3으로 지정하여 호환되는 버전으로 배포하면 문제가 해결됩니다.
Alloy Linux(Debian/Ubuntu) 설치
이번에는 alloy를 Linux 외부 VM에 배포하고 로그를 수집하여 Loki에 저장해 보는 실습을 진행하고자 Debian/Ubuntu 시스템에 Alloy를 설치한다.
# GPG 키 설치
sudo apt install gpg
# Grafana 저장소 키 추가
sudo mkdir -p /etc/apt/keyrings/
wget -q -O - https://apt.grafana.com/gpg.key | gpg --dearmor | sudo tee /etc/apt/keyrings/grafana.gpg > /dev/null
echo "deb [signed-by=/etc/apt/keyrings/grafana.gpg] https://apt.grafana.com stable main" | sudo tee /etc/apt/sources.list.d/grafana.list
# 패키지 목록 업데이트
sudo apt-get update
# Alloy 설치
sudo apt-get install alloy
# Alloy 데몬 확인
systemctl status alloy.service
Alloy 설정 파일 작성
Alloy의 로그 수집 및 전송 파이프라인을 정의하는 설정 파일을 작성한다.
alloy 설정 파일 생성
# 설정파일 생성
touch /etc/alloy/config.alloy
로그 파일 생성
이번 실습에서는 Alloy 서비스(데몬) 자체의 로그를 수집하여 Loki로 전송해 보자.
# 디렉토리가 없는 경우 생성
sudo mkdir -p /var/log/alloy
# 표준 입출력을 이용하여 로그 파일 생성
# Alloy 서비스의 로그를 실시간으로 파일에 기록
journalctl -u alloy.service -f > /var/log/alloy/alloy.log 2>&1 &
컴포넌트 1: 로그 파일 경로 지정
먼저 수집할 로그 파일의 경로를 지정한다.
local.file_match "local_files" {
path_targets = [{"__path__" = "/var/log/alloy/*.log"}] // /var/log/alloy/ 디렉토리의 모든 .log 파일 대상
sync_period = "5s" // 5초마다 파일 목록 갱신
}
컴포넌트 2: 로그 소스 설정
지정된 경로의 로그 파일을 읽어오는 컴포넌트를 설정한다.
loki.source.file "log_scrape" {
targets = local.file_match.local_files.targets // 위에서 정의한 파일 경로 사용
forward_to = [loki.process.filter_logs.receiver] // 수집한 로그를 필터링 컴포넌트로 전달
tail_from_end = true // 파일의 끝부터 읽기 시작 (기존 로그는 무시)
}
컴포넌트 3: 로그 필터링 설정
수집된 로그 중 특정 패턴을 제외하는 필터링 컴포넌트를 설정한다.
loki.process "filter_logs" {
stage.drop {
source = "" // 전체 로그 메시지 대상
expression = ".*Connection closed by authenticating user root" // 해당 패턴 포함 로그 제외
drop_counter_reason = "noisy" // 제외 이유 (메트릭에 표시됨)
}
forward_to = [loki.write.grafana_loki.receiver] // 필터링된 로그를 Loki 전송 컴포넌트로 전달
}
컴포넌트 4: Loki로 로그 전송 설정
필터링된 로그를 Loki로 전송하는 컴포넌트를 설정한다.
loki.write "grafana_loki" {
endpoint {
url = "http://10.233.40.103:3100/loki/api/v1/push" // Loki API 엔드포인트
//url = "http://localhost:3100/loki/api/v1/push" // 로컬 테스트용 대체 URL
// 인증이 필요한 경우 아래 주석 해제
// basic_auth {
// username = "admin"
// password = "admin"
// }
}
}
설정 파일(전체내용)
# /etc/alloy/config.alloy
local.file_match "local_files" {
path_targets = [{"__path__" = "/var/log/alloy/*.log"}]
sync_period = "5s"
}
loki.source.file "log_scrape" {
targets = local.file_match.local_files.targets
forward_to = [loki.process.filter_logs.receiver]
tail_from_end = true
}
loki.process "filter_logs" {
stage.drop {
source = ""
expression = ".*Connection closed by authenticating user root"
drop_counter_reason = "noisy"
}
forward_to = [loki.write.grafana_loki.receiver]
}
loki.write "grafana_loki" {
endpoint {
url = "http://10.233.40.103:3100/loki/api/v1/push"
//url = "http://localhost:3100/loki/api/v1/push"
// basic_auth {
// username = "admin"
// password = "admin"
// }
}
}
Alloy 설정을 다시 로드하여 변경사항을 적용한다.
curl -X POST http://localhost:30003/-/reload
Alloy 로그 확인
이제 Alloy가 로그를 수집하고 Loki로 전송하는지 확인해 보자.
Alloy 서비스 로그 확인
먼저 Alloy 서비스 로그를 확인하여 설정한 경로의 로그 파일을 제대로 찾고 있는지 확인할 수 있다.
Alloy를 배포한 노드에서 수집한 Alloy 서비스의 로그 파일 및 내용을 그라파나에서 확인할 수 있다.
이제 Alloy를 통한 로그 수집 파이프라인이 성공적으로 구성되었다. 이 설정을 기반으로 다양한 애플리케이션 및 시스템 로그를 중앙에서 수집하고 분석할 수 있다.
7. 결론
Grafana Alloy는 단순한 데이터 수집 도구를 넘어, 다양한 옵저버빌리티 도구들을 연결하는 중개자 역할을 수행한다. Grafana Labs는 Alloy를 Grafana 백엔드와 OpenTelemetry 영역을 효과적으로 연결하는 솔루션으로 전략적으로 잘 포지셔닝한 것 같다. 앞으로 옵저버빌리티 생태계에서 Alloy를 사용하게 되는 사례가 많이 나오지 않을까? 이후에는 Grafana LGTM 스택(Loki, Grafana, Tempo, Mimir)과 통합하여 경량화된 모니터링 시스템을 구축해 보자.
8. Reference
- https://grafana.com/docs/alloy/latest/tutorials/send-metrics-to-prometheus/
- https://grafana.com/docs/alloy/latest/tutorials/processing-logs/
- https://prometheus.io/docs/specs/remote_write_spec/
- https://velog.io/@wy9295/%EB%B2%88%EC%97%AD-grafana-alloy-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC
- Cursor AI
claude-3.7-sonnet
'Infrastructure' 카테고리의 다른 글
Istio 시리즈 # 2 - Istio의 핵심, Envoy Proxy를 이해하자 (0) | 2025.04.20 |
---|---|
Istio 시리즈 # 1 - 설치부터 기본 트레픽 제어까지 (0) | 2025.04.13 |
개발자(엔지니어)에게 꼭 필요한 기초 암호학(X.509) (0) | 2025.03.15 |
Scouter APM 설치 및 기본 사용 + Scouter-Paper (0) | 2024.08.14 |
Harbor Replications을 활용하여 이미지 레지스트리 간 미러링하기 (0) | 2024.01.06 |