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 경로 → 백엔드 로그 경로에 맞게 수정 필요 | |
필요 시 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"/> <!– STDOUT 이름을 가진 appender로 로그 전송 –>-->
<!-- </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
복사
로그 파일 쓰기 권한 확인
호스트 경로인 /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 | 정상 실행 중 | |
grafana | 비정상 종료 | |
kibana | 비정상 종료 | |
prometheus | 비정상 종료 | |
elasticsearch | 비정상 종료 |
•
에러 로그 확인
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 JVM 설정 미조정 | 기본 설정이 1GB 이상 요청 | |
Elasticsearch + Logstash + Prometheus + Grafana 동시 구동 | 매우 무거운 조합 |
•
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