Prometheus & Grafana 설정
1. spring boot actuator 외부 접근 제한(for 보안)
spring:
datasource:
driver-class-name: org.mariadb.jdbc.Driver
url: jdbc:mariadb://mariadb:3306/boeing
username: ${DB_USER}
password: ${DB_PASSWORD}
jpa:
show-sql: false
hibernate:
ddl-auto: update
properties:
hibernate:
format_sql: true
open-in-view: false
redis:
host: redis
port: ${REDIS_PORT}
password: ${REDIS_PASSWORD}
management:
endpoints:
web:
exposure:
include: health, info, metrics, prometheus
server:
port: ${PROMETHEUS_PORT}
base-path: ${PROMETHEUS_PATH}
server:
servlet:
context-path: /api/v1
include
: 사용할 엔드포인트(url) 설정
- ex) health, info, prometheus
port
: 프로메테우스 접속 시 사용할 포트(PROMETHEUS_PORT)
base-path
: 프로메테우스 접속 시 사용할 경로(PROMETHEUS_PATH)
- .env 파일과 docker-compose의 environment 영역에 application.yml에서 사용할 변수 추가 필요
2. docker-compose-monitoring.yml
version: '3'
services:
prometheus:
image: prom/prometheus:latest
container_name: prometheus
networks:
- deploy
ports:
- "9091:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
grafana:
image: grafana/grafana:latest
container_name: grafana
networks:
- deploy
ports:
- "3001:3000"
volumes:
- ./grafana-data:/var/lib/grafana
depends_on:
- prometheus
networks:
deploy:
external: true
- volumes를 설정하여 컨테이너가 내려가도 기존 Grafana에서 설정한 대시보드나 데이터 소스가 유지 되도록 함
- 프로메테우스는 9090, 그라파나는 3000번 포트를 default로 사용하므로 외부포트만 수정해야 한다!!
3. prometheus.yml
scrape_configs:
- job_name: 'bizscanner'
metrics_path: '/{PROMETHEUS_PATH}/actuator/prometheus'
scrape_interval: 5s
static_configs:
- targets: ['spring:{PROMETHEUS_PORT}']
PROMETHEUS_PORT
: monitoring
PROMETHEUS_PATH
: 8081
Promtail & Loki 설정
1. Logback 설정(logback-spring.xml
)
<configuration>
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
<property name="consoleLogPattern"
value="[%d{yyyy-MM-dd' T 'HH:mm:ss.SSS}] %clr(%-5level) %clr(${PID:-}){magenta} %clr(---){faint} %clr([%15.15thread]){faint} %clr(%-40.40logger{36}){cyan} %clr(:){faint} %msg%n"/>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${consoleLogPattern}</pattern>
</encoder>
</appender>
<root level="info">
<appender-ref ref="console"/>
</root>
<property name="infoLogPath" value="logs/backend/info"/>
<property name="warnLogPath" value="logs/backend/warn"/>
<property name="errorLogPath" value="logs/backend/error"/>
<property name="fileNamePattern" value="%d{yyyy-MM-dd}_%i.log"/>
<property name="logPattern" value="%d{HH:mm:ss.SSS} [%20thread] %-5level %-53logger{36} - %msg%n"/>
<appender name="info-file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<maxHistory>7</maxHistory>
<maxFileSize>1GB</maxFileSize>
<totalSizeCap>1GB</totalSizeCap>
<fileNamePattern>${infoLogPath}/${fileNamePattern}</fileNamePattern>
</rollingPolicy>
<encoder>
<pattern>${logPattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>NEUTRAL</onMismatch>
</filter>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARN</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="warn-file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<maxHistory>7</maxHistory>
<maxFileSize>1GB</maxFileSize>
<totalSizeCap>1GB</totalSizeCap>
<fileNamePattern>${warnLogPath}/${fileNamePattern}</fileNamePattern>
</rollingPolicy>
<encoder>
<pattern>${logPattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARN</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="error-file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<maxHistory>7</maxHistory>
<maxFileSize>1GB</maxFileSize>
<totalSizeCap>1GB</totalSizeCap>
<fileNamePattern>${errorLogPath}/${fileNamePattern}</fileNamePattern>
</rollingPolicy>
<encoder>
<pattern>${logPattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<root level="info">
<appender-ref ref="info-file"/>
<appender-ref ref="warn-file"/>
<appender-ref ref="error-file"/>
</root>
</configuration>
logback-spring.xml
: log를 level별로 구분하여 파일로 관리하기 위한 logback 설정 파일
- 코드 분석
1. conversionRule
및 property
: 이 부분에서는 로그 패턴과 색상을 정의합니다. %clr
토큰을 사용하여 로그 레벨과 기타 텍스트에 색상을 지정합니다. consoleLogPattern
은 콘솔 출력에 사용할 패턴을 정의하고, %d
, %clr
, %-5level
, %clr(${PID:-})
, %clr(---)
, %clr([%15.15thread])
, %clr(%-40.40logger{36})
, %clr(:)
, %msg%n
등이 포함되어 있습니다.
2. appender
요소: 로그를 출력하는 대상을 설정합니다. 여기에서는 console
앱렌더를 정의하여 콘솔에 로그를 출력합니다. 또한 info-file
, warn-file
, error-file
앱렌더를 정의하여 로그 파일에 저장합니다.
3. rollingPolicy
: 로그 파일 롤링 정책을 설정합니다. 로그 파일은 최대 7일 동안 보관되며, 파일 크기는 최대 1GB로 제한됩니다. totalSizeCap
은 모든 로그 파일의 총 크기 제한을 나타냅니다.
4. encoder
: 로그 메시지의 출력 형식을 정의합니다. 여기에서는 logPattern
에 정의된 패턴을 사용하여 로그 메시지를 출력합니다.
5. filter
: 각 앱렌더에는 로그 레벨에 따라 필터가 설정되어 있습니다. 이 필터는 각 앱렌더에 대한 로그 레벨을 제한합니다. 예를 들어, info-file
앱렌더는 INFO 레벨의 로그만 허용하고, warn-file
앱렌더는 WARN 레벨의 로그만 허용하도록 설정되어 있습니다.
6. root
로거: 루트 로거의 레벨을 info
로 설정하고, appender-ref
를 사용하여 각 앱렌더를 루트 로거에 연결합니다. 이로 인해 INFO, WARN, ERROR 레벨의 로그가 각각 다른 파일에 저장됩니다.
2. spring을 실행하는 docker-compose.yml 수정
volumes:
- /home/ubuntu/monitoring/logs:/app/logs
- 컨테이너 내부의 logs 폴더를 외부와 마운팅 하기 위해 volumes 구문 추가
3. docker-compose-monitoring.yml에 loki, promtail 설정 추가
loki:
image: grafana/loki:latest
networks:
- deploy
ports:
- "3100:3100"
command: -config.file=/etc/loki/local-config.yaml
promtail:
image: grafana/promtail:latest
networks:
- deploy
volumes:
- ./logs:/logs
- ./promtail-config.yml:/etc/promtail/config.yml
command: -config.file=/etc/promtail/config.y
4. promtail-config.yml
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki:3000/loki/api/v1/push
scrape_configs:
- job_name: info
static_configs:
- targets:
- localhost
labels:
job: info_logs
__path__: ./logs/info/*.log
- job_name: warn
static_configs:
- targets:
- localhost
labels:
job: warn_logs
__path__: ./logs/warn/*.log
- job_name: error
static_configs:
- targets:
- localhost
labels:
job: error_logs
__path__: ./logs/error/*.log
마주친 에러들….
1. docker-compose로 grafana 컨테이너 실행 시 ‘Grafana '/var/lib/grafana/plugins': Permission denied’ 에러 발생
user: "$UID:$GID"
: grafana image 내에 /var/lib/grafana의 쓰기 작업을 허용해줘도 계속해서 에러가 났다.
- docker-compose down 후 재실행하니까 해결됨…
2. docker-ufw 설정 관련 에러

- 프로메테우스는 -p 9091:9090, 그라파나는 -p 3001:3000으로 포트 포워딩 한 상태
- 도커 컨테이너를 띄울 때 포트포워딩을 했으므로 이더넷에는 외부포트(9091, 3001)이 열리게 된다.(도커는 ufw 규칙 위에 존재하기 때문에) ⇒ 따라서 굳이 docker-ufw allow로 외부 포트를 열어줄 필요가 없다.
- docker-ufw를 사용한 순간 네트워크들의 포트를 모두 막기 때문에 이더넷에서 deploy 네트워크의 내부 포트로(9090, 3000) 포워딩이 막힘 ⇒ 이를 해결하기 위해 docker-ufw allow 명령어로 내부 포트를 열어준다.
참고자료