[Docker] 다중 컨테이너 한번에 구현하기
- -
이전까지 Docker에 대해 학습한 내용으로 멀티 컨테이너 환경을 구현해본다 📖
이전에 작성한 내용이니 이 글을 읽기 전에 위의 링크의 글을 먼저 읽어보는 걸 권장한다.
다중 컨테이너 구현하기
mariadb 와 redis 컨테이너를 한번에 관리하는 것을 목표로 구현을 진행해 보겠다.
그 전에 먼저 Spring 에서 개발환경과 운영환경을 분리해서 설정해 주었다. 이는 실제로 스프링 부트를 도커에 배포해서 사용할 때와 같은 다양한 환경에 설정을 용이하게 하기 위한 확장성을 고려하였다.
1. application profile 환경 별 설정 분리
Spring Profile이란 ?
Spring Profile는 어플리케이션 설정을 특정 환경에서만 적용되게 하거나, 환경 별(local, develop, production 등)로 다르게 적용 할 때 사용 한다. Spring boot 2.4 버전이 릴리즈 되면서 application.properties, application.yml 파일 로드 방식에 변화가 있었다. Spring boot 는 어플리케이션이 실행될 때 자동으로 application.properties 또는 application.yml 을 찾는다.
application.properties와 application.yml을 동시에 사용하지 않도록 주의하자! 같이 존재할 경우 properties가 항상 나중에 로드되어 yaml에 정의한 profile 설정을 덮어 쓸 수 있기 때문이다.
Spring boot 2.4 이후의 Profile
아래 사진과 같이 Spring boot 2.4부터는 spring.profiles은 deprecated 되었다.
이전에 spring.profiles로 사용하는 것이 아닌 spring.config.active.on-profile로 더 직관적으로 알아 볼 수 있도록 변경되었다.
application.yml
spring:
profiles:
active: local # 기본적으로 활성화할 profile을 local로 설정
---
spring:
config:
activate:
on-profile: local
# ...
---
spring:
config:
activate:
on-profile: prod
# ...
💡 참고로 YAML은 하나의 profile에 (---) 구분자로 구분을 하면, 논리적으로 구분이 되어 파일을 나누어서 사용하는 효과를 볼 수 있다.
또한, 아래와 같이 spring.profiles.group 을 이용해서 여러 profile들을 한꺼번에 그룹지어 하나의 profile로 만들 수 있다. include 대신 group을 사용한다. spring.profiles.active=prod로 실행하게 되면 prod와 common 두개의 profile들을 한번에 실행할 수 있다.
application.yml
# default
spring:
profiles:
active: local # default
group:
local: # local, common profile을 그룹지어 함께 어플리케이션 구동
- common
prod: # prod, common profile을 그룹지어 함께 어플리케이션 구동
- common
---
spring:
config:
activate:
on-profile: common # application-common.yml 과 동일한 역할
---
spring:
config:
activate:
on-profile: local
---
spring:
config:
activate:
on-profile: prod
인텔리제이에서 profile 값을 주기 위해서는 아래와 같이 줄 수 있고 자바로 실행할 때 VM arguments로 java -jar -Dspring.profiles.active=local app.jar 로 줄 수도 있다.
실제로 application을 실행할 때 spring.profiles.active 설정을 주어 어떠한 profile를 활성화할 것인지 정해주어야 한다.
해당 설정이 없을 시에는 위에서 정해준 default 값으로 profile이 실행된다.
2. Dockerfile 작성
application 별 환경을 분리했으면 이제 각각의 Dockerfile 을 작성해주자. 여기서 Dockerfile이란 도커 이미지를 구성하기 위해 있어야할 패키지, 의존성, 소스코드 등을 하나의 file 로 기록하여 이미지화 시킬 명령 파일이다. 따로 Directory를 생성하여 redis 와 mariaDB 에 대해 작성해준다.
redis/Dockerfile
FROM redis:6
ENV TZ=Asia/Seoul
간단하게 사용할 이미지와 버전, Timezone 환경 변수만 작성해 주었다.
databse/Dockerfile
FROM mariadb:10
ENV TZ=Asia/Seoul
redis와 동일한 형식으로 작성한다. 여기에 추가로 mariaDB의 인코딩 설정을 추가로 진행했다. 이 내역은 docker-compose 를 작성할 때 추가해서 설정을 초기화 해준다.
database/config/mariadb.cnf
[client]
default-character-set=utf8mb4
[mysql]
default-character-set=utf8mb4
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
skip-character-set-client-handshake
[mysqldump]
default-character-set=utf8mb4
3. Docker-compose 작성하기
이제 작성한 Dockerfile 들을 기반으로 컨테이너를 구성해보자!
version: "3.8"
services:
pharmacy-recommendation-redis:
container_name: {container_name}
build:
dockerfile: Dockerfile
context: ./redis
image: {image_name}
ports:
- "6379:6379"
pharmacy-recommendation-database:
container_name: {container_name}
build:
dockerfile: Dockerfile
context: ./database
image: {image_name}
environment:
- MARIADB_DATABASE=pharmacy-recommendation
- MARIADB_ROOT_PASSWORD=${SPRING_DATASOURCE_PASSWORD}
# {host directory}:{docker가 바라보는 directory}
# 호스트의 디렉토리를 참조해서 설정 초기화
volumes:
- ./database/config:/etc/mysql/conf.d
ports:
- "3306:3306"
위의 작성한 내용에 대한 설명을 해보자면 !
- version : 도커 컴포즈 버전
- services : 이곳에 실행하려는 컨테이너들을 정의
- pharmacy-recommendation-app : 서비스 명(네트워크 호스트명) / 같은 네트워크에 속한 컨테이너끼리 서비스 명으로 접근 가능
- container_name : 컨테이너 이름
- build : Dockerfile 이 있는 위치
- depends_on : 특정 컨테이너에 대한 의존관계
- image : 컨테이너 생성할 때, 사용할 도커 이미지 지정
- environment : 환경 변수
- volums : {호스트 디렉토리} : {컨테이너 디렉토리}
- ports : 접근 포트 설정 (컨테이너 외부 : 컨테이너 내부) / docker run -p 옵션과 같으며 개방할 포트를 지정
- restart : 컨테이너 실행에 실패하는 경우 재시작 여부
와 같다. 여기서 ${SPRING_DATASOURCE_PASSWORD} 같은 코드들은 환경 변수로 설정된 값들이다. 💡 도커 환경에서는 알아서 .env 파일을 읽어서 환경 변수를 가져오지만 로컬 환경에서는 그것이 불가능하기 때문에 application.yml의 local 용 profile 의 환경 변수는 인텔리제이 자체에서 추가해 주어야 한다.
4. Docker-compose 실행하기
아래의 명령어를 통해 docker-compose 파일을 실행시켜보자.
$ docker-compose -f {docker-compose 파일명} up
-f 옵션으로 어떤 docker-compose 파일을 실행시킨지 명시해준다. 정상적으로 실행이 되는 지 확인해보면 ..
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
007a8820bf## ###################################### "docker-entrypoint.s…" 13 hours ago Up 13 hours 0.0.0.0:3306->3306/tcp pharmacy-recommendation-database
3584f91fe3## ###################################### "docker-entrypoint.s…" 13 hours ago Up 13 hours 0.0.0.0:6379->6379/tcp pharmacy-recommendation-redis
이렇게 확인이 가능하다. 이러한 과정들을 거치며 유용한 명령어가 있어 몇가지 더 작성한다.
// .env 파일 자동 반영
$ docker-compose config
// 이미지 없을 때 이미 지 빌드 후 컨테이너 실행, 있을 경우에는 해당 이미지 사용
$ docker-compose up
// 이미지가 여부 상관없이 재빌드하여 컨테이너 실행
$ docker-compose up --build
// 현재 실행중인 도커 프로세스 확인
$ docker ps
⭐️ 주의사항
도커 데이터베이스에 초기화 스크립트를 추가하는 상황에 발생한 에러에 대한 해결 방법에 대해 공유하고자 해당 내용을 추가한다!
테스트 데이터를 추가하기 위해 스크립트를 추가했다.
1. pharmacy.sql
- 테이블 스키마 작성
2. direction.sql
- 테이블 스키마 작성
- 테스트 데이터 생성
..
..
docker-compose.yml
[추가하는 스크립트]
volumes:
- ./database/config:/etc/mysql/conf.d
- ./database/init:/docker-entrypoint-initdb.d
테이블 스키마와 테스트 데이터를 INSERT 하는 sql 파일을 작성하고 docker-compose 파일의 volumes 에 위의 sql 을 실행하는 스크립트를 작성하고 신나서 docker-compose 를 실행시켰다. 그렇지만 테이블은 생성되지 않았다😂 궁금해서 구글링을 해보니..
'MariaDB와 같은 이미지는 데이터베이스 볼륨이 처음 생성될 때만 초기화 스크립트를 실행합니다.' 라는 문구를 확인할 수 있었다.
내가 생각한 해결 방안에는 2가지가 있었다.
1. docker-compose 를 실행시킬 때마다 기존의 컨테이너를 삭제하고 build
2. 볼륨을 완전히 삭제하고 다시 시도
당연하게 느낄수도 있지만 1번의 경우에는 매번 삭제하고 재생성하기에는 아무리 작은 규모의 서비스를 구현한다 해도 아닌 방안인 것 같았다. 그렇기에 아직 컨테이너가 신생아 수준이기에 완전히 삭제하기로 했다.
$ docker-compose -f docker-compose-local.yml down -v
[+] Running 3/2
✔ Container pharmacy-recommendation-redis Removed 0.2s
✔ Container pharmacy-recommendation-database Removed 0.4s
✔ Network pharmacy_default Removed
깔끔하게 날려주고 다시 docker-comose up
$ docker-compose -f docker-compose-local.yml up
이러한 과정을 통해 테이블을 생성까지 성공 .. !
내가 아는 지식들이 전부가 아닐 거라고 당연하게 생각한다. 틀린 부분, 더 효율적으로 해결할 수 있는 부분들이 있다면 꼭 댓글로 공유 부탁합니다!
'Server' 카테고리의 다른 글
[Docker] Docker란 ? (1) | 2024.02.11 |
---|
소중한 공감 감사합니다