우당탕탕 개발일지
[MSA] Dcoker-Compose (1) 본문
MSA 프로젝트 진행 후, 배포에서 가장 어려움을 겪었다. MSA의 의도대로라면 각 서비스를 서비스 단위로 배포를 해야하는데 그렇게 될 경우 ec2의 개수가 10개 이상 필요하고, 그러한 요금을 감당하면서까지 개인 프로젝트를 배포할 필요성은 없다고 판단하였다. 그래서 최대한 AWS 프리티어를 사용하며 배포하기로 결정하였고, 프론트는 CloudFront를, 데이터베이스는 RDS를 사용하였다. Eureka, Api-Gateway 외 다른 서비스는 Docker Compose를 이용할 계획이다.
1. Docker 설치
처음에는 ec2 프리티어로 진행하고자 했으나 t2.micro 성능이 너무 작아 컨테이너 두개를 띄우면 멈춰버렸다. 그래서 윈도우에 도커를 직접 설치하여 진행한 후, 완료된 상태를 보고 그에 맞는 ec2를 띄우기로 결정하였다.
https://docs.docker.com/desktop/install/windows-install/
우분투 컨테이너 실행
설치가 완료되면 cmd 창을 열어 도커 커맨드를 이용해 우분투 컨테이너가 실행이 되는지 확인해본다.
docker run -it --rm ubuntu
도커 데스크탑에서도 우분투 컨테이너가 실행중임을 확인할 수 있다. exit 커맨드를 입력하면 종료할 수 있다.
2. Docker Image 생성
서비스 개수가 많은 관계로 일일이 배포시켜주기 보다는 깃허브 액션을 사용하여 도커 이미지를 생성하는 것이 편하다고 판단하였다.
* 참고로 엔트리포인트 jar 파일이 명시된 파일명처럼 나오지 않아서 build.gradle에 추가 설정을 해주었다.
Dockerfile 작성
# Base image 설정
FROM openjdk:21-jdk-slim
# 작업 디렉토리 설정
WORKDIR /app
# 소스 복사
COPY . .
# Gradle 빌드 수행
RUN ./gradlew clean build
# JAR 파일 실행 설정
ENTRYPOINT ["java", "-jar", "mypage-service.jar"]
위 도커 파일은 기본 방식이며, 빌드 시 이미지 크기가 1.3GB정도로 크다고 판단되어 다음과 같이 경량화를 진행하였다.
# Stage 1: Build
FROM openjdk:21-jdk-slim as builder
# 작업 디렉토리 설정
WORKDIR /app
# Gradle Wrapper 및 소스 복사
COPY gradlew .
COPY gradle gradle
COPY build.gradle .
COPY settings.gradle .
COPY src src
# Gradle 빌드 수행 (테스트 제외)
RUN ./gradlew clean build -x test
# Stage 2: Runtime
FROM openjdk:21-slim
# 작업 디렉토리 설정
WORKDIR /app
# 빌드 스테이지에서 생성된 JAR 파일 복사
COPY --from=builder /app/build/libs/mypage-service.jar .
# 애플리케이션 실행
ENTRYPOINT ["java", "-jar", "mypage-service.jar"]
build.gradle 수정
. . .
repositories {
mavenCentral()
}
bootJar {
archiveBaseName = 'mypage-service' // 실행 가능한 JAR 파일 이름 설정
archiveVersion = '' // 버전 정보 제거
}
Docker Hub Token 발급
깃허브 액션을 사용하기 위해서는 먼저 깃허브에 도커 허브 토큰을 등록해주어야 한다. Account Settings > Personal access tokens 에 들어가서 토큰을 발급해준다. 생성된 키는 깃허브 등록 과정을 거쳐야 하기 때문에 메모장에 적어둔다.
* 도커 허브 토큰 권한을 Read & Write로 지정하지 않으면 오류가 발생한다.
GitHub 시크릿 키 등록
DOCKER_USERNAME
DOCKER_PASSWORD
Repository > Settings > Secrets and variables > actions 에서 시크릿 키를 등록해준다. 시크릿 키 이름은 위와 같이 지정해준다.
GitHub Action workflow 작성
이제 워크플로우를 작성해주면 도커 허브로 이미지가 올라간다.
name: Docker Image CI
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Docker Hub 로그인
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
# gradlew 파일에 실행 권한 추가
- name: Add execute permission for gradlew
run: chmod +x ./gradlew
# Docker 이미지 빌드 및 푸시
- name: Build and Push Docker Image
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: tirtir0827/mypage-service:latest
3. Docker Compose 구성
앞으로 사용할 기본적인 Docker 명령어는 다음과 같다.
도커 이미지 Pull
docker image pull 레파지토리명[:태그명]
ex) docker pull tirtir0827/api-service
ex) docker pull mysql:8.0
도커 이미지 목록 확인
docker images
도커 이미지 삭제
docker rmi <이미지 ID>
도커 이미지를 삭제하기 위해서는 해당 이미지의 컨테이너가 제거된 상태여야 한다.
도커 네트워크 생성
docker network create <네트워크 이름>
ex) docker network create docker-network
도커 네트워크 목록 확인
docker network ls
네트워크 내 도커 컨테이너 생성
docker run -d --name <컨테이너 이름> --network <네트워크 이름> -p <포트>:8088 <실행할 이미지>
ex) docker run -d --name mysql-container --network docker-network -e MYSQL_ROOT_PASSWORD=1234 mysql:latest
ex) docker run -d --name eureka-container --network docker-network -p 8761:8088 tirtir0827/service-discovery
* 네트워크로 묶어주면 MySQL 데이터베이스도 사용할 수 있다.
컨테이너 목록 확인
docker ps
docker ps -a
ps는 현재 동작중인 컨테이너 목록을 보여주고, -a를 붙이면 종료된 컨테이너를 포함한 목록을 보여준다.
컨테이너 삭제
docker stop <컨테이너 ID> // 컨테이너 정지
docker rm <컨테이너 ID>
컨테이너를 삭제하기 위해서는 해당 컨테이너가 정지되거나 종료된 상태여야 한다.
Docker Image Pull
앞서 빌드한 도커 이미지들을 모두 pull 받는다.
Dcoker Network 생성
docker network create docker-network
docker network inspect docker-network
'docker-network' 이름의 네트워크 생성 후 상태를 보면 아직 아무 컨테이너도 연결되지 않음을 확인할 수 있다.
Docker Container 생성
docker run -d --name config-container --network docker-network -p 8888:8088 tirtir0827/config-server
테스트로 가장 중요한 서비스인 Config-Server에 요청되는지 Postman으로 확인해본 결과 통신이 되지 않았다.
Docker 컨테이너 내부로 집입하여 살펴보기 위해서 다음 도커 커맨드를 실행하였다.
docker exec -it config-container sh
# apt update && apt install -y curl
# curl --location "http://localhost:8888/datagokr/dev"
Docker 컨테이너 내부에서의 localhost는 컨테이너 자체를 가리키며, Postman은 호스트 머신에서 실행된다. 컨테이너 내부에서는 호출이 되는 것으로 보아, 서버의 방화벽이 8888 포트를 허용하지 않아 문제가 발생한 것이라 판단하고 설정을 수정하였다.
1. 윈도우 방화벽 인바운드 규칙 설정 (해결 X)
대부분 배포를 ec2로 진행하다 보니 AWS 보안그룹 인바운드 규칙에 대한 내용만 다루고 있었다. 윈도우에서 외부 → Docker 포트로 접속하기 위해서는 프로그램 인바운드 규칙을 수정해주어야 한다. 방화벽 > 고급 설정 > 인바운드 규칙에 들어가서 다음과 같은 설정을 진행한다.
Docker Desktop Backend 여러 개가 있다. 전부 똑같은 프로그램에 대한 인바운드 규칙을 다루고 있고, 프로토콜이나 포트에 대해서만 차이가 있다. 여기서 공용으로 표시된 Docker Desktop Backend에 설정되어 있는 호스트 포트를 세팅해주며, 기본적으로 Docker의 포트는 8080이다.
2. Docker 포트 포워딩 설정 (해결)
문제 원인은 포트 포워딩이었다. 일반적인 애플리케이션이었다면 기본 포트인 8080이었겠지만 MSA 구조 상 애플리케이션은 8888 포트에서 실행이 된다. 하지만 기존 도커 커맨드는 내부 포트를 8088로 지정하려고 하는 바람에 매핑이 맞지 않아 작동하지 않는 것이었다.
해결 방안은 애플리케이션의 application.yml 파일에서 내부 포트를 8088로 변경하거나, 도커 커맨드에서 포트를 지정해주어야 했다.
docker run -d --name config-container -p 8888:8888 tirtir0827/config-server
'Cloud' 카테고리의 다른 글
[MSA] Docker-Compose(2) (1) | 2024.12.17 |
---|---|
[AWS] ECR + Lambda + API Gateway(feat. Cold Start) (1) | 2024.12.11 |
[AWS] S3 + CloudFront + Route53 (feat. AccessDenied) (1) | 2024.12.10 |
Typescript + Firebase 환경 설정 (0) | 2024.08.11 |
네이버 클라우드 Micro Server (0) | 2024.07.10 |