새소식

Server

[Docker] 다중 컨테이너 한번에 구현하기

  • -
728x90
이전까지 Docker에 대해 학습한 내용으로 멀티 컨테이너 환경을 구현해본다 📖

 

 

 

[Docker] Docker란 ?

Docker 에 대해 학습하고 직접 실행하며 배우고 느낀 것들에 대해 정리하고 공유하고자 글을 작성한다 📖 Docker 컨테이너를 사용하여 응용프로그램을 더 쉽게 만들고, 배포하고 실행할 수 있도록

seung-seok.tistory.com

이전에 작성한 내용이니 이 글을 읽기 전에 위의 링크의 글을 먼저 읽어보는 걸 권장한다.

 

다중 컨테이너 구현하기

mariadbredis 컨테이너를 한번에 관리하는 것을 목표로 구현을 진행해 보겠다.

그 전에 먼저 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

 

짜잔 ⭐️

 

이러한 과정을 통해 테이블을 생성까지 성공 .. ! 

 

내가 아는 지식들이 전부가 아닐 거라고 당연하게 생각한다. 틀린 부분, 더 효율적으로 해결할 수 있는 부분들이 있다면 꼭 댓글로 공유 부탁합니다!

728x90

'Server' 카테고리의 다른 글

[Docker] Docker란 ?  (1) 2024.02.11
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.