반응형
Docker에서 단일 빌드와 다단계 빌드의 차이점을 알아보자
📌 서론
사이드 프로젝트를 진행하면서 SpringBoot로 백엔드 서버를 구성했고 K8s를 통해 관리하고 있다.
이때 Spring 프로젝트를 Docker로 빌드하여 그 이미지를 pod에 올려서 배포하고 있는데 이 과정에서 스프링부트를 이미지화시키는 Dockerfile을 작성하면서 어떻게 작성해야 Dockerfile을 잘 작성했다고 할 수 있을지에 대한 고민이 생겼다.
그래서 여러 가지 작성 방식을 알아보다가 "단일 단계 빌드, 다단계 빌드"가 존재한다는 것을 알게 되었다.
이번 포스트에서는 이 내용들에 대해서 간단하게 정리해 보도록 하겠다.
1. Dockerfile 코드
단일 단계 빌드 Dockerfile
# 사용할 Java 이미지
FROM eclipse-temurin:21
# ARG는 빌드 시간에 사용할 수 있는 변수를 선언한다.여기서는 JAR_FILE 변수를 선언하고, 초기값으로 ./build/libs/jpashop-0.0.1-SNAPSHOT.jar를 설정했.
# 이는 도커 이미지를 빌드할 때 이 경로의 JAR 파일을 사용하겠다는 것이다.
ARG JAR_FILE=./build/libs/string-0.0.1-SNAPSHOT.jar
# COPY 명령어는 호스트 시스템에서 Docker 컨테이너로 파일이나 폴더를 복사할 때 사용한다.
# JAR_FILE 변수로 지정된 파일을 Docker 이미지 안의 app.jar라는 이름으로 복사하고 있다.
COPY ${JAR_FILE} app.jar
# ENTRYPOINT는 컨테이너가 시작될 때 실행될 명령어를 설정해. 여기서는 java 명령을 사용해 app.jar 파일을 실행하고 있다.
# 추가적으로, -Djava.security.egd=file:/dev/./urandom 옵션을 통해 더 빠른 엔트로피 생성을 위해 urandom 장치를 사용하도록 설정한다.
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
다단계 빌드 Dockerfile
- 다단계 빌드를 사용하면 빌드 과정에서 필요한 도구들은 최종 이미지에 포함되지 않게 할 수 있다. 이는 이미지의 크기를 줄이고 보안을 강화하는 데 도움을 줄 수 있다.
# 첫 번째 단계: 빌드를 위한 임시 이미지
FROM eclipse-temurin:21 as builder # 임시 빌드 이미지로 eclipse-temurin:21을 사용한다.
WORKDIR /app # 빌드 작업을 수행할 작업 디렉터리를 /app으로 설정한다.
COPY . /app # 호스트 시스템의 현재 디렉터리(프로젝트 루트)에서 모든 파일을 컨테이너의 /app 디렉터리로 복사한다. 이렇게 하면 소스 코드와 빌드 스크립트가 컨테이너 내부로 이동한다.
RUN ./gradlew build # Gradle 래퍼를 사용하여 애플리케이션을 빌드한다. 이 명령은 빌드된 JAR 파일을 생성한다.
# 두 번째 단계: 실행을 위한 최종 이미지
FROM eclipse-temurin:21 # 최종 실행 이미지로 또 다시 eclipse-temurin:21을 사용한다. 이는 앞서 빌드 단계에서 사용한 것과 동일한 이미지다.
COPY --from=builder /app/build/libs/*.jar app.jar # 빌드 단계에서 생성된 JAR 파일들을 최종 이미지 내부의 app.jar로 복사한다. -from=builder는 파일을 복사할 때 첫 번째 단계의 결과물을 사용하겠다는 것을 의미한다.
# 컨테이너가 시작될 때 java 명령을 사용해 app.jar를 실행하도록 설정한다. 추가적으로, Djava.security.egd=file:/dev/./urandom 옵션을 통해 더 빠른 엔트로피 생성을 위해 설정하고 있다.
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
2. 각 빌드별 장단점 이해하기
단일 단계 빌드 사용
- 장점:
- 빠른 빌드 시간: 이미 빌드된 JAR 파일을 사용하기 때문에 Docker 이미지 빌드 시간이 매우 짧다.
- 간단한 설정: Dockerfile이 간단하고 이해하기 쉬워서 관리가 용이하다.
- 단점:
- 환경 불일치 가능성: 다양한 개발 환경에서 빌드되어 환경에 따라 결과가 달라질 수 있다.
- 안전성 문제: 빌드 과정에서 발생할 수 있는 보안 문제가 이미지로 전파될 수 있다.
다단계 빌드 사용
- 장점:
- 환경 일관성: 모든 개발자와 배포 파이프라인이 동일한 환경에서 빌드를 수행하여 일관된 결과를 보장한다.
- 보안 및 독립성: 빌드 환경을 완전히 독립적으로 유지하여, 로컬 환경의 문제로부터 자유로워진다.
- 최소화된 런타임 이미지: 빌드 도구나 중간 생산물 없이 순수한 실행 파일만 포함된 깨끗한 이미지를 제공한다.
- 단점:
- 빌드 시간: 종속성 다운로드와 컴파일 프로세스로 인해 빌드 시간이 길어질 수 있다.
- 초기 구성 복잡성: 다단계 빌드를 설정하고 관리하는 것이 복잡할 수 있다.
3. 두 가지 방식으로 이미지 빌드하기
콘솔창에 빌드 명령어 입력하기
docker build . -t string:latest
# docker build {context} -t {name}:{tag}
# docker build . -t string:latest
# 'docker build': Docker 이미지를 빌드하는 명령어
# '.': 현재 디렉터리를 빌드 컨텍스트로 지정
# '-t': 생성된 이미지에 태그를 지정하기 위한 옵션
# 'string': 이미지의 이름
# 'latest': 이미지의 태그
빌드 프로세스를 살펴보자 (단일 단계 빌드)
- 단일 단계의 Dockerfile에서는 이미 빌드된 JAR 파일을 사용하고 있다. Gradle 빌드 과정은 Dockerfile 내에서 실행되지 않으며, COPY 명령을 통해 이미 생성된 JAR 파일만 Docker 이미지로 복사되고 있다.
- 제일 중요한 단계는 [2/2] COPY ./build/libs/string-0.0.1-SNAPSHOT.jar app.jar로, 이미 빌드된 JAR 파일을 이미지로 복사하고 있다. 이는 JAR 파일이 이미 프로젝트의 ./build/libs/ 디렉터리 내에 존재한다는 가정 하에 작업이 진행되고 있는 것이다.
- 아래의 로그를 자세히 보면 Gradle 빌드 프로세스(RUN ./gradlew build)가 없다는 것을 알 수 있다. 즉, 빌드는 도커 이미지 빌드 과정 외부에서 이루어졌다는 것이다.
[+] Building 0.9s (7/7) FINISHED docker:desktop-linux
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 1.18kB 0.0s
=> [internal] load metadata for docker.io/library/eclipse-temurin:21 0.7s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 115B 0.0s
=> CACHED [1/2] FROM docker.io/library/eclipse-temurin:21@sha256:17f4e7fc9aedb22147ff7271b1fac98a5b0e5daf003183610b466fbcce17095a 0.0s
=> [2/2] COPY ./build/libs/string-0.0.1-SNAPSHOT.jar app.jar 0.1s
=> exporting to image 0.1s
=> => exporting layers 0.1s
=> => writing image sha256:ddafc15d360ed121e0985bc2609b41e8ac19e9183defe995c698d806b4d07856 0.0s
=> => naming to docker.io/library/string:0.0.1 0.0s
빌드 프로세스를 살펴보자 (다단계 빌드)
- 여기서는 Docker 내부에서 전체 Gradle 빌드 프로세스가 실행되고 있다. 소스 코드를 이미지로 복사하고 (COPY . /app), 이후 RUN ./gradlew build 명령으로 Gradle을 통해 소스를 컴파일하고 JAR 파일을 생성한다.
- 제일 중요한 단계는[builder 4/4] RUN ./gradlew build이다. 이 부분은 Docker 내부에서 전체 Gradle 빌드 프로세스가 실행되고 있음을 나타낸다. 소스 코드를 컴파일하고 종속성을 다운로드하여 JAR 파일을 생성하는 작업이 이 단계에서 수행되고 있다.
[+] Building 98.2s (10/10) FINISHED docker:desktop-linux
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 463B 0.0s
=> [internal] load metadata for docker.io/library/eclipse-temurin:21 1.3s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load build context 0.1s
=> => transferring context: 312.32kB 0.1s
=> CACHED [builder 1/4] FROM docker.io/library/eclipse-temurin:21@sha256:17f4e7fc9aedb22147ff7271b1fac98a5b0e5daf003183610b466fbcce17095a 0.0s
=> CACHED [builder 2/4] WORKDIR /app 0.0s
=> [builder 3/4] COPY . /app 0.1s
=> [builder 4/4] RUN ./gradlew build 96.4s
=> [stage-1 2/2] COPY --from=builder /app/build/libs/*.jar app.jar 0.0s
=> exporting to image 0.1s
=> => exporting layers 0.1s
=> => writing image sha256:5b341a04479a53139829ba588ae7527f56806d3c70e7ad1018930f7516cfc129 0.0s
=> => naming to docker.io/library/string:0.0.1
4. 근데 왜 다단계 빌드로 진행하면 속도가 느릴까?
빌드 결과 시간초를 확인해보면 다단계 빌드가 엄청나게 느린것을 확인할 수 있다.
# 단일 단계 빌드
[+] Building 0.9s (7/7) FINISHED
# 다단계 빌드
[+] Building 98.2s (10/10) FINISHED
종속성 다운로드
- 다단계 빌드는 Docker 컨테이너 내부에서 Gradle 빌드 과정을 수행하기 때문에, 필요한 모든 종속성을 다운로드해야 한다. 이는 시간이 많이 걸릴 수 있다.
컴파일 프로세스
- 소스 코드를 컴파일하고 JAR 파일을 생성하는 과정이 Docker 빌드 과정에 포함되어 있어, 빌드 시간이 길어진다.
캐시 사용 제한
- 다단계 빌드는 각 단계에서 캐시를 사용할 수 있지만, 소스 코드나 종속성에 변경이 발생하면 전체 빌드 과정을 다시 수행해야 하므로 빌드 시간이 증가할 수 있다.
따라서, 다단계 빌드는 이미지 크기를 최소화하고 보안을 강화하는 장점이 있지만, 빌드 과정이 더 복잡하고 시간이 많이 소요될 수 있다. 프로젝트의 요구 사항에 따라 적절한 빌드 방식을 선택하는 것이 중요하다.
5. 어떤 상황에 어떤 빌드를 사용하는 게 좋을까?
프로젝트의 크기와 복잡성
- 큰 규모의 프로젝트나 많은 의존성을 가진 프로젝트에서는 다단계 빌드를 사용하는 것이 더 낫다고 할 수 있다. 이는 빌드 환경을 깔끔하게 유지하고, 의존성 관리를 용이하게 해 준다.
CI/CD 파이프라인의 구성
- 자동화된 파이프라인에서 빌드와 배포가 수행된다면, 다단계 빌드가 환경 일관성을 보장하는 데 도움이 된다.
개발 및 배포 주기
- 빠른 개발 및 테스트 주기가 필요한 경우, 단일 단계 빌드가 시간을 절약하는 데 유리할 수 있다.
반응형
'DevOps > Docker' 카테고리의 다른 글
[Docker] JIB로 이미지 빌드하기 (3) | 2024.05.19 |
---|