목표 : 여러 도커 컨테이너로 구성한 어플리케이션의 환경을 Docker Compose를 통해 한번에 설정하기.
Docker Compose는 여러 도커 컨테이너를 사용하는 어플리케이션 개발에 유용한 도구다.
도커 컨테이너로 MySQL과 MongoDB를 연동시켰던 Spring Boot 프로젝트를 이어서 사용한다. (시리즈 참고)
우선 어플리케이션의 이미지를 빌드하기 위한 DockerFile부터 생성한다.
DockerFile은 도커 이미지를 빌드하는 방법을 적어놓은 텍스트 파일이다. 빌드 과정에서 DockerFile에 적은 명령이 순서대로 실행된다.
./gradlew clean build
프로젝트 디렉토리에서 터미널을 열고, 위 명령어를 통해 프로젝트를 빌드하여 jar 파일을 생성한다. 중간에 clean을 넣으면 기존 jar 파일을 지운 후 다시 생성한다.
FROM openjdk:21
ARG JAR_FILE=build/libs/SimpleProject-0.0.1-SNAPSHOT.jar
COPY ${JAR_FILE} app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app.jar"]
간단하게 구성한 DockerFile이다.
Spring Boot 프로젝트 최상단에 DockerFile 파일을 만들고 내용을 적는다.
각 명령어의 의미는 다음과 같다.
FROM :
이미지의 기반이 될 이미지를 명시한다. 이 프로젝트에선 Java 21을 사용했으므로 openjdk:21
이미지를 사용하였다. 필요에 따라 jdk/jre, alpine 버전 여부 등을 선택해 사용하자.
ARG :
빌드 때 사용할 변수를 선언한다. 위에선 build/libs
에 있는 jar 파일을 JAR_FILE
변수에 저장하였다.
COPY :
파일이나 폴더를 생성할 컨테이너 안으로 복사한다. 위에선 JAR_FILE
변수에 저장되어 있는 jar 파일을 app.jar
라는 이름으로 복사하였다.
EXPOSE :
컨테이너 속 어플리케이션이 열어둘 포트 번호를 지정한다.
ENTRYPOINT :
이미지가 빌드 된 이후, 컨테이너에서 처음으로 실행될 명령어를 지정한다.
이외에 자주 사용되는 명령어로는 RUN, CMD, VOLUME 등이 있다.
RUN :
다음과 같은 형식으로 명령문을 실행한다. 이미지를 빌드할 때 필요한 요소를 다운받을 때 등 자주 쓰인다.
# Shell form:
RUN [OPTIONS] <command> ...
# Exec form:
RUN [OPTIONS] [ "<command>", ... ]
CMD :
ENTRYPOINT와 유사하게 이미지가 빌드 된 이후, 컨테이너에서 가장 먼저 명령어를 실행한다. ENTRYPOINT는 항상 실행이 되지만 CMD는 변경이 될 수 있다. (참고 확인)
DockerFile 명렁어 : https://docs.docker.com/reference/dockerfile/
docker build -t {이미지 이름} .
DockerFile이 있는 디렉토리, 즉 프로젝트 최상단에서 터미널을 열고 다음 명령어를 실행하여 도커 이미지를 빌드한다.
-t {이미지 이름}
부분은 이미지 이름을 위한 옵션이라 빼도 상관없다.
docker desktop이나 docker images
명령문을 통해 이미지 목록을 확인할 수 있다.
이후 docker run을 통해 컨테이너를 생성할 수 있지만 여기서 사용하는 프로젝트의 경우, 다른 컨테이너 속 MySQL이나 MongoDB와 연결되지 못해 에러가 발생한다.
이제 Docker Compose를 사용해보자.
링크 : https://docs.docker.com/compose/install/
공식 문서에서 소개하는 방법은 3가지가 있다.
1. Docker Desktop 설치 :
Docker Desktop에 포함되어 있으므로 이를 설치한다.
Docker Desktop 설치 관련 글 : https://velog.io/@woolzam/%EB%8F%84%EC%BB%A4-%EB%8D%B0%EC%8A%A4%ED%81%AC%ED%86%B1-%EC%84%A4%EC%B9%98
2. Compose 플러그인 설치 :
도커엔진과 도커 CLI가 설치되어 있을 경우.
https://docs.docker.com/compose/install/linux/#install-using-the-repository
3. Compose Standalone 설치:
https://docs.docker.com/compose/install/linux/#install-the-plugin-manually
1번이 docker 공식 문서에서 가장 추천하는 방법이며, 3번이 가장 덜 추천하는 방법이다.
services:
db_mysql:
image: "mysql:latest"
container_name: db_mysql
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: "0000"
MYSQL_DATABASE: "simpledb"
MYSQL_ROOT_HOST: '%'
networks:
- simple_network
db_mongodb:
image: "mongo:latest"
container_name: db_mongodb
ports:
- "27017:27017"
networks:
- simple_network
simple-web:
depends_on:
- db_mysql
- db_mongodb
build: .
ports:
- "8080:8080"
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://db_mysql:3306/simpledb
SPRING_DATASOURCE_USERNAME: "root"
SPRING_DATASOURCE_PASSWORD: "0000"
networks:
- simple_network
networks:
simple_network:
DockerFile을 만들었을 때와 마찬가지로, 프로젝트 최상단 디렉토리에 docker-compose.yml 파일을 생성한다.
이 docker-compose.yml를 통해 여러 개의 컨테이너를 구성하고 설정한다.
하나씩 짚어보자.
services :
serviecs 속성에선 어플리케이션이 사용하는 각 service를 정의한다. 위에서 services 아래에 정의한 db_mysql
, db_mongodb
, simple-web
이 service의 이름이다.
service란 독립적인 기능 요소라고 생각하면 될 것 같다. 각 service는 컨테이너를 생성하게 된다.
db_mysql
:db_mysql
서비스는 mysql:latest
이미지를 기반으로 생성된다.db_mysql
서비스는 여기서 root 사용자의 비밀번호, 생성할 데이터베이스, root로 접속이 가능한 호스트를 지정하였다. db_mongodb
:db_mysql
서비스와 딱히 다른 점은 없다.simple-web
:.
를 적었으니, 같은 디렉토리에 있는 DockerFile로부터 이미지를 생성하여 이 이미지를 기반으로 서비스를 빌드한다.SPRING_DATASOURCE_URL: jdbc:mysql://{mysql 컨테이너 이름}:{연결한 포트}/{데이터베이스 이름}
networks :
서비스 간 통신하는 영역이다. 최상단 요소인 networks에서 network 목록을 만들어 두고, 각 서비스 아래의 networks에서 통신할 network 목록을 지정한다. 같은 network를 지정한 서비스끼리는 통신이 가능하다.
spring:
#mysql
datasource:
#url: jdbc:mysql://db_mysql:3306/simpledb?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Seoul&zeroDateTimeBehavior=convertToNull&rewriteBatchedStatements=true
#driver-class-name: com.mysql.cj.jdbc.Driver
#username: root
#password: '0000'
data:
mongodb:
port: 27017
#host: localhost
host: db_mongodb
database: simplemongodb
jpa:
database-platform: org.hibernate.dialect.MySQLDialect
open-in-view: false
show-sql: true
hibernate:
format_sql: true
ddl-auto: update
더이상 localhost를 통해 db에 연결되는 것이 아니므로, 설정파일인 application.yml
을 다음과 같이 수정하였다.
(application.yml
보다 docker-compose.yml
설정이 우선된다는 글을 읽긴했는데 레퍼런스는 못찾았다.)
mongodb의 host도 mongodb 서비스의 컨테이너 이름으로 교체하였다.
docker compose up
docker-compose.yml
파일이 있는 디렉토리에서 터미널을 열고 다음 명령어로 docker compose 작업을 실행한다.
(생기는 문제점은 아래 확인)
잘 되었다면 테스트했을 때 로컬의 8080 포트와 spring boot 서비스 컨테이너의 8080포트를 연결시켰기 때문에 기존 주소로도 이상없이 웹 페이지가 나온다.
테스트 함수로 각 db에 데이터를 삽입해본 후, 컨테이너 안에서 확인해보면 데이터가 잘 들어갔음을 확인할 수 있다.
끝!
끝..이라고 썼지만 문제점이 하나 있는데, docker compose up
을 통해 처음 어플리케이션을 구동하면 다음과 같이 에러가 발생한다.
하지만 이후 생성된 컨테이너를 정지하였다가 다시 실행하면 잘 동작하는 것을 볼 수 있다.
예상되는 원인으로, mysql 서비스의 컨테이너가 제대로 구성되기 전에 웹 부분이 동작을 시도하다 오류가 났을 가능성이 있다.
해결하기 위한 방안으론 simple-web
서비스의 depends_on
속성에 service_healthy
같은 옵션을 활용하면 되지 않을까 싶다.
시도해보고 글을 수정하겠다.
이 밑에 더 글이 없다면면 귀찮아서 시도도 안 해본거임
역시 MySQL 문제였던 것 같다.
depends_on
속성을 service_healthy
로 하면 MySQL 서비스가 정상인지 체크한 후, 정상일 때 web 서비스가 시작된다.
정상인지 체크하는 방법은 MySQL 서비스 쪽에 명시해야 한다.
옵션은 다음과 같다.
db_mysql
서비스에 추가할 옵션 :
healthcheck:
test: [ "CMD", "mysqladmin" ,"ping", "-h", "localhost" ]
timeout: 20s
retries: 10
simple-web
서비스 쪽 옵션 :
depends_on:
db_mysql:
condition: service_healthy
최종 docker_compose.yml
파일
services:
db_mysql:
image: "mysql:latest"
container_name: db_mysql
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: "0512"
MYSQL_DATABASE: "simpledb"
MYSQL_ROOT_HOST: '%'
healthcheck:
test: [ "CMD", "mysqladmin" ,"ping", "-h", "localhost" ]
timeout: 20s
retries: 10
networks:
- simple_network
db_mongodb:
image: "mongo:latest"
container_name: db_mongodb
ports:
- "27017:27017"
networks:
- simple_network
simple-web:
depends_on:
db_mysql:
condition: service_healthy
db_mongodb:
condition: service_started
build: .
ports:
- "8080:8080"
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://db_mysql:3306/simpledb
SPRING_DATASOURCE_USERNAME: "root"
SPRING_DATASOURCE_PASSWORD: "0512"
networks:
- simple_network
networks:
simple_network:
entry point, run, cmd 차이점 : https://seokhyun2.tistory.com/61
공식 문서(docker compose, services) : https://docs.docker.com/compose/compose-file/05-services/
Dockerfile 참고 : https://adjh54.tistory.com/420
Docker Compose 참고 : https://velog.io/@mooh2jj/docker-compose%EB%A1%9C-SprongBoot-JPA-MySql-DB-%EC%84%9C%EB%B2%84%EB%A7%8C%EB%93%A4%EA%B8%B0
MySQL healthcheck 옵션 : https://stackoverflow.com/questions/42567475/docker-compose-check-if-mysql-connection-is-ready