Backend
home

AWS에서 ELK + Grafana + Prometheus 배포

생성 일시
2025/07/22 14:54
태그
AWS
게시일
2025/07/22
최종 편집 일시
2025/07/22 17:08

ELK 스펙 (t3.large)

vCPU: 2
Memory: 8 GB
Ubuntu 22.04
참고로 EC2 보안 그룹에서 다음 포트를 인바운드 허용해주어야 한다~!
5601 (Kibana)
9200 (Elasticsearch)
5044 (Logstash input)
9090 (Prometheus)
3000 (Grafana)

docker 설치

sudo apt-get update sudo apt-get install ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc # Add the repository to Apt sources: echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin ubuntu@ip-172-31-34-164:~$ sudo usermod -aG docker $USER ubuntu@ip-172-31-34-164:~$ newgrp docker ubuntu@ip-172-31-34-164:~$ docker info
Shell
복사

작업 디렉토리 구성

monitoring/ ├── docker-compose.monitoring.yml ├── logstash/ │ ├── logstash.conf │ └── logstash.yml ├── prometheus/ │ └── prometheus.yml ├── volumes/ │ ├── elasticsearch/ │ ├── kibana/ │ ├── grafana/ │ └── prometheus/ └── logs/ <-- 백엔드 서버 로그와 연결될 폴더
Shell
복사
항목
내용
네트워크
별도 custom network 제거 (EC2는 bridge 기본 사용 가능)
logs 공유
../../logs:/logs 경로 → 백엔드 로그 경로에 맞게 수정 필요
restart 정책
필요 시 restart: unless-stopped 옵션 추가 가능
버전
Elasticsearch, Kibana, Logstash는 8.12.0로 설정
볼륨
./volumes/서비스명으로 로컬 persist 데이터 구성
mkdir -p ~/monitoring cd ~/monitoring monitoring 디렉토리 구조 docker-compose.monitoring.yml logs logstash prometheus
Shell
복사

docker-compose.yml 파일 생성

monitoring 디렉토리에서 docker-compose.monitoring.yml 생성
elasticsearch, kibana, logstash 모두 8.12.0 으로 지정 (hanhinsam-0.1 사용 시 버전 호환)
version: '3.8' services: # Elasticsearch elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:8.12.0 container_name: elasticsearch environment: - discovery.type=single-node - node.name=es01 - xpack.security.enabled=false - bootstrap.memory_lock=true - ES_JAVA_OPTS=-Xms1g -Xmx1g # ES 메모리 제한 - TZ=Asia/Seoul ulimits: memlock: soft: -1 hard: -1 volumes: - ./volumes/elasticsearch:/usr/share/elasticsearch/data - /etc/localtime:/etc/localtime:ro # 한국 시간 설정 ports: - "9200:9200" - "9300:9300" # Kibana kibana: image: docker.elastic.co/kibana/kibana:8.12.0 container_name: kibana environment: - ELASTICSEARCH_HOSTS=http://elasticsearch:9200 - TZ=Asia/Seoul ports: - "5601:5601" depends_on: - elasticsearch volumes: - ./volumes/kibana:/usr/share/kibana/data - /etc/localtime:/etc/localtime:ro # 한국 시간 설정 # Logstash logstash: image: docker.elastic.co/logstash/logstash:8.12.0 container_name: logstash environment: - LS_JAVA_OPTS=-Xms512m -Xmx512m # Logstash 메모리 제한 - TZ=Asia/Seoul volumes: - ./logstash/logstash.conf:/usr/share/logstash/pipeline/logstash.conf - ./logstash/logstash.yml:/usr/share/logstash/config/logstash.yml:ro - /etc/localtime:/etc/localtime:ro # 한국 시간 설정 - /home/ubuntu/moodbook/logs:/logs # 백엔드 로그 경로를 공유 (host 경로에 맞게 수정 필요) ports: - "5044:5044" - "5001:5001" depends_on: - elasticsearch # Prometheus prometheus: image: prom/prometheus:latest container_name: prometheus environment: - TZ=Asia/Seoul volumes: - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml - ./volumes/prometheus:/prometheus - /etc/localtime:/etc/localtime:ro # 한국 시간 설정 mem_limit: 256m # 메모리 제한 restart: always ports: - "9090:9090" # Grafana grafana: image: grafana/grafana:latest container_name: grafana environment: - TZ=Asia/Seoul volumes: - ./volumes/grafana:/var/lib/grafana - /etc/localtime:/etc/localtime:ro # 한국 시간 설정 mem_limit: 256m # 메모리 제한 restart: always ports: - "3000:3000" depends_on: - prometheus 1,1 Top
YAML
복사

Logstash 파이프라인 설정

파일 경로: ~/monitoring/logstash/logstash.conf
mkdir -p logstash
Shell
복사
logstash.conf 작성
input { beats { port => 5044 # filebeat 적용 codec => json # ElasticSearch 인덱스를 json 형태로 구성했다면 꼭 이것을 적용 } } filter { #아래 규칙에서 자동으로 키워드로 형성되어 키바나에서 확인 가능 grok { match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} \[%{DATA:thread}\] \[%{LOGLEVEL:level}\s*\] %{JAVACLASS:logger} - %{GREEDYDATA:log}" } } # 로그가 AOP 로그인지 식별 if "AOP_LOG" in [message] { mutate { add_field => { "log_type" => "aop" } } } else if "OAuth2_LOG" in [message]{ mutate { add_field => { "log_type" => "OAuth2" } } } date { match => ["timestamp", "yyyy-MM-dd HH:mm:ss.SSS"] timezone => "Asia/Seoul" target => "@timestamp" } } output { elasticsearch { hosts => ["http://ELK의 EC2 IP:9200"] index => "moodbook-log-%{+YYYY.MM.dd}" } stdout { codec => rubydebug } }
Shell
복사
logstash.yml 생성
http.host: "0.0.0.0" xpack.monitoring.enabled: false
Shell
복사

Prometheus 설정 파일 작성

파일 경로: ~/monitoring/prometheus/prometheus.yml
global: scrape_interval: 15s # 15초마다 메트릭 수집 scrape_configs: - job_name: 'moodbook-server' metrics_path: '/actuator/prometheus' static_configs: - targets: ['Spring Boot IP:8080'] # Spring Boot의 Public IP
Shell
복사

Filebeat 설치 (SpringBoot EC2에서 진행)

현재 SpringBoot EC2와 ELK EC2가 다른 환경에 있으므로 Filebeat 를 설치하여 app.log 파일을 Logstash가 SpringBoot EC2로 전송하도록 설정
[백엔드 EC2] [ELK EC2] app.log → Filebeat → → → Logstash → Elasticsearch → Kibana
Shell
복사
구성 요소
설치 위치
설명
Spring Boot (백엔드)
EC2-A
app.log 생성
Filebeat
EC2-A (같은 서버)
로그 수집 및 전달
Logstash
EC2-B
수신 후 Elasticsearch로 전달
Elasticsearch/Kibana
EC2-B
시각화 및 저장

GPG키 및 Elastic 저장소 추가

curl -fsSL https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo gpg --dearmor -o /usr/share/keyrings/elastic-keyring.gpg echo "deb [signed-by=/usr/share/keyrings/elastic-keyring.gpg] https://artifacts.elastic.co/packages/8.x/apt stable main" | sudo tee /etc/apt/sources.list.d/elastic-8.x.list
Shell
복사

패키지 업데이트

sudo apt update sudo apt install filebeat -y
Shell
복사

filebeat.yml 설정 수정

sudo vi /etc/filebeat/filebeat.yml
Shell
복사
filebeat.inputs: - type: log enabled: true paths: - /home/ubuntu/moodbook-backend/logs/app.log # 📌 로그 경로 수정 (절대 경로로) output.logstash: hosts: ["ELK EC2 IP:5044"] # 📌 Logstash EC2의 Public IP # optional: 로그 수집 시작 위치 (처음부터) filebeat.registry.flush: 1m
Shell
복사

FileBeat 테스트 실행

단 logstash가 ELK EC2에서 컨테이너로 실행 중인 상태여야 한다.
성공 시: logstash: xx.xx.xx.xx:5044 ... OK
sudo filebeat test output
Shell
복사
logstash: xx.xx.xx.xx:5044... connection... OK
Shell
복사

Filebeat 서비스 시작 및 자동 시작 등록

sudo systemctl enable filebeat sudo systemctl start filebeat
Shell
복사
인스턴스
포트
설명
Logstash EC2 (xx.xx.xx.xx)
5044
Filebeat에서 수신 가능하도록 허용
Spring Boot EC2 (xx.xx.xx.xx)
-
송신이므로 별도 설정 없음

만약에 filebeat.yml 을 잘못 입력해서 수정을 했을 경우 수정 후 다음 명령어 실행

sudo filebeat test output # 정상 출력 logstash: xx.xx.xx.xx:5044... connection... OK # 수정 완료 후 filebeat 재시작 sudo systemctl restart filebeat # 로그 흐름 확인 sudo tail -f /var/log/filebeat/filebeat.log
Shell
복사

로그 흐름 확인

sudo tail -f /var/log/filebeat/filebeat.log
Shell
복사

docker 참고 명령어 (재시작, 포트 확인 여부) - 참고만!

docker-compose -f docker-compose.monitoring.yml restart logstash sudo lsof -i -P -n | grep LISTEN | grep 5044
Shell
복사

SpringBoot - build.gradle + application.yml + application-prod.yml + .env

build.gradle 에 다음을 추가
// Spring Boot Actuator 매트릭/모니터링 implementation 'org.springframework.boot:spring-boot-starter-actuator' // 프로메테우스 implementation 'io.micrometer:micrometer-registry-prometheus' // ElasticSearch implementation 'org.springframework.boot:spring-boot-starter-data-elasticsearch' implementation 'co.elastic.clients:elasticsearch-java'
Shell
복사
application.yml
spring: mail: host: smtp.gmail.com port: 587 username: test@gmail.com password: 1234 properties: mail: smtp: auth: true starttls: enable: true profiles: default: prod # 기본 실행 프로필 지정 server: port: 8080
Shell
복사
application-prod.yml
spring: datasource: url: jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_NAME}?serverTimezone=Asia/Seoul username: ${DB_USER} password: ${DB_PASSWORD} jpa: generate-ddl: true open-in-view: false show-sql: true hibernate: ddl-auto: update batch: jdbc: initialize-schema: never job: enabled: false sql: init: mode: never elasticsearch: uris: "ELK EC2 IP 입력" data: mongodb: uri: ${MONGODB_URI} database: ${MONGODB_DATABASE} redis: host: ${REDIS_HOST} port: ${REDIS_PORT} timeout: 6000 management: endpoints: web: exposure: include: "*" health: show-details: always health: elasticsearch: enabled: false prometheus: metrics: export: enabled: true cloud: aws: s3: bucketName: ${AWS_S3_BUCKET_NAME} stack: auto: false region: static: ${AWS_REGION} credentials: access-key: ${AWS_ACCESS_KEY} secret-key: ${AWS_SECRET_KEY} aladin: api: key: ${ALADIN_API_KEY} base-url: ${ALADIN_BASE_URL} jwt: secretKey: ${JWT_SECRET} access-token-expiration-ms: 1800000 refresh-token-expiration-ms: 6048020000 openai: api-key: ${OPENAI_API_KEY} url: ${OPENAI_URL} model: ${OPENAI_MODEL} logging: pattern: console: ${LOG_CONSOLE_PATTERN}
Shell
복사
.env (실제 값 입력 - RDS + ElasticCache 엔드포인트 주소 및 계정 + Redis + 각종 설정들)
해당 .env 파일은 Jenkins 배포 시 Credential의 Secret 파일 업로드로 처리
System > global 에서 진행!
# Docker PROJECT_NAME= APP_PORT= # MySQL DB_HOST= DB_PORT= DB_NAME= DB_USER= DB_PASSWORD= # MongoDB MONGODB_URI= MONGODB_DATABASE= # Redis REDIS_HOST= REDIS_PORT= # AWS S3 AWS_S3_BUCKET_NAME= AWS_REGION= AWS_ACCESS_KEY= AWS_SECRET_KEY= # JWT JWT_SECRET= # Aladin API ALADIN_API_KEY= ALADIN_BASE_URL= # OpenAI OPENAI_API_KEY= OPENAI_URL= OPENAI_MODEL= # env LOG_CONSOLE_PATTERN= # ElasticSearch ELASTICSEARCH_URI=
Shell
복사

백엔드 프로젝트 쪽 Logstash(src > main > resources)

Logstash 관련 logback-spring.xml 작성 (로그 저장)
<?xml version="1.0" encoding="UTF-8" ?> <configuration> <!-- 로그 파일이 저장될 경로 변수 --> <property name="LOG_PATH" value="/app/logs" /> <!-- 파일 로그 출력 패턴: 색상 X (ELK 연동용) --> <property name="FILE_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%-3level] %logger{5} - %msg%n" /> <!-- 운영 환경(prod) 전용 로그 설정 --> <!-- 콘솔 출력 없이 파일(app.log)에만 로그가 기록됨 --> <springProfile name="prod"> <root level="INFO"> <appender-ref ref="FILE"/> </root> </springProfile> <!-- ========================== 파일 로그 설정 (ELK 연동용) ========================== --> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- RollingFileAppender: 로그를 파일로 남김, 일자별로 파일 자동 분할 --> <file>${LOG_PATH}/app.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${LOG_PATH}/app-%d{yyyy-MM-dd}.log</fileNamePattern> <maxHistory>10</maxHistory> <!-- : 10일치까지만 파일 보관(자동 삭제) --> </rollingPolicy> <encoder> <Pattern>${FILE_PATTERN}</Pattern> </encoder> </appender> <!-- ========================== 콘솔(터미널) 로그 설정 ========================== --> <!-- 콘솔(터미널) 출력 패턴: 색상 O --> <!-- thread <- 현재 로그를 찍은 스레드 이름 level <- 로그 레벨 (INFO, WARN, ERROR) logger <- 로그를 찍은 클래스의 이름 msg <- 로그 메세지 --> <!-- 콘솔(터미널)에 출력할 때 사용할 패턴 (색상 적용) --> <property name="CONSOLE_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %magenta([%thread]) %highlight([%-3level]) %logger{5} - %msg %n" /> <!-- 개발 환경(dev) 전용 로그 설정 --> <!-- 파일로 로그 기록 없이, 콘솔(터미널)에만 로그 출력 --> <!-- <springProfile name="dev">--> <!-- <root level="DEBUG">--> <!-- <appender-ref ref="STDOUT"/> &lt;!&ndash; STDOUT 이름을 가진 appender로 로그 전송 &ndash;&gt;--> <!-- </root>--> <!-- </springProfile>--> <!-- 터미널에 출력하는 Appender 설정 (STDOUT) --> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <Pattern>${CONSOLE_PATTERN}</Pattern> </encoder> </appender> <!-- 콘솔 로그 비동기 처리(성능 최적화, 버퍼링) 아래 ASYNC는 위 STDOUT 출력을 비동기로 최적화(성능 향상) --> <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender"> <appender-ref ref="STDOUT"/> </appender> <!-- ========================== 패키지별 로그 레벨 지정 ========================== --> <!--(INFO, WARN, ERROR도 모두 출력)--> <!-- 특정 패키지(여기서는 org.boot.backend5project)만 따로 레벨 지정 가능 additive="false"면, 해당 패키지 로그는 상위(root)에 전달되지 않음 --> <logger name="org.com.moodbook" level="DEBUG, INFO, WARN, ERROR" additive="false" > <appender-ref ref="STDOUT"/> </logger> <!-- ========================== 전체(Global) 로그 설정 ========================== --> <!-- <root level="DEBUG">--> <!-- <root level="TRACE">--> <!-- <root level="INFO">--> <root level="INFO"> <appender-ref ref="ASYNC"/> <!-- 콘솔(터미널) 로그 비동기 처리 --> <appender-ref ref="FILE"/> <!-- 파일 로그(ELK 연동) --> </root> <!-- 로그 하나가 발생하면, 콘솔에도 찍히고 파일에도 기록됨(동시) --> <!-- 참고: springProfile 태그를 쓰면 <springProfile name="prod">, <springProfile name="dev"> 환경별로 다른 로그정책 적용 가능 --> </configuration>
Shell
복사

백엔드 쪽 docker-compose.yml

백엔드 프로젝트 쪽의 docker-compose.backend.yml 파일
version: '3.8' services: backend: container_name: ${PROJECT_NAME} build: context: . dockerfile: Dockerfile image: moodbook_backend ports: - "${APP_PORT}:${APP_PORT}" environment: - SPRING_PROFILES_ACTIVE=prod - JAVA_TOOL_OPTIONS=-Xms512m -Xmx512m # JVM 메모리 관리 - TZ=Asia/Seoul # 한국 시간 설정 # MySQL - DB_HOST=${DB_HOST} - DB_PORT=${DB_PORT} - DB_NAME=${DB_NAME} - DB_USER=${DB_USER} - DB_PASSWORD=${DB_PASSWORD} # ElasticSearch - ELASTICSEARCH_URI=${ELASTICSEARCH_URI} # Logstash - LOG_CONSOLE_PATTERN=${LOG_CONSOLE_PATTERN} # MongoDB - MONGODB_URI=${MONGODB_URI} - MONGODB_DATABASE=${MONGODB_DATABASE} # Redis - REDIS_HOST=${REDIS_HOST} - REDIS_PORT=${REDIS_PORT} # AWS S3 - AWS_S3_BUCKET_NAME=${AWS_S3_BUCKET_NAME} - AWS_REGION=${AWS_REGION} - AWS_ACCESS_KEY=${AWS_ACCESS_KEY} - AWS_SECRET_KEY=${AWS_SECRET_KEY} # JWT - JWT_SECRET=${JWT_SECRET} # Aladin API - ALADIN_API_KEY=${ALADIN_API_KEY} - ALADIN_BASE_URL=${ALADIN_BASE_URL} # OpenAI - OPENAI_API_KEY=${OPENAI_API_KEY} - OPENAI_URL=${OPENAI_URL} - OPENAI_MODEL=${OPENAI_MODEL} volumes: - /etc/localtime:/etc/localtime:ro # 한국 시간 설정 - /home/ubuntu/moodbook/logs:/app/logs restart: always
Shell
복사
/app/logs는 컨테이너 안의 경로고, logback에서는 상대 경로 logs/app.log/app/logs/app.log로 매핑된다.
로그 파일 쓰기 권한 확인
호스트 경로인 /home/ubuntu/moodbook/logs존재하며, 권한도 열려 있어야 한다.
권한 설정
sudo mkdir -p /home/ubuntu/moodbook/logs sudo chown -R 1000:1000 /home/ubuntu/moodbook/logs # 1000은 보통 ubuntu 사용자 uid sudo chmod -R 755 /home/ubuntu/moodbook/logs
Shell
복사

Docker 컨테이너 내부에서 로그 경로 유효한지 확인

docker exec -it [컨테이너이름] /bin/bash ls -al logs cat logs/app.log
Shell
복사

logstash 컨테이너의 마운트 경로가 호스트와 동일한지 확인

logstash: ... volumes: - /home/ubuntu/moodbook/logs:/logs
Shell
복사
→ 이때 /logs/app.log가 컨테이너 내에서 존재해야 함
logstash쪽 docker-compose.monitoring.yml 쪽에 다음과 같이 되어 있어야 함

컨테이너 실행

docker compose -f docker-compose.monitoring.yml up -d
Shell
복사

에러 확인

현재 컨테이너 상태를 보면 일부 서비스는 실행(Up) 중이지만, 일부는 비정상 종료(Exited) 상태
NAME
STATUS
비고
logstash
Up
정상 실행 중
grafana
Exited (1)
비정상 종료
kibana
Exited (2)
비정상 종료
prometheus
Exited (2)
비정상 종료
elasticsearch
Exited (1)
비정상 종료
에러 로그 확인
docker logs grafana docker logs kibana docker logs prometheus docker logs elasticsearch
Shell
복사
호스트 디렉토리 권한 수정
# 472는 Grafana 공식 이미지에서 사용하는 UID sudo chown -R 472:472 ./volumes/grafana
Shell
복사
이후 다시 실행:
docker compose -f docker-compose.monitoring.yml up -d
Shell
복사

볼륨 초기화 (초기 개발 환경인 경우)

아예 볼륨 디렉토리 삭제 후 다시 생성 - 이게 제일 확실한 방법! (Grafana)
sudo rm -rf ./volumes/grafana mkdir -p ./volumes/grafana sudo chown -R 472:472 ./volumes/grafana
Shell
복사
Grafana 외에 Prometheus, ElasticSearch 등도 비슷한 퍼미션 이슈가 발생할 수 있기에 이런 식으로 명령어를 입력하여 퍼미션 이슈를 해결해주는 게 좋음
sudo chown -R 1000:1000 ./volumes/prometheus # Prometheus UID sudo chown -R 1000:1000 ./volumes/elasticsearch # Elasticsearch UID sudo chown -R 1000:1000 ./volumes/kibana
Shell
복사

모든 컨테이너 빌드 시 이슈 발생하는 경우

기존 컨테이너 중지 및 정리
docker compose -f docker-compose.monitoring.yml down
Shell
복사
퍼미션 문제 재발 방지 (docker-compose.yml 실행하기에 앞서 이 방법을 추천!) ⇒ 컨테이너 빌드하기 전에 이 명령어를 입력해주는 게 가장 확실한 방법이다!
# Kibana sudo chown -R 1000:1000 ./volumes/kibana # Elasticsearch sudo chown -R 1000:1000 ./volumes/elasticsearch # Grafana sudo chown -R 472:472 ./volumes/grafana # Prometheus sudo chown -R 1000:1000 ./volumes/prometheus
Shell
복사
다시 실행
docker compose -f docker-compose.monitoring.yml up -d
Shell
복사
수정 후 컨테이너 상태 확인 및 개별 컨테이너 로그로 진행 상태 확인
docker ps -a docker logs kibana docker logs grafana docker logs elasticsearch
Shell
복사
만약에 프로메테우스에서 다음과 같은 이슈가 발생하는 경우
Error opening query log file" component=activeQueryTracker file=/prometheus/queries.active err="open /prometheus/queries.active: permission denied" panic: Unable to create mmap-ed active query log
Shell
복사
다음과 같이 명령어 입력함
sudo chown -R 65534:65534 ./volumes/prometheus
Shell
복사
그 이후 컨테이너 재시작
docker compose -f docker-compose.monitoring.yml down docker compose -f docker-compose.monitoring.yml up -d
Shell
복사
추가적으로 Prometheus는 반드시 Spring Security에서 Spring Boot Actuator 보안 정책으로 인해 기본적으로 엔드포인트에 보안이 걸려 있음. /actuator/prometheus도 마찬가지이며, Prometheus는 인증 없이 접근하므로 403 오류가 발생
— application.yml 에서 접근 허용 설정 —
management: endpoints: web: exposure: include: health,info,prometheus endpoint: prometheus: enabled: true spring: security: permit-all: true # 보안 설정을 완전히 열어야 하는 경우 management: server: port: 8080 endpoints: web: exposure: include: '*' security: enabled: false
Shell
복사
SecurityConfig 에 적용
백엔드 프로젝트 배포 시 적용됨
@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(authorize -> authorize .requestMatchers("/actuator/**").permitAll() .anyRequest().authenticated() ) .csrf().disable(); return http.build(); } }
Java
복사

Filebeat 테스트

이 명령은 실행 중 로그를 콘솔에 출력
app.log 경로에 로그가 쌓이면 실시간으로 전송됨
sudo filebeat -e
Shell
복사

Logstash 로그 확인 (ELK EC2 에서)

docker logs logstash
Shell
복사

Kibana 접속

http://EC2 ELK IP:5601
Shell
복사

Kibana Index Pattern 등록

1.
"Management" > "Stack Management" > "Index Patterns"
2.
filebeat-* 또는 moodbook-log-* 입력
3.
생성 후 "Discover"에서 로그 확인

메모리 이슈

각 서버별로 남은 메모리가 부족하여 서버가 다운되는 것을 방지하기 위해 메모리 최적화를 진행함

ELK + Grafana + Prometheus

위험 항목
설명
영향
free 메모리 173MiB
컨테이너 부하 증가 시 Out of Memory 발생 가능
Logstash, Elasticsearch 메모리 부족 시 크래시 가능
Logstash JVM 설정 미조정
기본 설정이 1GB 이상 요청
Logstash 과부하 시 느려짐 또는 종료
Elasticsearch + Logstash + Prometheus + Grafana 동시 구동
매우 무거운 조합
EC2 t3.medium 이상 권장
swap 설정 여부 확인 및 생성
sudo fallocate -l 2G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile free -h # swap 생겼는지 확인
Java
복사
docker-compose.monitoring.yml 수정
services: elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:8.12.0 container_name: elasticsearch environment: - discovery.type=single-node - ES_JAVA_OPTS=-Xms1g -Xmx1g # ✅ 메모리 제한 추가 ... logstash: image: docker.elastic.co/logstash/logstash:8.12.0 container_name: logstash environment: - LS_JAVA_OPTS=-Xms512m -Xmx512m # ✅ 메모리 제한 추가 ... prometheus: image: prom/prometheus:latest container_name: prometheus mem_limit: 256m # ✅ 옵션 (compose v3 이상 지원) ... grafana: image: grafana/grafana:latest container_name: grafana mem_limit: 256m ...
YAML
복사
메모리 제한을 지정하지 않으면?
컨테이너가 필요 이상으로 메모리를 잡아먹어 전체 서버가 느려지거나 **OOM-Kill(메모리 초과 종료)**가 발생할 수 있음.
swap 파일 설정 (메모리 부족 방지용)
sudo fallocate -l 2G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab free -h # Swap 생겼는지 확인 ===================================================================== ubuntu@ip-172-31-35-108:~$ free -h total used free shared buff/cache available Mem: 3.7Gi 1.0Gi 133Mi 3.0Mi 2.6Gi 2.4Gi Swap: 0B 0B 0B ubuntu@ip-172-31-35-108:~$ sudo fallocate -l 2G /swapfile ubuntu@ip-172-31-35-108:~$ sudo chmod 600 /swapfile ubuntu@ip-172-31-35-108:~$ sudo mkswap /swapfile Setting up swapspace version 1, size = 2 GiB (2147479552 bytes) no label, UUID=e45f159b-4e86-4cbb-ae0b-48c5371121a4 ubuntu@ip-172-31-35-108:~$ sudo swapon /swapfile ubuntu@ip-172-31-35-108:~$ echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab /swapfile none swap sw 0 0 ubuntu@ip-172-31-35-108:~$ free -h # Swap 생겼는지 확인 total used free shared buff/cache available Mem: 3.7Gi 1.0Gi 131Mi 3.0Mi 2.6Gi 2.4Gi Swap: 2.0Gi 0B 2.0Gi
Shell
복사
기존 컨테이너 중지 및 재시작
# 기존 컨테이너 종료 docker-compose -f docker-compose.monitoring.yml down # 변경사항 반영하여 재실행 (필수!) docker-compose -f docker-compose.monitoring.yml up -d --build
Shell
복사
정상 동작 확인
docker ps -a docker stats
Shell
복사
docker stats 명령어 입력 후 실시간 메모리 현황 확인
springboot의 docker-compose.backend.yml 에도 적용
ubuntu@ip-172-31-18-181:~$ free -h total used free shared buff/cache available Mem: 7.6Gi 3.7Gi 173Mi 3.0Mi 3.8Gi 3.5Gi Swap: 0B 0B 0B ubuntu@ip-172-31-18-181:~$ sudo fallocate -l 2G /swapfile ubuntu@ip-172-31-18-181:~$ sudo chmod 600 /swapfile ubuntu@ip-172-31-18-181:~$ sudo mkswap /swapfile Setting up swapspace version 1, size = 2 GiB (2147479552 bytes) no label, UUID=51172bb2-ee03-4b25-b9b8-7f7f90a29ca2 ubuntu@ip-172-31-18-181:~$ sudo swapon /swapfile ubuntu@ip-172-31-18-181:~$ echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab /swapfile none swap sw 0 0 ubuntu@ip-172-31-18-181:~$ free -h total used free shared buff/cache available Mem: 7.6Gi 3.7Gi 190Mi 3.0Mi 3.8Gi 3.5Gi Swap: 2.0Gi 0B 2.0Gi
Shell
복사
Jenkins도 동일하게 적용
ubuntu@ip-172-31-40-214:~$ free -h total used free shared buff/cache available Mem: 3.7Gi 1.3Gi 802Mi 2.0Mi 1.7Gi 2.2Gi Swap: 0B 0B 0B ubuntu@ip-172-31-40-214:~$ sudo fallocate -l 2G /swapfile ubuntu@ip-172-31-40-214:~$ sudo chmod 600 /swapfile ubuntu@ip-172-31-40-214:~$ sudo mkswap /swapfile Setting up swapspace version 1, size = 2 GiB (2147479552 bytes) no label, UUID=e8d27023-dd62-43c3-affb-7c458c2aef2f ubuntu@ip-172-31-40-214:~$ sudo swapon /swapfile \ubuntu@ip-172-31-40-214:~$ free -h total used free shared buff/cache available Mem: 3.7Gi 1.3Gi 822Mi 2.0Mi 1.7Gi 2.2Gi Swap: 2.0Gi 0B 2.0Gi
Shell
복사

최종 결과

ELK
Grafana
Prometheus