ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 스프링 가이드 - docker build
    책책책 책을 읽읍시다/프로그래밍 2024. 2. 14. 22:51

    들어가며

    저번 글(https://baby-care-dev.tistory.com/65)에서는 간단한 RESTful API를 만들어보았다. 이번엔 이 프로젝트를 docker 이미지로 빌드해서 코드와 환경설정 등을 다른 사람들도 그대로 가져다 쓸 수 있게 해보자. 그래야 같은 설정으로 각기 다른 엔드포인트들을 테스트해볼 수 있으니까 말이다.


    준비물


    Dockerfile 이란?

    이미지를 빌드하는 스크립트라고 보면 된다. 크게 아래와 같은 내용을 기술하며 자세한 내용은 공식 가이드(https://docs.docker.com/engine/reference/builder/)를 참조하자.

    • 베이스 이미지의 리포지터리
    • 설치할 패키지
    • 애플리케이션 코드와 설정 파일
    • 컨테이너 가동 시 실행될 명령어

    Dockerfile 만들기

    프로젝트 최상위 경로에 Dockerfile이라고 만들어주면 된다.

    도커 파일 만들 위치

     

    결론부터 보자. 가이드 링크에서 Multi-Stage Build에 있는 스크립트를 그대로 가져왔다. 여기서 하드코딩된 스프링부트 메인 클래스와 EXPOSE port만 손봤다.

    # syntax=docker/dockerfile:experimental
    FROM eclipse-temurin:17-jdk-alpine AS build
    WORKDIR /workspace/app
    
    COPY . /workspace/app
    RUN --mount=type=cache,target=/root/.gradle ./gradlew clean build
    RUN mkdir -p build/dependency && (cd build/dependency; jar -xf ../libs/*-SNAPSHOT.jar)
    
    FROM eclipse-temurin:17-jdk-alpine
    VOLUME /tmp
    ARG DEPENDENCY=/workspace/app/build/dependency
    COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib
    COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF
    COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app
    
    EXPOSE 8080
    
    ENTRYPOINT ["java","-cp","app:app/lib/*","com.example.greeting.GreetingApplication"]

     

    상단부터 하나씩 보자.

     

    FROM eclipse-temurin:17-jdk-alpine AS build

    FROM은 베이스 이미지를 지정하는 것이다. 이미지가 로컬에 없으면 도커 허브에서 다운로드한다. eclipse-temurin은 JDK 벤더이고 뒤에 붙은 alpine은 가장 기본적인 기능들만 제공하는 경량 이미지에 들어가는 tag이다. 뒤에 AS build는 옵셔널한 값으로 빌드 스테이지를 뜻한다. 여기서는 multi stage build라고 해서 gradle로 먼저 빌드하고 실행할 도커 이미지는 실행에 관련한 패키지들만 포함시켜 경량화하는 방법을 썼는데 각 stage 명을 명시하기 위함이다.

     

    WORKDIR /workspace/app

    RUN, CMD, ENTRYPOINT, COPY 같은 다른 명령어들이 수행될 working directory를 지정하는 것으로 /workspace/app 에서 도커 관련 작업을 하겠다는 의미이다. 명시한 경로가 없으면 새로 생성한다.

     

    COPY . /workspace/app

    COPY는 말그대로 복사하는 명령어이다. 현재 컨테이너(.)에 있는 모든 파일들을 /workspace/app으로 복사하겠다는 뜻이다.

     

    RUN --mount=type=cache,target=/root/.gradle ./gradlew clean build

    RUN으로 컨테이너에서 실행할 명령어를 지정할 수 있다. 의존 라이브러리는 gradle 빌드 캐시를 사용하여 속도를 올리고, 프로젝트 clean 후에 build하겠다고 실행 내용을 지시한 것이다.

     

    RUN mkdir -p build/dependency && (cd build/dependency; jar -xf ../libs/*-SNAPSHOT.jar)

    build/dependency 디렉터리를 만들고 여기에 스프링부트 jar 파일 압축을 푼다.

     

    FROM eclipse-temurin:17-jdk-alpine

    두 번째 스테이지를 설정한 것이다. 마찬가지로 eclipse-temurin alpine 버전을 베이스로 한다.

     

    VOLUME /tmp

    VOLUME은 호스트나 다른 컨테이너와 같은 컨테이너 외부와 데이터를 공유할 때 사용할 디렉터리를 지정하는 지시어이다.

    생성된 컨테이너로 들어가면 아래와 같이 8080 톰캣 포트 관련 파일이 생성된 것을 볼 수 있다.

    VOLUME으로 지정한 tmp 내용

     

    ARG DEPENDENCY=/workspace/app/build/dependency

    ARG는 스크립트 내부에서 사용할 사용자 변수라고 보면된다. /workspace/app/build/dependency 경로를 여러번 쓸거라 변수 지정하였다.

     

    COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib
    COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF
    COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app

    이전 빌드 스테이지인 build에서 압축 풀었던 파일들을 컨테이너의 /app 디렉터리에 복사한다라는 의미다. 그래야 스프링부트 애플리케이션을 구동할 수 있으니까 말이다. 간단하게 jar 파일만 가져와서 실행시킬수도 있는데, FatJar다 보니 용량이 큰 단점이 있는데 스프링부트 layered 아키텍처의 경량화된 장점을 살리기 위해 각각 가져와 컨테이너 이미지 용량을 줄이기 위함이다.

     

    EXPOSE 8080

    EXPOSE는 도커 컨테이너가 어떤 포트를 열 것인지를 정의할 때 쓴다. 샘플 애플리케이션에서 사용하는 8080 포트를 열도록 설정했다.

     

    ENTRYPOINT ["java","-cp","app:app/lib/*","com.example.greeting.GreetingApplication"]

    ENTRYPOINT는 컨테이너가 시작될 때 실행될 명령어를 지정한다. java 명령어로 GreetingApplication을 실행한다. 여기에 heap 메모리 설정 등을 추가하면 된다.

     


    Docker 빌드와 실행

    이제 docker build -t greeting:1.0 . 명령어로 빌드를 하면 아래와 같이 도커 이미지가 빌드된다.

    -t는 tag란 뜻이고 현재 위치(.)에서 greeting:1.0 태그로 빌드하겠다는 뜻이다.

     

    docker images로 로컬 도커 이미지들을 검색해보면 아래와 같이 greeting 이미지가 생성된 것을 확인할 수 있다.

    multi-stage로 안하고 full stage에서 fatJar로 만들면 바로 밑에 있는 842MB 만한 이미지가 만들어지는데, 335MB 까지 줄인 것이다.

     

    도커 이미지가 만들어 졌으니 docker run -p 8080:8080 greeting:1.0 명령어로 컨테이너를 구동하면 된다.

     

    그럼 intelliJ나 스프링부트 jar로 실행했을 때와 마찬가지로 로컬에서 기존 엔드포인트로 테스트할 수 있다.

     


    마무리

    이번 글을 올리면서 자세히 몰랐던 스프링부트 layer나 각종 도커 기본 설정에 대해 알 수 있어 학습효과가 있었다. 회사에서 쓰는 도커 설정의 한줄 한줄씩 이해할 수 있게 되었고, 개선할 점도 찾아 의미있는 시간이었다.

    이제 남은 건 그라파나 연동과 k6로 부하 테스트이다. 열심히 또 배워보자.

     

     

    댓글

Designed by Tistory.