본문 바로가기
Web Development/MicroService Architecture

[MSA / Spring Cloud / Docker] 여러 가지 조건을 적용한 Docker Compose 구축

by 감자맹고우 2023. 4. 24.
728x90
반응형

 

Why Docker Compose ?

 

왜 Docker Compose를 쓰게 되었는가?

그것은 이전 글에서 밝혔듯이 모든 프로젝트(서비스)를 IDE에서 관리하는 것은 필요도 없고, 비효율적이고 불편했기 때문이다. 그리고 Docker를 실무에서 사용해보고 싶은 마음이 컸는데, ChatGPT가 Docker Compose를 사용해보라는 답변까지 주었기 때문에 사용하게 되었다.

 

그러면 Docker는 무엇인가?

 

그것은 이미 많은 답변이 있다.

 

하지만, VMware로 가상머신을 사용했던 나로서는 기존 설명들이 너무 이론적으로만 느껴져서, 비유를 통해 쉽게 이해해보기로 했다.

일단 컨테이너를 올린 것은, 새로운 VM을 만든 것과 유사하다고 생각하면 첫 접근이 쉽다. 큰 차이가 느껴지지 않기 때문이다.

다만, 여기서 VM은 사용을 위해 OS부터 여러가지 프로그램을 설치해야한다. 하나의 컴퓨터 환경을 구성하는 것과 마찬가지다.

하지만, Docker는 호스트 OS 위에서 어플리케이션 형태로 동작하는 것이다. 즉, 스마트폰에 어플리케이션 여러개를 설치한 것으로 생각하면 쉽다. 각 어플리케이션은 따로 돌지만, OS를 별도로 설치해줄 필요도 없고 설정만 잘 해주면 되는 것이다.

그렇다보니 로컬 환경에서 VM을 몇 개 돌리는 것과 Docker 컨테이너를 몇 개 실행시키는 것은 리소스 사용에 큰 차이가 있다.

 

그럼 Docker Compose는 무엇인가?

 

여러 개의 Docker 컨테이너를 한 번에 처리할 수 있게 하는 기능이다. 해줄 것은 Docker Compose를 설치하고 docker-compose.yml 파일을 작성하면 끝이다. 이마저도 Docker Desktop을 설치했다면 Docker Compose가 내장되어 있어 설치가 필요없다. 즉, yml 파일 하나만 작성하면 되니 어떻게 보면 yml 파일 그 자체라고 생각할 수도 있을 것 같다.

프로젝트로 치면 application.yml 파일 하나만 작성하면 끝난다.

 

 

[ 구축 방법 ]

 

그럼 이제 Docker Compose를 구축해보자.

먼저 각 프로젝트(서비스)를 도커 이미지로 만들고, 컨테이너로 올릴 수 있도록 해야한다.

이를 위해서는 각 프로젝트에 Dockerfile을 구성해주어야하는데, 예시 작성법은 아래와 같다.

 

FROM openjdk:17-ea-jdk-slim
VOLUME /tmp
COPY target/test-service-1.0.jar TestService.jar
ENTRYPOINT ["java", "-jar", "TestService.jar"]
RUN apt-get update && apt-get install -y curl

 

- 내용 설명 : 

우선, 도커 허브를 통해 필요한 도커 이미지로 어플리케이션이 구성되도록 한다.

Line1. openjdk:17-ea-jdk-slim 이미지로 컨테이너를 구성하겠다.
Line2. VOLUME으로 tmp 폴더라는 컨테이너 내부에 데이터 저장 공간을 생성한다.
Line3. COPY로 현재 호스트에서 해당 Dockerfile이 포함된 프로젝트의 target 폴더에서 jar파일을 컨테이너 내부로 복사한다.
Line4. ENTRYPOINT로 컨테이너가 시작할 때 java -jar 로 jar 파일이 실행되도록 하며
Line5. RUN으로 이후 추가로 실행할 내용을 작성해준다. (apt-get update 및 curl 설치)

 

이렇게 각 프로젝트에 Dockerfile을 작성하여 Docker Image가 생성될 수 있도록 구성해놓고,

프로젝트 jar 패키징, docker build, docker push까지 완료하고나면, docker-compose.yml 파일을 작성하여 여러 개의 프로젝트를 동시에 관리할 수 있도록 구성할 수 있다.

이 부분은 추후 local private registry를 다루면서 설명하겠다. 대부분의 자료는 github처럼 docker hub에서 처리하는 방법을 잘 설명하고 있으니 참고하면 될 것 같다.

 

그럼 Dockerfile은 여기서 마무리짓고, 이어서 본 글의 주제인 docker-compose.yml 파일을 작성해보자!

 

반응형

 

먼저 파일 내용부터 살펴보자.

예시 작성은 아래와 같다.

 

version: '3'
services:
  discovery-service:
    image: {docker push된 경로}/discovery-service:latest
    ports:
      - 8761:8761
    networks:
      - test-network
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://discovery-service:8761/actuator/health || exit 1"]
      interval: 10s
      timeout: 3s
      retries: 3 
  apigateway-service:
    image: {docker push된 경로}/apigateway-service:latest
    ports:
      - 8000:8000
    environment:
      - DISCOVERY_SERVICE_URL=http://discovery-service:8761/eureka/
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://apigateway-service:8000/actuator/health || exit 1"]
      interval: 10s
      timeout: 3s
      retries: 3
    depends_on:
      discovery-service:
        condition: service_healthy
    networks:
      - test-network
    restart: on-failure
  test-service:
    image: {docker push된 경로}/test-service:latest
    ports:
      - 0:1000
    environment:
      - DISCOVERY_SERVICE_URL=http://discovery-service:8761/eureka/
    depends_on:
      discovery-service:
        condition: service_healthy
    networks:
      - test-network
    restart: on-failure
  config-service:
    image: {docker push된 경로}/config-service:latest
    ports:
      - 8888:8888
    networks:
      - test-network
    restart: on-failure

networks:
  test-network:

 

일단, MSA에서 기본적으로 가지고 가는 discovery, apigateway, config service를 포함하고, 하나의 test service를 구성하여 총 4개의 서비스를 docker compose로 한 번에 관리할 수 있도록 구성을 해보았다.

 

내부 작성은 yml 파일 형식으로 작성하는데, 각 서비스 별로 필요한 속성을 작성해준다.

 

일단 기본적으로, 실행에 필요한 image를 작성해주고 ports를 작성해준다.

이때, 포트의 왼쪽은 Host 포트이고 오른쪽은 Container 포트이다.

주의할 점은 랜덤 포트를 사용하고 싶다면, 호스트 포트는 랜덤으로 사용하되 컨테이너 포트는 지정을 해주어야하는 것 같다. 컨테이너 포트를 지정해주지 않으면 discovery service와 연동되지 못한다. test-service의 ports 부분을 보면 왼쪽의 호스트 포트는 랜덤포트인 0을 지정했고, 오른쪽의 컨테이너 포트는 지정포트인 1000으로 지정해주었다. 이렇게 하지 않으면 discovery service에서 찾질 못하는지 연결이 되지 않는다.

 

그 다음으로 healthcheck 속성이 있다. healthcheck는 서비스가 정상 동작하는지 테스트하는 설정을 작성하는 속성이다. 이를 작성한 이유는, 초기 시작 시 discovery service가 가장 먼저 켜지지 않으면, 다른 서비스들이 이후에도 discovery service와 연결하지 못하는 케이스를 발견해서이다. 그래서 depends_on 속성으로 다른 서비스가 discovery service를 참조하도록 하고, condition으로 discovery service가 service_healthy 상태가 되고서야 시작이 되도록 설정해주었다. (actuator/health가 동작하려면 서비스가 완전히 켜져야 한다. actuator를 사용하려면 프로젝트에 별도로 actuator를 의존성 추가해주어야 한다.)

 

그리고 networks 부분이 또 중요한데, 각각의 docker container는 따지고 보면 별개의 어플리케이션이나 마찬가지이므로  따로 설정해주지 않으면 각자 별도의 네트워크에서 동작을 한다. 그러다보니 기본적으로 서로 찾질 못한다. 그래서 같은 네트워크 상에서 동작할 수 있도록, yml 파일의 가장 하단에서 test-network를 생성하도록 하였고, yml 파일의 각 서비스에서는 networks 속성으로 같은 test-network에서 동작하도록 구성해주었다.

 

또 중요한 것은, 같은 네트워크를 사용한다고 했어도 각각의 컨테이너는 localhost 같은 호스트 IP주소로 찾는 것이 아니고 컨테이너로 찾는다. 그렇기 때문에 각 서비스 컨테이너명(discovery-service:8761)과 포트를 적어주어야 연결된다.

 

이렇게 작성한 후, cmd창에서 docker-compose.yml 파일이 있는 경로에서 docker compose up과 down 등의 명령어로 여러 개의 서비스를 한 번에 관리할 수 있다. 팀원의 말에 따르면 초기에 한번 up 해주고 나면, 경로가 바뀌어도 해당 명령어가 먹힌다고 하니 참고하면 좋을 것 같다.

 

🤞 도움이 되셨기를 바랍니다. 한 번의 클릭과 댓글은 어딘가의 누군가에게 진실로 큰 힘이 됩니다. 🐱‍🏍

 

728x90
반응형

댓글