마이크로서비스에 도커의 개념을 살펴보고 서비스에 적용해보도록 하자.
먼저 도커의 아키택쳐다
도커데몬 : 도커 이미지를 생성하고 관리하는 dockerd라는 서버다
도커 클라이언트 : 도커 사용자는 클라이언트로 도커와 상호작용한다 도커에 명령이 실행되면 데몬에 명령을 보내는 역할을 한다.
도커 레지스트리 : 도커 이미지가 저장되는 곳이다. 공개 또는 사설 레지스트리일 수 있고, 도커 허브는 기본 공개 레지스트리이다.
도커 이미지 : 도커 컨테이너를 생성하는 몇가지 명령이 포함된 읽기 전용 템플릿이다. 또한 Dockerfile을 사용하여 새로운 이미지를 생성할 수 도 있다.
도커 컨테이너 : docker run 명령이 생성되고 수행되면 도커 이미지는 컨테이너를 생성한다. 애플리케이션과 주변환경은 이 컨테이너에서 실행된다.
도커 볼륨 : 도커가 생성하고 컨테이너가 사용한 데이터를 저장하는데 적합한 메커니즘이다.
도커 네트워크 : 도커 네트워크를 사용하면 컨테이너를 가능한 많은 네트워크에 연결할 수 있다. 네트워크를 격리된 컨테이너의 통신 수단으로 생각할 수 있으며 도커에는 bridge, host, overlay, none, macvlan 다섯 가지의 네트워크 드라이버 타입이 있다.
도커파일은 도커 클라이언트가 이미지를 생성하고 준비하기 위해 호출하는데 필요한 지시어와 명령어 들이 포함된 단순한 테스트 파일이다. 이 파일은 이미지 생성 과정을 자동화 한다.
예시)
명령어 종류
도커 컴포즈는 서비스 설계와 구축이 용이한 스크립트를 작성하여 도커를 더 쉽게 작성하게 한다. 도커 컴포즈를 사용하면 여러 컨테이너를 하나의 서비스로 실행하거나 다른 컨테이너를 동시에 생성할 수 있다.
컴포즈 명령어
컴포즈 지시어
pom.xml 파일에 도커 메이븐 플러그인을 추가해서 도커 이미지를 빌드한다.
이 플러그인을 사용하면 메이븐 pom.xml 파일에서 도커 이미지와 컨테이너를 관리 할 수 있다.
pom.xml
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- This plugin is used to create a docker image and publish the image to docker hub-->
<plugin> <!-- 여기서 Dockerfile 메이븐 플러그인을 시작한다.-->
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.4.13</version>
<configuration>
<!-- 리모트 저장소 이름을 설정한다. 플러그인에서 정의된 변수인 docker.image.prefix 와 project.artifactId 를 사용한다.-->
<repository>${docker.image.prefix}/${project.artifactId}</repository>
<tag>${project.version}</tag>
<buildArgs>
<!-- buildArgs를 사용하여 JAR파일 위치를 설정한다 이 값은 Dockerfile에서 사용된다.-->
<JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
</buildArgs>
</configuration>
<executions>
<execution>
<id>default</id>
<phase>install</phase>
<goals>
<goal>build</goal>
<goal>push</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<properties>
<java.version>11</java.version>
<!-- 기존 프로퍼티 선언 부분에 docker-prefix명을 추가한다 ostock으로 만들었다 -->
<docker.image.prefix>ostock</docker.image.prefix>
</properties>
그리고 dockerfile에서 스프링 부트 jar 파일을 도커 이미지에 복사한 후 애플리케이션 jar를 실행할 코드를 작성한다.
이 dockerfile에서는 멀티스테이지 빌드를 사용한다
스프링 부트의 경우 도커이미지에 target 디렉터리를 모두 복사하는 대신 스프링 부트 애플리케이션에 실행하는데 필요한것만 복사하여 생성할 도커 이미지를 최적화 한다.
Dockerfile
#stage 1
#Start with a base image containing Java runtime
# 도커 런타임에 사용될 도커 이미지를 지정한다
FROM openjdk:11-slim as build
# Add Maintainer Info
LABEL maintainer="Illary Huaylupo <illaryhs@gmail.com>"
# The application's jar file
# docker-maven-plugin에 설정된 JAR_FILE 변수를 정의한다.
ARG JAR_FILE
# Add the application's jar to the container
# JAR 파일을 이미지의 파일 시스템에 app.jar로 복사한다.
COPY ${JAR_FILE} app.jar
#unpackage jar file
# 앞서 빌드 이미지의 파일 시스템에 복사한 app.jar의 압축을 푼다
RUN mkdir -p target/dependency && (cd target/dependency; jar -xf /app.jar)
# excute the apllication
# 컨테이너가 생성될 떼 이미지의 라이선싱 서비스 애플리케이션을 실행 대상으로 지정한다.
# 여기서는 멀티스테지 빌드를 사용하므로 이 부분은 사용하지않는다.
#ENTRYPOINT ["java", "-jar", "/app.jar"]
#stage 2
#Same Java runtime
# 새로운 이미지는 스프링 부트 앱에 대한 통짜 JAR 파일 대신 여러 레이어로 구성된다.
FROM openjdk:11-slim
#Add volume pointing to /tmp
VOLUME /tmp
#Copy unpackaged application to new container
# stage1에서 build라고 명명된 첫 이미지에서 여러 레이어를 복사한다.
ARG DEPENDENCY=/target/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
#execute the application
# 컨테이너가 생성될 떼 이미지의 라이선싱 서비스 애플리케이션을 실행 대상으로 지정한다.
ENTRYPOINT ["java","-cp","app:app/lib/*","com.optimagrowth.license.LicenseServiceApplication"]
Dockerfile은 전체 애플이케이션 JAR 대신 이들 레이어만 포함된 또 다른 이미지를 생성한다. 두번째 스테이지에서 Dockerfile은 여러 레이어를 새 이미지에 복사한다.
터미널에서 다음 명령어를 통해 JAR 파일을 추출한다
mvn clean package
그리고 도커이미지를 빌드 하려면 아래 명령을 실행한다.
mvn package dockerfile:build
docker images 명령어를 통해 이미지 목록에서 이미지를 볼 수 있다.
도커의 이미지가 있다면 docker run -d ostock/licensing-service:0.0.1-SNAPSHOT 명령을 통해 컨테이너를 백그라운드에서 실행할 수 있다.
docker ps, docker ps -a를 통해 컨테이너 상태를 볼 수 있다.
컨테이너를 중지하려면 docker stop <container_id> 를 통해 중지할 수 있다.
도커 컴포즈는 서비스를 그룹으로 정의한 후 단일 단위로 시작할 수 있는 서비스 오케스트레이션 도구다. 도커 컴포즈는 서비스별 환경 변수를 정의하는 기능도 있다.
구조를 살펴보자
docker-compose.yml
version: '3'
services:
# 시작한 서비스에 레이블을 적용한다. 이 서비스 이름은 도커 인스턴스가 시작할 때 이에 대한 DNS 엔트리가 되며, 다른 서비스가 엑세스하는 데 사용된다.
licensingservice:
# 도커 컴포즈는 먼저 로컬 도커 저장소에서 시작할 대상 이미지를 찾으려고 시도한다. 찾을 수 없다면 도커 허브에서 확인한다
image: ostock/licensing-service:0.0.1-SNAPSHOT
# 시작한 도커 컨테이너의 포트 번호를 정의한다 이 포트 번호는 외부에 노출된다
ports:
- "8080:8080"
# 시작하는 도커 이미지에 환경 변수를 전달한다 이 경우 SPRING_PROFILES_ACTIVE 환경번수를 지정한다.
environment:
- "SPRING_PROFILES_ACTIVE=dev"
networks:
backend:
aliases:
# 네트워크상의 서비스에 대한 대체 호스트 이름을 지정한다.
- "licenseservice"
networks:
backend:
# 디폴트 타입은 bridge이며 backend라고 명명된 커스텀 네트워크를 실행한다.
driver: bridge
해당 파일이 위치한 디렉터리에서 docker-compose up 명령으로 서비스를 시작할 수 있다.
아래 링크는 도커에 대한 더 자세한 내용 설명한다.
🧨 다음 챕터는 Spring cloud config 통해 각 서비스의 구성정보를 별도 보관하는 서비스를 만들어 보겠다.