이전 글에서 캐싱을 적용하게 된 계기와 CPU 점유율과 관련한 이슈가 하나 있었기에 도입하게 되었다. 이에 더해, 추후 로깅 시스템을 발전시켜 나간다면 더욱 요긴하게 활용할 수 있을 것이라고 판단했다.
Prometheus는 SoundCloud에서 만든 오픈소스 시스템 모니터링 및 경고 툴킷으로 현재는 독립적으로 오픈소스로 운영되고 있다.
Prometheus는 아래와 같은 구조로 이루어져 있다.
(출처: 프로메테우스 홈페이지/2024-04-01)
Spring Boot Actuator를 통해 HTTP또는 JMX형식으로 모니터링에 활용할 수 있는 데이터를 조회할 수 있다. 이는 다양한 모니터링 툴을 통해서 수집할 수 있는데, 난 그중 Prometheus를 선택하게 되었다. Prometheus는 간편하게 통합 및 운영이 가능하기에 쿠버네티스 환경에서 굉장히 용이한데, 이는 도커 컨테이너를 사용하려는 내 환경에서도 편리하다는 의미이기에 선택하게 되었다.
Grafana는 오픈 소스 시각화 및 분석 소프트웨어로 로그 및 추적을 쿼리, 시각화, 경고 및 탐색할 수 있습니다. 시계열 데이터베이스(TSDB) 데이터를 통찰력 있는 그래프와 시각화로 전환하는 도구를 제공한다.
추가적으로 Grafana는 Prometheus가 시각화와 관련된 Docs에서 별도로 Grafana 페이지를 구성하여 다루고 있을 정도이기에 문서를 기반으로 빠른 속도로 적용할 수 있다고 판단했다.
implementation 'org.springframework.boot:spring-boot-starter-actuator'
runtimeOnly 'io.micrometer:micrometer-registry-prometheus'
로컬호스트 기준으로, localhost:8080/actuator로 접속하면 Spring Boot Actuator가 기본적으로 제공하고 있는 Endpoint를 알 수 있다.
yml에 exposure 설정을 변경하거나 추가하면서, 다양한 actuator endpoint를 접근할 수 있도록 열어줄 수 있다.
exposure 설정 중 prometheus가 서버 메트릭스를 수집할 수 있도록 설정한다.
management:
endpoints:
web:
exposure:
include: "prometheus"
Prometheus를 통해 서버 로그를 수집해와야 하므로, 타겟이 되는 서버와 관련된 정보를 저장하는 yml파일이 필요하다. 이는 prometheus.yml로 서버 내에 생성하고 docker-compose를 통해 Prometheus를 실행할 때, 해당 yml파일을 이용하도록 설정하면 된다. 아래는 실제 적용한 yml이다.
monitoring-docker-compose.yml
version: '3'
services:
prometheus:
image: prom/prometheus:latest
container_name: prometheus
user: "$UID:$GID"
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- ./prometheus-data:/prometheus
grafana:
image: grafana/grafana:latest
container_name: grafana
user: "$UID:$GID"
ports:
- "3000:3000"
volumes:
- ./grafana-data:/var/lib/grafana
depends_on:
- prometheus
UID와 GID를 설정해야 하는 이유?
https://sweethoneybee.tistory.com/28volume mount를 해야하는 이유?
도커 컨테이너를 삭제 후 재실행하는 상황이 발생하게 된다면, 도커 컨테이너 내에서 저장되고 있던 로그 데이터들이 휘발되는 상황이 발생할 수 있으므로 서버의 특정 경로와 연결시켜 로그 데이터들이 유실되는 상황을 방지해야 한다.
prometheus.yml
scrape_configs:
- job_name: 'Gongjakso Server'
metrics_path: '/actuator/prometheus'
scrape_interval: 5s
static_configs:
- targets: ['{SERVER_IP}:8080']
여기서 Localhost가 아닌 서버의 실제 IP를 SERVER IP 자리에 사용해야 한다.
스프링부트 서버와 Proemtheus간의 연동하기 위한 내 시도들
(1번과 3번은 가능했으며, 2번은 불가능했다.)
1. 도커 네트워크에서 host를 활용하는 방법
targets: ['localhost:8080']
로 prometheus.yml을 설정하면, 도커 컨테이너에 올라가 있기에 해당 localhost가 컨테이너 ID로 잡히게 된다. 이는 도커의 기본 네트워크 모드가 브릿지 모드이기 때문인데, 이를docker run {IMAGE} --net host
와 같이 실행될 수 있도록 구성하면, localhost가 서버 IP로 잡힐 수 있다.
- 같은 Docker network를 사용하도록 구성하기
기존 스프링부트 서버와 Redis를 컨테이너로 올려놓았기에, 별도의 도커 컴포즈를 통해 서버를 구축하게 되므로, 각 컨테이너 간 네트워크가 달라 Prometheus가 서버의 메트릭스를 정상적으로 수집하지 못하는 상황으로 판단하여, 공통적으로 사용하는 네트워크를 별도로 생성하여 연결하는 방식으로 해결을 시도했다.
(1) 네트워크 생성 및 확인
sudo docker network create gongjakso-network sudo docker network ls
(2) 스프링부트 & Redis 컴포즈 파일 네트워크 정보 추가
# docker-compose.yml version: '3' services: redis: container_name: redis image: redis:7.0.11 ports: - "6379:6379" networks: - default springboot: container_name: server image: springboot ports: - "8080:8080" depends_on: - redis networks: - default networks: default: external: name: gongjakso_network
(3) 모니터링 컴포즈 파일 네트워크 정보 추가
version: '3' services: prometheus: image: prom/prometheus:latest container_name: prometheus user: "$UID:$GID" ports: - "9090:9090" volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml - ./prometheus-data:/prometheus networks: - default grafana: image: grafana/grafana:latest container_name: grafana user: "$UID:$GID" ports: - "3000:3000" volumes: - ./grafana-data:/var/lib/grafana depends_on: - prometheus networks: - default networks: default: external: name: gongjakso_network
위 방식을 사용한 이후, 잡힌 targets을 확인해보았더니, Prometheus의 Conatainer ID가 타겟으로 잡혀 정상적으로 서버의 정보를 가져오지 못하는 것을 확인했다.
- 스프링부트 서버의 컨테이너 ID를 prometheus.yml에 설정하기
해당 방식은 배포가 이루어질 때마다 타겟을 수동으로 변경해야 하기에, 시간 비용이 높은 행위라고 판단되어 진행하지 않았다.
처음 접근하면, 아래와 같이 로그인 페이지가 뜨게 된다. 초기 아이디와 비밀번호는 admin으로 구성되어 있으며, 로그인하면 비밀번호 변경 안내가 뜨게 된다.
2. Prometheus 클릭
Grafana Labs에서 사용하고자 하는 대시보드를 가져올 수 있다. 나는 Spring Boot 3.x를 사용했다.
기존의 root 계정을 활용하여 READ 권한을 가진 계정을 생성해준다. 나는 TablePlus의 SQL Query Console을 사용해서 생성을 진행했다.
# 계정 생성
create user 'id'@'%' identified by 'password';
# 권한 부여
GRANT SELECT ON db_name.* TO 'id'@'%';
여기서 id와 password가 실제 게정의 아이디/비밀번호가 되며, %는 외부에서 접근할 때 사용할 수 있는 계정이라는 의미가 된다.
이후 SELECT라는 권한을 부여하여 읽기 권한을 가질 수 있도록 해줬다.
Connection에는 DB 서버 IP와 포트를, Authentication에는 1번 과정에서 생성한 계정 정보를 입력한다.
Dashboard Settings -> Variables로 접속하면, 대시보드에서 활용하는 변수를 추가할 수 있다.
select * from 스키마명.$table limit 50
스키마명 또한 변수로 등록하여 관리할 수 있다. 나는 변경될 일이 없다고 판단하여 지정하여 쿼리를 작성했다.
느낀 점
Prometheus와 Grafana를 통해 보다 시각화된 모니터링을 통해, 누수 발생 지점을 파악 및 대응할 수 있을 것으로 생각한다. 또한 Grafana를 이용해 대시보드 형식으로 DB 정보를 조회할 수 있도록 구축함으로서 단순 조회 형식의 백오피스 구현의 코스트를 줄일 수 있다는 것이 프로젝트의 초기 단계에서는 꽤나 효과적일 것으로 생각한다.
다만, 해당 모니터링 서비스는 모두가 접근하면 안되기에 접근 제한을 걸어두어야 한다. 나는 Whitelist 방식으로 특정 IP만 접근이 가능하도록 하여 보안성을 높일 생각이다.
다양한 로그들을 수집하여 분석하고 활용해야 하므로, logback과 AOP 등을 활용하여 로그들을 계속 쌓아갈 수 있도록 빠르게 업데이트할 필요성을 느꼈다.
참고자료