프로젝트를 운영환경에서 돌리면서, 에러가 발생했을 때, 예상된 응답이 나오지 않을 때 등 로그를 확인해야하는 상황은 매우 빈번하고 다양하다. 지금까지는 EC2에 직접 접근하여 로그를 확인했지만, 모니터링 대시보드를 활용하면 프로그램의 상태와 문제점을 시각적으로 표현하여 실시간으로 모니터링할 수 있다. 또한, 문제가 발생했을 때 신속하게 조치할 수 있으므로, 이번 프로젝트에 적용해보고자 한다.
1. 로그
로그는 시스템 내부에서 발생하는 이벤트와 정보를 기록한 데이터로, 이 데이터를 수집하고 분석하면 서비스의 문제를 식별하고 해결하는 데 도움이 된다. 로그 모니터링 도구로는 AWS openSearch 를 사용하고자 한다.
2. 로그 모니터링 대시보드 구현
ElasticSearch가 아닌 AWS openSearch를 사용한 이유는, 아무래도 사이드 프로젝트이다보니 리소스를 최대한 적게 가져가기 위해서이다. ElasticSearch는 자체 호스팅 버전만 무료이기 때문에, EC2에 ElasticSearch를 띄우면 운영 서버에 부담이 갈 것이라고 생각했다. 또한 OpenSearch Dashboards를 기반으로 Kibana 없이도 모니터링 대시보드를 구현할 수 있다.
2-1. logback-spring.xml 구현
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
<property name="LOG_FILE_LOCATION" value="/app/logs"/>
<property name="LOG_FILE_NAME" value="ctrlu.log"/>
<appender name="FILE_JSON" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_FILE_LOCATION}/${LOG_FILE_NAME}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_FILE_LOCATION}/archived/${LOG_FILE_NAME}.%d{yyyy-MM-dd}.gz</fileNamePattern>
<maxHistory>30</maxHistory> </rollingPolicy>
<-- file에 저장되는 로그은 json으로 인코딩하여 opeansearch가 파싱하기 쉽도록 -->
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<includeMdc>true</includeMdc> <fieldNames>
<timestamp>@timestamp</timestamp> <version>[ignore]</version> </fieldNames>
<customFields>{"application":"ctrlu-app", "environment":"production"}</customFields>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/> <appender-ref ref="FILE_JSON"/> </root>
<logger name="com.example.myapp" level="DEBUG"/> </configuration>
* logstash-logback-encoder 라이브러리 추가 필요
dependencies {
implementation 'net.logstash.logback:logstash-logback-encoder:7.4'
}
이렇게 로컬 파일에 저장을 하게 되면 우리는 도커 이미지를 통해 배포를 하고 서비스를 운영하기 때문에 실제로는 ec2 instance 에 로그파일이 쌓이는 것이 아니고, 도커 이미지 안에 위치하게 된다. 그렇기 때문에, 도커 volume 을 통해서 파일을 공유해야한다.
backend:
image: foureach/ctrlu-backend:latest
container_name: prod-spring-app
restart: unless-stopped
env_file:
- .env.prod
networks:
- backend-net
volumes:
# 호스트의 /var/log/ctrlu-app-logs 디렉토리를 컨테이너의 /app/logs 디렉토리로 마운트
- /var/log/ctrlu-app-logs:/app/logs
2-2. IAM 역할 생성 및 적용
Filebeat가 OpeanSearch Service에 데이터를 쓸 수 있는 권한을 정의하는 IAM 정책을 생성한다.

Filebeat가 실행될 AWS 자원(EC2 인스턴스)이 사용할 IAM 역할을 생성하고, 위에서 만든 정책을 연결한다.

EC2 인스턴스에 IAM 역할 적용

2-3. OpeanSearch 도메인 생성

2-4. FileBeat 생성
EC2에 filebeat 설치 후 설정 파일을 작성한다. 그 후 filebeat를 실행하면서 제대로 작동하는지 확인한다.

트러블 슈팅: /var/logs/ctrlu-app-logs/ctrlu.log 가 존재하지 않음
- 문제 상황
ubuntu@ip-172-31-41-232:/$ sudo tail -f /var/log/ctrlu-app-logs/ctrlu.log tail: cannot open '/var/log/ctrlu-app-logs/ctrlu.log' for reading: No such file or directory
- 해결 과정
1. 도커 내에 로그 파일이 생성 되는지 확인
ubuntu@ip-172-31-41-232:/var/log/ctrlu-app-logs$ docker exec -it prod-spring-app bash bash-4.4# ls -l /app/logs/ total 24 -rw-r--r-- 1 root root 24392 Jul 1 09:58 ctrlu.log-> 생성됨
2. 새로운 내용이 로그 파일에 추가 되는지 확인
-> 추가안됨bash-4.4# tail -f /app/logs/ctrlu.log
- 해결
JWT 에러 발생 시, 로그를 출력안하고 있었다.. 로그 출력문 추가!
log.warn("인증/인가 오류 발생: {}", message);
2-5. 대시보드 SSH 터널링
현재 OpeanSearch 도메인이 VPC 안에 있어서 외부에서 직접 접근이 불가하다. 따라서 VPC 내부의 EC2 인스턴스를 점프 서버로 활용하여 OpenSearch 대시보드에 접근하고자 한다.
ssh -i <pem키 위치> ubuntu@<ec2 퍼블릭 ip 주소> -N -L 9200:<대시보드 url>:443
** 인바운드 규칙 설정
- EC2 인스턴스 보안 그룹의 아웃바운드 443번 포트(HTTPS)를 OpenSearch 도메인의 보안 그룹 ID로 열기
- OpenSearch 도메인 보안 그룹의 인바운드 443번 포트(HTTPS)를 EC2 인스턴스의 보안 그룹 ID로 열기
구축은 다음 블로그 참고!
https://coding-today.tistory.com/168
OpenSearch Dashboard Index Patterns 생성
OpenSearch에서 데이터를 조회하고 시각화할 때 사용하는 Index들의 구조를 정의하는 설정*자세한 설명 생략 ▷ Index Patterns 생성 ① Opensearch Dashboard 접속*localhost:5601*OpenSearch 설치 시 설정한 비밀
coding-today.tistory.com
트러블 슈팅: OpenSearch에 로그 정보가 도착하지 않음
- 문제 상황

-> 화면 하단에 Your index pattern can match any of your 5 sources. 아래에 나열된 인덱스들은 모두 샘플 데이터나 다른 OTel(OpenTelemetry) 관련 인덱스들이다.. spring-app-logs-* 패턴의 인덱스가 없다.
- 해결 과정
1. Filebeat 실시간 로그 확인
ubuntu@ip-172-31-41-232:/var/log$ susystail -f /var/log/filebeat/filebeat.log
tail: cannot open '/var/log/filebeat/filebeat.log' for reading: No such file or directory
tail: no files remaining
-> 없다..
2. filebeat.yml을 더미 데이터 로그 파일을 읽고, output 또한 터미널 내에서 직접 확인할 수 있도록 수정
# Filebeat 입력 설정: 임시 더미 파일을 읽도록 합니다.
filebeat.inputs:
- type: filestream # 'log' 대신 'filestream'을 권장합니다. (Filebeat 로그에 경고가 뜨지 않음)
id: dummy-test-input
enabled: true
paths:
- /tmp/dummy.log # 이 경로에 더미 로그 파일을 생성할 것입니다.
# JSON 로그이므로 관련 설정을 유지합니다.
json.keys_under_root: true
json.overwrite_keys: true
json.add_error_key: true
# Filebeat 출력 설정: OpenSearch 대신 콘솔로 출력합니다.
# 이렇게 하면 Filebeat가 무엇을 읽고 보내는지 journalctl에서 직접 확인할 수 있습니다.
output.console:
pretty: true # 콘솔 출력을 보기 좋게 포맷합니다.
codec.json:
pretty: true # JSON 로그를 보기 좋게 포맷합니다.
# Filebeat 자체 로깅 설정 (이 부분이 제대로 작동하는지 확인해야 합니다.)
logging.level: debug # 디버그 레벨로 설정하여 Filebeat의 내부 동작에 대한 자세한 로그를 확인합니다.
logging.to_files: true # 로그를 파일에 기록하도록 설정합니다.
logging.files:
path: /var/log/filebeat # Filebeat 로그 파일이 저장될 디렉토리
name: filebeat.log # Filebeat 로그 파일 이름
keepfiles: 7 # 로그 파일 보관 개수
permissions: 0644 # 로그 파일 권한 (소유자 읽기/쓰기, 그룹/기타 읽기)
->아래 명령어 실행 시, output은 성공적으로 되는 걸 볼 수 있지만, 여전히 filebeat.log은 생성되지 못하고 있다..
ubuntu@ip-172-31-41-232:/lib/systemd/system$ sudo journalctl -u filebeat -xe --since "10 minute ago"
- 해결
/var/log/filebeat/filebeat.log가 존재하지 않는 이유는 구글링해서 도저히 찾을 수 없었다..
따라서 filebeat 대신 로그 수집 및 전송이 가능하고, filebeat와 유사하게 경량화되어 있고 효율적인 fluent bit를 사용했다.
2-6. 대시보드 구축
인덱스 패턴 생성

확인 성공!

3. 디스코드와 연결
로그 레벨이 WARN 혹은 ERROR 인 경우, 디스코드에 알림이 오도록 설정하고자 한다.
- Discord 웹훅 생성: 알림 받을 채널에서 웹훅을 만들고 URL을 복사한다. (URL 끝에 ?wait=true 추가)
- OpenSearch 채널 등록: Dashboards의 Notifications > Channels에서 Custom webhook 타입으로 Discord 웹훅 URL을 등록한다.
- 모니터 생성: Alerting > Monitors에서 메시지 포맷 등의 설정을 완료하여 모니터를 생성한다.

'Coding > 사이드 플젝' 카테고리의 다른 글
| [CtrlU] 메트릭 모니터링 대시보드 구축 (0) | 2025.08.24 |
|---|---|
| [배달뚝배기] 인덱스와 배치를 적용한 성능 최적화 (4) | 2025.08.20 |
| [팔레트] 스프링 시큐리티와 Refresh Token 제대로 알고 사용하기 (0) | 2025.05.24 |
| [CtrlU] 아키텍처 (feat. Docker, Redis, 테스트 인스턴스) (0) | 2025.05.03 |
| [뚝배기] 동시성 제어 (비관적 락, 낙관적 락) (2) | 2025.04.27 |