20250108 TIL : MSA 프로젝트 Docker Compose로 도커 컨테이너화하기

MCS·2025년 1월 8일

TIL

목록 보기
40/45

오늘 학습한 내용

  • MSA 프로젝트 Docker Compose로 도커 컨테이너화하기
    • Docker 네트워크와 컨테이너 간 통신
    • 각 서비스에 Dockerfile 작성하기
    • .env파일로 환경변수 설정하기
    • docker-compose.yml 작성 및 docker compose를 실행해 컨테이너화하기
    • 마무리

MSA 프로젝트 Docker Compose로 도커 컨테이너화하기

최종프로젝트를 진행하면서 Docker Compose를 통해 MSA의 각 서비스를 도커 이미지로 빌드하고, 도커 컨테이너로 만들어서 배포하는 방법을 알아보았다.
우선 간단하게 eureka server, gateway, user 서비스에 대해서만 진행했으며, 로컬에서 테스트를 진행했다.

Docker 네트워크와 컨테이너 간 통신

Docker 컨테이너화해서 각 서비스를 배포하기 전에, Docker 네트워크 개념에 대해 알아보았다.

도커는 컨테이너에 내부 IP를 순차적으로 할당하며, 내부 IP는 컨테이너를 재시작할 때마다 변경될 수 있다.
내부 IP는 도커가 설치된 호스트, 즉 내부망에서만 쓸 수 있는 IP이므로 외부와 연결될 필요가 있다.

따라서 도커는 각 컨테이너에 외부와의 네트워크를 제공하기 위해 컨테이너마다 가상 네트워크 인터페이스를 호스트에 생성하며 이 인터페이스의 이름은 veth로 시작한다.
veth 인터페이스는 사용자가 직접 생성할 필요는 없으며 컨테이너가 생성될 때 도커 엔진이 자동으로 생성한다.

veth 인터페이스는 호스트가 갖고 있는 eth0, eth1 등과 연결되어 있으며, docker0 브리지는 각 veth 인터페이스와 바인딩돼 호스트의 eth0 인터페이스와 이어주는 역할을 한다.

컨테이너를 생성하면 기본적으로 docker0 브리지를 사용하지만, 따로 네트워크 드라이버를 지정하고 네트워크를 생성할 수도 있다.
브리지(Bridge), 호스트(Host), 논(none), 컨테이너(container), 오버레이(overlay) 등의 종류가 있는데, 이 글에서는 브리지에 대해서만 알아보고 사용할 것이다.

브리지 네트워크를 생성하고 지정하면, docker0이 아닌 사용자 정의 브리지 네트워크를 통해 각 컨테이너에 연결할 것이고, 컨테이너는 연결된 브리지를 통해 외부와 통신할 것이다.

만약 docker compose로 컨테이너들을 띄운다면, compose에 네트워크를 따로 지정하지 않으면 자체적으로 브리지 네트워크를 생성해 compose에 의해 생성된 컨테이너들만 통신할 수 있게 된다.
기본적으로 도커의 각 네트워크들은 논리적으로 격리되어 있기 때문에, 다른 컨테이너에서 compose의 네트워크의 접근하거나, compose의 컨테이너에서 다른 컨테이너에 접근하는 것은 불가능하다.

프로젝트 로컬 테스트 시 MySQL과 redis를 도커 컨테이너로 띄워 사용하고 있었는데, 이들은 compose 네트워크에 접근이 불가능하므로, 네트워크를 생성해 지정해주거나, compose 파일에서 함께 띄우도록 해야 한다. 전자의 방법이 더 좋아보이지만, 우선 후자의 방법으로 진행해 보았다.

각 서비스에 Dockerfile 작성하기

# Run Stage
FROM openjdk:17-jdk-slim

VOLUME /tmp

ARG JAR_FILE=build/libs/*.jar

COPY ${JAR_FILE} app.jar

ENTRYPOINT ["java","-jar","/app.jar"]

# 컨테이너 실행 시 사용할 포트 설정
EXPOSE 19091

위와 같이 간단하게 Dockerfile을 작성했다. 포트는 사실 컨테이너 내부 포트이기 때문에 docker-compose.yml에서 매핑만 잘 해주어도 되겠지만, 우선 직관성을 높이기 위해 호스트 네트워크에 매핑할 포트와 같은 포트로 지정했다.

.env파일로 환경변수 설정하기

docker-compose.yml 파일에서 넣어 줄 환경변수를 .env 환경변수 파일을 만들어 관리한다. 만약 Github Actions를 통해 CI/CD 파이프라인을 사용한다면, 깃허브 레포지토리에 지정해놓고 사용할 수도 있을 것이다.
우선 로컬에서 사용하기 위해 .env파일을 사용했다.(깃허브에 올라가지 않도록 미리 .gitignore에 추가하자.)

REDIS_LOCAL_USERNAME="USERNAME 입력"

이런 형식으로 사용할 수 있다. 쌍따옴표까지 제거하고 원하는 값을 입력해주면 된다.

docker-compose.yml 작성 및 docker compose를 실행해 컨테이너화하기

version: '3.8'

services:
  eureka-server:
    build:
      context: ./server
      dockerfile: Dockerfile
    container_name: eureka-server
    ports:
      - "19090:19090"
    networks:
      - hotdealnetwork

  gateway-service:
    build:
      context: ./gateway
      dockerfile: Dockerfile
    container_name: gateway-service
    ports:
      - "19091:19091"
    environment:
      - JWT_SECRET_KEY=${JWT_SECRET_KEY}
    depends_on:
      - eureka-server
    networks:
      - hotdealnetwork

  user-service:
    build:
      context: ./user
      dockerfile: Dockerfile
    container_name: user-service
    ports:
      - "19092:19092"
    environment:
      - ACTIVE_PROFILE=${ACTIVE_PROFILE}
      - EMAIL_SERVICE_USERNAME=${EMAIL_SERVICE_USERNAME}
      - EMAIL_SERVICE_PASSWORD=${EMAIL_SERVICE_PASSWORD}
      - LOCAL_DB_ENDPOINT=${LOCAL_DB_ENDPOINT}
      - LOCAL_DB_USERNAME=${LOCAL_DB_USERNAME}
      - LOCAL_DB_PASSWORD=${LOCAL_DB_PASSWORD}
      - REDIS_LOCAL_HOST=${REDIS_LOCAL_HOST}
      - REDIS_LOCAL_PORT=${REDIS_LOCAL_PORT}
      - REDIS_LOCAL_USERNAME=${REDIS_LOCAL_USERNAME}
      - REDIS_LOCAL_PASSWORD=${REDIS_LOCAL_PASSWORD}
      - JWT_SECRET_KEY=${JWT_SECRET_KEY}
    depends_on:
      - gateway-service
      - redis
      - mysql-db
    networks:
      - hotdealnetwork


# redis 및 mysql은 로컬 테스트용으로 작성되었으며, redis, mysql-db, volumes는 배포 시 제거 예정입니다.
  redis:
    image: redis:latest
    command: [ "redis-server", "--requirepass", "${REDIS_LOCAL_PASSWORD}" ]  # Redis 패스워드 설정
    ports:
      - "${REDIS_LOCAL_PORT}:${REDIS_LOCAL_PORT}"
    environment:
      - REDIS_LOCAL_PASSWORD=${REDIS_LOCAL_PASSWORD}  # Redis 패스워드 환경 변수 전달
      - REDIS_LOCAL_USERNAME=${REDIS_LOCAL_USERNAME}  # Redis 사용자명 환경 변수 전달

  mysql-db:
    image: mysql:latest
    container_name: mysql-db
    environment:
      MYSQL_ROOT_PASSWORD: ${LOCAL_DB_PASSWORD}  # 루트 비밀번호 설정
    ports:
      - "3307:3306"  # MySQL 포트를 로컬 머신과 연결
    volumes:
      - mysql-data:/var/lib/mysql  # 데이터 지속성을 위한 볼륨 사용
    networks:
      - hotdealnetwork

volumes:
  mysql-data:
    driver: local

networks:
  hotdealnetwork:
    driver: bridge

가장 아래의 networks를 통해 hotdealnetwork라는 브리지 네트워크를 생성하고 사용하는 것을 볼 수 있다. 각 서비스들은 서비스 모듈 내부에 작성된 Dockerfile을 통해 도커 이미지화 및 도커 컨테이너화된다.
redis, mysql은 dockerhub의 이미지를 가져오도록 되어 있다.
environment를 통해 환경변수를 설정해 줄 수 있으며, 앞의 key 부분은 환경변수 이름이고, value는 환경변수 값인데, ${REDIS_LOCAL_PASSWORD}와 같이 작성해서 .env 파일에 설정된 환경변수를 가져올 수 있다.

redis, mysql, eureka server, gateway, user 컨테이너는 hotdealnetwork 내부에서 동작하므로 서로 통신할 수 있다.

마무리

MSA 프로젝트 Docker Compose로 도커 컨테이너화를 하는 방법을 알아보았다.
여기서는 redis와 mysql을 compose에 포함시켰지만, AWS에 실제 배포 시에는 AWS ElastiCache, RDS 등으로 통신하도록 설정할 수 있을 것이다.
그리고 각 서비스에서 다른 서비스를 찾을 때, 주소의 호스트를 컨테이너명으로 설정해주어야 한다.

defaultZone: http://eureka-server:19090/eureka/

기존에 로컬에서 테스트할 때는 localhost:19090과 같이 사용했는데, 도커 컨테이너화를 하게 되면 localhost를 사용하면 안 된다. 왜냐하면 localhost는 각 컨테이너 자체를 가리키게 되기 때문이다.

profile
백엔드를 잘 하고 싶은 사람

0개의 댓글