비용 절감과 성능 향상을 위해 Docker 이미지 최적화

junhyeong·2024년 2월 4일
0

뽀모도로 메이트

목록 보기
5/5
post-thumbnail

실제 사용자가 있는 서비스를 목표로 프로젝트를 진행하다보니 프로덕션 서버 외에 개발 서버가 필요해졌습니다. 하지만 아직 MVP 규모이고 수익모델이 없기 때문에 비용문제를 생각하지 않을 수 없었습니다.

그래서 개발서버는 따로 배포하지 않고, Docker를 이용해 Local에서 실행하는 방법을 선택하게 되었습니다.

또한 Docker를 이용한 방법에서도 Docker 이미지 용량에 따른 비용 차이가 있어 이미지 최적화를 진행하여 비용을 줄였습니다. 그리고 그 결과 이미지 용량을 568MB에서 163MB까지 줄일 수 있었습니다.

이번 글에서는 간단하게 Docker 컨테이너 저장소를 비교해보고, Docker 이미지를 최적화하는 방법에 대해 알아보겠습니다.

Docker 컨테이너 저장소 비교

프로젝트를 수행하면서 팀원 간에 Docker 이미지를 공유하려면 Docker 이미지를 저장하고 배포할 수 있는 레지스트리가 필요합니다. 이러한 요구를 충족시키기 위해 AWS에서 제공하는 ECR(Elastic Container Registry)과 Docker Hub를 비교해 보았습니다.

AWS ECR(Elastic Container Registry)

Amazon Web Services(AWS)가 제공하는 컨테이너 레지스트리 서비스입니다.

AWS IAM(Identity and Access Management)을 사용하여 리소스 기반 권한으로 프라이빗 도커 리포지토리를 지원하므로 특정 사용자나 Amazon EC2 인스턴스가 리포지토리 및 이미지에 액세스할 수 있습니다. 또한 Docker CLI를 사용하여 이미지 푸시, 가져오기 및 관리할 수 있습니다.

ECR의 비용은 저장 공간과 데이터 전송량에 따라 청구되며, AWS의 다른 서비스와 함께 사용할 경우 추가 비용이 발생할 수 있습니다. 모든 ECR 레포지토리는 기본적으로 프라이빗으로 설정되어 있어, 보안을 중요시하는 기업에게 적합합니다.

프리티어로 AWS를 이용하는 경우 public은 월 50GB, private은 월 500MB까지 무료로 전송할 수 있습니다.

Docker Hub

Docker Hub는 Docker 사가 운영하는 컨테이너 레지스트리로, 가장 널리 사용되는 컨테이너 레지스트리 중 하나입니다. Docker Hub는 다양한 CI/CD 도구와의 통합을 가능하게 하며, Docker CLI와 호환되어 Docker가 설치된 모든 시스템에서 접근 가능합니다.

Docker Hub는 무료 플랜에서 한 달에 한정된 수의 pull 요청을 제공하며, 더 많은 요청이 필요한 경우 유료 플랜을 선택해야 합니다. 또한, 무료 플랜에서는 한 개의 프라이빗 레포지토리를 제공하며, 더 많은 프라이빗 레포지토리가 필요한 경우 유료 플랜을 선택해야 합니다.


저는 Docker Hub를 선택하였습니다. 실제로 운영할 서비스인만큼 보안이 중요하기 때문에 Private 저장소를 사용한다고 가정했을 때, 비용측면에서 Docker Hub를 이용하는게 더 저렴하기 때문입니다.

먼저 ECR을 이용하면 현재 Docker 이미지의 크기가 163MB이기 때문에 월 한도 500MB를 3번만에 초과해버리고 지속적으로 비용이 나가게 됩니다.

하지만 Docker Hub를 이용하면 (공용계정을 사용한다고 가정했을 때) 무료로 6시간마다 200개의 이미지를 받아올 수 있기 때문에 무료로 사용할 수 있습니다.

Docker 이미지 최적화

앞에서 말했듯 비용을 최소화하기 위해 Docker 이미지를 최적화 했습니다.

이 외에도 Docker 이미지 최적화에는 다양한 이점이 있습니다.

  1. 빠른 배포: 이미지 크기가 작을수록 컨테이너를 시작하는 데 필요한 시간도 줄어듭니다. 따라서 빠른 배포가 가능해집니다.
  2. 저장 공간 절약: 최적화된 이미지는 더 적은 디스크 공간을 차지합니다. 이는 저장 공간을 절약하고, 레지스트리에서 이미지를 더 빠르게 pull하고 push할 수 있게 해줍니다.
  3. 보안 강화: 불필요한 소프트웨어나 파일을 제거함으로써 공격 범위를 줄일 수 있습니다. 이는 컨테이너의 보안을 강화하는 데 도움이 됩니다.
  4. 네트워크 대역폭 절약: 작은 이미지는 네트워크 대역폭을 덜 사용하므로 원격 서버 간 이미지 전송에 있어 이점을 제공합니다.

스프링 부트 기반으로 개발한 어플리케이션을 컨테이너 이미지로 만들 때 이미지를 최적화하는 방법에 대해 알아보겠습니다.

기존의 Dockerfile

기존에 작성해두었던 Dockerfile입니다.

FROM openjdk:17 

COPY build/libs/*.jar app.jar

ENTRYPOINT ["java", "-Dspring.profiles.active=production", "-jar", "./app.jar"]

base 이미지로 openjdk:17을 사용했고 build/libs 디렉토리에 있는 모든 .jar 파일을 Docker 이미지에 'app.jar'라는 이름으로 복사하여 실행하는 방법입니다.

이미지 사이즈가 568MB인걸 확인할 수 있습니다.

1. 베이스 이미지 변경

보통 이미지를 작게하기 위해서 Base 이미지 태그로 alpine 이나 slim 을 많이 사용합니다.

따라서 더 작은 사이즈로 만들기 위해서 alpine 이미지를 찾아서 적용해보겠습니다.
저는 Base 이미지를 amazoncorretto 로 변경했습니다.

Amazon Corretto란 무료로 사용할 수 있는 Open Java Development Kit (OpenJDK) 의 프로덕션용 멀티플랫폼 배포판입니다.

  • 성능: Corretto는 AWS의 다양한 서비스에서 테스트되고 사용되며, 높은 성능을 제공합니다.
  • 호환성: Corretto는 OpenJDK와 완벽하게 호환되므로, OpenJDK에서 실행되는 애플리케이션을 수정 없이 Corretto에서도 실행할 수 있습니다.
  • 무료: Corretto는 무료로 사용할 수 있으며, 추가 비용 없이 프로덕션 환경에서 사용할 수 있습니다.
  • 장기 지원: Corretto는 LTS 버전에 대해 최소 4년 동안 무료로 업데이트를 제공합니다.
FROM amazoncorretto:17-alpine

COPY build/libs/*.jar app.jar

ENTRYPOINT ["java", "-Dspring.profiles.active=production", "-jar", "./app.jar"]

다시 사이즈를 확인해보면 568MB에서 354MB로 줄어든걸 확인할 수 있습니다.

장점:

  • 베이스 이미지를 경량화된 버전으로 변경하여 이미지 크기를 줄일 수 있습니다.
  • 경량화된 이미지는 더 빠른 다운로드와 배포가 가능합니다.

고려 사항:

  • 경량화된 이미지를 사용할 경우 해당 이미지에 포함된 패키지 및 라이브러리가 충분한지 확인해야 합니다.

2. Multi Stage

Multi Stage는 여러 개의 BaseImage를 사용하여 docker build를 수행합니다.

FROM키워드를 기준으로 작업공간이 분리되는데, 분리된 작업공간을 stage라고합니다. Multi Stage는 stage가 2개 이상일 때 해당합니다.

stage가 여러개인 상태로 docker build를 수행하게 되면 가장 마직막에 실행된 stage 작업이 Docker 이미지로 생성됩니다.

그리고 이 특성을 이용하면 Docker 이미지의 사이즈를 줄일 수 있습니다. stage를 build하는 stage와 build결과를 실행하는 stage로 분리하는 것입니다. 이를 통해 불필요한 파일이나 빌드 도구 등을 최종 이미지에서 제외할 수 있어 이미지 사이즈를 줄일 수 있습니다.

FROM gradle:8.5-jdk17 as builder

COPY . /app

WORKDIR /app

RUN gradle clean build

#----------------------------

FROM amazoncorretto:17-alpine

COPY --from=builder app/*.jar app.jar

ENTRYPOINT ["java", "-Dspring.profiles.active=production", "-jar", "./app.jar"]
  1. builder 스테이지: 여기에서는 gradle:8.5-jdk17 이미지를 베이스로 하여 소스 코드를 컴파일하고 JAR 파일을 생성합니다. 이 스테이지에서는 Gradle 빌드 도구와 소스 코드, 그리고 빌드 중에 생성되는 여러 임시 파일들이 이미지에 포함됩니다.

  2. amazoncorretto:17-alpine 스테이지: 여기에서는 최종 이미지를 생성합니다. 이 스테이지에서는 COPY --from=builder build/libs/*.jar app.jar 명령을 사용하여 builder 스테이지에서 생성된 JAR 파일만을 복사합니다. 그외의 빌드 도구나 임시 파일들은 이 이미지에 포함되지 않습니다.

따라서, 이 Dockerfile을 사용하면 빌드 도구와 임시 파일들이 포함되지 않은 최종 이미지를 얻을 수 있습니다.

장점:

  • 여러 개의 stage를 사용하여 필요한 파일만 최종 이미지에 포함시킬 수 있습니다.
  • 불필요한 파일이나 빌드 도구 등을 최종 이미지에서 제외하여 이미지 크기를 줄일 수 있습니다.

고려 사항:

  • Multi Stage를 사용할 경우 Dockerfile 작성이 복잡해질 수 있습니다.
  • 각 stage에서 필요한 의존성 및 파일을 올바르게 설정해야 합니다.

3. alpine에 추가

마지막으로 기본 alpine 이미지에 JRE 와 어플리케이션을 설치하는 방법으로 이미지 사이즈를 줄여보겠습니다

# 첫 번째 스테이지: JRE 생성
FROM amazoncorretto:17-alpine3.18 as builder-jre

RUN apk add --no-cache --repository=http://dl-cdn.alpinelinux.org/alpine/edge/main/ binutils=2.41-r0

RUN $JAVA_HOME/bin/jlink \
    --module-path "$JAVA_HOME/jmods" \
    --verbose \
    --add-modules ALL-MODULE-PATH \
    --strip-debug \
    --no-man-pages \
    --no-header-files \
    --compress=2 \
    --output /jre

# 두 번째 스테이지: 애플리케이션 빌드
FROM gradle:8.5-jdk17 as build

WORKDIR /app
COPY . /app

RUN gradle clean build

# 세 번째 스테이지: 최종 이미지 생성
FROM alpine:3.18.4

ENV JAVA_HOME=/jre
ENV PATH="$JAVA_HOME/bin:$PATH"

COPY --from=builder-jre /jre $JAVA_HOME

COPY --from=build /app/build/libs/*.jar app.jar

ENTRYPOINT ["java", "-Dspring.profiles.active=production", "-jar", "./app.jar"]
  1. 첫 번째 스테이지
  • binutils를 설치합니다. 이 패키지는 바이너리 파일을 조작하는 도구를 포함하고 있으며 strip-debug 옵션을 사용하기 위해 필요합니다.
  • jlink 도구를 사용하여 사용자 정의 JRE를 생성합니다. 이 JRE는 애플리케이션 실행에 필요한 최소한의 Java 모듈만 포함하고 있습니다.
  1. 두 번째 스테이지
  • /app 디렉토리를 작업 디렉토리로 설정하고, 현재 디렉토리의 모든 파일을 /app 디렉토리에 복사합니다.
  • gradle clean build 명령을 실행하여 애플리케이션을 빌드합니다.
  1. 세 번째 스테이지
  • 첫 번째 스테이지에서 생성한 사용자 정의 JRE를 이미지에 복사합니다.
  • 두 번째 스테이지에서 빌드한 애플리케이션 JAR 파일을 이미지에 복사합니다.
  • java -Dspring.profiles.active=production -jar ./app.jar 명령을 실행하여 애플리케이션을 시작합니다.


docker build를 실행하면 최종적으로 이미지 사이즈가 163MB로 줄어든걸 확인할 수 있습니다.

장점:
Alpine Linux는 경량화된 리눅스 배포판으로, 작은 이미지 사이즈와 빠른 속도를 제공합니다.
JRE와 애플리케이션을 함께 설치하여 최종 이미지의 크기를 크게 줄일 수 있습니다.

고려 사항:
Alpine Linux는 경량화되어 있지만 특정 라이브러리나 패키지의 호환성 문제가 발생할 수 있습니다.
필요한 패키지 및 라이브러리가 모두 포함되어 있는지 확인해야 합니다.

마치며

이번 글에서는 Docker 컨테이너 저장소를 비교하고, Docker 이미지를 최적화하는 방법에 대해 다뤄보았습니다. 개발 및 프로덕션 환경에서 Docker를 사용하는 경우 이미지 사이즈를 최적화하여 비용을 절감하고 성능을 향상시킬 수 있습니다.

먼저 Docker Hub와 AWS ECR을 비교하여 두 서비스의 특징과 장단점을 살펴보았습니다. Docker Hub는 다양한 CI/CD 도구와의 통합을 지원하고, 무료 플랜에서도 사용이 가능한 반면, AWS ECR은 AWS의 다양한 서비스와의 통합이 가능하며 보안 측면에서 우수한 기능을 제공합니다.

그리고 Docker 이미지를 최적화하기 위해 Base 이미지 변경, Multi Stage, 그리고 Alpine Linux를 사용하는 방법을 소개하였습니다. 이를 통해 최종적으로 이미지 사이즈를 568MB에서 163MB로 줄일 수 있었습니다.

참고

profile
매일매일이 성장하는 하루가 될 수 있도록!

1개의 댓글

comment-user-thumbnail
2024년 4월 11일

좋은 글 너무나 잘봤습니다. 실무에서도 이렇게 하나요 ㄷㄷ

2024.04.11 기준
지금은 binutils=2.42-r0 이걸로 되나봐요!!

따라하시는분 참고요~

답글 달기