본문 바로가기
카테고리 없음

springboot with docker

코동이 2022. 2. 2.

*개요

Docker를 사용해봤지만, 부분적으로 강의에서 확인했기 때문에, Spring Boot환경에서 Docker는 어떻게 사용되는지 정리해보기 위해서 작성합니다. 공식 문서 주소는 바로 아래 있습니다.

 

https://spring.io/guides/gs/spring-boot-docker/

 

Spring Boot with Docker

this guide is designed to get you productive as quickly as possible and using the latest Spring project releases and techniques as recommended by the Spring team

spring.io

 

 

 

준비사항

- JDK 1.8버전 이상

- Gradle 4+, Maven 3.2+

 

Linux 환경이 아니라면, 가상화 서버가 필요합니다. VirtualBox 혹은 맥의 boot2docker같은 도구를 사용합니다.

(윈도우에는 WSL2을 사용해도 좋습니다)

 

 

https://www.virtualbox.org/wiki/Downloads

 

Downloads – Oracle VM VirtualBox

Download VirtualBox Here you will find links to VirtualBox binaries and its source code. VirtualBox binaries By downloading, you agree to the terms and conditions of the respective license. If you're looking for the latest VirtualBox 6.0 packages, see Virt

www.virtualbox.org

 

https://www.lainyzine.com/ko/article/how-to-install-wsl2-and-use-linux-on-windows-10/

 

[Windows 10] WSL2 설치 및 사용법

Microsoft에서는 2020년 5월 리눅스를 윈도우와 통합해서 사용할 수 있는 WSL2를 발표했습니다. 이 글에서는 WSL2를 설치하고 사용하는 방법을 소개합니다

www.lainyzine.com

 

또한 64-bit 환경 도커가 필요합니다. 

https://docs.docker.com/get-docker/

 

Get Docker

 

docs.docker.com

 

*Spring Boot Application 준바하기

 

package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class Application {

  @RequestMapping("/")
  public String home() {
    return "Hello Docker World";
  }

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }

}

 

기본 SpringBootApplication의 파일입니다. 먼저 도커 컨테이너 없이도 어플리케이션을 실행해보겠습니다.

 

*Gradle

 

./gradlew build && java -jar build/libs/gs-spring-boot-docker-0.1.0.jar

 

*Maven

 

./mvnw package && java -jar target/gs-spring-boot-docker-0.1.0.jar

 

이제, localhost:8080에 접속하면 "Hello Docker World" 메세지를 확인할 수 있습니다. Gradle과 Maven으로 각각 build, package를 하였고 java 명령어를 통해 jar를 실행했습니다. 또한 각각 jar파일 경로가 Gradle은 build/libs이고 Maven은 target/입니다.

 

 

*컨테이너화하기

 

도커는 간단한 "Dockerfile" 파일 형식을 가지고 있는데, 이미지의 레이어를 지정하기 위해서입니다. 

아래 Dockerfile을 따라서 파일을 만들어 봅니다.

 

FROM openjdk:8-jdk-alpine
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

 

만약에 Gradle을 사용한다면, 다음의 명령어로 실행할 수 있습니다.

 

docker build --build-arg JAR_FILE=build/libs/\*.jar -t springio/gs-spring-boot-docker .

 

만약에 Maven을 사용한다면, 다음의 명령어로 실행할 수 있습니다.

 

docker build -t springio/gs-spring-boot-docker .

 

이 명령어로, 이미지를 build되고 springio/gs-spring-boot-docker로 태그가 생성됩니다.

 

이 Dockerfile은 매우 간단하지만, 스프링부트 애플리케이션을 Java와 JAR 파일만으로도 실행할 수 있게 만드는 전부이기도 합니다. build는 스프링 사용자들이 애플리케이션을 실행하도록 합니다. COPY 명령어로, 프로젝트 JAR 파일이 app.jar 컨테이너로 복사됩니다. 이 파일은 ENTRYPOINT에서 실행됩니다. Java 프로세스를 래핑하는 쉘이 없도록 Dockerfile ENTRYPOINT의 배열 형식이 사용됩니다.

 

사용자 권한으로 애플리케이션을 실행하면 일부 위험을 완화하는 데 도움이 됩니다(예: StackExchange의 스레드 참조). 따라서 Dockerfile에 대한 중요한 개선 사항은 루트가 아닌 사용자로 애플리케이션을 실행하는 것입니다.

 

FROM openjdk:8-jdk-alpine
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

 

이제, 어플리케이션을 build, run 시킬 때, 어플리케이션 시작 로그에서 username을 확인할 수 있습니다.

 

docker build -t springio/gs-spring-boot-docker .
docker run -p 8080:8080 springio/gs-spring-boot-docker

 

INFO log에서 started by를 참고합니다.

 

 :: Spring Boot ::        (v2.2.1.RELEASE)

2020-04-23 07:29:41.729  INFO 1 --- [           main] hello.Application                        
: Starting Application on b94c86e91cf9 with PID 1 (/app started by spring in /)
...

 

스프링부트의 무거운 JAR 파일에 의존성과 어플리케이션 자원사이에 명확한 구분이 있습니다. 그 사실을 통해 성능향상을 할 수 있습니다. 그 방법은 컨테이너 파일시스템에서 레이어를 생성하는 것입니다. 레이어들은 build 혹은 runtime(대부분 runtime)모두에 캐시가되므로, 더 자주 바뀌는 자원들이 더 느리게 변하는 자원 다음에 레이어 되기를 원합니다. (대게 자주 바뀌는 자원들은 어플리케이션 내에 있는 클래스와 정적자원들입니다.) 따라서, Dockerfile의 미세한 다른 구현이 가능합니다.

 

FROM openjdk:8-jdk-alpine
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
ARG DEPENDENCY=target/dependency
COPY ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY ${DEPENDENCY}/META-INF /app/META-INF
COPY ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","hello.Application"]

 

위의 Dockerfile은 DEPENDENCY 매개변수를 가지는데, 무거운 JAR의 압축을 푼 디렉터리를 가리킵니다.

 

Gradle로 DEPENDENCY 매개변수를 사용하려면 다음 명령어를 실행시킵니다.

 

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

 

Maven으로 DEPENDENCY 매개변수를 사용하려면 다음 명령어를 실행시킵니다.

 

mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)

 

정상적으로 실행하면, 이미 의존성 JARs에 BOOT-INF/lib 디렉토리가 포함되어 있습니다. 그리고 어플리케이션 클래스에 BOOT-INF/classes도 포함하고 있습니다. 우리는 어플리케이션 자신의 메인 클래스인 hello.Application을 사용한다는 사실을 기억해야 합니다.(이 방법이 무거운 JAR 런처에 의한 우회방법보다 빠릅니다.)

 

Gradle build에서, 명확한 build 매개변수를 추가하는 것이 필요합니다.

 

docker build --build-arg DEPENDENCY=build/dependency -t springio/gs-spring-boot-docker .

 

Maven에서 이미지를 build한다면 조금 더 간단합니다.

 

docker build -t springio/gs-spring-boot-docker .

 

Docker 명령어를 build하는 대신에, build plugin을 이용할 수도 있습니다. 스프링부트는 Maven이나 Gradle 그 자체 build plugin으로 컨테이너를 빌드하도록 지원합니다. 구글 또한 jib라는 오픈소스 도구가 있는데 Maven과 Gradle plugin들을 가지고 있습니다. 아마도 이 접근의 가장 흥미로운 점은 Dockerfile이 필요하지 않다는 사실입니다. docker build와 같이 같은 표준 컨테이너 포맷을 사용해서 이미지를 build할 수 있습니다. 또한 이 방법은 docker가 설치되지 않은 환경에서도 동일하게 작동합니다.

 

Docker Image를 Gradle로 빌드하기

 

태그된 docker image를 Gradle로 build할 수 있습니다.

 

./gradlew bootBuildImage --imageName=springio/gs-spring-boot-docker

 

Docker Image를 Maven으로 빌드하기

 

빠르게 시작하기 위해서, pom.xml을 수정하지 않고도 스프링부트 이미지 생성기를 실행할 수 있습니다.(Dockerfile이 있다면, 무시됩니다)

 

./mvnw spring-boot:build-image -Dspring-boot.build-image.imageName=springio/gs-spring-boot-docker

 

Docker 저장소에 push하기 위해서, push 권한이 필요합니다. (일반적으로 권한이 없습니다.) image prefix를 자신의 Dockerhub ID로 바꿔야 하며, docker login으로 Docker 실행전에 인증을 해야 합니다.

 

*Push 이후

 

만약에 Dockerhub에서 springio 기관의 일원이 아니라면, docker push는 실패합니다. 하지만, docker ID에 맞게 설정을 변경한다면, 성공합니다. 이제 새롭게 태그된 배포된 이미지가 생성됩니다.

 

local 환경에 만들어진 docker image를 실행하기 위해서 도커를 재등록하거나 발행할 필요는 없습니다. 만약에 Docker로 빌드를 했었다면, 여전히 local에 태그도니 이미지가 있을 거고, 다음과 같이 실행하면 됩니다.

 

$ docker run -p 8080:8080 -t springio/gs-spring-boot-docker
Container memory limit unset. Configuring JVM for 1G container.
Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -
XX:MaxMetaspaceSize=86381K -XX:ReservedCodeCacheSize=240M -Xss1M -Xmx450194K (Head 
Room: 0%, Loaded Class Count: 12837, Thread Count: 250, Total Memory: 1073741824)
....
2015-03-31 13:25:48.035  INFO 1 --- [           main] 
s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2015-03-31 13:25:48.037  INFO 1 --- [           main] hello.Application 
: Started Application in 5.613 seconds (JVM running for 7.293)

 

docker가 실행되면, 다음으로 컨테이너 리스트를 확인할 수 있습니다.

 

$ docker ps
CONTAINER ID        IMAGE                                   COMMAND                  
CREATED             STATUS              PORTS                    NAMES
81c723d22865        springio/gs-spring-boot-docker:latest   "java -Djava.secur..."   
34 seconds ago      Up 33 seconds       0.0.0.0:8080->8080/tcp   goofy_brown

 

다시 종료하고 싶다면, docker stop으로 container ID를 입력하면 됩니다.

 

docker stop goofy_brown
81c723d22865

 

만약 원할경우, 작업이 끝났따면 컨테이너를 삭제할 수 있습니다.(/var/lib/docker 어딘가에 저장되어 있습니다.)

 

docker rm goofy_brown

 

*Spring Profiles 사용하기

 

Spring 프로필을 사용하여 새로 생성된 Docker 이미지를 실행하는 것은 환경 변수를 Docker 실행 명령(prod 프로필용)에 전달하는 것만큼 쉽습니다

 

docker run -e "SPRING_PROFILES_ACTIVE=prod" -p 8080:8080 -t springio/gs-spring-boot-docker

 

또한 dev용 프로필도 실행할 수 있습니다.

 

docker run -e "SPRING_PROFILES_ACTIVE=dev" -p 8080:8080 -t springio/gs-spring-boot-docker

 

*Docker Container에서 디버깅하기

 

어플리케이션을 디버깅하기 위해서, JPDA Transport를 사용할 수 있습니다. 컨테이너를 원격 서버처럼 다룹니다. 기능을 이용하기 위해서 JAVA_OPTS 변수에서 설정을 하고, 컨테이너 실행동한 포트를 매핑시킵니다. Mac에서는 제한적인데, 왜냐하면 black magic usage없이는 IP로 컨테이너에 접근할 수 없기 때문입니다.

 

docker run -e "JAVA_TOOL_OPTIONS=-
agentlib:jdwp=transport=dt_socket,address=5005,server=y,suspend=n" -p 8080:8080 -p 
5005:5005 -t springio/gs-spring-boot-docker
반응형