현재 배포 서버로 EC2 인스턴스 1대를 사용중입니다. 무중단 배포를 위해 인스턴스를 추가로 사용하기 위해선 기술 검토 요청을 보내야 하며 그동안의 대기 시간이 존재합니다. 또, 추가 사용이 가능한지도 미지수입니다.
따라서 한 대의 서버에서도 충분히 무중단 배포를 진행할 수 있다고 판단했기에 블루/그린 방식을 적용합니다.
기본적으로 8080포트와 8081포트를 사용합니다.
전체적인 시나리오는 다음과 같습니다.
이제 위 시나리오의 과정에 대해 더 자세히 말씀드리겠습니다.
Idle Port는 8080 포트에 요청을 쏘아보고 응답의 결과로 찾습니다. 만약 응답 코드가 200이면 현재 8080 포트에서 블루가 동작중인 셈이니 8081포트가 Idle Port가 됩니다.
이렇게 찾은 포트로 그린을 배포합니다.
그 다음 그린의 헬스 체크를 진행하기 위해 30초의 대기 시간을 가집니다. 이유는 곧바로 실행한 헬스 체크 이후에 예상치 못한 상황으로 그린 서버가 죽게될 가능성을 염두해두었기 때문입니다. 그리고 현재 스프링 애플리케이션이 서버에 제대로 띄어지기까지 약 20초의 시간이 소요되기 때문입니다.
헬스 체크 결과, 그린 서버가 정상적으로 배포가 됐다면 Nginx의 포트 포워딩 설정을 변경합니다. 그리고 5초의 대기 시간을 주고 블루 서버를 종료합니다. 그린 서버에 문제가 생겨 정상적으로 실행되지 않았다면 종료합니다.
아래는 작성된 배포 스크립트입니다.
RESPONSE_CODE=$(curl -o /dev/null -w "%{http_code}" <http://localhost:8080/celebs>)
if [ ${RESPONSE_CODE} = 200 ];
then
IDLE_PORT=8081
IDLE_MONITORING_PORT=18082
USED_PORT=8080
else
IDLE_PORT=8080
IDLE_MONITORING_PORT=18081
USED_PORT=8081
fi
echo "IDLE_PORT=${IDLE_PORT}"
echo "IDLE_MONITORING_PORT=${IDLE_MONITORING_PORT}"
IMAGE_TAG=back-prod-${APP_VERSION_TAG}
DOCKER_CONTAINER_NAME=backend-${IDLE_PORT}
DOCKER_HUB_REPOSITORY=celuveat/celuveat
SERVER_LOG_DIR_PATH=~/log
DOCKER_LOG_DIR_PATH=/app/logs
docker pull ${DOCKER_HUB_REPOSITORY}:${IMAGE_TAG}
docker run \\
-d \\
--name ${DOCKER_CONTAINER_NAME} \\
-p $IDLE_PORT:8080 \\
-p $IDLE_MONITORING_PORT:18080 \\
-e "SPRING_PROFILES_ACTIVE=prod" \\
-v ${SERVER_LOG_DIR_PATH}:${DOCKER_LOG_DIR_PATH} \\
${DOCKER_HUB_REPOSITORY}:${IMAGE_TAG}
# 새로 뜬 컨테이너 확인
sleep 30
HEALTHY_CODE=$(curl -o /dev/null -w "%{http_code}" <http://localhost>:${IDLE_PORT}/celebs)
if [ ${HEALTHY_CODE} != 200 ];
then
IDLE_CONTAINER_ID=$(docker ps -q --filter "publish=${IDLE_PORT}")
docker stop ${IDLE_CONTAINER_ID}
docker rm ${IDLE_CONTAINER_ID}
echo "TERMINATED"
exit 1
fi
echo "set \\$service_url <http://127.0.0.1>:${IDLE_PORT};set \\$service_monitoring_url <http://127.0.0.1>:${IDLE_MONITORING_PORT};" | sudo tee /etc/nginx/conf.d/service-url.inc
sudo service nginx reload
sleep 5
USED_CONTAINER_ID=$(docker ps -q --filter "publish=${USED_PORT}")
docker stop ${USED_CONTAINER_ID}
docker rm ${USED_CONTAINER_ID}
docker image prune -f