-
호스트 빌드에서 도커의 멀티 스테이지 빌드까지인프라/도커 2024. 1. 3. 01:22
도커에 익숙해지기 위한 몇 가지 실습들을 진행해보았습니다.
아래 순서로 글을 진행해보겠습니다.
1. 로컬에서 스프링 프로젝트 빌드해서 실행하기
2. 로컬에서 빌드한 파일을 컨테이너에서 실행시키기
3. 컨테이너에서 빌드 후 실행시키기
4. 멀티스테이지 빌드 후 실행시키기
1. 로컬에서 빌드해서 실행하기
개발환경에서 가장 기초적으로 사용하는 세팅입니다.
localhost에서 서버를 띄우는 세팅입니다.
스프링 cli를 이용해서 프로젝트를 생성하고 8080에 접속했을 때 'Hello World'가 보이도록 index.html을 추가합니다.
$ spring init --dependencies=web myserver
빌드명령어를 실행해서 jar파일을 생성합니다.
$ ./gradlew build
빌드된 파일을 실행시켜서 웹서버를 띄웁니다.
$ java -jar ./build/libs/myserver-0.0.1-SNAPSHOT.jar
IDE에서 bootRun으로 실행시키는 과정을 수동으로 진행해보았습니다.2. 로컬에서 빌드한 파일을 컨테이너에서 실행시키기
서버를 띄운다는 것은 빌드된 파일을 멈추지 않는 환경에서 실행시킨다고 생각할 수 있습니다.
도커를 사용해서 컨테이너 환경에서 웹서버를 띄워보겠습니다.
이미지 생성을 위해서 Dockerfile을 작성합니다.
# 빌드된 파일이 실행되어야할 환경을 컨테이너에 설치 FROM eclipse-temurin:17 # 호스트에서 빌드된 파일을 컨테이너로 복사 COPY ./build/libs/myserver-0.0.1-SNAPSHOT.jar ./app.jar # 컨테이너 실행시에 app.jar를 실행 ENTRYPOINT ["java","-jar","/app.jar"]
도커 이미지를 생성합니다.
# -t 옵션은 이미지에 이름 부여 $ docker build -t myserver .
만들어진 이미지를 사용해서 컨테이너를 실행시킵니다.
# -p 옵션 [호스트포트]:[컨테이너포트] 맵핑 $ docker run -it -p 8090:8080 myserver
호스트의 8090에 요청시 "Hello world" 확인 가능
3. 컨테이너에서 빌드 후 실행시키기
2번처럼 도커를 사용한다면 도커의 큰 장점을 포기하는 것과 같습니다.
자바가 JVM을 통해서 OS에 관계 없이 실행할 수 있느 것이 큰 장점인 것 처럼, 도커의 컨테이너 기술도 호스트 환경에 관계없이 동일한 환경을 제공하는 것에 큰 이점이 있습니다. 도커 컨테이너 안에서 작동하는것은 호스트 환경과 무관하게 컨테이너가 실행만 되면 동일한 동작을 보장하기 때문입니다.
빌드에 필요한 요소들을 컨테이너로 복사하는 Dockerfile을 생성합니다.
# 빌드된 파일이 실행되어야할 환경을 컨테이너에 설치 FROM eclipse-temurin:17 # 컨테이너의 작업 경로 설정 WORKDIR ./workspace/app # 호스트에서 빌드된 파일을 컨테이너로 복사 COPY ./gradlew /workspace/app/ COPY ./settings.gradle /workspace/app/ COPY ./build.gradle /workspace/app/ COPY ./gradle /workspace/app/gradle/ COPY ./src /workspace/app/src/ # 빌드 실행 RUN /workspace/app/gradlew build
위 Dockerfile을 사용해서 도커 이미지를 빌드하고 스프링 서버가 잘 빌드되었는지 확인합니다.
$ docker build -t myapp . $ docker run -it myapp bash
컨테이너의 bash를 사용해서 / 경로로 이동하면 'WORKDIR' 로 설정한 workspace를 찾아볼 수 있습니다.
build 폴더를 찾아가면 빌드된 파일을 확인할 수 있습니다.
도커 이미지 생성 중에 문제가 있다면 이처럼 bash로 접속해서 한 단계씩 확인해 볼 수 있습니다.
그러면 도커 이미지 실행시 바로 서버가 실행될 수 있도록 Dockerfile을 수정해줍니다.
# 빌드된 파일이 실행되어야할 환경을 컨테이너에 설치 FROM eclipse-temurin:17 # 컨테이너의 작업 경로 설정 WORKDIR ./workspace/app # 호스트에서 빌드된 파일을 컨테이너로 복사 COPY ./gradlew /workspace/app/ COPY ./settings.gradle /workspace/app/ COPY ./build.gradle /workspace/app/ COPY ./gradle /workspace/app/gradle/ COPY ./src /workspace/app/src/ # 빌드 실행 RUN /workspace/app/gradlew build # 빌드 파일 위치 이동 RUN mv /workspace/app/build/libs/myserver-0.0.1-SNAPSHOT.jar /workspace/app/app.jar # 컨테이너 실행시에 app.jar를 실행 ENTRYPOINT ["java","-jar","app.jar"]
이제 도커에서 서버를 실행시킵니다.
호스트의 8090 포트로 접속하면 도커 컨테이너에 떠있는 서버를 호출할 수 있습니다.
$ docker run -it -p 8090:8080 myapp
호스트의 8090에 요청시 "Hello world" 확인 가능
4. 멀티스테이지 빌드 후 실행시키기
빌드된 자바 파일을 실행하기 위해서는 jre가 필요하다. jdk는 jre + @ 이기 때문에 빌드 이후 시점에서는 @들은 불필요한 파일이 된다.
따라서, 빌드된 파일을 실행하기 위해 꼭 필요한 요소들로 이미지를 구성하면 더 적은 리소스를 사용하는 이점을 가질 수 있다.
멀티 스테이지 빌드는 빌드 과정을 여러 단계로 나눠서 단계별로 관리가 가능하고, 최종 결과물에서는 꼭 필요한 파일만 사용해서 이미지 사이즈를 줄이는 이득을 취할 수 있다.
https://docs.docker.com/build/building/multi-stage/#differences-between-legacy-builder-and-buildkit
도커 파일을 변경해서 멀티 스테이지 빌드를 가능하게 만듭니다.
각 스테이지는 'FROM' 키워드를 기준으로 구분됩니다.
# 첫번째 스테이지 시작 FROM eclipse-temurin:17-jdk as builder # 컨테이너의 작업 경로 설정 WORKDIR /workspace/app # 호스트에서 빌드된 파일을 컨테이너로 복사 COPY ./gradlew /workspace/app/ COPY ./settings.gradle /workspace/app/ COPY ./build.gradle /workspace/app/ COPY ./gradle /workspace/app/gradle/ COPY ./src /workspace/app/src/ # 빌드 실행 RUN /workspace/app/gradlew build RUN mv /workspace/app/build/libs/myserver-0.0.1-SNAPSHOT.jar /workspace/app/app.jar # 두번째 스테이지 시작 FROM eclipse-temurin:17-jre as runner # 각 스테이지는 별도의 환경이기 때문에 다시 선언 WORKDIR /workspace/app # 이전 스테이지에서 jar파일 복사 COPY --from=builder /workspace/app/app.jar /workspace/app/app.jar # 컨테이너 실행시에 app.jar를 실행 ENTRYPOINT ["java","-jar","app.jar"]
도커 이미지를 생성해보면 단일 스테이지 빌드 때와 비교해서 용량이 많이 줄어든 것을 볼 수 있습니다.
단일 스테이지 빌드에는 673 mb 였던 이미지가 멀티 스테이지 빌드에서는 282mb 줄어들었습니다.
# 이미지 빌드 $ docker build -t myapp . # 이미지의 메타정보 확인 $ docker inspect myapp
이전 과정들과 마찬가지로 컨테이너 실행을 통해서 서버를 띄울 수 있습니다.
$ docker run -it -p 8090:8080 myapp
24.03.20 추가
이런 식으로도 작성이 가능합니다.
# Build stage FROM eclipse-temurin:17-jdk as build WORKDIR /workspace/app COPY gradlew . COPY gradle gradle COPY build.gradle . COPY settings.gradle . COPY src src RUN ./gradlew build -x test # Run stage FROM eclipse-temurin:17-jre VOLUME /tmp ARG JAR_FILE=/workspace/app/build/libs/*.jar COPY --from=build ${JAR_FILE} app.jar ENTRYPOINT ["java", "-jar","/app.jar"]
[출처]
https://docs.docker.com/build/building/multi-stage/#differences-between-legacy-builder-and-buildkit
'인프라 > 도커' 카테고리의 다른 글
컨테이너에서 Docker 명령어 사용하기 (1) 2024.01.13 컨테이너와 컨테이너 사이의 통신 (0) 2024.01.03 호스트와 컨테이너 사이의 통신 (0) 2024.01.03 자주 쓰이는 도커 명령어 정리 (1) 2023.12.21