Post

무작정 따라 해보는 배포 4

무작정 따라 해본 배포 - 무중단 배포

무작정 따라 해보는 배포 4

무중단 배포란?

왜 BLUE-GREEN이어야 할까?

이전 프로젝트에서 BLUE-GREEN 무중단 배포를 사용했기에 정확히 알고 싶어서, 책에 나와있는 방법이라..

장점

  • 장점 1

단점

  • 단점1

BLUE-GREEN을 위해 필요한 것은?

  1. (로드밸런서로의) NGINX
    • 8081, 8082 포트 중 어느 서버로 요청을 전송해야 하는지 결정
    • upstream을 통해 서버의 그룹을 관리 한다.
    • upstream은 기본적으로 round robin 방식으로 동작하므로 특정 port로 요청을 보내도록 고정하는 명령어가 별도로 실행되어야 한다. (depoly.sh에서 고정됨, reload 필요)
    • Blue-Green 배포에서는 8081 또는 8082로 요청을 보내야 한다.
  2. deploy.sh에서의 추가적인 동작
    • 현재 어느 포트에서 서버가 실행 중인지 확인
    • 사용되지 않는 포트에서 jar 파일을 실행
    • 새로운 포트에서 실행한 서버에 대한 health check (해당 서버로 트래픽을 받아도 충분한가에 대한 테스트)
    • 모든 트래픽을 새롭게 실행된 서버가 받도록 NGINX를 Reload
    • 이전에 사용되고 있는 포트에서 실행되는 서버를 종료

나의 걱정과 고민들…

걱정 1 : deploy.sh에서 property파일을 어떻게 교체하는거지? EC2에 property파일이 있어야 하나?

NO!!

1
nohup java -jar -Dspring.profiles.active=blue deploy-study-0.0.1-SNAPSHOT.jar 1>stdout.txt 2>err.txt &

다음과 같이 실행을 하면 resouces/application-blue.properties를 property로해 jar 파일이 실행된다. 즉, active=${IDLE_PROFILE} 현재 사용중이지 않은 profile로 jar 파일을 실행할 수 있게 된다.

  1. nohup : 터미널 세션이 종료되어도 SIGHUP(Hangup) 시그널을 무시하고 프로세스를 계속 실행
  2. -Dspring.profiles.active=blue : application-blue.properties로 jar 파일을 실행
  3. 1>stdout.txt : 표준 출력(stdout)을 stdout.txt로 리다이렉션(stdout.txt에 표준 출력을 작성)
  4. 2>err.txt : 표준 에러(stderr)를 err.txt로 리다이렉션
  5. & : 백그라운드로 실행

걱정 2 : NGINX가 들어오는 요청을 8081 또는 8082로 보내야하는데 그건 어디서 설정해야하지? deploy.sh에서 설정하면 된다. deploy.sh에서 NGINX의 설정 파일을 변경(overwrite 된다.)하고, reload해 요청이 새로운 포트에서 실행되는 스프링부트 서버에게 전달되도록 한다.

1
2
# etc/nginx/conf.d/service-prod-url.inc
set $service_url http://127.0.0.1:8081;

health-check에 성공한 후, 다음 명령어가 실행됨과 동시에 service-url.inc 내부 내용은 다음과 같이 바뀐다.

1
2
echo "set \$service_url http://localhost:${IDLE_PORT};" | sudo tee /etc/nginx/conf.d/service-prod-url.inc
sudo service nginx reload
1
2
# etc/nginx/conf.d/service-prod-url.inc
set $service_url http://127.0.0.1:8082;

NGINX 설정

deploy.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#!/bin/bash
# Step1. 현재 서버에 curl 요청을 보내 현재 porfile 정보를 알아내기
# 어느 포트에서 jar가 실행 중인지를 확인
RESPONSE_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost/profile)

if [ "${RESPONSE_CODE}" -ge 400 ] # 오류가 발생한 경우 (서버가 켜져 있지 않은 경우)

then
  CURRENT_PROFILE=green
else
  CURRENT_PROFILE=$(curl -s http://localhost/profile)
fi

if [ "${CURRENT_PROFILE}" == "blue" ]
  then
    IDLE_PROFILE=green
    IDLE_PORT=8082
    CURRENT_PORT=8081
  else 
    IDLE_PROFILE=blue
    IDLE_PORT=8081
    CURRENT_PORT=8082
fi

echo "> 1. ${CURRENT_PROFILE}로 현재 애플리케이션이 실행중 입니다."
echo "> ${IDLE_PROFILE}로 애플리케이션을 실행을 시도합니다."

# Step2. 사용하지 않는 port를 통해서 jar 파일을 실행
BUILD_PATH=/home/ubuntu/mydeploy/build/libs

# 실행할 jar파일의 파일명을 알아내기
JAR_PATH=$(ls -tr $BUILD_PATH/*.jar | grep -v plain | tail -n 1)

echo "> 애플리케이션을 profile=$IDLE_PROFILE 로 실행합니다."
nohup java -jar -Dspring.profiles.active=$IDLE_PROFILE $JAR_PATH 1>stdout.txt 2>err.txt &

# Step3. 새로운 포트에서 실행중인 서버에 대한 health check - curl 요청을 보내서 정상적인 응답이 들어오는지 확인하기
# health-check 성공 시, 새로운 포트로 요청을 보내도록 NGINX를 재설정(Re)
echo "> Health Check : ${IDLE_PORT}에서 서비스를 안정적으로 제공할 수 있는지 확인합니다. "
sleep 10

for RETRY_COUNT in {1..10}
do
  STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:${IDLE_PORT}/health || true)
  if [ "$STATUS" ==  "200" ]
then
      echo "> Health check 성공! ${IDLE_PORT}로 요청이 전달되도록 NGINX를 reload 합니다."
      # 기존 포트에서 idle port로 switch
      echo "set \$service_url http://127.0.0.1:${IDLE_PORT};" | sudo tee /etc/nginx/conf.d/service-url.inc
      sudo service nginx reload
      break
  else
      echo "> Health check의 응답을 알 수 없거나 혹은 실행 상태가 아닙니다."
  fi

  if [ ${RETRY_COUNT} -eq 10 ] # 10번 ping을 보냈지만 정상적인 응답을 받지 못한 경우
  then
    echo "> Health check 실패. NGINX를 reload하지 않습니다."
    exit 1
  fi
  echo "> ${RETRY_COUNT}번째 Health check 연결 실패. 재시도 합니다. (최대 10번)"
  sleep 10
done

# Step4. 기존 포트에서 실행중이던 서비스 종료
CURRENT_PID=$(lsof -ti tcp:${CURRENT_PORT})

if [ -z "${CURRENT_PID}" ]
then
  echo "> ${CURRENT_PORT}에서 구동중인 애플리케이션이 없으므로 종료하지 않습니다."
else
  echo "> ${CURRENT_PORT}에서 구동중이던 애플리케이션을 종료합니다."
  kill -15 ${CURRENT_PID}
  sleep 5
fi

echo "> Blue-Green 무중단 배포가 성공적으로 끝났습니다!"

마주했던 문제점

deploy.sh에서 set \$service_url http://localhost:${IDLE_PORT};라고 명령어를 넣어서 “no resolver defined to resolve localhost”라는 오류를 만났다.

This post is licensed under CC BY 4.0 by the author.