Docker Compose 이용하여 모니터링 환경 구축

이세원·2024년 7월 10일
post-thumbnail

배경

springboot, mysql, prometheus, grafana를 활용하여
스프링부트 서버 메트릭을 수집하고 모니터링하는 프로젝트 진행중
보다 손쉽게 서버를 다루기 위하여 docker를 사용하였다.

문제점

매번 컨테이너를 하나 하나 생성하고 관리함에 있어서 불편함이 있었다.
(이 컨테이너가 단 4개가 아닌 1억개라고 상상해봐라.)

해결책

docker compose를 이용하여 여러개의 컨테이너를 한번에 생성, 관리하도록 해보자.

결론부터 말하면 docker-compose.yml 파일 내용은 아래와 같다.

networks:
  2024_summer_coding:
    name: 2024_summer_coding_network
    driver: bridge
    ipam:
      config:
        - subnet: 172.18.0.0/16
          gateway: 172.18.0.1
    labels:
      com.docker.compose.network: '2024_summer_coding'
      com.docker.compose.project: '2024_summer_coding'
    external: false


services:
  mysqldb:
    image: mysql:latest
    container_name: mysqldb 
    environment:
      MYSQL_ROOT_PASSWORD: 'dl-13579' 
      MYSQL_DATABASE: '2024_summer_coding'
    volumes:
      - ./mysql-data:/var/lib/mysql
    ports:
      - 3306:3306
    networks:
      - 2024_summer_coding

  springboot:
    image: springboot:latest
    container_name: springboot
    environment:
      SPRING_DATASOURCE_URL: jdbc:mysql://mysqldb:3306/2024_summer_coding?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true
      SPRING_DATASOURCE_USERNAME: root
      SPRING_DATASOURCE_PASSWORD: dl-13579
      SPRING__PROFILES_ACTIVE: docker
    ports:
      - 8080:8080
    networks:
      - 2024_summer_coding
    depends_on:
      - mysqldb

  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    volumes:
      - ./prometheus-data:/prometheus
      - ./prometheus:/etc/prometheus
    ports:
      - 9090:9090
    networks:
      - 2024_summer_coding
    depends_on:
      - springboot

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    volumes:
      - ./grafana-data:/var/lib/grafana
    ports:
      - 3000:3000
    networks:
      - 2024_summer_coding
    depends_on:
      - prometheus

과정중 트러블슈팅

1. 네트워크는 어떻게 설정되는 것일까?

컨테이너들을 실행시키고 네트워크를 확인해보면
'2024_summercoding_default'라는 bridge가 자동으로 생성되었음을 확인할 수 있다.

inspect 명령으로 확인해보면 아래와 같이 자동으로 사설망 주소를 받고 같은 네트워크 내에서 컨테이너들이 실행되었음을 확인할 수 있다.

따라서 별도의 설정 없이도 컨테이너들이 잘 동작하지만 다른 디바이스를 쓰거나 커스텀이 필요할때는 따로 정의하여 사용하여야한다.

2. mysql 데이터와 프로메테우스 메트릭이 자꾸 사라지네?

아래는 초기에 내가 작성한 docker-compose.yml 이다.
보면 알겠지만 mysqldb와 프로메테우스에대한 적절한 volume 설정이 누락되어있다.
도커 볼륨은 도커 컨테이너와 호스트 사이에서 데이터를 영속적으로 저장하고 공유하기 위해 사용되는 메커니즘이다.
그래서 볼륨 설정이 없으면 매 실행마다 db가 날아가고 메트릭이 초기화 되었던 것이다.

services:
  mysqldb:
    image: mysql:latest
    container_name: mysqldb 
    environment:
      MYSQL_ROOT_PASSWORD: 'dl-13579' 
      MYSQL_DATABASE: '2024_summer_coding'
    ports:
      - 3306:3306

  springboot:
    image: springboot:latest
    container_name: springboot
    environment:
      SPRING_DATASOURCE_URL: jdbc:mysql://mysqldb:3306/2024_summer_coding?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true
      SPRING_DATASOURCE_USERNAME: root
      SPRING_DATASOURCE_PASSWORD: dl-13579
      SPRING__PROFILES_ACTIVE: docker
    ports:
      - 8080:8080

  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    volumes:
      - ./prometheus-data:/prometheus
    ports:
      - 9090:9090
      
  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    ports:
      - 3000:3000

이제 호스트에 원하는 이름으로 폴더를 생성하고, 컨테이너 내부의 적절한 경로와 연결해주면 된다.

services:
  mysqldb:
    image: mysql:latest
    container_name: mysqldb 
    environment:
      MYSQL_ROOT_PASSWORD: 'dl-13579' 
      MYSQL_DATABASE: '2024_summer_coding'
    volumes:
      - ./mysql-data:/var/lib/mysql #여기
    ports:
      - 3306:3306

  springboot:
    image: springboot:latest
    container_name: springboot
    environment:
      SPRING_DATASOURCE_URL: jdbc:mysql://mysqldb:3306/2024_summer_coding?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true
      SPRING_DATASOURCE_USERNAME: root
      SPRING_DATASOURCE_PASSWORD: dl-13579
      SPRING__PROFILES_ACTIVE: docker
    ports:
      - 8080:8080

  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    volumes:
      - ./prometheus-data:/prometheus #여기
      - ./prometheus:/etc/prometheus #여기
    ports:
      - 9090:9090

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    volumes:
      - ./grafana-data:/var/lib/grafana #여기
    ports:
      - 3000:3000

다음과 같이 volumes: 에 적절히 설정해주면 재실행후에도 데이터가 잘 남아있음을 확인할 수 있다.

추가

1. 쉘 스크립트를 작성하여 부가 과정 자동화

아래는 내가 작성한 runner.sh 파일이다.
실행 권한을 주고 ./runner.sh로 실행하면 모든 컨테이너들이 잘 실행됨을 확인할 수 있다.

#!/bin/bash

# 명령이 0이 아닌 종료값을 가질때 즉시 종료
set -e

echo "Starting all docker containers..."
docker-compose up -d

echo "Pruning unused Docker images..."
docker image prune -f

echo "Containers are up and running."
docker-compose ps -a

실행결과

2. 추가적으로 스프링부트 이미지를 생성하는것도 번거로워서 자동화

먼저 다음과같은 Dockerfile 을 준비해야한다.

FROM openjdk:17
ARG JAR_FILE=build/libs/ass1-0.0.1-SNAPSHOT.jar
COPY ${JAR_FILE} springboot.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/springboot.jar"]

아래는 자동화를 위한 쉘 스크립트이다.

#!/bin/bash

set -e

cd ./ass/2024-Ass-System-Design-SummerCoding

echo "Building Spring Boot application..."
./gradlew build -x test || { echo 'gradlew build failed'; exit 1; }

echo "Building Docker image..."
docker build -t springboot . || { echo 'docker build failed'; exit 1; }
# springboot는 이미지 이름을 지정한 것이다.

echo "Pruning unused Docker images..."
docker image prune -f

실행결과

결론

docker compose를 이용하니 기존의 번거로웠던 문제들이 해결되고, 추가적으로 스크립트문으로 자동화해보니 더욱 생성, 관리가 편리하고 확장성에 용이하다고 느껴졌다.

확장성 말나온김에 nginx도 붙여보자.

우선 docker pull 로 nginx 이미지를 가져온다.

기존코드 docker-compose.yml 아래부분에 해당 코드를 추가

  nginx:
    image: nginx:latest
    container_name: nginx
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/conf.d/:/etc/nginx/conf.d
    ports:
      - 80:80
      - 443:443
    networks:
      - 2024_summer_coding  
    depends_on:
      - springboot  

볼륨으로 연결한 nginx.conf에 적절히 reverse_proxy 설정
(80포트로 들어온 요청을 스프링부트 서버인 8080포트로 보내줘야한다.)

user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    upstream springboot {
        server springboot:8080; # 서비스 이름으로 작성
    }

    server{
        listen 80;
        location / {
            proxy_pass http://springboot;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}

끝이다.

아래와 같이 80포트로 접근해도 springboot 서버 rest api로 잘 접근함을 확인할 수 있다.

+a

아까 docker-compose.yml 파일의 해당 부분을 보고 의문이 들었을 수 있다.

      SPRING__PROFILES_ACTIVE: docker

스프링부트에서 active profile을 설정하는 역할로
스프링부트 내에서 local과 docker로 yml 파일을 분리해둬서 해당 설정을 추가했다.

application-docker.yml 파일은 다음과 같다.

spring:
  application:
    name: ass1
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: ${SPRING_DATASOURCE_URL}
    username: ${SPRING_DATASOURCE_USERNAME}
    password: ${SPRING_DATASOURCE_PASSWORD}

management:
  endpoints:
    web:
      exposure:
        include: health, info, prometheus, metrics

local의 경우 application-local.yml로 생성하여 사용하면 된다.

0개의 댓글