모니터링 시스템 구축기 1편- Metric
이 글은 우아한테크코스 백엔드 6기 냥인, 러쉬, 명오에 의해 작성되었습니다.
구축 배경
3차: 서비스 운영 환경 구축
로깅 프레임워크 적용
API 문서 작성
(로그, 메트릭) 모니터링 대시보드 구성
백엔드 3차 요구사항으로 ‘모니터링 대시보드 구성하기’가 주어졌습니다.
팀 크루루는 아직 개발 환경만 구축되어 있기 때문에 개발 환경에 모니터링을 구축하였습니다.
모니터링 도구 선택 기준
💡 ▶︎ 요구사항에서 언급된 대로 빠르게 구축할 수 있어야 한다.
▶︎ 스프링 부트와 연동하기 쉬워야 한다.▶︎ 비용이 저렴해야 한다.
위 기준에 따라 메트릭 모니터링 도구는 Spring Actuator와 Prometheus 그리고 Grafana로 선택하였습니다.
고려했던 또 다른 서비스로 Prometheus + Grafana외에도 AWS의 CloudWatch도 있습니다. 그러나 CloudWatch는 기본적으로 GC 및 Heap Memory 등 JVM 메트릭을 수집하지 않는다는 점, 그리고 CloudWatch를 사용하는 비용보다 Prometheus + Grafana를 띄울 모니터링 서버용 EC2 인스턴스를 새로 생성하는 금전적 비용이 더 저렴하다는 점에서 Prometheus + Grafana로 최종 결정하였습니다.
구조
Public subnet에 위치한 인스턴스(이하 인스턴스 A)에는 스프링부트와 MySQL이 Docker Container를 통해 실행되고 있습니다. Private subnet에 위치한 인스턴스(이하 인스턴스 B)에는 Prometheus와 Grafana가 Docker Container를 통해 실행되고 있습니다.
모니터링 흐름
다음은 메트릭 정보를 수집하고 Grafana를 통해 모니터링 하는 흐름입니다.
인스턴스 B에 있는 Prometheus는 인스턴스 A에 위치한 스프링 부트로부터 메트릭 정보를 수집합니다. Grafana는 Prometheus에 저장된 메트릭 정보를 가져와 시각화 합니다. 관리자가 Grafana 모니터링 도메인에 접속하게 되면, NGNIX의 리버스 프록시를 통해 Grafana에 접속할 수 있게됩니다.
WAS와 모니터링 서버 분리
팀 크루루는 다음 목적을 위해 WAS와 모니터링 서버를 분리하였습니다.
-
가용성 확보
WAS와 모니터링 서버를 분리하지 않을 경우, 한 곳에서 발생한 장애가 다른 곳에 영향을 미치게 됩니다. 즉, 장애가 전파되는 것이죠. WAS와 모니터링 서버를 분리하면 한 서버에서 발생한 장애가 다른 서버에 영향을 미치지 않게 됩니다. WAS에 문제가 생겨도 모니터링은 가능하며, 반대의 경우도 가능합니다.
-
보안 강화
메트릭과 로그 데이터는 보안상 외부에 노출시키지 않는 것이 적합하다고 생각했습니다. 따라서 모니터링 서버는 WAS와 달리 Private subnet에 별도로 두었습니다.
리버스 프록시 적용
Grafana에 접근하기 위해서는 3000 포트에 접근해야합니다. 하지만 인바운드 규칙에는 80, 443 포트만 허용되었으며 IAM 권한상 규칙을 추가할 수 없었습니다. 따라서 Ngnix의 리버스 프록시를 적용했습니다. grafana.cruru.kr
URL의 443 port로의 요청을 모니터링_서버_privte_ip:3000
으로 전달하도록 설정했습니다. 결과적으로 인바운드 규칙을 추가하지 않고 모니터링 서버로 접근할 수 있게 했습니다.
포트 포워딩
Prometheus가 Spring Actuator의 Metric을 가져오기 위해서는 Actuator가 제공하는 엔드포인트에 접근해야 합니다. 저희는 Actuator의 엔드포인트는 별도의 포트에서 제공되도록 설정했습니다. 따라서 Prometheus가 이 포트로 요청할 수 있도록 도커 컨테이너를 설정했습니다. 이를 위해 도커 컨테이너를 실행할 때, 해당 포트를 외부와 연결하고 포워딩하기 위한 설정을 docker-compose.yml
파일에 추가했습니다.
모니터링 시스템 구축
Metric, Spring Actuator, Prometheus에 대한 이론적 설명은 포함하지 않았습니다.
Spring Actuator 및 Prometheus 의존성 추가
build.gradle
// Spring Actuator 의존성
implementation 'org.springframework.boot:spring-boot-starter-actuator'
// Micrometer Prometheus 레지스트리 의존성
implementation 'io.micrometer:micrometer-registry-prometheus'
모니터링 엔드포인트 정의
application.yml
...
management:
server:
# default 포트 변경
port: ${MONITORING_PORT}
endpoints:
web:
# default 경로 변경
base-path: ${MONITORING_BASE_PATH}
exposure:
# 필요한 엔드포인트만 노출
include: prometheus
# 엔드포인트 all disable
enabled-by-default: false
jmx:
exposure:
exclude: "*"
endpoint:
prometheus:
enabled: true
보안을 위해 추가한 부분에 대해 간단히 살펴봅시다.
- 포트나 base-path같이 중요한 정보는 따로 환경 변수로 저장해 두고 주입 받아 사용합니다.
- 필요한 엔드포인트만 화이트 리스트 형태로 관리합니다.
- 모든 엔드포인트에 대해 enable을 false로 설정합니다(
enabled-by-default: false
). - 필요한 엔드포인트인
/prometheus
만 노출합니다. - JMX 형태는 사용하지 않으므로 모든 엔드포인트를 노출하지 않습니다.
- 모든 엔드포인트에 대해 enable을 false로 설정합니다(
- dafault 포트를 변경합니다(
server: port: ${MONITORING_PORT}
). - default 경로를 변경합니다(
base-path: ${MONITORING_BASE_PATH}
).
모니터링 서버 구성
-
EC2 인스턴스 생성
모니터링 정보는 외부에 노출되면 안 되는 부분이 포함되기 때문에, private subnet 내에 EC2 인스턴스를 생성합니다. 이 subnet은 현재 public subnet 내의 EC2 인스턴스를 통해서만 접근할 수 있습니다.
public 인스턴스에서 private 인스턴스로 접근하는 방법은 두 가지입니다.
💡 ✔︎ 방법 1) public 인스턴스로 pem-key 파일을 전송한 뒤, public 인스턴스에서 그 pem-key를 이용하여 연결하기
✔︎ 방법 2) 로컬에서 ProxyJump로 public 인스턴스에서 바로 private 인스턴스로 접근하기팀 크루루가 선택한 방법은 두 번째 방법으로, ProxyJump를 하는 명령어는 아래와 같습니다.
ssh -J {호스트명}@{public_ip} -i {SSH_키_파일} {호스트명}@{private_ip}
-
EC2 인스턴스에 도커 설치 후 docker-compose.yml 파일 작성
먼저 EC2 인스턴스에 도커를 설치합니다(도커 설치 방법은 다루지 않겠습니다).
그 후, 두 개의 컨테이너에 Prometheus와 Grafana를 각각 띄우기 위해 아래와 같은 docker-compose.yml 파일을 작성합니다.
-
docker-compose.yml
services: prometheus: # 컨테이너 개별 실행을 가능케 하기 위해 profile 추가합니다. profiles: - prometheus container_name: "prometheus" restart: always image: prom/prometheus:latest volumes: - "./prometheus.yml:/etc/prometheus/prometheus.yml" ports: - 9090:9090
-
-
prometheus.yml 파일 작성
prometheus.yml 파일은** **Prometheus 서버의 설정 파일입니다. Prometheus가 수집할 메트릭과 관련된 설정을 포함합니다. 아래는 팀 크루루에서 작성한 부분 중 일부입니다.
scrape_configs: - job_name: 'cruru' metrics_path: '/{monitoring_base_path}/prometheus' scrape_interval: 10s static_configs: - targets: ['{public_server_private_ip}:{monitoring_port}']
Grafana 대시보드 구성
-
모니터링 서버에 Grafana docker container build 및 run
docker-compose-monitoring.yml
services: # ...prometheus... grafana: profiles: - grafana container_name: 'grafana' user: "$UID:$GID" image: grafana/grafana:latest restart: always ports: - '3000:3000' volumes: - ./grafana-data:/var/lib/grafana
-
Grafana 웹 접속
default username과 password는 모두
admin
입니다.(default username은 Docker Compose를 통해 build될 때 Grafana에서 지정한 키워드를 통해 변경할 수 있습니다.)
-
Data Sources 설정
왼쪽 사이드 바에서
Connections
에 들어가면 Data Sources를 설정할 수 있습니다.“prometheus”를 검색하여 [Add new data source]를 합니다.
Name을 임의로 지정하고, Connection에서 Prometheus의 서버 URL을 입력하고 [Save & test]를 하면 설정이 완료됩니다.
-
대시보드 설정
마찬가지로 왼쪽 사이드 바에서
Dashboards
에 들어간 후 [New - New Dashboard] 버튼을 누르면 대시 보드를 생성할 수 있습니다.위와 같이 세 가지 방법으로 대시보드를 생성할 수 있습니다.
- 직접 대시보드 커스터마이징
- 라이브러리 패널 추가
- grafana.com에서 제공하는 템플릿 import
grafana.com에서는 여러 대시보드 템플릿을 제공하고 있는데요, 팀 크루루는 이 중에서 “Spring Boot 2.1 System Monitor”를 기반으로 커스터마이징을 진행했습니다.
해당 템플릿 상세 페이지에서 확인할 수 있는 ID 값(11378)을 아래와 같이 ID 입력 칸에 입력 후 Load한 후 Import하면 대시보드 설정이 완료됩니다.
결과
이 대시보드를 통해 여러 metric을 확인할 수 있습니다.
기본적으로 서버 가동 시간, Heap 사용량, CPU 사용량 등 각종 실시간 리소스 사용량을 확인할 수 있습니다. 추가적으로는 JVM의 리소스 사용 및 가용 정보 등을 확인할 수도 있고, 더 나아가 각종 API 엔드포인트별 단위 시간당 요청 횟수 및 평균 처리 시간과 같은 세부 정보도 조회할 수 있습니다.
더 나아가
현재는 우선 grafana.com에서 제공되는 템플릿을 이용하여 메트릭 정보를 시각화하였습니다. 추후 모니터링 시스템이 좀 더 안정적으로 구축되면, Alert 규칙도 설정하고 크루루에 필요한 메트릭만 시각화할 수 있도록 보완해 갈 예정입니다.
다음 글에서는 Log 모니터링 구축기를 다뤄 볼 예정입니다.