SW 개발/Spring

Spring Boot, Docker 이미지 빌드하기

지단로보트 2020. 11. 10. 10:16

개요

  • 이번 글에서는 Spring Boot 기반 애플리케이션을 Docker 이미지로 빌드하는 방법을 소개하고자 한다.

사전 참고할만한 글

Docker 이미지 빌드 및 실행

  • 아래는 가장 일반적인 방법의 Docker 이미지 빌드 및 실행 방법이다.
### 애플리케이션 빌드
$ ./gradlew build

# Dockerfile 생성
$ nano Dockerfile
FROM amazoncorretto:17 # FROM public.ecr.aws/bitnami/java:17-prod
ARG JAR_FILE=build/libs/*.jar
COPY ${JAR_FILE} app.jar
ENV JAVA_OPTS=""
CMD java $JAVA_OPTS -server -jar app.jar

# Docker 이미지 빌드
$ docker build -t foo/bar .

# 빌드된 이미지 확인
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
foo/bar    latest              422fb2d589f3        2 minutes ago       734MB

# Docker 컨테이너 실행
$ docker run -d -p 8080:80 foo/bar
9442d550adb3

# Docker 컨테이너 로그 확인
$ docker logs -f 9442d550adb3

# 실행 중인 Docker 컨테이너 목록 확인
$ docker ps
CONTAINER ID        IMAGE               COMMAND                CREATED              STATUS              PORTS                  NAMES
9442d550adb3        foo/bar    "java -jar /app.jar"   About a minute ago   Up About a minute   0.0.0.0:8080->80/tcp   elated_jackson

# 실행 중인 Docker 컨테이너 쉘 접속
$ docker exec -it 9442d550adb3 /bin/bash
bash-4.2#

# Docker 컨테이너 종료
$ docker stop 9442d550adb3

Spring Boot 2.3.x 플러그인으로 Docker 이미지 빌드

  • Spring Boot 2.3부터 Gradle 플러그인으로 Docker 이미지 빌드를 지원한다. 로컬에 Docker 데몬 설치를 요구하며, Dockerfile 파일을 이용하지 않고 빌드한다.
# docker.io/foo/bar/latest 이름의 이미지를 빌드
$ gradlew bootBuildImage --imageName=foo/bar

# 멀티 프로젝트일 경우 특정 프로젝트의 이미지를 빌드
$ gradlew {project_name}:bootBuildImage --imageName=foo/bar
  • 생성한 이미지에 대해 Docker 컨테이너 실행시 JAVA_OPTS 환경 변수를 아래와 같이 전달하여 실행할 수 있다.
# 컨테이너 실행
$ docker run -m 8g -d -p 8080:8080 --env JAVA_OPTS="-Xms512m -Xmx512m -Dspring.profiles.active=prod" foo/bar
{container_id}

# 실행 중인 컨테이너 쉘 접속
$ docker exec -it {container_id} sh

# 현재 uid
> id
uid=1000(cnb) gid=1000(cnb) groups=1000(cnb)

# 실행 중인 프로세스
> ps -ef
UID PID PPID C STIME TTY TIME     CMD
cnb   1    0 8 06:53 ?   00:00:11 java org.springframework.boot.loader.JarLauncher
  • 플러그인으로 빌드된 컨테이너를 실행하면 별도의 CMD를 정의하지 않아도 Spring Boot 애플리케이션이 자동으로 실행된다.

트러블슈팅: 컨테이너 파일 생성시 한글 파일명 깨짐 문제

  • 애플리케이션에서 빌드된 컨테이너 런타임에서 파일 저장시 파일명에 한글을 포함한 CJK 계열 문자열이 포함될 경우 ???와 같이 문자열이 깨지는 이슈를 있어 유의해야 한다.
  • 원인은 도커 컨테이너 이미지의 기본 로케일이 POSIX로 설정되어 있기 때문인데 이를 C.UTF-8로 수정하면 해결된다. 수정 방법은 도커 컨테이너 실행시 전달되는 환경 변수에 아래 항목을 추가하면 된다. [관련 링크]
LC_ALL=C.UTF-8

AWS에서의 Docker 이미지 배포 전략

  • 생성한 Docker 이미지를 AWS 생태계에 배포하여 서비스를 운영하는 방법에는 2가지가 있다. 2가지 경우 모두, 사전 조건으로 Amazon ECR에 리파지터리를 생성하고 빌드한 이미지를 푸시해야 한다.
  • 첫째, EC2 인스턴스를 생성하고 Docker 컨테이너를 직접 운영하는 것이다. 오토 스케일링은 EC2의 기능을 이용하게 되며 인스턴스 관리를 직접 해야 하는 부담이 있다.
  • 둘째, AWS Fargate에서 푸시한 이미지를 기반으로 태스크를 생성하는 것이다. 완전한 매니지드 서비스로 관리할 수 있는 장점을 가진다. 이 경우 AWS CloudFormation 또는 Terraform 도구를 이용하여 완전한 코드에 의한 인프라 관리(IaS)가 가능하다.

Amazon ECR에 이미지 푸시

  • Amazon ECRAWS가 제공하는 프라이빗 도커 레지스트리 상품이다. 개인적으로 또는 상업적인 목적으로 생성한 이미지를 자유롭게 push/pull할 수 있다.
  • Docker 이미지를 푸시하려면 먼저 리파지터리가 생성되어야 한다. 생성된 리파지터리에는 태그 단위로 여러 버전의 이미지를 푸시할 수 있다. 리파지터리 생성 방법은 아래와 같다.
Amazon ECR 콘솔 접속 → [리포지토리 생성] 클릭
→ 리포지토리 이름: foo/bar
→ [리포지토리 생성] 클릭
  • 생성된 리파지터리에 Docker 이미지를 푸시하는 방법은 아래와 같다.
# Amazon ECR 로그인 비밀번호 획득
$ aws ecr get-login-password --region ap-northeast-2
{login-password}

# 앞서 획득한 비밀번호를 이용하여 Docker 리파지터리 로그인
$ docker login -u AWS -p {login-password} xxxxx.dkr.ecr.ap-northeast-2.amazonaws.com
Login Succeeded

# ECR에 푸시할 이미지 태그를 지정하고, 생성한 리파지터리를 연결
$ docker tag foo/bar:latest xxxxx.dkr.ecr.ap-northeast-2.amazonaws.com/foo/bar

# ECR에 생성한 리파지터리에 이미지를 푸시
$ docker push xxxxx.dkr.ecr.ap-northeast-2.amazonaws.com/foo/bar

AWS Fargate로 Docker 이미지 기동

  • 앞서 푸시한 Docker 이미지는 다양한 방식으로 온전한 애플리케이션의 형태로 기동이 가능하다. 최근 AWS 생태계에서 가장 선호되는 방식은 AWS Fargate를 이용하여 완전한 매니지드 형태로 애플리케이션을 서비스하고 관리하는 것이다. 자세한 내용은 이 글을 참고한다.

참고 글