Skip to content

[BE] 무중단 배포

iamjooon2 edited this page Nov 21, 2023 · 5 revisions

다운타임

서버를 재배포하고 다시 실행되는 동안 유저는 서비스를 이용할 수 없게 되고, 이를 다운타임이라고 합니다. 사용자 불편함을 최소로 하기 위해 무중단 배포를 적용하여 다운타임을 제거해봅시다.

롤링 배포

서버를 하나씩 점진적으로 새로운 버전으로 바꾸는 방식

image

카나리 배포

서버를 하나씩 점진적으로 새로운 버전으로 바꾸는 방식인 롤링과 비슷한 방식이지만 새로운 버전을 소수의 유저들에게만 배포를 해보고 문제가 없는 것을 확인하면 점차 많은 유저들에게 배포하는 방식입니다

image

블루/그린 배포

새로운 버전으로 한번에 바꾸는 방식으로 보통 현재 버전을 Blue, 새 버전을 Green이라 한다. 현재 버전과 동일한 서버 구성을 새로운 버전으로 만든 후, 문제가 없을 때 기존의 Blue 서버를 제거할 수 있습니다.

image

블루/그린 배포를 선택한 이유

카나리, 롤링의 경우 구버전과 신버전이 동시에 서비스 될 가능성이 있어 호환성 문제가 일어날 수 있고, 에러 발생시 기존 버전으로 롤백이 용이하다는 이유로 블루/그린 배포 방식을 선택하게 되었습니다.

집사의고민 무중단 배포 플로우

  1. 현재 구동중인 서버 포트 확인합니다
  2. 사용하고 있지 않은 포트에 신버전(green) 배포
  3. 신버전이 제대로 배포된 경우에만 4, 5만 진행. 제대로 배포되지 않았으면 구버전(blue) 그대로 유지
  4. 포트 스위칭
  5. 구버전(blue) 종료
  • 이전에 작업하고 있을 수도 있으므로 gracefully shutdown

사용한 스크립트는 다음과 같습니다

#!/bin/sh
echo "> 현재 구동중인 서버 포트 확인"
RESPONSE_8080=$(curl -s http://localhost:8080/actuator/health)
echo "> $RESPONSE_8080"

UP_COUNT=$(echo $RESPONSE_8080 | grep 'UP' | wc -l)
if [ $UP_COUNT -ge 1 ]
then # $UP_COUNT >= 1 ("UP" 문자열이 있는지 검증)
  IDLE_PROFILE=dev2
  IDLE_PORT=8081
else
  IDLE_PROFILE=dev1
  IDLE_PORT=8080
fi

echo "> $IDLE_PROFILE 에서 구동중인 애플리케이션 pid 확인"
IDLE_PID=$(pgrep -f ${IDLE_PORT}-zipgo-backend-0.0.1-SNAPSHOT.jar)
echo "> pid: $IDLE_PID"

if [ -z "$IDLE_PID" ]
then
  echo "> 구동중인 서버가 없습니다"
else
  echo "> kill -9 $IDLE_PID"
  sudo kill -9 $IDLE_PID
  sleep 5
fi

echo "> 신버전을 배포합니다"
echo "> 배포할 프로필 $IDLE_PROFILE"
echo "> 배포할 포트 $IDLE_PORT"
sudo chmod +x ~/${IDLE_PORT}-zipgo-backend-0.0.1-SNAPSHOT.jar
sudo nohup java \
-Dspring.profiles.active=$IDLE_PROFILE \
-Dspring.config.import=file:/home/ubuntu/env.properties \
~/${IDLE_PORT}-zipgo-backend-0.0.1-SNAPSHOT.jar > ~/application.log 2>&1 &

echo "> $IDLE_PROFILE 30초 후 Health check 시작"
echo "> curl -s http://localhost:$IDLE_PORT/actuator/health "
sleep 30

for retry_count in {1..5}
do
  RESPONE=$(curl -s http://localhost:$IDLE_PORT/actuator/health)
  up_count=$(echo $RESPONE | grep 'UP' | wc -l)

  if [ $up_count -ge 1 ]
  then # $up_count >= 1 ("UP" 문자열이 있는지 검증)
      echo "> Health check 성공"
      break
  else
      echo "> Health check의 응답을 알 수 없거나 혹은 status가 UP이 아닙니다 "
      echo "> Health check: ${RESPONE}"
  fi

  if [ $retry_count -eq 10 ]
  then
    echo "> Health check 실패 "
    echo "> 배포를 종료합니다"
    exit 1
  fi

  echo "> Health check 실패 재시도..."
  sleep 10
done

echo "> 리버스 프록시 대상 포트를 변경합니다"
sleep 1

echo "> 전환할 포트: $IDLE_PORT"
echo "set \$service_url http://127.0.0.1:${IDLE_PORT};" |sudo tee /etc/nginx/conf.d/service-url.inc
echo "> Nginx Reload"
sudo service nginx reload

if [ $IDLE_PORT -eq 8081 ]
then
    FORMER_PID=$(sudo netstat -lntp | grep 8080 | awk '{print $7}' | cut -d'/' -f1)
else
    FORMER_PID=$(sudo netstat -lntp | grep 8081 | awk '{print $7}' | cut -d'/' -f1)
fi

echo "> 이전 서버를 종료합니다"
sudo kill -15 $FORMER_PID

echo "> 완료"

완료된 후, Nginx에서 리버스 프록시 설정 포트를 새로 배포한 포트로 변경시켜줍니다.

Clone this wiki locally