- 목차
- docker-compose를 이용하여 단일 어플리케이션 인스턴스 띄우기
- docker-compose를 이용하여 멀티 어플리케이션 인스턴스 띄우기 및 컨테이너간 통신
- docker 컨테이너 내부의 인스턴스에 디버그 모드 적용
- 필요 기술
- docker
- docker-compose
- gradle
- 예제 repository
docker-compose를 이용하여 단일 어플리케이션 인스턴스 띄우기
프로젝트 디렉토리는 다음과 같이 구성해두었다.
루트 프로젝트 하위에 하위 모듈로 A라는 스프링부트 프로젝트를 생성하였고
이를 docker를 이용하여 인스턴스를 띄워보고자 한다.
해당 A모듈을 docker로 실행하기 위한 docker-compose.yml 파일의 내용은 위와 같으며,
위에서부터 한줄씩 분석해보도록 하겠다.
1번째 라인, 해당 docker-compose.yml 이 어떤 버전의 compose file 포맷을 사용할지 선언
3 ~ 4번째 라인, 해당 docker-compose.yml을 실행하는 경우, 어떤 서비스들이 실행될지를 선언
5번째 라인, 실행되는 docker 컨테이너의 이름을 정의
6번째 라인, 어떤 docker image를 사용할지를 선언, 해당 글에서는 openjdk 이미지를 사용하도록 선언하였다.
7 ~ 8번째 라인, host PC의 특정 디렉토리를 docker 컨테이너에 연결? 시키는 설정
자세한 내용은 docker volume을 확인하면 될 듯 하다.
현재의 설정에서는 host PC의 해당 docker-compose.yml의
상위 디렉토리(boot-with-docker/ 디렉토리)를
docker 컨테이너의 /usr/src/app-root 디렉토리에 연결?한다고 보면 된다.
쉽게 말해서 특정 디렉토리를 host PC와 docker 컨테이너가 공유하게 된다.
9번째 라인, docker 컨테이너에서 어느 디렉토리에서 작업을 실행할지 선언하는 부분이다.
10번째 라인, 9번째 라인에서 정의한 디렉토리에서 쉘을 실행하게 된다.
쉘의 내용을 간단하게 분석하면 7~8번째 라인에서 연결한 디렉토리를
docker 컨테이너의 작업중인 디렉토리에 복사하고 gradle wrapper를 이용하여
A모듈을 실행하는 명령이다.
연결한 디렉토리를 복사하여 사용하는 이유는 아래 여러 인스턴스를 띄울 때 설명하도록 하겠다.
11 ~ 12번째 라인, host PC의 port를 docker 컨테이너의 port에 바인딩 하는 선언이다.
해당 docker-compose.yml을 이용하여 실행하면 host PC의 연결한 port로 호출시
docker 컨테이너에 접근이 가능하다.
(스프링부트 프로젝트를 8001 port로 띄워 host PC의 8001 port로
접속하면 연결하게 해두었다.)
실행 화면
docker-compose를 이용하여 멀티 어플리케이션 인스턴스 띄우기 및 컨테이너간 통신
여러 어플리케이션 인스턴스를 여러 docker 컨테이너에 띄우고 서로 다른 컨테이너간 통신을 진행해보고자 한다.
위의 docker-compose.yml 파일의 설정에서는
a-application, b-application 총 2개의 컨테이너를 띄우도록 설정하였고
단일 어플리케이션 실행 docker-compose.yml과 다른 부분에 대해서만 설명 해보겠다.
6번째 라인, 타 컨테이너에서 해당 컨테이너에 접속할 때의 hostname을 설정한다.
a-application에서 b-application의 인스턴스를 호출할 때,
http://host-b.com:8002/hello 와 같이 호출이 가능하게 된다.
11번째 라인, 해당 라인에서 연결된 디렉토리를 복사하여 사용한 이유에 대해서 설명해보자면
gradle wrapper를 사용하여 동시에 프로젝트를 실행하게 되는 경우,
gradle wrapper에서 관리하고 있는 dependency들에 대해서
lock을 걸게 되어 이로 인해 다른 인스턴스는 실행이 불가능하게 된다.
이를 해결하기 위해서 해당 설정에서는 연결된 디렉토리를 복사하여 사용하게 되었다.
14번째 라인, networks 해당 docker 컨테이너가 어떤 docker network들과 join될지를 선언한다.
해당 설정에서는 아래 32번째 라인에서 생성한 test-network와 join 되도록 설정하였다.
DB도 별도의 컨테이너로 띄우는 경우, 별도의 네트워크를 join하여 연결하면 된다.
16 ~ 17번째 라인, 주석처리 되어 있지만 주석을 해제하게 되는 경우,
b-application이 실행된 이후에 a-application의 컨테이너를 실행하게 된다.
(단, b-application이 완전히 실행이 끝났음을 보장하지는 않는다.
b-application이 완전히 실행되었음을
보장하기 위해서는 별도의 쉘을 작성하도록 공식 사이트에서는 가이드하고 있다.)
32 ~ 34번째 라인, 별도의 docker 네트워크를 선언하여 이를 각 docker 컨테이너에서 join하여 사용하고 있다.
driver의 종류는 여기 사이트에서 확인해보면 될 듯하다.
여기 설정에서 사용한 사용자 설정 bridge의 경우, docker에서 각 컨테이너 간의 통신시,
dns resolution을 제공한다고 한다.
실행화면
다음은 a-application에서 b-application을 호출하는 예시이다.
docker 컨테이너 내부의 인스턴스에 디버그 모드 적용
다음은 A 모듈의 인스턴스에 디버그 모드를 적용하기 위해서 build.gradle 의 내용중 bootRun task의 내용을 수정하였다.
39 ~ 42번째 라인, bootRun task실행시 전달받은 시스템 프로퍼티로 debug값이 true로 들어오게 되면 jvmArgs값을 설정하게 된다.
이중에서 bootRun task는 docker 내부의 인스턴스에서 실행될 것이기 때문에
address=*:${debugPort} 값으로 설정해두어서
모든 ip로부터의 해당 port로의 접속을 허용하도록 한다.
위의 이미지는 A 모듈의 bootRun task를 실행시,
컨테이너 내부에서는 9000번 port로 바인딩 하도록 하였고 host PC의 port는 8000으로 해당 port(9000)에 접근하도록 하였다.
다음의 사진은 intelliJ 기준으로 remote JVM debug 설정을 한 이미지이다.
host PC에서 8000번 port로 a-application 인스턴스의 디버그 포트에 접근하고 있다.
docker-compose 실행시 jvmArgs의 suspend=y에 의해서 디버그 포트에 붙기 전에는
스프링 부트 어플리케이션이 실행을 대기하게 된다.
실행화면