무작정 따라 해보는 배포 4
무작정 따라 해본 배포 - 무중단 배포
무작정 따라 해보는 배포 4
무중단 배포란?
왜 BLUE-GREEN이어야 할까?
이전 프로젝트에서 BLUE-GREEN 무중단 배포를 사용했기에 정확히 알고 싶어서, 책에 나와있는 방법이라..
장점
- 장점 1
단점
- 단점1
BLUE-GREEN을 위해 필요한 것은?
- (로드밸런서로의) NGINX
- 8081, 8082 포트 중 어느 서버로 요청을 전송해야 하는지 결정
- upstream을 통해 서버의 그룹을 관리 한다.
- upstream은 기본적으로 round robin 방식으로 동작하므로 특정 port로 요청을 보내도록 고정하는 명령어가 별도로 실행되어야 한다. (depoly.sh에서 고정됨, reload 필요)
- Blue-Green 배포에서는 8081 또는 8082로 요청을 보내야 한다.
- 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 파일을 실행할 수 있게 된다.
nohup: 터미널 세션이 종료되어도 SIGHUP(Hangup) 시그널을 무시하고 프로세스를 계속 실행-Dspring.profiles.active=blue: application-blue.properties로 jar 파일을 실행1>stdout.txt: 표준 출력(stdout)을 stdout.txt로 리다이렉션(stdout.txt에 표준 출력을 작성)2>err.txt: 표준 에러(stderr)를 err.txt로 리다이렉션- & : 백그라운드로 실행
걱정 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.