[Docker] 멀티 스테이지와 애플리케이션 빌드
도커는 빌드 툴체인을 한 번에 패키징해서 공유할 수 있다. 개발에 필요한 모든 도구를 배포하는 Dockerfile 스크립트를 작성한 다음 이를 이미지로 만든다. 그리고 애플리케이션 패키징을 위한 Dockerfile 스크립트에서 이 이미지를 사용해 소스 코드를 컴파일함으로써 애플리케이션을 패키징하는 것이다.
Docker를 사용하면 빌드 툴체인을 패키징하고 공유하는 것이 가능하다.
빌드 툴체인은 소스 코드를 실행 가능한 애플리케이션으로 변환하는 데 필요한 모든 도구와 프로세스를 말한다. (ex. 컴파일러, 빌드 자동화 도구, 의존성 관리 도구 등)
빌드에 필요한 모든 도구를 포함하는 Dockerfile(ex. 특정 OS, 필요한 시스템 라이브러리, 빌드 도구, 소스 코드 등을 포함)을 작성하여 이미지를 빌드하면, 빌드 툴체인이 완전히 패키징된 Docker 이미지가 생성된다.
이렇게 생성된 이미지는 도커 허브나 로컬 도커 레지스트리에 업로드하여, 다른 개발자나 CI/CD 파이프라인에서 동일한 빌드 툴체인을 사용하여 소스 코드를 컴파일하고 패키징할 수 있다.
멀티 스테이지 빌드
: Docker 이미지의 크기를 줄이고, 빌드 과정을 체계적으로 관리
멀티 스테이지 빌드는 Dockerfile 내에 여러 개의 임시 빌드 단계를 정의하고, 각 단계는 다른 이미지에서 도출될 수 있도록 하는 Docker의 기능이다.
이 기능은 복잡한 애플리케이션의 빌드 프로세스를 단순화하고, 최종 도커 이미지의 크기를 줄일 수 있도록 한다.
예시 1
// 멀티 스테이지 빌드를 적용한 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 'Testing...' >> /build.txt
FROM diamol/base
COPY --from=test-stage /build.txt /build.txt
CMD cat /build.txt

어느 한 단계에서 명령이 실패하면 전체 빌드가 실패한다.
예시 2 - 자바 애플리케이션

애플리케이션 프레임워크의 공식 이미지를 사용하면 애플리케이션의 빌드와 배포가 더욱 간편해진다.
애플리케이션 프레임워크에서 제공하는 공식 이미지는 해당 프레임워크를 실행하는 데 필요한 모든 종속성과 라이브러리를 이미 포함하고 있다. 따라서 개발자는 이를 바탕으로 자신의 애플리케이션을 빌드하고 실행할 수 있다. 이 과정에서 복잡한 환경 설정이나 종속성 설치 등의 작업을 거의 건너뛸 수 있다.
또한 공식 이미지는 그 프레임워크의 최신 업데이트를 반영한다. 이는 보안 패치, 성능 개선, 새로운 기능 추가 등의 혜택을 자동으로 받을 수 있음을 의미한다. 즉, 공식 이미지를 사용하면 개발자가 수동으로 이러한 업데이트를 추적하고 적용하는 데 들어가는 시간과 노력을 크게 줄일 수 있다.
Docker Hub는 수많은 공식 이미지를 제공하며, 대표적인 프레임워크와 언어는 대부분 공식 이미지를 가지고 있다.
개발자는 Dockerfile에서 `FROM` 지시문을 통해 이러한 이미지를 기반으로 자신의 이미지를 만들 수 있다. 이는 강력한 툴체인과 잘 정의된 환경을 제공하므로, 개발자는 애플리케이션 로직에 집중할 수 있게 해준다.
예제 3 - 스프링 부트
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"]
위 Dockerfile은 두 단계로 구성되어 있다.
1. builder 단계
이 단게에서는 'diamol/maven' 이미지를 기반으로 메이븐을 실행하여 필요한 의존 모듈을 다운로드하고, 소스 코드를 복사하여 애플리케이션을 빌드 및 패키징한다. 패키징은 자바 소스 코드를 JAR 파일 형태로 만드는 과정을 말한다. 이 단계에서 빌드 문제가 발생하면, 전체 빌드가 실패하게 된다.
2. app 단계
이 단계에서는 'diamol/openjdk 이미지를 기반으로 작업 디렉터리를 만들고, builder 단게에서 생성한 JAR 파일을 복사한다. 그리고 이 애플리케이션은 웹 서버로, 80번 포트를 사용하기 때문에 이를 EXPOSE하여 외부에 공개한다. ENTRYPOINT는 컨테이너가 시작될 때 실행될 명령어를 지정하며, 여기서는 빌드된 JAR 파일을 실행하는 java 명령이 지정되어 있다.
이 Dockerfile 스크립트를 사용하면, 빌드 도구나 특정 버전의 자바 런타임 설치 없이 소스 코드와 Dockerfile만 있으면 어디서든 애플리케이션을 실행할 수 있다.
또한, 최종적으로 생성되는 애플리케이션 이미지에는 빌드 도구가 포함되지 않는다. 이는 빌드 단계에서 생성된 콘텐츠 중 원하는 내용만 마지막 단계에서 복사해 오도록 함으로써, 이미지의 크기를 최소화한다.
멀티 스테이지 Dockerfile 스크립트의 장점
1. 표준화
개발자는 개발, 빌드 배포 환경을 코드 형태로 정의하고 관리할 수 있으므로 동일한 환경을 보장받는다.
2. 성능 향상
멀티 스테이지 빌드는 각 단계마다 자체 캐시를 사용하여 빌드 시간을 크게 단축시킨다. 도커는 빌드 중에 각 인스트럭션에 해당하는 캐시된 레이어를 검색하고 이를 재사용한다. 빌드 과정에서 캐시가 재사용되지 않는 경우에만 실제로 빌드가 이루어지므로 시간을 절약할 수 있다.
3. 이미지 최적화
멀티 스테이지 Dockerfile을 사용하면 최종 이미지의 크기를 줄일 수 있다. 빌드와 테스트에 필요한 도구들이 모든 단계에 걸쳐 필요한 것이 아니므로, 필요하지 않은 단계에서는 해당 도구를 제거함으로서 이미지 크기를 줄일 수 있다.
이를 통해 애플리케이션의 시작 시간을 단축시키고, 필요 이상의 도구나 라이브러리로 인한 보안 취약점을 해소할 수 있다.
이러한 접근 방식은 CI/CD(Continuous Integration/Continuous Delivery) 전략을 적용하는 데 매우 유용하다. 개발자들은 자주 코드 변경사항을 버전 제어 저장소에 병합하고, 그 변경사항들을 자동으로 테스트하고 빌드하여 배포 환경에 릴리즈하는 것을 목표로 한다. 이는 코드 품질을 유지하고, 오류를 조기에 찾아 해결하는 데에 도움이 된다.
그러나 일반적으로 CI/CD 파이프라인은 Jenkin, Travis CI, CircleCi 등의 도구를 사용하여 구성하고 관리해야 하며, 이는 복잡하고 전문 지식이 필요하다. 반면에 Docker를 사용하면 "docker image build"라는 명령을 통해 애플리케이션의 빌드과정을 간단히 정의하고 실행할 수 있다.