Introduce
📌 개요
2025.01.25 - [DevOps/Monitoring] - 로그 시스템이 필요한가요? (ELK Stack과 PLG Stack)
로그 시스템이 필요한가요? (ELK Stack과 PLG Stack)
개요 로그는 어떻게 수집해야할까? 나는 최근까지 로그 수집에 대한 지식도, 관심도 가져 본적이 없었다. 그 이유는 개발만 하면서 로그 대한 필요성도 크게 느끼지 못했고, 익숙하지 않은것도
woojjam.tistory.com
이전 시간에는 로그 시스템의 필요성과 ELK Stack / PLG Stack에 대해서 정말 간략히 알아보았다.
이번 시간에는 실제로 PLG Stack을 구축해 보고, 실 서버에 적용해보고자 한다.
Definition
📌 목표
구축을 하기전에 내가 하고자 하는 것이 무엇인지 정확하게 정의해야 한다. 지금 내가 구축하고자 하는 것이 무엇인가?
서버 로그들을 문제가 발생할 경우 필터링과 검색을 통해 쉽게 로그를 추적하고, SSH로 접근하여 직접 로그 파일을 열어보지 않아도 되며 더 나아가 모든 로그를 한 곳에 모아 관리하는 중앙 집중식 로깅 시스템을 구축하고 싶은 것이다.
즉, 핵심 키워드는 필터링과 검색, 중앙 집중식 이다.
Architecture
📌 Architecture
구축하고자 하는 시스템이 무엇인지 정확히 정의하였고, 이전글에서 언급했듯이 PLG Stack을 구축할 것이다.
PLG Stack은 파일로 저장된 로그를 Promtail을 이용하여 Loki로 전달하며 수집된 로그를 Grafana를 통해 시각화하는 것이다.
구축하기 전 이 3가지 기술들을 정확히 공부하고, 구축에 들어가면 좋겠지만 개인적으론 재미없는 Docs를 무작정 읽는 것보다 일단 덤벼보고 하나씩 뜯어보는걸 더 좋아하기에 머리부터 박아보자. 하지만 그전에 구축할 아키텍처가 어떤 건지는 알아야 되지 않겠는가?
정말 간략한 아키텍처는 위 그림과 같다. 그럼 이 간략한 아키텍처와 현재 우리 아키텍처와 합쳐보자.
정리해 보면 내가 구축하고자 하는 로깅 시스템은 이러한 구조가 될 것이다. Log를 수집할 모든 서버에 Promtail을 구축하고, 모니터링 서버의 Loki로 로그를 전달한 뒤 수집된 로그를 Grafana를 통해 시각화할 것이다.
Implementation
Grafana Loki | Grafana Loki documentation
Manage Loki Learn how to manage tenants, log ingestion, storage, queries, and more.
grafana.com
자세한 내용들은 해당 Docs를 참고하는 것을 추천합니다.
📌 Loki & Grafana
먼저 Monitoring Server를 분리하고, 해당 Server에 Loki와 Grafana를 구축해주어야 한다. 나는 Docker Compose를 통해 설치하고자 한다.
wget https://raw.githubusercontent.com/grafana/loki/v3.3.2/production/docker-compose.yaml -O docker-compose.yaml
docker-compose.yaml을 받아주고, 해당 파일을 열어보면
- 변경 전 docker-compose.yaml
networks:
loki:
services:
loki:
image: grafana/loki:latest
ports:
- "3100:3100"
command: -config.file=/etc/loki/local-config.yaml
networks:
- loki
promtail:
image: grafana/promtail:latest
volumes:
- /var/log:/var/log
command: -config.file=/etc/promtail/config.yml
networks:
- loki
grafana:
environment:
- GF_PATHS_PROVISIONING=/etc/grafana/provisioning
- GF_AUTH_ANONYMOUS_ENABLED=true
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
entrypoint:
- sh
- -euc
- |
mkdir -p /etc/grafana/provisioning/datasources
cat <<EOF > /etc/grafana/provisioning/datasources/ds.yaml
apiVersion: 1
datasources:
- name: Loki
type: loki
access: proxy
orgId: 1
url: http://loki:3100
basicAuth: false
isDefault: true
version: 1
editable: false
EOF
/run.sh
image: grafana/grafana:latest
ports:
- "3000:3000"
networks:
- loki
기본적으로 이렇게 작성되어 있다. 해당 docker-compose.yaml을 그대로 사용해도 문제는 없지만 내가 구축하고자 하는 시스템은 별도로 구축된 모니터링 서버에서 로그를 수집하는 것이다. 따라서 현재 내가 구축하고자 하는 아키텍처로 변경해주어야 한다. 그렇기에 Promtail은 모니터링 서버가 아닌 로그를 남길 운영 서버에 있어야 하므로 해당 설정은 제거해주어야 한다.
또한 로그인하지 않은 Anonymous 사용자에 대해서 권한을 인가해주지 않고, 반드시 로그인을 한경우에만 접근을 가능하도록 변경하고 싶다.
Configure authentication | Grafana documentation
Getting started with managing your metrics, logs, and traces using Grafana In this webinar, we’ll demo how to get started using the LGTM Stack: Loki for logs, Grafana for visualization, Tempo for traces, and Mimir for metrics.
grafana.com
관련된 설정은 해당 파트를 참고하면 설정 방법을 알 수 있다.
- 변경 후 docker-compose.yaml
networks:
loki:
services:
loki:
image: grafana/loki:latest
ports:
- "3100:3100"
command: -config.file=/etc/loki/local-config.yaml
networks:
- loki
grafana:
environment:
- GF_PATHS_PROVISIONING=/etc/grafana/provisioning
- GF_AUTH_ANONYMOUS_ENABLED=false
- GF_AUTH_ANONYMOUS_ORG_ROLE=Viewer
image: grafana/grafana:latest
ports:
- "3000:3000"
networks:
- loki
GF_AUTH_ANONYMOUS_ENABLED를 false로 오버라이드하여 익명 사용자의 접근을 막고,
GF_AUTH_ANONYMOUS_ORG_ROLE를 통해 익명 사용자의 경우 인가 권한을 Viewer로 지정하였다.
(익명 사용자 접근을 막지 않는다면 인가 권한을 재정의 해주는 것이 의미가 있겠지만 익명 사용자의 접근을 막는다면 인가 권한을 재정의하는 것이 사실 큰 의미는 없는 것 같기는 하다. )
entrypoint:
- sh
- -euc
- |
mkdir -p /etc/grafana/provisioning/datasources
cat <<EOF > /etc/grafana/provisioning/datasources/ds.yaml
apiVersion: 1
datasources:
- name: Loki
type: loki
access: proxy
orgId: 1
url: http://localhost:3100
basicAuth: false
isDefault: true
version: 1
editable: false
EOF
/run.sh
내가 수정한 docker compose에서는 해당 설정을 제거해 주었는데 이 설정은 Grafana가 자동으로 Loki 데이터 소스를 구성하도록 하는 역할을 한다. 하지만 나는 대시보드 설정 시 데이터 소스를 직접 수동으로 만들어 줄 것이므로 해당 설정을 제거하였다.
(만약 자동으로 데이터 소스가 만들어지도록 변경하고 싶다면 해당 부분을 조금 손 보면 될 것 같다.)
📌 Promtail
위 단계까지 성공적으로 완료한다면 Loki & Grafana 컨테이너가 정상적으로 동작중일 것이다. 이제 남은 부분은 운영 서버에 모인 로그들을 Loki로 전달하면 된다. 따라서 Promtail을 구축해 보자. Promtail은 실제로 수집할 로그들을 Loki로 전달하는 역할을 해야 하므로 운영 서버와 같은 환경에서 존재해야 한다.
Promtail도 docker compose로 실행시킬 예정이므로 promtail-docker-compose.yaml을 작성해야 한다. 새로 작성해도 되지만 나는 이전에 가져온 docker-compose.yaml 에서 Promtail 부분만 추출하여 재활용할 것이다.
- 변경 전 promtail-docker-compose.yml
services:
promtail:
image: grafana/promtail:latest
volumes:
- /var/log:/var/log
command: -config.file=/etc/promtail/config.yml
networks:
- loki
Promtail 만 추출하여 재작성한 promtail-docker-compose.yaml 파일이다. 여기서 수집할 로그들을 Volume으로 마운트 하여 Promtail이 로그들을 수집할 수 있도록 변경해주어야 한다.
- 변경 후 promtail-docker-compose.yml
services:
promtail:
container_name: promtail
image: grafana/promtail:latest
volumes:
- /home/ubuntu/server-log/app:/home/ubuntu/server-log/app # APP 로그 디렉터리 마운트
- /home/ubuntu/server-log/owner:/home/ubuntu/server-log/owner # OWNER 로그 디렉터리 마운트
- /home/ubuntu/server-log/batch:/home/ubuntu/server-log/batch # BATCH 로그 디렉터리 마운트
- ./promtail-config.yaml:/etc/promtail/promtail-config.yaml # 설정 파일 마운트
command: -config.file=/etc/promtail/promtail-config.yaml
현재 운영서버는 Spring의 Logback을 통해 모듈별로 server-log/{모듈이름}
와 같은 형태로 로그들이 저장된다. 수집할 로그들은 APP, WEB, BATCH 3개의 서버들에 대한 로그들이다. 따라서 3개의 서버들에 대한 로그 파일들을 모두 Volume으로 마운트 해주어야만 한다.
수집할 로그 파일들을 마운트 한 뒤, 로그 수집 파이프라인을 구축할 수 있다. 로그 수집 파이프라인은 Promtail이 로그 데이터를 읽고, 전처리하며, 라벨을 추가하여 Loki로 전송하는 전체 과정을 의미하는데 이 과정에서 필터링 가능한 라벨을 설정해 Loki에서 효율적인 로그 관리를 가능하게 해 준다.
로그 수집 파이프라인을 구성하기 위해 promtail-config.yaml을 작성해 보자.
- promtail-config.yaml
positions:
filename: /tmp/positions.yaml
clients:
- url: http://{LOKI_PUBLIC_IP}:{LOKI_PORT}/loki/api/v1/push
scrape_configs:
- job_name: "sponjy_logs" # 잡 이름
static_configs:
- targets:
- localhost
labels:
job: "app_logs" # 로그의 라벨
__path__: /home/ubuntu/server-log/app/*.log # 수집할 로그 파일 경로
- targets:
- localhost
labels:
job: "owner_logs" # 로그의 라벨
__path__: /home/ubuntu/server-log/owner/*.log # 수집할 로그 파일 경로
- targets:
- localhost
labels:
job: "batch_logs" # 로그의 라벨
__path__: /home/ubuntu/server-log/batch/*.log # 수집할 로그 파일 경로
pipeline_stages:
- multiline:
firstline: '^\[.*\] .* (DEBUG|INFO|WARN|ERROR) .*' # 로그 시작 패턴
- regex:
expression: '^\[.*\] .* (?P<level>DEBUG|INFO|WARN|ERROR) .*' # 로그 레벨 추출
- labels:
level: "level" # 추출한 로그 레벨을 라벨로 추가
내가 구성한 파이프라인이다.
- 로그 파일 지정 (Scrape Configs)
- 특정 디렉터리의 경로를 지정해 "job" 라벨로 설정
- 로그 읽기 위치 저장 (Positions)
- Promtail이 로그 파일의 마지막 읽은 위치를 기록하여 중복 수집을 방지
- 이를 통해 Promtail이 재시작되더라도 마지막 위치부터 다시 수집이 가능
- 로그 전처리 (Pipeline Stages)
- Promtail이 수집한 로그들을 처리하며, 멀티라인 로그 병합 수행
- 정규식 패턴으로 특정 로그 레벨들을 "level" 라벨로 설정
- Loki로 전송
- 전처리가 완료된 최종 로그 데이터는 Loki 서버로 전송됨
- 추가된 라벨들을 기준으로 로그를 필터링하고 검색 가능
Visualizing Logs in Grafana
이제 Promtail, Loki, Grafana를 통한 모니터링 시스템은 구축이 완료되었다. 그렇다면 이 수집된 로그들을 Grafana에서 확인해 보자.
Grafana로 접속해 보면
이전에 설정한 대로 익명 사용자에 대한 접근을 막고 반드시 로그인을 해야지만 접속할 수 있다.
시각화하기 위한 대시보드를 만드는 과정은 생략하겠다.
나는 간단하게 오늘 발생한 로그, Log Level 별 로그, 모듈별 로그 3가지를 보기 위해 대시보드를 구성하였다. 기존에는 SSH로 서버에 접속하여 직접 로그를 확인해야 했지만 PLG Stack과 별도의 로그 모니터링 서버를 통해 중앙 집중식 로그 관리시스템이 완성된 것을 확인할 수 있다.
상단의 Log Level 변수를 통해 로그들을 레벨별로 필터링할 수도 있으며
Search 변수를 통해 특정 키워드로 로그를 검색도 가능해졌다. 이뿐만 아니라 로그 수집 파이프라인에서 원하는 라벨을 설정해 주고 쿼리문을 변경한다면 내가 원하는 대로 로그를 시각화할 수 있다.
따라서 Definition에서 언급한 3가지 목표인 필터링과 검색, 중앙 집중식을 모두 만족한 로그 시스템이 성공적으로 구축되었다.
Trouble Shooting
❗️ Grafana를 재시작 시 Dashboard가 삭제됨
Grafana의 인가 규칙을 변경한다고 설정을 변경하고 docker compose로 재시작하니 이전에 만들어 놓은 Dashboard가 사라졌다. (열심히 만들었는데..)
Run Grafana Docker image | Grafana documentation
Run Grafana Docker image You can use Grafana Cloud to avoid installing, maintaining, and scaling your own instance of Grafana. Create a free account to get started, which includes free forever access to 10k metrics, 50GB logs, 50GB traces, 500VUh k6 testin
grafana.com
해당 문서를 참고하니 이러한 내용이 있었다.
Grafana는 기본적으로 컨테이너를 중지하고, 제거하면 모든 파일 시스템의 변경 사항이 삭제된다는 것이다. (다시 한번 문서 중요성을 느꼈다..) 그렇기에 Grafana에서는 Docker 볼륨을 사용하는 것을 권장한다는 것이다. 그러므로 Grafana를 실행하는 docker-compose.yaml 파일을 수정해 보자.
networks:
loki:
services:
loki:
image: grafana/loki:latest
ports:
- "3100:3100"
command: -config.file=/etc/loki/local-config.yaml
networks:
- loki
grafana:
environment:
- GF_PATHS_PROVISIONING=/etc/grafana/provisioning
- GF_AUTH_ANONYMOUS_ENABLED=false
- GF_AUTH_ANONYMOUS_ORG_ROLE=Viewer
image: grafana/grafana:latest
volumes:
- grafana-storage:/var/lib/grafana # Grafana 데이터 디렉토리 마운트
ports:
- "3000:3000"
networks:
- loki
volumes:
grafana-storage: {}
grafana-storage라는 익명 볼륨을 정의하고 Grafana 데이터를 볼륨에 마운트 한 뒤, 데이터가 Docker 내부에 저장되도록 변경하여 문제를 해결할 수 있었다.
'DevOps > Monitoring' 카테고리의 다른 글
스프링 모니터링 시스템 구축하기 (Actuator, Prometheus, Grafana) (0) | 2025.02.05 |
---|---|
로그 시스템이 필요한가요? (ELK Stack과 PLG Stack) (0) | 2025.01.25 |