회사에서 배포 환경을 Jenkins 에서 Github Action을 변경하는 작업을 진행했다. 사용자인 나도 굳이 젠킨스에 접속하지 않고, Github 에서 CICD 를 할 수 있어 편했다.
그래서 진행하고 있는 사이드 프로젝트에 적용해보았고, 팀의 반응은 좋았다🎉
나는 별도의 배포 스크립트를 따로 작성해서 CD를 구성하였고, letsencrypt 를 활용해 인증서 등록 및 갱신도 스크립트로 해주었다.
AWS 비용을 최소화하려는 노력을 많이 하였다💸
배포 방법은 간단하다. Github Action 특정 워크 플로우에서 내가 배포하고 싶은 서비스 이미지 태그를 입력하면 된다. 물론 이미지는 ECR 등 팀에서 사용하는 도커 이미지 레지스트리에 존재해야한다.
블루/그린 배포 방식으로 무중단 배포 방식을 선택했다.
name: Heylu Server Dev CD
on:
workflow_dispatch:
inputs:
tag:
description: '개발환경에 배포할 서비스 버전을 입력합니다.'
required: true
jobs:
trigger:
name: 개발 환경 ${{ inputs.tag }} 배포한다
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Custom Deployment Task
env:
TAG: ${{ inputs.tag }}
run: |
echo "Image Name: $IMAGE_NAME"
echo "Tag: $TAG"
- name: Excuting remote ssh commands
uses: appleboy/ssh-action@v1.0.3
env:
TAG: ${{ inputs.tag }}
with:
host: ${{ secrets.REMOTE_IP }}
username: ${{ secrets.REMOTE_USER }}
key: ${{ secrets.REMOTE_PRIVATE_KEY }}
envs: TAG
script: |
aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin 281727968100.dkr.ecr.ap-northeast-2.amazonaws.com
cd /home/ec2-user/heylu-server/deploy/dev
sh deploy.sh $TAG
workflow_dispatch:
inputs
내에 tag
라는 인자를 받는다. 개발 환경에 배포할 서비스 이미지 버전을 의미한다.jobs:
trigger
${{ inputs.tag }}
로 설정되며, 사용자가 입력한 서비스 버전이 포함된다.runs-on
을 사용하여 이 작업이 실행될 환경을 지정합니다. 여기서는 ubuntu-latest
를 사용하여 Ubuntu 환경에서 실행된다.steps:
actions/checkout@v4
액션을 사용하여 수행되며, fetch-depth: 1
은 최신 커밋만 가져온다.TAG
를 환경 변수로 정의하고 있다.uses
를 사용하여 SSH 연결을 설정하는데 필요한 액션을 지정한다.env
를 사용하여 TAG
환경 변수를 정의한다. 사용자가 개발환경에 배포할 이미지 태그가 쉘 스크립트 환경 변수로 넘어가서 원하는 버전의 형상이 배포될 수 있도록 한다.with
를 사용하여 SSH 연결에 필요한 정보를 지정하고, script
를 사용하여 원격 서버로 전달될 스크립트를 정의한다.#!/bin/bash
# 태그 값으로 받은 변수
export TAG=$1
# BLUE가 실행중인지 확인
APP_NAME=heylu-server
EXIST_BLUE=$(docker-compose -p heylu-server-blue -f docker-compose.blue.yml ps | grep heylu-server-blue)
if [ -z "${EXIST_BLUE}" ] # -z는 문자열 길이가 0이면 true.BLUE가 실행중이면 false
then
# start blue
START_CONTAINER=blue
TERMINATE_CONTAINER=green
START_PORT=8070
TERMINATE_PORT=8071
else
# start green
START_CONTAINER=green
TERMINATE_CONTAINER=blue
START_PORT=8071
TERMINATE_PORT=8070
fi
echo " ========== [start] change ${APP_NAME}-${TERMINATE_CONTAINER} to ${APP_NAME}-${START_CONTAINER} =========="
echo "[step 1] deploy ${APP_NAME}-${START_CONTAINER}"
docker-compose -p ${APP_NAME}-${START_CONTAINER} -f docker-compose.${START_CONTAINER}.yml pull
docker-compose -p ${APP_NAME}-${START_CONTAINER} -f docker-compose.${START_CONTAINER}.yml up -d
for RETRY_CNT in {1..10}
do
echo "Health Check Start...(${RETRY_CNT})"
HEALTH_CHECK_RESPONSE=$(curl -s http://127.0.0.1:${START_PORT}/health | grep 'UP')
if [ -z "${HEALTH_CHECK_RESPONSE}" ] # 실행되었다면 break
then
echo "health check fail..${HEALTH_CHECK_RESPONSE}"
else
echo "health check success!"
break
fi
if [ ${RETRY_COUNT} -eq 11 ]
then
echo "deployment failed."
exit 1
fi
echo "wait 5 seconds..."
sleep 5
done
echo "deploy ${APP_NAME}-${START_CONTAINER} success!"
# sed 명령어를 이용해서 아까 지정해줬던 service-url.inc의 url값을 변경해줍니다.
# sed -i "s/기존문자열/변경할문자열" 파일경로 입니다.
# 종료되는 포트를 새로 시작되는 포트로 값을 변경해줍니다.
# ex ) sudo sed -i "s/8080/8081/" /nginx/conf.d/app.conf
echo -e "\n[step - 2] change port ${TERMINATE_PORT} to ${START_PORT}"
sed -i "s/${TERMINATE_PORT}/${START_PORT}/" /home/ec2-user/heylu-server/deploy/infra-dev/nginx/conf.d/app.conf
# 새로운 포트로 스프링부트가 구동 되고, nginx의 포트를 변경해주었다면, nginx 재시작해줍니다.
# docker exec -it {nginx container name} nginx -s reload
echo -e "\n[step - 3] nginx reload.."
docker exec -i nginx nginx -s reload
# 기존에 실행 중이었던 docker-compose는 종료시켜줍니다.
echo -e "\n[step - 4] exit ${APP_NAME}-${TERMINATE_CONTAINER}"
docker-compose -p ${APP_NAME}-${TERMINATE_CONTAINER} -f docker-compose.${TERMINATE_CONTAINER}.yml down
echo "exit ${APP_NAME}-${TERMINATE_CONTAINER} success!"
echo " ========== [end] change ${APP_NAME}-${TERMINATE_CONTAINER} to ${APP_NAME}-${START_CONTAINER} =========="
환경 변수 설정:
TAG
에 할당한다.export TAG=$1
블루/그린 배포 전략 선택:
컨테이너 배포:
포트 변경:
sed -i "s/${TERMINATE_PORT}/${START_PORT}/" /home/ec2-user/heylu-server/deploy/infra-dev/nginx/conf.d/app.conf
Nginx 재시작:
docker exec -i nginx nginx -s reload
이전 컨테이너 종료:
docker-compose -p ${APP_NAME}-${TERMINATE_CONTAINER} -f docker-compose.${TERMINATE_CONTAINER}.yml down