현재 내가 일하고 있는 회사의 서버팀에서는 Spring Boot 기반으로 모든 API 서버를 만들며 (목적에 따라 Node 또는 Go, C++로 작성된 서버도 있다.) Docker를 이용하여 애플리케이션을 배포하고 있다.
Spring Boot 프로젝트를 어떻게 Docker Image로 만드는지 간단하게 예시와 설명을 해보겠다.
1) spring boot 프로젝트 만들기
우선 간단하게 Spring Boot 프로젝트를 만들어보자. spring boot 2.3.4 기반의 gradle 프로젝트이며, build.gradle에 사용된 라이브러리는 다음과 같다.
plugins {
id 'org.springframework.boot' version '2.3.4.RELEASE'
id 'io.spring.dependency-management' version '1.0.10.RELEASE'
id 'java'
}
group = 'com.tistory.johnmark'
version = '1.0'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
test {
useJUnitPlatform()
}
application 메인 클래스는 다음과 같다. 부트 애플리케이션이 로딩되면 간단한 로깅 메시지를 찍고, path variable로 들어온 데이터를 json 응답으로 리턴하는 간단한 Controller를 만들어봤다.
package com.tistory.johnmark.bootdocker;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
public class BootdockerApplication {
public static void main(String[] args) {
SpringApplication.run(BootdockerApplication.class, args);
}
@EventListener(ApplicationReadyEvent.class)
public void applicationReadyEventHandler() {
System.out.println(ZonedDateTime.now() + " Server Is Ready!!!");
}
static class ResponseMessage {
private final String message;
private final LocalDateTime responseDate;
ResponseMessage(String message, LocalDateTime responseDate) {
this.message = message;
this.responseDate = responseDate;
}
public String getMessage() {
return message;
}
public LocalDateTime getResponseDate() {
return responseDate;
}
}
@RestController
static class HiController {
@GetMapping("/{message}")
public ResponseEntity<ResponseMessage> messageEcho(@PathVariable("message") String message) {
return ResponseEntity.ok(new ResponseMessage(message, LocalDateTime.now()));
}
}
}
어플리케이션을 실행시키면 다음과 같이 콘솔에 로그가 찍히고, API을 호출했을 때의 화면은 아래와 같다.
2) Dockerfile 만들기
이제 해당 프로젝트를 Docker Image로 만들기 위해 Dockerfile을 만들어 보자. Dockerfile은 사실 어느 위치에 만들던 사실 상관없다. docker build 명령어에서 -f 옵션으로 Dockerfile 경로를 설정할 수 있기 때문이다. 하지만 기본적으로 현재 디렉터리를 찾아보기에 명령어를 치기 쉽도록 프로젝트 루트 디렉터리에 만들어보자.
Dockerfile을 만들었다면, 안에 내용을 적어보자.
# openjdk:8-jre-alpine 이미지를 기본으로 사용
FROM openjdk:8-jre-alpine
# jar 파일이 복사되고, docker image를 구성하면서 명령어를 실행하면서 지지고 볶을 디렉토리 설정
ENV APP_HOME=/usr/app/
# 해당 디렉토리에서 작업을 시작하겠다는 구문
WORKDIR $APP_HOME
# jar 파일을 복사한다.
COPY build/libs/*.jar application.jar
# 어플리케이션 포트쓰~
EXPOSE 8080
CMD ["java", "-jar", "application.jar"]
3) gradle 빌드
이제 Gradle을 이용하여 build 디렉토리에 executable jar 파일을 만들고, docker build 명령어를 통해 Dockerfile을 기반으로 Docker Image를 만들어보자.
gradle에서 --console 옵션은 빌드시 콘솔에 출력되는 내용을 어떤 방식으로 출력할지 방법을 설정할 수 있는데, 기본이 auto이며 보통 interaction 하게 빌드 과정이 표현되지만, 나는 스샷을 찍어야 하기에 plain옵션을 줘서 빌드 시 각각 어떤 Task를 실행하는지 살펴봤다.
프로젝트 생성시 메인클래스를 바탕으로 test 디렉터리에 기본 클래스가 생기는데 @SpringBootTest 어노테이션이 붙어져 있어서, ApplicationContext를 모두 로딩하고 Bean을 생성하기에 시간이 오래 걸린다(약 24초).
지금은 필요 없으니 gradle에서 test Task를 스킵해보자.
테스트 과정을 생략했더니 3초컷이였다. 구욷..
4) Docker build
이제 Docker build 명령어를 통해 Dockerfile을 읽고 Docker Image를 만들어보자.
Docker build 명령어에 대한 자세한 내용은 스킵하겠다. 아래 공식 문서를 참조하길 바란다.
docs.docker.com/engine/reference/commandline/build/
도커 이미지가 정상적으로 만들어진 것 같다. docker images 명령어와 grep 명령어로 이미지가 잘 있는지 확인해보자.
만든 docker image의 레이어 정보도 한번 확인해보자.
Dockerfile에 기술한 내용대로 아주 잘 만들어진것 같다.
그럼 해당 Docker Image를 바탕으로 컨테이너를 실행시켜보고 애플리케이션이 정상적으로 동작하는지 확인해보자.
docker ps 명령어로 해당 컨테이너가 잘 실행되고 있는지 확인해보자.
어플리케이션이 실행됐을 때 로그가 잘 찍히는지 컨테이너 로그도 한번 살펴보자
그럼 API를 한번 호출해보자. 보통 curl 명령어를 사용하지만 난 예쁜 걸 좋아하기에 (그래서 예쁜 여자 친구를 만나고 있다 ㅎㅎㅎㅎ) httpie를 사용하고 있다. 관심 있다면 아래 링크를 참고하자.
로컬에서 애플리케이션을 실행시킨 것과 똑같이 동작하는 것을 확인할 수 있다.
간단하게 Spring Boot 프로젝트를 Dockerfile을 통해 Docker Image로 만들고 Docker Container로 실행시켜보는 Dockerizing 과정에 대해서 설명해봤다. 다음 포스팅은 Spring Boot 프로젝트를 Docker Image로 동일하게 만드는데, Dockerfile이 아닌 Jib를 사용해보겠다!
'Programming > Server' 카테고리의 다른 글
[Shell Script] Docker 컨테이너별 이름과 Docker IP 리스트 출력 (0) | 2020.11.17 |
---|---|
[OpenSSL] openSSL로 SSL 인증서 내용 확인해보기 (0) | 2020.10.27 |
[Docker] Docker Container Deploy for Private Network (0) | 2020.07.19 |
[Shell Script] ECR 도커 컨테이너 이미지 명 및 태그 정보 추출 (0) | 2020.07.16 |
[ELK] AWS Elastic Load Balancer Log 분석하고 대시보드 만들기[2] (0) | 2020.01.03 |