1. Dockerfile이 있는데 빌드 서버가 필요할까?

빌드 툴체인을 한 번에 패키징해서 공유하면 편리할 것이다. 물론 도커를 사용하면 가능하다. 개발에 필요한 모든 도구를 배포하는 Dockerfile 스크립트를 작성한 다음 이를 이미지로 만든다. 그리고 애플리케이션 패키징을 위한 Dockerfile 스크립트에서 이 이미지를 사용해 소스 코드를 컴파일함으로써 애플리케이션을 패키징하는 것이다.

FROM diamol/base AS build-stage
RUN echo 'Building...' > /build.txt

FROM diamol/base AS test-stage
COPY --from=build-stage /build.txt /build.txt
RUN echo 'Testion...' >> /build.txt

From diamol/base AS test-stage
COPY --from=test-stage /build.txt /build.txt
CMD cat /build.txt

이 스크립트는 빌드가 여러 단계로 나뉘는 멀티 스테이지 빌드를 적용한 것이다. 각 빌드 단계는 FROM 인스트럭션으로 시작된다. 필요한 경우 빌드 단계에 AS 파라미터를 이용해 이름을 붙일 수도 있다. 위 실습 예제는 세 단계로 나뉜 멀티 스테이지 빌드의 예다. 두 단계는 build-stage, test-stage로 이름이 붙어 있고, 마지막 한 단계는 이름이 없다. 빌드가 여러 단계로 나뉘어 있다고는 하지만, 최종 산출물은 마지막 단계의 내용물을 담은 도커 이미지다.

각 빌드 단계는 독립적으로 실행되지만, 앞선 단계에서 만들어진 디렉터리나 파일을 복사할 수는 있다. COPY 인스트럭션을 보면 --from 인자를 사용해 해당 파일이 호스트 컴퓨터의 파일 시스템이 아니라 앞선 빌드 단계의 파일 시스템에 있는 파일임을 알려 준다. 위 예제에는 build-stage 단계에서 파일 하나를 생성하는데, 이 파일을 test-stage로 복사하고 다시 test-stage에서 생성한 파일을 마지막 단계로 복사한다.

RUN 인스트럭션은 파일을 생성하기 위해 사용했다. RUN 인스트럭션은 빌드 중에 컨테이너 안에서 명령을 실행한 다음 그 결과를 이미지 레이어에 저장하는 기능을 한다. RUN 인스트럭션에서 실행할 수 있는 명령에는 특별한 제한이 없지만 FROM 인스트럭션에서 지정한 이미지에서 실행할 수 있는 것이어야 한다. 여기서는 diamol/base를 기반 이미지로 지정했으며, 이 이미지가 echo 명령을 포함하고 있기 때문에 이 RUN 인스트럭션이 정상적으로 동작한다.

# multi-stage 디렉토리에서 진행
docker image build -t multi-stage .

2. 애플리케이션 빌드 실전 예제

애플리케이션의 소스 코드는 이 책에서 제공하는 소스 코드 중 ch04/exercises/image-of-the-day에서 볼 수 있다. 이 애플리케이션은 표준적인 자바 빌드 도구인 메이븐(Maven)과 OpenJDK를 사용한다. 메이븐은 빌드 절차와 의존 모듈의 입수 방법을 정의하는 도구이고, OpenJDK는 자유로이 재배포가 가능한 자바 런타임이자 개발자 키트다. 메이븐은 빌드 절차가 정의된 XML 문서를 사용하며, mvn 명령을 실행해 사용한다.

FROM diamol/maven AS builder

WORKDIR /usr/src/iotd
COPY pom.xml .
RUN mvn -B dependency:go-offline

COPY . .
RUN mvn package

# app
FROM diamol/openjdk

WORKDIR /app
COPY --from=builder /usr/src/iotd/target/iotd-service-0.1.0.jar .

EXPOSE 80
ENTRYPOINT ["java", "-jar", "/app/iotd-service-0.1.0.jar"]

도커의 레이어 캐시를 최대한 활용할 수 있도록 인스트럭션이 배치되어 있다.

컨테이너는 컨테이너가 실행될 때 부여되는 가상 네트워크 내 가상 IP를 통해 서로 통신한다. 이 가상 네트워크 역시 명령행 인터페이스를 통해 관리할 수 있다.

docker network create nat

이 명령을 실행했을 때 오류 메시지가 출력된다면, nat이라는 이름의 도커 네트워크를 이미 생성했기 때문이다. 이 오류 메시지는 무시해도 좋다. 그리고 컨테이너를 실행할 때 --network 옵션을 사용하면 새로 만들 컨테이너를 연결할 네트워크를 직접 지정할 수 있다. 같은 네트워크 안에 속한 컨테이너 간에는 서로 자유롭게 통신이 가능하다.