-
Notifications
You must be signed in to change notification settings - Fork 5
백엔드 서버 모니터링 및 로깅
백엔드 서버는 nginx 를 통해 80/443 포트를 이용해 EC2 내부에서 8080 포트에서 실행중인 Spring Boot에 요청을 전달하는 구조다. 이 환경에서 발생하는 로그나 여러 매트릭을 확인하기 위해 모니터링과 로깅 시스템을 구축하기로 결정했다.
-
다음 항목들을 모니터링 할 수 있어야 한다.
- JVM 메모리 사용량
- CPU 사용량
- 커넥션, 스레드 풀 고갈
- error 레벨 로그 증가량 : 에러가 급증한다는 것은 뭔가 문제가 있다는 뜻이기 때문에
- 단위 시간 당 API 별 호출 횟수 : 어떤 기능을 사용자가 많이 사용하는지 파악하기 위해
- API 별 응답 시간 : 어떤 API를 최적화하는 것이 사용자에게 유리할지 파악하기 위해
- 로그를 시각화 대시보드에서 볼 수 있어야 한다.
- 모니터링 서버는 스프링 서버와 분리되어야 한다.
- 모니터링 및 로깅 정보는 외부에서 함부로 볼 수 없어야 한다.
우리가 모니터링하고자 하는 매트릭들은 모두 SpringBoot에서 발생하는 정보다. 따라서, Spring Boot에서 자신의 정보를 제공해야 한다. 이를 위해 Spring Boot Actuator를 사용하기로 했다. 아래 의존성을 Gradle에 추가했다. 공식 문서를 참고한다.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'io.micrometer:micrometer-registry-prometheus'
}
또한, Spring Boot Actuator가 제공하는 엔드포인트를 활성화하기 위해 다음 설정을 application.yml에 추가했다.
management:
server:
port: 8082
endpoints:
web:
exposure:
include: "prometheus,health"
모니터링 요구사항에서 외부에서 모니터링 정보를 접근할 수 없도록 해야 한다는 요구사항이 있었다. 이를 위해서 메트릭 정보를 제공하는 엔드포인트의 포트를 8082로 설정했다.
스프링 부트 서버와 모니터링 서버는 분리되어있어야 안정적으로 모니터링을 할 수 있기 때문에 분리하기로 결정했다. 이를 도식으로 나타내면 다음과 같다.
두 서버는 같은 서브넷에 속하고, 보안 그룹 설정에서 같은 서브넷에 속한 경우 포트 제한 없이 통신할 수 있도록 되어있다. 이것이 Spring Boot Actuator가 8082 포트로 정보를 제공할 수 있는 이유다.
Spring Boot Actuator가 제공하는 메트릭을 주기적으로 수집하기 위해 프로메테우스를 사용하기로 결정했다. 자료도 많고, Spring Boot가 이를 위한 엔드포인트를 따로 제공해주는 점에서 신뢰도가 높다고 판단했기 때문이다. 시각화 도구로는 그라파나를 사용하기로 했고 이 역시 많은 자료가 있고, 프로메테우스 공식 문서에서 그라파나와의 통합을 내세우고 있기 때문이었다. 이를 도식화 하면 다음과 같다.
프로메테우스와 그라파나의 손쉬운 설치를 위해 도커와 도커 컴포스를 사용했다. 손쉽게 둘을 순차적으로 재시작하거나 다른 서버로 확장할 수 있기 때문이다. 도커 컴포스 설정의 예시를 모아둔 레퍼지토리를 참고해 구축했다. 정확한 설정은 보안상의 문제를 고려해 공개하지 않는다.
모니터링과 로깅 시스템을 구축하는 시점에 아직 어떤 정보를 로그로 남길 것인지 정하는 정책이 불완전했다. 이는 기획 변경으로 인해 아직 기능 구현이 덜 되어있었기 때문이다. 따라서 지금 구축하는 로깅 시스템은 임시로 Spring Boot의 로그를 그라파나에서 볼 수 있도록 하는 것을 목표로 했다. 이를 위해 로키를 사용했다. 로키는 쉽게 말해 로그를 저장하는 것을 전문으로 하는 데이터베이스다. Spring Boot 웹 서비스에서는 기본적으로 LogBack을 이용해 로깅을 한다. 그리고 관련 설정을 따로 xml 로 작성하면, 그에 맞게 로그를 저장할 수 있다. 이 설정을 이용해 로그를 로그백에 저장하도록 설정했다. 아래는 설정 파일이다.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<springProperty scope="context" name="appName" source="spring.application.name"/>
<appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<charset>UTF-8</charset>
<pattern>
%d{yyyy-MM-dd HH:mm:ss.SSS} | %t | %highlight(%-5p) | %cyan(%logger{36}) | %m%n
</pattern>
</encoder>
</appender>
<appender name="LOKI" class="com.github.loki4j.logback.Loki4jAppender">
<http>
<url>http://10.0.0.35:3100/loki/api/v1/push</url>
</http>
<format>
<label>
<pattern>app=${appName},host=${HOSTNAME},traceID=%X{traceId:-NONE},level=%level</pattern>
</label>
<message>
<pattern>${FILE_LOG_PATTERN}</pattern>
</message>
<sortByTime>true</sortByTime>
</format>
</appender>
<springProfile name="local">
<root level="INFO">
<appender-ref ref="Console"/>
</root>
<logger name="com.sb" level="DEBUG"/>
<logger name="org.springframework.orm.jpa" level="DEBUG"/>
<logger name="org.springframework.orm.transaction" level="DEBUG"/>
<logger name="org.hibernate.orm.jdbc.bind" level="TRACE"/>
</springProfile>
<springProfile name="dev">
<root level="INFO">
<appender-ref ref="LOKI"/>
</root>
</springProfile>
</configuration>
로키를 도입한 뒤 상황을 도식으로 나타내면 다음과 같다.