도커 컴포즈를 활용한 복수의 컨테이너 관리하기
포스트
취소

도커 컴포즈를 활용한 복수의 컨테이너 관리하기

도커 컴포즈를 사용하는 이유

도커 컴포즈는 복수의 컨테이너를 동시에 사용하기 위해 사용한다.
하지만 이전 게시글에서는 단 하나의 컨테이너만 사용했다.
이번에는 실제로 하나의 서비스에서 복수의 컨테이너를 사용해보자.

서비스 정의하기

사실 복수의 컨테이너를 사용하는 방법은 크게 어렵지 않다.
그냥 compose.yml에서 서비스 목록을 여러 개 작성하면 된다.
만약에 docker compose test라는 애플리케이션이 있다고 가정해보고,
이를 축약해서 dct라고 정의해보자.
우선 간단한 테스트를 위해 index.html 정도만 있는 프로젝트를 만들어서 MySQL을 쓴다는 가정 하에 작성해보자.

services:
  dct-server:
    container_name: compose_nginx
    image: nginx
    ports: 
    - 80:80
  dct-db:
    container_name: compose_mysql
    image: mysql
    environment:
      MYSQL_ROOT_PASSWORD: mysql_password
    volumes:
      - /mysql_volume:/var/lib/mysql
    ports:
      - 3306:3306

위와 같이 프로젝트 루트에 compose.yml을 작성하고
터미널이나 CMD 창에서 해당 프로젝트 경로로 이동해서
docker compose up -d를 실행해보자.

그런 다음에 docker compose ps를 실행시켜 보면
compose.yml에서 정의한 3개의 서비스가 모두 실행된 것을 확인할 수 있다.

실제 애플리케이션을 가정해보기

이번에는 실제로 실무에서 사용한다고 생각하고 서비스를 관리해보자.
dct라는 애플리케이션은 Spring Boot, Redis, MySQL을 사용한다고 가정할 것이다.

JDBC 드라이버

우선 MySQL을 사용할 것이니 MySQL에 대한 라이브러리를 추가해주자.

runtimeOnly 'com.mysql:mysql-connector-j'

그리고 DB가 잘 연결되는지 확인하기 위해 JPA에 대한 라이브러리를 추가해주자.

implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

데이터 소스 연결하기

만약에 mydb라는 데이터베이스가 있다면 application.yml에서는 아래와 같이 작성할 것이다.
실제로 프로젝트를 실행해보면 콘솔창에서 잘 연결된 것을 확인할 수 있다.

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb
    username: root
    password: mysql_password
    driver-class-name: com.mysql.cj.jdbc.Driver

순서대로 실행하게 하기

스프링 부트에서 데이터베이스를 연결하려면 우선 데이터베이스가 실행이 되있어야 할 것이다.
만약 데이터베이스가 먼저 실행되기 전에 서버가 먼저 실행된다면
데이터베이스가 없는 상황이나 마찬가지니 스프링 부트에서는 연결에 실패했다고 메시지를 뱉을 것이다.

그러면 데이터베이스가 먼저 실행이 되고 그 다음에 서버가 실행되게 하려면 어떻게 해야할까?
그럴 때는 depends_on이라는 옵션을 사용하면 된다.

우선 compose.yml을 아래와 같이 바꿔보자.

services:
  dct-server:
    build: .
    ports:
      - 8080:8080
    depends_on:
      dct-db:
        condition: service_healthy
  dct-db:
    container_name: compose_mysql
    image: mysql
    environment:
      MYSQL_ROOT_PASSWORD: mysql_password
    volumes:
      - /mysql_volume:/var/lib/mysql
    ports:
      - 3306:3306
    healthcheck:
      test: ["CMD", "mysqladmin", "ping"]
      interval: 5s
      retries: 10

depends_on 옵션과 healthcheck 옵션이 사용되는 것을 알 수 있다.

depends_on 옵션은 일종의 의존성을 나타낸 것이라고 볼 수 있다.
해당 서비스가 실행되기 이전에 어떤 서비스가 실행되어야 하는지를 나타낸다.
하위 옵션인 condition을 통해 의존하는 서비스가 어떤 상태일 때
해당 서비스를 실행할 지를 나타낸다.
service_healthy라고 명시하면 의존하는 서비스가 정상적으로 동작 중이라고 판단하는 것을 의미한다.

healthcheck 옵션은 해당 서비스가 정상적으로 동작 중이라고 판단하기 위한 방법을 명시한다.
하위 옵션 중에서 test는 해당 서비스가 정상적으로 동작하는 지 확인하기 위한 명령어 목록을 작성해준다.
서비스 종류마다 확인 방법이 다르니 이는 직접 찾아봐야 한다.
하위 옵션 중에서 interval은 테스트하는 간격을 의미한다.
테스트할 때 실패하면 얼마 정도의 시간 후에 재시도할 지를 의미한다.
하위 옵션 중에서 retries는 테스트하는 횟수를 의미한다.
테스트할 때 실패하면 최대 몇 번까지 재시도할 지를 의미한다.

그런데 사실 이렇게 하고 다시 명령문을 통해 서비스를 실행해보면
또 다시 서버 실행에 실패하는 것을 볼 수 있다.
분명 데이터베이스가 뜨고 나서 서버가 실행되게 했는 데도 왜 실패할까?

데이터베이스가 문제인지 확인하기 위해 docker psdocker compose ps를 통해
컨테이너나 서비스 목록을 확인해보면 데이터베이스가 실행된 것을 확인할 수 있다. 그런데도 왜 서버는 실행되지 않았을까?

사실 원인은 도커 컨테이너의 독립성에 있다.

도커의 독립성

컨테이너는 각각의 독립적인 환경을 가지고 있는 일종의 미니 컴퓨터다.
여기서 각각의 독립적인 환경을 가지고 있다는 뜻은
즉, 각각의 컨테이너가 로컬이 된다는 뜻이고 이것은 서로 다른 localhost가 된다는 것을 의미한다.

그래서 MySQL 컨테이너가 실행되고 있어 봤자,
스프링 부트가 실행되어야 하는 Java 컨테이너는 MySQL 컨테이너가 실행되었는지 알 방법이 없다.
그러면 Java 컨테이너에서 MySQL 컨테이너를 연결하려면 어떻게 해야 할까?

도커 컴포즈의 서비스

Java 컨테이너와 MySQL 컨테이너를 연결하고 싶은 이 때 도커 컴포즈가 활약한다.
compose.yml에서 서비스 목록을 정의했었다.
도커 컴포즈는 이 서비스 목록에 정의된 서비스의 이름을 통해
서로 다른 컨테이너인 서비스 간에 연결을 가능하게 해준다.

데이터 소스 변경하기

아까는 데이터베이스의 URL을 jdbc:mysql://localhost:3306/mydb와 같이 작성했다.
이번에는 여기서 localhost 대신에 dct-db를 작성해보자.

spring:
  datasource:
#    url: jdbc:mysql://localhost:3306/mydb
    url: jdbc:mysql://dct-db:3306/mydb
    username: root
    password: mysql_password
    driver-class-name: com.mysql.cj.jdbc.Driver

그런 다음에 다시 애플리케이션을 빌드해주고
docker compose up을 통해 서비스를 다시 띄워보자.

이제 실제로 localhost:8080으로 접속하면 서버가 잘 띄워진 것을 확인할 수 있다.

출처

비전공자도 이해할 수 있는 Docker 입문/실전

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.

도커 컴포즈 (Docker Compose)

-