728x90
반응형
Docker를 통한 배포
이전의 글에서는 단순히 docker 없이 배포하는 방법을 설명했었습니다.
이 글에서는 dockerfile을 작성해서 배포하는 방법을 알려드리겠습니다. 이전의 글이 궁금하신 분은 아래의 링크를 봐주세요.
[배포] github actions, EC2, nginx를 통한 무중단 배포
github actions, EC2, nginx를 통한 무중단 배포지금은 익숙한 프로세스이지만 누구나 처음 배포하는 과정은 매우 어려울 것 같습니다. 그렇지만 미래를 상상하면서 지금의 과정을 즐기는 것도 나쁘지
gotobill.tistory.com
디렉토리 구조
ec2 서버의 디렉토리 구조
로컬 프로젝트 디렉토리 구조
- 여기서 jar 파일은 깃허브의 가상 서버에 만들어지는 파일입니다.
728x90
설치
패키지 업데이트
sudo apt-get update
기본 패키지 설치
sudo apt-get install \
ca-certificates \
curl \
gnupg \
lsb-release
gpg 키 추가
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
- sudo mkdir -p /etc/apt/keyrings: gpg 키를 저장할 디렉토리 생성
- curl -fsSL https://download.docker.com/linux/ubuntu/gpg: gpg 키 다운로드
- sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg: gpg 키를 바이너리로 저장
-> 이렇게 다운로드를 받음으로써 패키지를 안전하게 다운 받을 수 있습니다.
도커 repository 설정
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
-> Docker의 공식 APT 리포지토리를 Ubuntu 시스템에 추가하여 Docker 패키지를 설치할 수 있도록 설정하는 과정입니다.
도커 설치
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
도커 시작 및 버전 확인
sudo systemctl start docker
sudo systemctl enable docker
sudo docker --version
도커 사용자 그룹에 추가
sudo usermod -aG docker $USER
nginx 설정 및 Dockerfile 작성
nginx 설정
upstream backend {
server app_blue:8081;
server app_green:8082;
}
server {
listen 80;
server_name [공인 ip]; # 서버의 공인 IP 주소를 설정합니다
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
위 코드에서 이 부분은 로드밸런싱을 위한 코드입니다.
우리가 새로운 버전을 배포할 때 두 개의 서버를 통해서 새로운 버전의 서버가 다 배포됐을 경우,
기존의 서버를 다운시켜서 총 1개의 서버로 유지시키는 기법을 이용할텐데 그때 기존의 이용자가 끊김을 겪지 않게 하기 위해서 nginx가 트래픽을 분산시키기 위한 설정입니다.
nginx Dockerfile
# Nginx 이미지를 기반으로 설정합니다
FROM nginx:alpine
# Nginx 설정 파일을 컨테이너로 복사합니다
COPY nginx.conf /etc/nginx/conf.d/default.conf
etc/nginx/conf.d/ 경로에 copy를 왜 하는가?
- conf.d/ 디렉토리에 있는 모든 .conf 파일은 Nginx가 자동으로 로드합니다.
- Nginx의 디폴트 설정에서는 include /etc/nginx/conf.d/*.conf; 명령어가 일반적으로 포함되어 있어, conf.d 디렉토리에 있는 모든 설정 파일을 자동으로 불러옵니다.
이는 새로운 설정을 추가할 때마다 nginx.conf를 직접 수정할 필요 없이 파일을 추가하기만 하면 되게 하기에 일종의 관례입니다.
docker-compose.yml 작성
services:
app_blue:
build:
context: .
dockerfile: Dockerfile
image: 도커의_유저네임/app_blue:latest
container_name: roomescape-app-blue
environment:
- SERVER_PORT=8081
ports:
- "8081:8081"
restart: always
app_green:
build:
context: .
dockerfile: Dockerfile
image: 도커의_유저네임/app_green:latest
container_name: roomescape-app-green
environment:
- SERVER_PORT=8082
ports:
- "8082:8082"
restart: always
nginx:
build:
context: ./nginx
dockerfile: Dockerfile
image: 도커의_유저네임/nginx:latest
ports:
- "80:80"
depends_on:
- app_blue
- app_green
container_name: roomescape-nginx
restart: always
알아야 할 점
- docker의 build할 때 도커 파일은 context의 폴더 설정 기준으로 Dockerfile이라고 적혀있는 파일을 씁니다.
- SERVER_PORT=8081 -> 애플리케이션이 8081 포트를 사용하게 합니다.
- ports
- "8082:8082"
-> 호스트의 포트 8082와 컨테이너의 포트 8082를 연결한다는 의미. - restart: always
-> 컨테이너가 중지되거나 시스템이 재부팅되었을 때 항상 자동으로 다시 시작하도록 설정
app_green과 app_blue의 Dockerfile 설정
# OpenJDK 17을 기반으로 하는 이미지를 사용합니다
FROM openjdk:17-jdk-slim
# 컨테이너 내의 작업 디렉터리를 설정
WORKDIR /app
# 호스트의 JAR 파일을 컨테이너로 복사합니다
# ec2 서버 내부로 app.jar 파일을 github actions를 통해 가져와야함.
COPY ./app.jar app.jar
# 애플리케이션이 사용할 포트를 노출합니다
EXPOSE 8080
# JAR 파일을 실행하는 명령을 지정합니다
CMD ["java", "-jar", "app.jar"]
- WORKDIR /app
-> WORKDIR 명령어를 사용하여 작업 디렉토리를 설정하면 이후의 모든 COPY, ADD, RUN, CMD 명령어는 이 디렉토리를 기준으로 실행됩니다. 따라서 경로를 명확하게 설정할 수 있습니다.
build.sh 작성
#!/bin/bash
# 현재 구동 중인 애플리케이션 확인
CURRENT_APP=$(docker-compose ps | grep "app_blue" | grep "Up" || echo "")
# 새 버전을 배포할 대상 결정
if [[ -n "$CURRENT_APP" ]]; then
TARGET="app_green"
else
TARGET="app_blue"
fi
echo "배포할 대상: $TARGET"
# Docker 이미지 빌드
docker-compose build $TARGET
# 새로운 버전 실행
docker-compose up -d $TARGET
# 새로운 버전이 구동될 때까지 대기
echo "새로운 버전의 애플리케이션이 구동될 때까지 대기 중..."
sleep 15
# 오래된 버전 중지
if [[ "$TARGET" == "app_green" ]]; then
OLD_APP="app_blue"
else
OLD_APP="app_green"
fi
echo "오래된 버전 중지: $OLD_APP"
docker-compose stop $OLD_APP
github actions 설정
name: CI/CD Pipeline
on:
pull_request:
branches:
- main
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
distribution: 'adopt'
java-version: '17'
- name: Install Docker Compose
run: |
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
# 깃허브의 가상 서버에 docker-compose 설치
- name: Build with Gradle
run: ./gradlew clean -x test build
- name: Copy JAR for Docker
run: cp build/libs/프로젝트_이름-0.0.1-SNAPSHOT.jar app.jar
- name: Upload app.jar as artifact
uses: actions/upload-artifact@v3
with:
name: app-jar
path: ./app.jar
# 최신의 버전을 ec2로 옮기기 위한 과정
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Build and Push Docker Image
run: docker-compose build && docker-compose push
# 도커 허브에 이미지 삽입
deploy:
if: github.event_name == 'push'
needs: build
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Download app.jar artifact
uses: actions/download-artifact@v3
with:
name: app-jar
path: .
- name: Deploy with Docker Compose
env:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
run: |
echo "$SSH_PRIVATE_KEY" > private_key.pem
chmod 600 private_key.pem
scp -i private_key.pem -o StrictHostKeyChecking=no \
docker-compose.yml \
build.sh \
app.jar \
Dockerfile \
${{ secrets.EC2_USERNAME }}@${{ secrets.EC2_HOST }}:/home/${{ secrets.EC2_USERNAME }}/
# 나는 나의 로컬에서 nginx의 폴더를 제외한 설정 파일을 만들어서 ec2로 보내는 과정을 거쳤다.
# 하지만 그렇게 하지 않고 ec2에 미리 만들어놔도 된다.
ssh -i private_key.pem -o StrictHostKeyChecking=no ${{ secrets.EC2_USERNAME }}@${{ secrets.EC2_HOST }} "chmod +x build.sh && ./build.sh"
rm private_key.pem
728x90
반응형
'개발' 카테고리의 다른 글
[Spring] Redis 써서 JWT 토큰 캐시에 저장하기 (0) | 2024.11.24 |
---|---|
[Spring] Spring Security, OAuth 2.0, JWT로 카카오 로그인 구현하기 (0) | 2024.11.22 |
[spring mvc] 스프링 mvc 꼭 알아야 하는 필수 지식 (0) | 2024.10.15 |
[java] JVM 개념 및 기능 : 왜 쓰는 걸까? (3) | 2024.10.10 |
[배포] github actions, EC2, nginx를 통한 무중단 배포 (3) | 2024.10.09 |