Spring & Spring Boot/Release & Deploy

[3] Application Release Model(애플리케이션 릴리스 모델) - Blue-Green Deployment (블루-그린 배포), 스크립트 실행

carrotweb 2026. 10. 27. 20:13
728x90
반응형

배포할 때마다 매번 설정 파일을 수정하고 검사하고 체크해야 하는 반복적인 프로세스를 스크립트로 자동화할 수 있습니다.

 

스크립트로 자동 전환

1. C:\workspaces\docker\nginx에 deploy 디렉터리를 생성하고 blud-green-deploy.sh 파일을 생성합니다.

C:\workspaces\docker\nginx\deploy
echo "Blue-Green Deployment를 시작합니다."

# 설정 파일 경로
CONF_FILE="../conf.d/appservers.conf"

# 파일 존재 여부 확인
if [ ! -f "$CONF_FILE" ]; then
    echo "에러 : $CONF_FILE 파일을 찾을 수 없습니다."
    exit 1
fi

# 현재 설정 확인
if grep -q "dev-transmission:8080" "$CONF_FILE"; then
    CURRENT="dev-transmission:8080"
    TARGET="dev-transmission-third:8080"
elif grep -q "dev-transmission-third:8080" "$CONF_FILE"; then
    CURRENT="dev-transmission-third:8080"
    TARGET="dev-transmission:8080"
else
    echo "에러 : $CONF_FILE 파일에서 도커 컨테이너 이름을 찾을 수 없습니다."
    exit 1
fi

# 헬스 체크
TARGET_HEALTH_URL="http://$TARGET/app-monitoring/health"

# 헬스 체크 요청 후 응답 바디 저장
TARGET_HEALTH_RESPONSE_BODY=$(curl -s "$TARGET_HEALTH_URL")

# 헬스 체크 상태가 UP인지 확인
if echo "$TARGET_HEALTH_RESPONSE_BODY" | grep -q '"status":"UP"'; then
    echo "정보 : [Green] $TARGET의 애플리케이션 상태가 UP(정상 작동 중)입니다."
else
    echo "에러 : [Green] $TARGET의 애플리케이션 상태가 DOWN(중지된 상태)입니다."
    exit 1
fi

echo "[Blue] $CURRENT에서 [Green] $TARGET으로 전환을 시작합니다."

# 문자열 치환 후 백업 파일 생성
sed -i.bak "s|$CURRENT|$TARGET|g" "$CONF_FILE"
echo "정보 : $CONF_FILE 파일을 ${CONF_FILE}.bak 파일로 백업하였습니다."

echo "Nginx 설정 파일 문법 검사 시작"
nginx -t
if [ $? -ne 0 ]; then
    echo "에러 : Nginx 설정 파일에 오타나 문법적 오류가 있습니다. 원본 파일로 롤백합니다."
    mv "${CONF_FILE}.bak" "$CONF_FILE"
    exit 1
fi
echo "Nginx 설정 파일 문법 검사 종료"

# Nginx Reload
echo "Nginx를 Reload합니다."
nginx -s reload
if [ $? -eq 0 ]; then
    echo "정보 : [Green] $TARGET으로 전환이 완료되었습니다."
    rm -f "${CONF_FILE}.bak"
else
    echo "에러 : Reload가 실패하였습니다. [Blue]으로 롤백합니다."
    mv "${CONF_FILE}.bak" "$CONF_FILE"
    nginx -s reload
    exit 1
fi

 

echo [옵션] [문자열 또는 변수]
  • echo(에코) : 터미널에 문자열을 출력합니다.

 

변수명=값
  • 변수 선언 : 변수명에 값을 설정합니다.
  • = 앞뒤에 공백이 있으면 에러가 발생합니다.
  • 문자열에 공백이 포함된 경우 쌍따옴표("")나 홀따옴표('')로 감싸야 합니다.
  • 값에 다른 변수나 명령어 결과를 넣을 때는 쌍따옴표("")를 사용합니다.

 

$변수명
${변수명}
  • 변수에 저장된 값을 가져옵니다.

 

exit [n]
  • exit(엑시트) : 스크립트 실행을 종료합니다.
  • 숫자를 리턴할 수 있습니다. (0이면 정상 종료, 1이면 오류가 발생하여 강제 종료로 많이 사용합니다.)

 

[ -f 파일 ]
  • -f : 파일이 존재하는지 확인합니다.

 

grep [옵션] [문자열이나 패턴] [파일]
  • grep(그랩) : 파일에서 특정 문자열이나 패턴을 검색합니다.
    • -q : 터미널에 아무런 결과도 출력하지 않도록 합니다. 특정 문자열이나 패턴을 찾으면 0, 찾지 못하면 1, 에러가 발생하면 2 값을 반환합니다.

 

curl [옵션] [URL]
  • curl : URL로 HTTP 요청을 보냅니다.
    • -s(silent) : 조용히 실행합니다.

 

$(...)
  • 명령어 치환 : 괄호 안을 실행하고 그 결과를 변수에 할당합니다.

 

[명령어] | [명령어]
  • 명령어와 명령어 사이에 |가 오면, 앞 명령어의 출력 결과를 뒤 명령어의 입력으로 넘겨줍니다.

 

sed -i.bak s|문자열이나 패턴|교체할 문자열|g 파일
  • sed(세드) : 파일 안에서 문자열을 검색, 치환, 삭제합니다. Stream Editor(스트림 에디터)
    • -i(in-place) : 원본 파일을 수정(Overwrite)합니다.
    • .bak : 원본 파일을 수정하기 전에 같은 위치에 {원본 파일}.bak이라는 백업 파일을 자동으로 생성합니다.
    • s(substitute) : 구분자 뒤에 오는 문자열이나 패턴을 다음 구분자 뒤에 오는 교체할 문자열로 교체(Replace)합니다.
    • g(global) : 전부 교체(Replace)합니다.

 

[ $? -eq 0 ]
  • $? (종료 상태 변수) : 직전에 실행한 명령어가 성공하면 0, 실패하거나 에러가 발생하면 0이 아닌 값을 가지고 있습니다.
  • -eq(equal) : 같다 (동등 비교 연산자)
  • -ne(not equal) : 같지 않다 (부등 비교 연산자)

 

mv [옵션] [원본 파일 또는 디렉터리] [이동할 경로 또는 변경할 이름]
  • mv(move) : 파일을 이동하거나 파일 이름을 변경합니다.

 

rm [옵션] [파일 또는 디렉터리 이름]
  • rm(remove) : 파일이나 디렉터리를 삭제합니다. 
    • -f : 경고나 확인 메시지 없이 강제로 삭제합니다. (파일이 없어도 에러를 출력하지 않습니다.)

 

 

2. C:\workspaces\docker\nginx\docker-compose.yml 파일을 수정합니다.

볼륨에 deploy를 추가합니다.

# 컨테이너 내의 서비스들 정의
services:
  # 서비스명
  nginx:
    # 가져올 이미지:태그(버전)
    image: nginx:latest
    # 컨테이너명
    container_name: dev-nginx
    # 연결된 포트 (컨테이너 외부:컨테이너 내부)
    ports:
      - "80:80"
      - "443:443"
    # 볼륨 (컨테이너 외부 경로:컨테이너 내부 경로)
    volumes:
      # 설정 파일
      - C:\workspaces\docker\nginx\nginx.conf:/etc/nginx/nginx.conf
      # 설정 파일
      - C:\workspaces\docker\nginx\conf:/etc/nginx/conf.d
      # 정적 파일
      - C:\workspaces\docker\nginx\static:/usr/share/nginx/static
      # SSL 파일
      - C:\workspaces\docker\nginx\ssl:/etc/nginx/ssl
      # deploy 파일
      - C:\workspaces\docker\nginx\deploy:/etc/nginx/deploy
    # 컨테이너 재시작 - 컨테이너 자동 재시작
    restart: unless-stopped
    # 사용할 네트워크
    networks:
      - dev-network

networks:
  dev-network:
    external: true

 

3. 수정된 설정 파일(docker-compose.yml)을 반영하기 위해 실행합니다.

docker compose up -d

 

4. 실행 중인 Nginx 컨테이너에서 Bash 쉘을 실행하고 스크립트를 실행합니다.

docker exec -it dev-nginx bash
cd etc/nginx/deploy/
sed -i 's/\r$//' blud-green-deploy.sh
./blud-green-deploy.sh

 

스크립트를 실행하면 자동으로 Blue에서 Green으로 전환됩니다.

 

스크립트에서는 항상 현재 실행 중인 버전이 Blue가 됩니다.

 

주의

셸 스크립트를 실행할 때 syntax error가 발생한다면 Windows 환경에서 스크립트 파일을 만들었기 때문입니다.

Windows에서는 줄바꿈 문자가 CRLF(\r\n)으로 저장되기 때문입니다. 리눅스에서는 줄바꿈 문자가 LF(\n)으로 저장되어야 합니다. 그래서 sed 명령어를 이용해 개행 문자(\r)를 모두 제거합니다.

sed -i 's/\r$//' blud-green-deploy.sh

 

 

만약, 도커 컨테이너에 있는 새(신규) 버전의 애플리케이션의 실행이 완료되기 전에는 스크립트를 실행했다면 헬스 체크가 DOWN일 겁니다.

현재 스크립트는 도커 컨테이너에 있는 새(신규) 버전의 애플리케이션의 실행이 완료 상태에서 진행해야 합니다.

 

스크립트에서 애플리케이션의 헬스 체크가 정상(UP)이 될 때까지 대기하게 수정할 수 있습니다.

# 헬스 체크
TARGET_HEALTH_URL="http://$TARGET/app-monitoring/health"

# 최대 재시도 횟수
HEALTH_MAX_RETRY_COUNT=5
# 재시도 사이의 대기 시간(초)
HEALTH_WAIT_SECONDS=3
# 헬스 체크 결과
TARGET_HEALTH_RESULT=false

for ((index=1; index<=HEALTH_MAX_RETRY_COUNT; index++)); do
    # 헬스 체크 요청 후 응답 바디 저장
    TARGET_HEALTH_RESPONSE_BODY=$(curl -s "$TARGET_HEALTH_URL")

    # 헬스 체크 상태가 UP인지 확인
    if echo "$TARGET_HEALTH_RESPONSE_BODY" | grep -q '"status":"UP"'; then
        echo "정보 : [$index/$HEALTH_MAX_RETRY_COUNT] [Green] $TARGET의 애플리케이션 상태가 UP(정상 작동 중)입니다."
        TARGET_HEALTH_RESULT=true
        break
    else
        echo "에러 : [$index/$HEALTH_MAX_RETRY_COUNT] [Green] $TARGET의 애플리케이션 상태가 DOWN(중지된 상태)입니다. 잠시후 다시 확인합니다."
    fi
    # 대기
    sleep $HEALTH_WAIT_SECONDS
done

if [ "$SUCCESS" = false ]; then
    echo "에러 : [Green] $TARGET의 애플리케이션 상태가 DOWN(중지된 상태)입니다."
    exit 1
fi

 

for ((i = 0; i < 5; i++)); do
    명령어
    break
done
  • C언어 스타일의 반복문입니다.

 

sleep [시간]
  • 지정한 시간 동안 셸 스크립트의 실행을 일시 정지(대기)시킵니다.
    • s : 초(기본값), m : 분, h : 시간, d : 일

 

 

 

Nginx Reload 후에 자동으로 구(기존) 버전으로 실행 중인 도커 컨테이너 Blue를 중지(stop)하려면 다음을 스크립트 마지막에 추가하시면 됩니다.

echo "[Blue]에 있는 기존 요청이 처리될 때까지 10초간 대기합니다."
sleep 10

echo "Blue 컨테이너를 안전하게 종료합니다."
docker stop "${CURRENT%:*}"

 

현재는 로컬 PC에서 도커를 실행하고 있기 때문에 도커 컨테이너가 중지되지 않을 겁니다.

리눅스 서버에서 도커 컨테이너를 실행했다면 도커 컨테이너는 중지될 겁니다.

 

스크립트에서 sleep 하지 않고 도커 RUN할 때 --stop-timeout 설정을 추가하면 설정한 시간(60초)까지 대기 시킬수 있습니다. (--stop-timeout의 기본 값은 10초입니다.)

docker run -d --name dev-transmission --network dev-network --stop-timeout 60 -v "C:/tempUpload/data:/appdata/upload" -v "C:/workspaces/docker/scouter/scouter-agent:/scouter-agent" -e JAVA_TOOL_OPTIONS="-javaagent:/scouter-agent/scouter.agent.jar -Dscouter.config=/scouter-agent/scouter.conf" transmission-app:latest
docker run -d --name dev-transmission-third --network dev-network --stop-timeout 60 -v "C:/tempUpload/data:/appdata/upload" -v "C:/workspaces/docker/scouter/scouter-agent:/scouter-agent" -e JAVA_TOOL_OPTIONS="-javaagent:/scouter-agent/scouter.agent.jar -Dscouter.config=/scouter-agent/scouter.conf" transmission-app:latest
728x90
반응형