MSA로 헬스/영양 관리 어플리케이션 만들기 (3) - 도커라이즈

백근영·2020년 4월 8일
2

Spring Cloud MSA

목록 보기
3/4
post-thumbnail

MSA와 Docker

msa와 docker는 뗄레야 뗄 수 없는 관계에 있다고 생각한다. 도커를 기반으로 하여 AWS fargate 혹은 kubernetes등을 통해 이루어지는 컨테이너 오케스트레이션은 microservice 별로 scale-out이 가능하다는 MSA의 장점과 특히 잘어울린다. 이번 장에서는 MSA로 만든 어플리케이션을 도커라이징하는 과정을 설명하려 한다.

컨테이너 구조

이번 실습에서는 아래와 같이 총 8개의 컨테이너를 만든다. (프론트까지 추가하면 9개)

Dockerfile

MSA를 구성하는 각각의 service들은 모두 같은 환경(java & gradle)에서 돌아가기 때문에 도커파일의 생김새 자체는 모두 똑같다.

FROM gradle:6.2.2-jdk11 AS build
COPY --chown=gradle:gradle . /home/gradle/src
WORKDIR /home/gradle/src
RUN gradle clean build --no-daemon -x test

FROM openjdk:11-jre-slim

EXPOSE 8080

RUN mkdir /app

COPY --from=build /home/gradle/src/build/libs/*.jar /app/spring-boot-application.jar

ENTRYPOINT ["java", "-XX:+UnlockExperimentalVMOptions", "-Djava.security.egd=file:/dev/./urandom","-Dspring.profiles.active=test", "-jar","/app/spring-boot-application.jar"]

의존성 관리 모듈로 gradle을 사용하였기 때문에 베이스 이미지를 gradle로 했는데, 이미지의 크기를 줄이기 위해 멀티 스테이지 빌드를 적용했다.

docker-compose

이번 실습은 가능하다면 MSA로 작성한 앱을 쿠버네티스로 배포하는 것이 최종적인 목표지만, 우선은 로컬 환경에서 테스트가 가능하게 docker-compose를 이용해 하나의 호스트 위에 모든 컨테이너를 띄워본다.

version: '3'

services:
  mysql:
    image: mysql:5.7
    container_name: mysql-db
    environment:
      MYSQL_ROOT_PASSWORD: "password"
    ports:
      - 3306:3306
    networks:
      - msa-network

  config:
    image: dvmflstm/config
    depends_on:
      - mysql
    environment:
      MYSQL_CONNECTION_URI: "jdbc:mysql://mysql:3306/test_db?serverTimezone=UTC&characterEncoding=UTF-8&autoReconnection=true"
      MYSQL_ROOT_PASSWORD: "password"
    ports:
      - 8888:8888
    networks:
      - msa-network

  registry:
    image: dvmflstm/registry
    depends_on:
      - config
      - mysql
    environment:
      CONFIG_SERVER_URI: "http://config:8888"
    ports:
      - 8761:8761
    networks:
      - msa-network

  gateway:
    image: dvmflstm/gateway
    depends_on:
      - config
      - registry
      - mysql
    environment:
      CONFIG_SERVER_URI: "http://config:8888"
      EUREKA_SERVER_URI: "http://registry:8761/eureka"
      MYSQL_CONNECTION_URI: "jdbc:mysql://mysql:3306/test_db?serverTimezone=UTC&characterEncoding=UTF-8&autoReconnection=true"
      MYSQL_ROOT_PASSWORD: "password"
    ports:
      - 8080:8080
    networks:
      - msa-network

  auth:
    image: dvmflstm/auth
    depends_on:
      - config
      - registry
      - mysql
    environment:
      CONFIG_SERVER_URI: "http://config:8888"
      EUREKA_SERVER_URI: "http://registry:8761/eureka"
      MYSQL_CONNECTION_URI: "jdbc:mysql://mysql:3306/test_db?serverTimezone=UTC&characterEncoding=UTF-8&autoReconnection=true"
      MYSQL_ROOT_PASSWORD: "password"
      JWT_SECRET_KEY: "geunyoung_jwt_secret"
    ports:
      - 8081:8080
    networks:
      - msa-network

  diet:
    image: dvmflstm/diet
    depends_on:
      - config
      - registry
      - mysql
    environment:
      CONFIG_SERVER_URI: "http://config:8888"
      EUREKA_SERVER_URI: "http://registry:8761/eureka"
      MYSQL_CONNECTION_URI: "jdbc:mysql://mysql:3306/test_db?serverTimezone=UTC&characterEncoding=UTF-8&autoReconnection=true"
      MYSQL_ROOT_PASSWORD: "password"
    ports:
      - 8082:8080
    networks:
      - msa-network

  exercise:
    image: dvmflstm/exercise
    depends_on:
      - config
      - registry
      - mysql
    environment:
      CONFIG_SERVER_URI: "http://config:8888"
      EUREKA_SERVER_URI: "http://registry:8761/eureka"
      MYSQL_CONNECTION_URI: "jdbc:mysql://mysql:3306/test_db?serverTimezone=UTC&characterEncoding=UTF-8&autoReconnection=true"
      MYSQL_ROOT_PASSWORD: "password"
    ports:
      - 8083:8080
    networks:
      - msa-network

  statistics:
    image: dvmflstm/statistics
    depends_on:
      - config
      - registry
      - mysql
    environment:
      CONFIG_SERVER_URI: "http://config:8888"
      EUREKA_SERVER_URI: "http://registry:8761/eureka"
      MYSQL_CONNECTION_URI: "jdbc:mysql://mysql:3306/test_db?serverTimezone=UTC&characterEncoding=UTF-8&autoReconnection=true"
      MYSQL_ROOT_PASSWORD: "password"
    ports:
      - 8084:8080
    networks:
      - msa-network

networks:
  msa-network:
    external: true

주의해서 봐야할 부분은 각 마이크로서비스에 config server의 uri와 eureka server의 uri를 환경변수로 주입해주는 부분인데, docker-compose에서 제공하는 service discovery 기능의 도움을 받아 간단하게 docker-compose가 생성하는 service의 이름으로 name resolution이 가능하다.

컨테이너 구동 순서 정하기

spring cloud에서는 각각의 마이크로서비스가 컨피그 서버와 유레카 서버의 도움을 받아야 하므로 이들이 구동된 후에 시작되어야 한다. 위에서 작성한 docker-compose.yml 에서는 depends_on으로 각 서비스의 시작 순서를 정해주었지만, 이것이 이전 서비스가 완전히 구동된 후에 다음 서비스를 시작시키는 것은 아니다. 그래서 완벽하게 구동 순서를 제어하기 위해서는 아래와 같은 방법을 활용해야 한다.

1. restart: always

컨테이너가 비정상적으로 종료되었을 때 무조건 재시작시키는 옵션이다. 가장 간단한 해결 방법이지만, 컨테이너가 종료되는 것이 구동 순서 때문이 아니라 예기치 못한 다른 원인 때문이라면 의도치 않게 컨테이너는 무한히 시작-종료-시작-.. 을 반복하게 될 가능성이 있으며, 그에 따라 메모리 사용량 등도 높아질 수 있을 것 같다.

2. dockerize 이용하기

dockerize 명령의 -wait 옵션을 이용하여 원하는 컨테이너가 정상적으로 준비될 때 까지 기다리도록 할 수 있다.

예를 들어 8888번 포트의 config server가 준비될 때 까지 기다리도록 하는 명령은 아래와 같다.

dockerize -wait http://config:8888 -timeout 20s

아무튼 우여곡절 끝에 아래와 같이 모든 컨테이너를 정상적으로 구동시킬 수 있었다.

그리고 유레카서버에 등록되어 있는 서비스들의 목록도 잘 확인할 수 있었다.

profile
서울대학교 컴퓨터공학부 github.com/BaekGeunYoung

0개의 댓글