이전에 로그 시스템을 Cloudtype으로 구축했으나 최근 정책 변경으로 인해 Cloudtype으로 운용하기 어려워졌다. 따라서 로그 시스템을 ec2 환경으로 다시 구축해보겠다. 대략적인 구조는 다음과 같다.
test 서버(free tier) 환경 (mem: 1GB, swap mem: 2GB)
docker compose
로 was 환경과 monitoring 환경을 관리한다.
was: nginx, spring boot(tomcat)
monitor: grafana, loki, prometheus, promtail
실서버에서 스프링 서버는 logback logging framework를 사용하여 로그를 포멧팅하고 레벨별로 나누어 file에 저장하고 있었다. 이 로그 파일을 promtail가 읽어 라벨링하여 Loki로 전달하게 하고, Grafana가 Loki를 통해 로그를 수집하도록 구축했다.
# docker-compose.yml
version: '3'
services:
prometheus: # 현재 게시글에선 필요없는 부분
image: prom/prometheus
container_name: prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
command:
- --config.file=/etc/prometheus/prometheus.yml
networks:
- prometheus
loki:
image: grafana/loki
container_name: loki
user: "$UID:$GID"
ports:
- "3100:3100"
volumes:
- ./loki/local-config.yaml:/etc/loki/local-config.yaml
- ./loki/data:/var/loki
command: -config.file=/etc/loki/local-config.yaml
networks:
- loki
promtail:
image: grafana/promtail
container_name: promtail
volumes:
- /home/ubuntu/was/logs:/logs
- ./promtail/promtail-config.yml:/etc/promtail/config.yml
command: -config.file=/etc/promtail/config.yml
depends_on:
- loki
networks:
- loki
grafana:
image: grafana/grafana
container_name: grafana
user: "$UID:$GID"
ports:
- "3000:3000"
volumes:
- ./grafana:/var/lib/grafana
depends_on:
- prometheus
- loki
networks:
- prometheus
- loki
networks:
prometheus:
driver: bridge
loki:
driver: bridge
promtail이 push한 로그를 수집하고 저장한다. 컨테이너 내부에서 실행되는 프로세스의 사용자와 그룹을 호스트의 UID와 GID로 설정하여 마운트한 파일 접근 권한을 부여하고, config 파일과 data 파일을 바인딩 마운트한다. 따라서 loki가 실행될 때 config 파일을 적용할 수 있도록 커멘드를 작성하고, 네트워크를 구성하여 grafana와 통신하도록 구성한다.
# local-config.yaml
# 사용자 인증 활성화 여부
auth_enabled: false
# Loki 서버의 포트번호
server:
http_listen_port: 3100
# Loki 인스턴스의 주소
common:
instance_addr: 127.0.0.1
path_prefix: /var/loki
storage:
filesystem: # 파일시스템 기반 저장소를 사용
chunks_directory: /var/loki/chunks
rules_directory: /var/loki/rules
replication_factor: 1
ring:
kvstore:
store: inmemory
# 쿼리 범위에 대한 설정
query_range:
results_cache:
cache:
embedded_cache:
enabled: true
max_size_mb: 100
# Loki의 스키마 설정
schema_config:
configs:
- from: 2020-10-24
store: tsdb
object_store: filesystem
schema: v12
index:
prefix: index_
period: 24h
ruler:
alertmanager_url: http://localhost:9093
/tmp/loki
로 되어있지만 /var/loki
로 구성한 이유는 /tmp
경로는 임시 파일 및 디렉토리를 저장하는 데 사용되기에 임시 파일, 캐시, 일시적인 데이터 등을 저장하는 목적으로 적합하다고 판단했기 때문이다.서버에 저장된 로그를 Loki로 보내기 위해 필요한 서비스이다. log 파일과 config 파일을 바인드 마운트하고 promtail이 실행될 때 config 파일이 등록되도록 구성한다. Loki가 뜬 이후에 서비스를 띄우도록 하여 promtail이 loki로 push할 때 fail이 뜨는 경우가 안 나오도록 설정했다. 마지막으로 loki 네트워크에 참여하여 통신하도록 구성했다.
# promtail-config.yml
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
- job_name: info
static_configs:
- targets:
- localhost
labels:
job: test_info_logs
__path__: /logs/*/info-*.log
- job_name: warn
static_configs:
- targets:
- localhost
labels:
job: test_warn_logs
__path__: /logs/*/warn-*.log
- job_name: error
static_configs:
- targets:
- localhost
labels:
job: test_error_logs
__path__: /logs/*/error-*.log
Loki에서 수집한 로그를 받아 시각하기 위한 서비스로 loki가 뜬 이후로 실행한다. grafana 설정 정보를 바인드 마운트하여 혹시나 컨테이너가 삭제될 경우를 대비했다.
docker compose로 로그 시스템을 띄운 뒤 grafana 연동 작업은 다음과 같다.
http://loki:3100
) → 등록그라파나로 연동하는 과정에서 data source가 등록되지 않는 이슈가 발생했다. 나와 비슷한 문제를 겪는 사람들이 있어서 찾아보니 loki가 promtail 메트릭을 수집할게 없어서 발생한 문제였다. 위 링크의 조언대로 system log를 전송하도록 구성 파일을 바꾸니 정상적으로 data source가 등록됐다. 나중에 확인해보니 로그를 수집할 대상 파일 경로에 ~
를 넣어서 경로 인식을 제대로 못 했었던 것이다.(system log를 전송하지 않아서 발생한 문제가 아니었던 것이다)
loki가 로그를 수집하지 못하면 보통 promtail 설정 문제이다.(보통 에러는 promtail에 error log로 확인 가능하지만 불친절하다. error log가 아예 뜨지 않는 경우도 있다)
이렇게 간단하게 로그 시스템을 구축해보았다. 하지만 우리 목표는 단순히 로그 시스템을 구축하는 것이 아니다. 다음은 앞으로 달성할 과제들이다.
다음 게시글에서 위 과제를 해결해보자!