728x90
💡 이 글은 2편으로 1편인https://suzuworld.tistory.com/419에 이어 쓰는 글이다.
🤗 쉘 스크립트 작성 준비
- 이제 할 일은 먼저 쉘스크립트로 배포 파일을 만드는 것이다.
- 스프링 부트 프로젝트 상위 폴더에 script라는 디렉터리를 만든 다음에 deploy.sh라는 이름의 파일을 만들자.
🤔 들어가기 전에
- 본격적으로 들어가기 전에 위 그림을 한 번 더 보고 시작하면 스크립트 이해에 많은 도움이 될 것이다.
- 이번에 쉘 스크립트 파일을 처음 만져봐서 상당히 애먹었는데 대부분의 블로그가 각 명령어에 대해 자세히 설명하지 않아 시간이 더 걸렸던 것 같다.
- 나처럼 쉘 스크립트 작성이 처음인 사람을 위해 최대한 자세히 설명해 보겠다.(설명이 다소 장황하고 틀릴 수도 있음😅)
- 묶음 단위로 설명하고 맨 아래에는 완성본을 보여주는 방식으로 진행하겠다.
- 다 이해한다면 스크립트 파일에 대한 응용 동작도 만들어 낼 수 있을 것이다.
✍🏻 쉘 스크립트 작성
#!/bin/bash
- /bin/bash 경로에 있는 Bash 해석기를 해당 스크립트를 실행할 때 사용하겠다고 명시하는 것이다.
SWITCH_SCRIPT="./switch.sh"
DEFAULT_SWITCH_SCRIPT="./default_switch.sh"
SERVER_HOME=젠킨스 워크스페이스 내 빌드 테스트 경로
DEFAULT_SWITCH_SCRIPT="./default_switch.sh"
SERVER_HOME=젠킨스 워크스페이스 내 빌드 테스트 경로
- SWITCH_SCRIPT와 DEFAULT_SWITCH_SCRIPT는 변수를 선언한 것이다.
- 이 두 변수는 deploy.sh 파일 내에서 조건문으로 분기하여 실행되는 또 다른 쉘스크립트 파일의 경로를 담고 있다.
- SWITCH_SCRIPT는 블루, 그린 포트 중 한 곳이 실행중일 때, 다른 곳으로 리버스 프록시를 스위칭하는 데에 사용된다.
- DEFAULT_SWITCH_SCRIPT의 경우 두 포트 모두 죽어있을 때(스프링 부트 서버가 꺼져있을 때) 디폴트인 블루 포트를 실행시키고 NGINX의 프록시 방향을 블루로 맞춰놓는 데에 사용된다.
- SERVER_HOME은 프로젝트의 젠킨스에서 빌드한 jar 파일이 있는 경로이다.
- 나의 경우에는 완전히 로컬 PC 한 곳에서만 테스트했기 때문에 젠킨스의 워크스페이스 폴더 내에 빌드 파일을 저장하고 서버를 실행시킨다는 시나리오로 접근했다.
- 만약 배포를 위한 외부 서버(AWS 인스턴스 등)에 보낼 것이라면 다른 블로그를 참고하면서 따라하면 될 것 같다.
echo "-> 👨🏻💼<Jenkins 배포 시작>"
CURRENT_SERVER_PROFILE=$(curl -s http://localhost:28080/profile | head -n 1)
CURRENT_SERVER_PROFILE=$(curl -s http://localhost:28080/profile | head -n 1)
- echo 출력으로 젠킨스 배포가 시작됨을 알린다.
- 쉘 스크립트가 예쁘게 출력되면 배포할 때 기분도 좋아지지 않을까 하는 생각에 이모지를 고를 때 신중을 가했다.🤗
- 정리하면 배포 시작을 알리는 출력문을 내보내고 curl을 이용해 해당 프로젝트의 페이지에서 맨 앞 줄 첫 번째 키워드만을 변수에 넣는다.
- 여기서 변수에 담기는 내용은 바로 앞선 글에서 설정했던 application.yml의 프로필 이름인 blue, green이다.
- blue로 서버가 실행되는 8081 포트라면 blue가 담길것이고, green으로 실행되는 8082 포트라면 green이 담길 것이다.
- 28080은 nginx로 포트포워딩한 것이다.
- nginx에 대한 설명은 아래에 있다.
- if blue) 28080 -> 8081
- else if green) 28080 -> 8082
- echo : 주어진 인자를 표준 출력으로 출력하는 명령어이다.
- curl : 주어진 URL을 통해 데이터를 전송하거나 받아오는 명령어이다.
- -s : curl이 실행되는 동안 진행 상황이나 진단 메시지 등의 출력을 표시하지 않음을 의미한다.(silent)
- head : 텍스트의 앞 부분을 의미한다.
- -n 1 : 첫번째 줄을 의미한다.
if [ "$CURRENT_SERVER_PROFILE" == blue ]
then
echo "-> 💡 현재 구동중인 Profile : $CURRENT_SERVER_PROFILE"
CURRENT_SERVER_PORT=8081
SWITCH_SERVER_PROFILE=green
SWITCH_SERVER_PORT=8082
elif [ "$CURRENT_SERVER_PROFILE" == green ]
then
echo "-> 💡 현재 구동중인 Profile : $CURRENT_SERVER_PROFILE"
CURRENT_SERVER_PORT=8082
SWITCH_SERVER_PROFILE=blue
SWITCH_SERVER_PORT=8081
else
echo "-> ⚠️ profile not found : 일치하는 Profile이 없습니다."
echo "-> ⚠️ set default profile : 기본 Profile인 blue를 할당합니다."
echo "-> ⚠️ 블루, 그린이 모두 동작중이 아니었으므로, blue만 배포하고 스크립트를 종료합니다."
SWITCH_SERVER_PROFILE=blue
SWITCH_SERVER_PORT=8081
fi
then
echo "-> 💡 현재 구동중인 Profile : $CURRENT_SERVER_PROFILE"
CURRENT_SERVER_PORT=8081
SWITCH_SERVER_PROFILE=green
SWITCH_SERVER_PORT=8082
elif [ "$CURRENT_SERVER_PROFILE" == green ]
then
echo "-> 💡 현재 구동중인 Profile : $CURRENT_SERVER_PROFILE"
CURRENT_SERVER_PORT=8082
SWITCH_SERVER_PROFILE=blue
SWITCH_SERVER_PORT=8081
else
echo "-> ⚠️ profile not found : 일치하는 Profile이 없습니다."
echo "-> ⚠️ set default profile : 기본 Profile인 blue를 할당합니다."
echo "-> ⚠️ 블루, 그린이 모두 동작중이 아니었으므로, blue만 배포하고 스크립트를 종료합니다."
SWITCH_SERVER_PROFILE=blue
SWITCH_SERVER_PORT=8081
fi
- 현재 실행중인 포트가 8081이라면 green과 8082를 각 switch 변수에 담고, 8082라면 blue와 8081을 각 switch 변수에 담는다.
- 둘 다 아니라면 디폴트 프로필인 blue와 포트 8081을 각 switch 변수에 담는다.
- fi : if문이 종료됨을 뜻한다.
echo "-> 🚚 $SWITCH_SERVER_PROFILE 배포"
SWITCH_PORT_PID=$(netstat -anv | grep $SWITCH_SERVER_PORT | head -n 1 | awk '{print $9}')
if [ ! -z "$SWITCH_PORT_PID" ]
then
echo "-> 📦 $SWITCH_SERVER_PROFILE 의 PID : $SWITCH_PORT_PID"
kill $SWITCH_PORT_PID >/dev/null 2>&1
fi
SWITCH_PORT_PID=$(netstat -anv | grep $SWITCH_SERVER_PORT | head -n 1 | awk '{print $9}')
if [ ! -z "$SWITCH_PORT_PID" ]
then
echo "-> 📦 $SWITCH_SERVER_PROFILE 의 PID : $SWITCH_PORT_PID"
kill $SWITCH_PORT_PID >/dev/null 2>&1
fi
- swith 변수에 담긴 프로필로 스프링 부트 서버 배포를 시작한다.
- 만약 switch 포트가 이미 서버에서 실행 중이라면 에러가 발생할 것임으로 먼저 switch 포트를 죽이고 시작한다.
- 이때 핵심은 운영체제 환경마다 해당 포트를 찾고 그 포트의 pid를 찾는 것이 다를 수 있다는 점인데 위 명령어를 응용하여 출력문을 보고 해당 pid의 위치를 찾게 만들면 된다.
wonyonghwang@Wonyongs-MacBook-Pro jenkins % netstat -anv | grep 8081
tcp46 0 0 *.8081 *.* LISTEN 131072 131072 3953 0 00000 00000006 00000000000057ce 00000000 00000800 2 0 000001
wonyonghwang@Wonyongs-MacBook-Pro jenkins % netstat -anv | grep 8082
tcp46 0 0 *.8082 *.* LISTEN 131072 131072 4392 0 00000 00000006 0000000000008378 00000000 00000800 2 0 000001
- netstat -anv : 무수히 많은 프로세스 관련 내용이 출력된다.
- grep : 여기서 grep으로 스위치할 포트 번호를 추려낸다.
- head -n 1 : 맨 위 줄만 출력하겠다는 의미이다.
- awk '{print $9}' : 9 번째 문자를 출력하겠다는 의미이다.
- 위의 출력문을 세어보면 3953, 4392가 9번째에 위치해 있음을 확인할 수 있다.
wonyonghwang@Wonyongs-MacBook-Pro jenkins % netstat -anv | grep 8081 | head -n 1 | awk '{print $9}'
3953
wonyonghwang@Wonyongs-MacBook-Pro jenkins % netstat -anv | grep 8082 | head -n 1 | awk '{print $9}'
4392
- 이렇게 출력하면 PID만 출력이 가능하다.
- 이를 PID 변수에 넣고 해당 포트의 PID로 포트를 kill 하는 것이다.
- /dev/null 2>&1 : 명령어 결과를 출력하지 않겠다는 뜻이다.
- kill 했을 때 출력문이 나오는게 보기 싫어 넣었다.
echo "-> 🚚 nohup java -jar $SERVER_HOME/forJenkins.jar --spring.profiles.active=$SWITCH_SERVER_PROFILE > /dev/null &"
nohup java -jar $SERVER_HOME/forJenkins.jar --spring.profiles.active=$SWITCH_SERVER_PROFILE > /dev/null &
echo "-> 🚑 $SWITCH_SERVER_PROFILE : 10초 후 Health Check Start"
echo "-> 🚑 curl -s http://localhost:$SWITCH_SERVER_PORT/actuator/health"
sleep 10
nohup java -jar $SERVER_HOME/forJenkins.jar --spring.profiles.active=$SWITCH_SERVER_PROFILE > /dev/null &
echo "-> 🚑 $SWITCH_SERVER_PROFILE : 10초 후 Health Check Start"
echo "-> 🚑 curl -s http://localhost:$SWITCH_SERVER_PORT/actuator/health"
sleep 10
- 스위칭할 프로필 스프링부트 서버를 백그라운드로 실행을 한다.(nohup)
- SERVER_HOME은 위의 jar 파일이 저장될 위치이며, PROFILE은 switch 할 프로필이다.
- 서버가 실행되면 sleep으로 10초 후 health check가 시작된다.
- 스프링부트 실행 완료 시간을 넉넉하게 잡아 설정한 것이다.
- echo는 지금 이 출력문이 실행된다는 것을 알리기 위한 출력문일 뿐이니 큰 의미을 강조한다. 자유롭게 변경하자.
for retry_count in {1..10}
do
response=$(curl -s http://localhost:$SWITCH_SERVER_PORT/actuator/health)
count=$(echo $response | grep "UP" | wc -l)
if [ $count -ge 1 ]
then
echo "-> 🚨 [$retry_count 번째 Health Check]"
echo "-> ✅ Health Check 성공!!!"
break
else
echo "-> ❌ Health Check 실패, 서버로부터 응답을 받지 못했습니다."
echo "-> 📣 서버로부터 받은 응답 : $response"
fi
if [ $retry_count -eq 10 ]
then
echo "-> 😵💫 Health Check $retry_count 회 시도 모두 실패..."
echo "-> 🫠 배포 종료"
exit 1
fi
echo "-> 🤔 Health Check 실패, 5초 후 재시도..."
sleep 5
done
do
response=$(curl -s http://localhost:$SWITCH_SERVER_PORT/actuator/health)
count=$(echo $response | grep "UP" | wc -l)
if [ $count -ge 1 ]
then
echo "-> 🚨 [$retry_count 번째 Health Check]"
echo "-> ✅ Health Check 성공!!!"
break
else
echo "-> ❌ Health Check 실패, 서버로부터 응답을 받지 못했습니다."
echo "-> 📣 서버로부터 받은 응답 : $response"
fi
if [ $retry_count -eq 10 ]
then
echo "-> 😵💫 Health Check $retry_count 회 시도 모두 실패..."
echo "-> 🫠 배포 종료"
exit 1
fi
echo "-> 🤔 Health Check 실패, 5초 후 재시도..."
sleep 5
done
- wc -l : 입력된 텍스트의 줄 수를 세는 역할을 한다.
- -ge : "Greater than or equal to"라는 의미로, 여기서는 저장된 값이 1보다 크거나 같은지를 확인한다.
- -eq : "Equal to"라는 의미로, 주어진 값이 다른 값과 같은지를 비교하는 역할을 한다.
- exit 1 : 0 이외의 값은 오류임을 명시하고 프로그램을 종료하며, 0일 경우 정상 종료를 의미한다.
- 총 10 번을 체크한다. do done 안에는 반복문 내용이 들어간다.
if [ -z "$CURRENT_SERVER_PORT" ]
then
echo "-> 🔄 NGINX : Default Port로 프록시 설정"
if [ ! -x "$DEFAULT_SWITCH_SCRIPT" ]
then
chmod +x "$DEFAULT_SWITCH_SCRIPT"
fi
"$DEFAULT_SWITCH_SCRIPT"
echo "->👨🏻💼<Jenkins 배포 성공> 스크립트를 종료합니다."
exit 0
fi
then
echo "-> 🔄 NGINX : Default Port로 프록시 설정"
if [ ! -x "$DEFAULT_SWITCH_SCRIPT" ]
then
chmod +x "$DEFAULT_SWITCH_SCRIPT"
fi
"$DEFAULT_SWITCH_SCRIPT"
echo "->👨🏻💼<Jenkins 배포 성공> 스크립트를 종료합니다."
exit 0
fi
- -z : 변수가 비어있을 경우를 의미한다.
- -x : 파일 실행 권한이 없는 경우를 의미한다.
- chmod +x : .sh 파일 실행 권한을 추가한다.
- exit 0 : 정상 종료임을 명시한다.
- 여기서 위에서 언급한 분기가 시작된다.
- 현재 포트가 비어있다면 blue, green 모두 실행되고 있지 않다는 의미이므로 DEFAULT_SWITCH_SCRIPT를 실행시키고 배포 스크립트를 종료한다.
echo "-> 🤼 NGINX 포트 스위칭 스크립트 시작"
if [ ! -x "$SWITCH_SCRIPT" ]
then
chmod +x "$SWITCH_SCRIPT"
# .sh 파일 실행 권한을 확인하고 없으면 추가
fi
"$SWITCH_SCRIPT"
echo "-> 🔫 기존 포트인 $CURRENT_SERVER_PROFILE 포트 : $CURRENT_SERVER_PORT KILL"
CURRENT_PORT_PID=$(netstat -anv | grep $CURRENT_SERVER_PORT | head -n 1 | awk '{print $9}')
echo "-> 📦 $CURRENT_SERVER_PROFILE 의 PID : $CURRENT_PORT_PID"
kill $CURRENT_PORT_PID >/dev/null 2>&1
echo "->👨🏻💼<Jenkins 배포 성공> 스크립트를 종료합니다."
if [ ! -x "$SWITCH_SCRIPT" ]
then
chmod +x "$SWITCH_SCRIPT"
# .sh 파일 실행 권한을 확인하고 없으면 추가
fi
"$SWITCH_SCRIPT"
echo "-> 🔫 기존 포트인 $CURRENT_SERVER_PROFILE 포트 : $CURRENT_SERVER_PORT KILL"
CURRENT_PORT_PID=$(netstat -anv | grep $CURRENT_SERVER_PORT | head -n 1 | awk '{print $9}')
echo "-> 📦 $CURRENT_SERVER_PROFILE 의 PID : $CURRENT_PORT_PID"
kill $CURRENT_PORT_PID >/dev/null 2>&1
echo "->👨🏻💼<Jenkins 배포 성공> 스크립트를 종료합니다."
- 만약 위의 if 문에서 해당이 안 된다면(blue or green 중 하나가 살아있음) 포트 스위칭을 위해 이 if 문으로 내려온다.
- SWITCH_ SCRIPT를 실행하여 nginx의 프록시 방향을 변경하고 기존에 실행되고 있던 프로필의 포트를 죽인다.
- 마지막으로 스크립트 종료를 알리는 출력문을 출력하고 배포 스크립트 파일은 최종적으로 종료된다.
📌 deploy.sh 스크립트 완성본
#!/bin/bash
SWITCH_SCRIPT="./switch.sh"
DEFAULT_SWITCH_SCRIPT="./default_switch.sh"
SERVER_HOME=젠킨스 워크스페이스 내 빌드 테스트 경로
echo "-> 👨🏻💼<Jenkins 배포 시작>"
CURRENT_SERVER_PROFILE=$(curl -s http://localhost:28080/profile | head -n 1)
if [ "$CURRENT_SERVER_PROFILE" == blue ]
then
echo "-> 💡 현재 구동중인 Profile : $CURRENT_SERVER_PROFILE"
CURRENT_SERVER_PORT=8081
SWITCH_SERVER_PROFILE=green
SWITCH_SERVER_PORT=8082
elif [ "$CURRENT_SERVER_PROFILE" == green ]
then
echo "-> 💡 현재 구동중인 Profile : $CURRENT_SERVER_PROFILE"
CURRENT_SERVER_PORT=8082
SWITCH_SERVER_PROFILE=blue
SWITCH_SERVER_PORT=8081
else
echo "-> ⚠️ profile not found : 일치하는 Profile이 없습니다."
echo "-> ⚠️ set default profile : 기본 Profile인 blue를 할당합니다."
echo "-> ⚠️ 블루, 그린이 모두 동작중이 아니었으므로, blue만 배포하고 스크립트를 종료합니다."
SWITCH_SERVER_PROFILE=blue
SWITCH_SERVER_PORT=8081
fi
echo "-> 🚚 $SWITCH_SERVER_PROFILE 배포"
SWITCH_PORT_PID=$(netstat -anv | grep $SWITCH_SERVER_PORT | head -n 1 | awk '{print $9}')
if [ ! -z "$SWITCH_PORT_PID" ]
then
echo "-> 📦 $SWITCH_SERVER_PROFILE 의 PID : $SWITCH_PORT_PID"
kill $SWITCH_PORT_PID >/dev/null 2>&1
fi
echo "-> 🚚 nohup java -jar $SERVER_HOME/forJenkins.jar --spring.profiles.active=$SWITCH_SERVER_PROFILE > /dev/null &"
nohup java -jar $SERVER_HOME/forJenkins.jar --spring.profiles.active=$SWITCH_SERVER_PROFILE > /dev/null &
echo "-> 🚑 $SWITCH_SERVER_PROFILE : 10초 후 Health Check Start"
echo "-> 🚑 curl -s http://localhost:$SWITCH_SERVER_PORT/actuator/health"
sleep 10
for retry_count in {1..10}
do
response=$(curl -s http://localhost:$SWITCH_SERVER_PORT/actuator/health)
count=$(echo $response | grep "UP" | wc -l)
if [ $count -ge 1 ]
then
echo "-> 🚨 [$retry_count 번째 Health Check]"
echo "-> ✅ Health Check 성공!!!"
break
else
echo "-> ❌ Health Check 실패, 서버로부터 응답을 받지 못했습니다."
echo "-> 📣 서버로부터 받은 응답 : $response"
fi
if [ $retry_count -eq 10 ]
then
echo "-> 😵💫 Health Check $retry_count 회 시도 모두 실패..."
echo "-> 🫠 배포 종료"
exit 1
fi
echo "-> 🤔 Health Check 실패, 5초 후 재시도..."
sleep 5
done
if [ -z "$CURRENT_SERVER_PORT" ]
then
echo "-> 🔄 NGINX : Default Port로 프록시 설정"
if [ ! -x "$DEFAULT_SWITCH_SCRIPT" ]
then
chmod +x "$DEFAULT_SWITCH_SCRIPT"
fi
"$DEFAULT_SWITCH_SCRIPT"
echo "->👨🏻💼<Jenkins 배포 성공> 스크립트를 종료합니다."
exit 0
fi
echo "-> 🤼 NGINX 포트 스위칭 스크립트 시작"
if [ ! -x "$SWITCH_SCRIPT" ]
then
chmod +x "$SWITCH_SCRIPT"
fi
"$SWITCH_SCRIPT"
echo "-> 🔫 기존 포트인 $CURRENT_SERVER_PROFILE 포트 : $CURRENT_SERVER_PORT KILL"
CURRENT_PORT_PID=$(netstat -anv | grep $CURRENT_SERVER_PORT | head -n 1 | awk '{print $9}')
echo "-> 📦 $CURRENT_SERVER_PROFILE 의 PID : $CURRENT_PORT_PID"
kill $CURRENT_PORT_PID >/dev/null 2>&1
echo "->👨🏻💼<Jenkins 배포 성공> 스크립트를 종료합니다."
🛠️ NGINX 설정
server {
listen 28080;
server_name localhost;
include /usr/local/etc/nginx/conf.d/service-url.inc;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
proxy_pass $service_url;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header Connection "";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
- nginx를 다운로드하여 설치한다.
- 보통 nginx 폴더의 경로는 두 가지인 것 같다.
- /usr/local/etc/nginx
- /opt/homebrew/etc/nginx
- nginx.conf에 들어가 보면 기본적으로 적힌 내용이 있을 텐데 전부 무시하고 http {} 안에 위와 같이 적는다.
- 여기서 핵심은 아래와 같다.
💡 listen
- 포트포워딩할 포트이다.
- nginx에 위와 같이 28080이라 적는다면 포트 포워딩되어 nginx 포트로 접속할 때 28080 -> 8081 or 28080 -> 8082로 스프링부트 서버와 연결될 것이다.
💡 include /usr/local/etc/nginx/conf.d/service-url.inc;
- include로 해당 경로의 service-url.inc를 nginx.conf에 가져온다는 뜻이다.
- nginx 폴더에 conf.d가 없다면 만들어도 무방하다. 이 폴더 안에 vi service-url.inc 명령어로 파일을 만들어둔다.
- 초기값으로 set \$service_url http://127.0.0.1:8081과 같이 블루 or 그린 포트 하나를 지정해 놓는다.(테스트를 위해 blue로 해놓자.)
💡 location / {proxy_pass $service_url}
- 위의 service-url.inc의 service_url 변숫값을 가져와 사용한다.
⌨️ NGINX 테스트
- 로컬에서 스프링부트 서버를 blue로 실행시켜 놓고 localhost:28080으로 접속하면 280280 -> 8081로 연결되어 스프링부트의 출력문이 화면에 나올 것이다.
📌 DEFAULT_SWITCH_SCRIPT
#!/bin/bash
SWITCH_PROXY_PORT=8081
echo "-> ⚡️ set default port : 기본 포트인 $SWITCH_PROXY_PORT 를 할당합니다."
echo "-> 🔄 NGINX 내의 프록시 방향 설정"
echo "-> 🥳 set \$service_url http://127.0.0.1:${SWITCH_PROXY_PORT};"
echo "set \$service_url http://127.0.0.1:${SWITCH_PROXY_PORT};"| tee /usr/local/etc/nginx/conf.d/service-url.inc >/dev/null 2>&1
echo "-> 🔄 NGINX 재시작"
/usr/local/bin/nginx -s reload
SWITCH_PROXY_PORT=8081
echo "-> ⚡️ set default port : 기본 포트인 $SWITCH_PROXY_PORT 를 할당합니다."
echo "-> 🔄 NGINX 내의 프록시 방향 설정"
echo "-> 🥳 set \$service_url http://127.0.0.1:${SWITCH_PROXY_PORT};"
echo "set \$service_url http://127.0.0.1:${SWITCH_PROXY_PORT};"| tee /usr/local/etc/nginx/conf.d/service-url.inc >/dev/null 2>&1
echo "-> 🔄 NGINX 재시작"
/usr/local/bin/nginx -s reload
- set a b : a 변수에 b를 할당한다.
- tee : 앞에서 출력한 문자열을 해당 경로에 덮어쓴다.
- nginx 설정을 완료했다면 이제 이 스크립트가 이해가 될 것이다.
- 만약 blue, green이 모두 실행 중이 아니라면 디폴트 프로필인 blue가 실행하도록 하는 것이다.
- SWITCH 할 포트를 8081로 설정하여 nginx.conf의 proxy_pass에 연결할 포트를 8081로 설정하는 것이다.
- 마지막에는 nginx 폴더로 경로를 설정하고 reload를 실행한다.
- 이렇게 해주는 이유는 젠킨스가 로컬 서버에 스크립트 파일을 실행시킬 때 nginx 폴더를 찾지 못하기 때문이다.
📌 SWITCH_SCRIPT
#!/bin/bash
CURRENT_PROFILE=$(curl -s http://localhost:28080/profile)
echo "-> 💡 현재 구동중인 Spring Boot Port : $CURRENT_PROFILE"
if [ $CURRENT_PROFILE == blue ]
then
CURRENT_PROXY_PORT=8081
SWITCH_PROXY_PORT=8082
elif [ $CURRENT_PROFILE == green ]
then
CURRENT_PROXY_PORT=8082
SWITCH_PROXY_PORT=8081
else
echo "-> ⚠️ profile not found : $CURRENT_PROFILE -> 일치하는 Profile이 없습니다."
echo "-> ⚠️ set default port : 기본 포트인 8081을 할당합니다."
SWITCH_PROXY_PORT=8081
fi
echo "-> ⚡️ 현재 NGINX 프록시 포트 : $CURRENT_PROXY_PORT"
echo "-> ⚡️ 변경할 NGINX 프록시 포트 : $SWITCH_PROXY_PORT"
echo "-> 🔄 NGINX 내의 프록시 방향 변경"
echo "-> 🥳 set \$service_url http://127.0.0.1:${SWITCH_PROXY_PORT};"
echo "set \$service_url http://127.0.0.1:${SWITCH_PROXY_PORT};"| tee /usr/local/etc/nginx/conf.d/service-url.inc >/dev/null 2>&1
echo "-> ✅ NGINX 재시작"
/usr/local/bin/nginx -s reload
CURRENT_PROFILE=$(curl -s http://localhost:28080/profile)
echo "-> 💡 현재 구동중인 Spring Boot Port : $CURRENT_PROFILE"
if [ $CURRENT_PROFILE == blue ]
then
CURRENT_PROXY_PORT=8081
SWITCH_PROXY_PORT=8082
elif [ $CURRENT_PROFILE == green ]
then
CURRENT_PROXY_PORT=8082
SWITCH_PROXY_PORT=8081
else
echo "-> ⚠️ profile not found : $CURRENT_PROFILE -> 일치하는 Profile이 없습니다."
echo "-> ⚠️ set default port : 기본 포트인 8081을 할당합니다."
SWITCH_PROXY_PORT=8081
fi
echo "-> ⚡️ 현재 NGINX 프록시 포트 : $CURRENT_PROXY_PORT"
echo "-> ⚡️ 변경할 NGINX 프록시 포트 : $SWITCH_PROXY_PORT"
echo "-> 🔄 NGINX 내의 프록시 방향 변경"
echo "-> 🥳 set \$service_url http://127.0.0.1:${SWITCH_PROXY_PORT};"
echo "set \$service_url http://127.0.0.1:${SWITCH_PROXY_PORT};"| tee /usr/local/etc/nginx/conf.d/service-url.inc >/dev/null 2>&1
echo "-> ✅ NGINX 재시작"
/usr/local/bin/nginx -s reload
- SWITCH 스크립트의 경우 별도의 스크립트 파일이기 때문에 다시 한번 현재 포트의 프로필을 확인한다.
- 이후 blue라면 green으로, green이라면 blue로 스위칭할 SWITCH 포트 변수에 값을 채워준다.
- 프록시 방향을 변경하고 나서는 위와 마찬가지로 nginx를 reload 한다.
🤔 nginx reload 테스트
- 이 그림을 다시 보자.
- 위의 SWITCH_SCRIPT는 이 부분에 해당되는 것인데 문득 의문이 들었다.
- 지금 내가 하는 배포 방식은 과연 100% 무중단 배포라고 할 수 있을까?
- blue에서 green으로, green에서 blue로 nginx 프록시 방향을 변경하고 nginx reload 하는 순간 nginx는 찰나의 순간이지만 분명 중단될 것이다.
- 즉, reload 되는 순간 요청이 들어온다면 그 요청은 처리 못하는 게 아닐까?
- 그래서 실험해 봤다.
- 스프링부트 프로젝트 내에 nginx_test.sh와 count_line이라는 파일을 생성했다.
📌 nginx_test.sh
count = 0
while true
do
count=$((count+1))
profile=$(curl -s http://localhost:28080/profile | head -n 1)
current_time=$(date "+%Y-%m-%d %H:%M:%S")
echo "Profile : $profile | Count: $count | Time: $current_time"
done
while true
do
count=$((count+1))
profile=$(curl -s http://localhost:28080/profile | head -n 1)
current_time=$(date "+%Y-%m-%d %H:%M:%S")
echo "Profile : $profile | Count: $count | Time: $current_time"
done
- 위와 같이 작성한다.
- 테스트 방식은 간단하다.
- 매 반복마다 현재 Nginx가 바라보고 있는 스프링부트 서버의 프로필이 출력하고, nginx가 reload 되고 blue로 바뀌는 순간 얼마나 중단이 지속되는지를 테스트하는 것이다.
- 반복문 조건을 true로 설정하여 무한 반복한다.
- 반복문은 curl 명령어로 담신 profile을 가지고 있다가 "프로필 | 반복문 횟수 | 초단위 현재시간을 매번 출력할 것이다.
- deploy.sh 파일을 로컬에서 실행한다.
- nginx 배포 스크립트가 종료되어 프록시 방향이 변경되면 ctrl + c로 nginx_test.sh를 종료시킨다.
- 이후 출력된 내용에서 1초 안에 보내진 요청을 잘라 count_line.txt에 붙여 넣고 1초 안에 몇 번 중단되는지를 확인한다.
실험 결과
Profile : blue | Count: 442 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 443 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 444 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 445 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 446 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 447 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 448 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 449 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 450 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 451 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 452 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 453 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 454 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 455 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 456 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 457 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 458 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 459 | Time: 2023-11-22 21:39:30
Profile : <!DOCTYPE html> | Count: 460 | Time: 2023-11-22 21:39:30
Profile : green | Count: 461 | Time: 2023-11-22 21:39:30
Profile : green | Count: 462 | Time: 2023-11-22 21:39:30
Profile : green | Count: 463 | Time: 2023-11-22 21:39:30
Profile : green | Count: 464 | Time: 2023-11-22 21:39:30
Profile : green | Count: 465 | Time: 2023-11-22 21:39:30
Profile : green | Count: 466 | Time: 2023-11-22 21:39:30
Profile : green | Count: 467 | Time: 2023-11-22 21:39:30
Profile : green | Count: 468 | Time: 2023-11-22 21:39:30
Profile : green | Count: 469 | Time: 2023-11-22 21:39:30
Profile : green | Count: 470 | Time: 2023-11-22 21:39:30
Profile : green | Count: 471 | Time: 2023-11-22 21:39:30
Profile : green | Count: 472 | Time: 2023-11-22 21:39:30
Profile : green | Count: 473 | Time: 2023-11-22 21:39:30
Profile : green | Count: 474 | Time: 2023-11-22 21:39:30
Profile : green | Count: 475 | Time: 2023-11-22 21:39:30
Profile : green | Count: 476 | Time: 2023-11-22 21:39:30
Profile : green | Count: 477 | Time: 2023-11-22 21:39:30
Profile : green | Count: 478 | Time: 2023-11-22 21:39:30
Profile : green | Count: 479 | Time: 2023-11-22 21:39:30
Profile : green | Count: 480 | Time: 2023-11-22 21:39:30
Profile : green | Count: 481 | Time: 2023-11-22 21:39:30
Profile : green | Count: 482 | Time: 2023-11-22 21:39:30
Profile : green | Count: 483 | Time: 2023-11-22 21:39:30
Profile : green | Count: 484 | Time: 2023-11-22 21:39:30
Profile : green | Count: 485 | Time: 2023-11-22 21:39:30
Profile : green | Count: 486 | Time: 2023-11-22 21:39:30
Profile : green | Count: 487 | Time: 2023-11-22 21:39:30
Profile : green | Count: 488 | Time: 2023-11-22 21:39:30
Profile : green | Count: 489 | Time: 2023-11-22 21:39:30
Profile : green | Count: 490 | Time: 2023-11-22 21:39:30
Profile : green | Count: 491 | Time: 2023-11-22 21:39:30
Profile : green | Count: 492 | Time: 2023-11-22 21:39:30
Profile : green | Count: 493 | Time: 2023-11-22 21:39:30
Profile : green | Count: 494 | Time: 2023-11-22 21:39:30
Profile : green | Count: 495 | Time: 2023-11-22 21:39:30
Profile : green | Count: 496 | Time: 2023-11-22 21:39:30
Profile : green | Count: 497 | Time: 2023-11-22 21:39:30
Profile : green | Count: 498 | Time: 2023-11-22 21:39:30
Profile : green | Count: 499 | Time: 2023-11-22 21:39:30
Profile : green | Count: 500 | Time: 2023-11-22 21:39:30
Profile : green | Count: 501 | Time: 2023-11-22 21:39:30
Profile : green | Count: 502 | Time: 2023-11-22 21:39:30
Profile : green | Count: 503 | Time: 2023-11-22 21:39:30
Profile : green | Count: 504 | Time: 2023-11-22 21:39:30
Profile : green | Count: 505 | Time: 2023-11-22 21:39:30
Profile : green | Count: 506 | Time: 2023-11-22 21:39:30
Profile : green | Count: 507 | Time: 2023-11-22 21:39:30
Profile : green | Count: 508 | Time: 2023-11-22 21:39:30
Profile : green | Count: 509 | Time: 2023-11-22 21:39:30
Profile : green | Count: 510 | Time: 2023-11-22 21:39:30
Profile : green | Count: 511 | Time: 2023-11-22 21:39:30
Profile : green | Count: 512 | Time: 2023-11-22 21:39:30
Profile : green | Count: 513 | Time: 2023-11-22 21:39:30
Profile : green | Count: 514 | Time: 2023-11-22 21:39:30
Profile : green | Count: 515 | Time: 2023-11-22 21:39:30
Profile : green | Count: 516 | Time: 2023-11-22 21:39:30
Profile : green | Count: 517 | Time: 2023-11-22 21:39:30
Profile : green | Count: 518 | Time: 2023-11-22 21:39:30
Profile : green | Count: 519 | Time: 2023-11-22 21:39:30
Profile : green | Count: 520 | Time: 2023-11-22 21:39:30
Profile : green | Count: 521 | Time: 2023-11-22 21:39:30
Profile : green | Count: 522 | Time: 2023-11-22 21:39:30
Profile : green | Count: 523 | Time: 2023-11-22 21:39:30
Profile : green | Count: 524 | Time: 2023-11-22 21:39:30
Profile : green | Count: 525 | Time: 2023-11-22 21:39:30
Profile : green | Count: 526 | Time: 2023-11-22 21:39:30
Profile : green | Count: 527 | Time: 2023-11-22 21:39:30
Profile : green | Count: 528 | Time: 2023-11-22 21:39:30
Profile : green | Count: 529 | Time: 2023-11-22 21:39:30
Profile : green | Count: 530 | Time: 2023-11-22 21:39:30
Profile : green | Count: 531 | Time: 2023-11-22 21:39:30
Profile : green | Count: 532 | Time: 2023-11-22 21:39:30
Profile : green | Count: 533 | Time: 2023-11-22 21:39:30
Profile : green | Count: 534 | Time: 2023-11-22 21:39:30
Profile : green | Count: 535 | Time: 2023-11-22 21:39:30
Profile : green | Count: 536 | Time: 2023-11-22 21:39:30
Profile : green | Count: 537 | Time: 2023-11-22 21:39:30
Profile : green | Count: 538 | Time: 2023-11-22 21:39:30
Profile : green | Count: 539 | Time: 2023-11-22 21:39:30
Profile : green | Count: 540 | Time: 2023-11-22 21:39:30
Profile : green | Count: 541 | Time: 2023-11-22 21:39:30
Profile : green | Count: 542 | Time: 2023-11-22 21:39:30
Profile : green | Count: 543 | Time: 2023-11-22 21:39:30
Profile : green | Count: 544 | Time: 2023-11-22 21:39:30
Profile : green | Count: 545 | Time: 2023-11-22 21:39:30
Profile : green | Count: 546 | Time: 2023-11-22 21:39:30
Profile : green | Count: 547 | Time: 2023-11-22 21:39:30
Profile : green | Count: 548 | Time: 2023-11-22 21:39:30
Profile : green | Count: 549 | Time: 2023-11-22 21:39:30
Profile : green | Count: 550 | Time: 2023-11-22 21:39:30
Profile : green | Count: 551 | Time: 2023-11-22 21:39:30
Profile : green | Count: 552 | Time: 2023-11-22 21:39:30
Profile : green | Count: 553 | Time: 2023-11-22 21:39:30
Profile : green | Count: 554 | Time: 2023-11-22 21:39:30
Profile : green | Count: 555 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 443 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 444 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 445 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 446 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 447 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 448 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 449 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 450 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 451 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 452 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 453 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 454 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 455 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 456 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 457 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 458 | Time: 2023-11-22 21:39:30
Profile : blue | Count: 459 | Time: 2023-11-22 21:39:30
Profile : <!DOCTYPE html> | Count: 460 | Time: 2023-11-22 21:39:30
Profile : green | Count: 461 | Time: 2023-11-22 21:39:30
Profile : green | Count: 462 | Time: 2023-11-22 21:39:30
Profile : green | Count: 463 | Time: 2023-11-22 21:39:30
Profile : green | Count: 464 | Time: 2023-11-22 21:39:30
Profile : green | Count: 465 | Time: 2023-11-22 21:39:30
Profile : green | Count: 466 | Time: 2023-11-22 21:39:30
Profile : green | Count: 467 | Time: 2023-11-22 21:39:30
Profile : green | Count: 468 | Time: 2023-11-22 21:39:30
Profile : green | Count: 469 | Time: 2023-11-22 21:39:30
Profile : green | Count: 470 | Time: 2023-11-22 21:39:30
Profile : green | Count: 471 | Time: 2023-11-22 21:39:30
Profile : green | Count: 472 | Time: 2023-11-22 21:39:30
Profile : green | Count: 473 | Time: 2023-11-22 21:39:30
Profile : green | Count: 474 | Time: 2023-11-22 21:39:30
Profile : green | Count: 475 | Time: 2023-11-22 21:39:30
Profile : green | Count: 476 | Time: 2023-11-22 21:39:30
Profile : green | Count: 477 | Time: 2023-11-22 21:39:30
Profile : green | Count: 478 | Time: 2023-11-22 21:39:30
Profile : green | Count: 479 | Time: 2023-11-22 21:39:30
Profile : green | Count: 480 | Time: 2023-11-22 21:39:30
Profile : green | Count: 481 | Time: 2023-11-22 21:39:30
Profile : green | Count: 482 | Time: 2023-11-22 21:39:30
Profile : green | Count: 483 | Time: 2023-11-22 21:39:30
Profile : green | Count: 484 | Time: 2023-11-22 21:39:30
Profile : green | Count: 485 | Time: 2023-11-22 21:39:30
Profile : green | Count: 486 | Time: 2023-11-22 21:39:30
Profile : green | Count: 487 | Time: 2023-11-22 21:39:30
Profile : green | Count: 488 | Time: 2023-11-22 21:39:30
Profile : green | Count: 489 | Time: 2023-11-22 21:39:30
Profile : green | Count: 490 | Time: 2023-11-22 21:39:30
Profile : green | Count: 491 | Time: 2023-11-22 21:39:30
Profile : green | Count: 492 | Time: 2023-11-22 21:39:30
Profile : green | Count: 493 | Time: 2023-11-22 21:39:30
Profile : green | Count: 494 | Time: 2023-11-22 21:39:30
Profile : green | Count: 495 | Time: 2023-11-22 21:39:30
Profile : green | Count: 496 | Time: 2023-11-22 21:39:30
Profile : green | Count: 497 | Time: 2023-11-22 21:39:30
Profile : green | Count: 498 | Time: 2023-11-22 21:39:30
Profile : green | Count: 499 | Time: 2023-11-22 21:39:30
Profile : green | Count: 500 | Time: 2023-11-22 21:39:30
Profile : green | Count: 501 | Time: 2023-11-22 21:39:30
Profile : green | Count: 502 | Time: 2023-11-22 21:39:30
Profile : green | Count: 503 | Time: 2023-11-22 21:39:30
Profile : green | Count: 504 | Time: 2023-11-22 21:39:30
Profile : green | Count: 505 | Time: 2023-11-22 21:39:30
Profile : green | Count: 506 | Time: 2023-11-22 21:39:30
Profile : green | Count: 507 | Time: 2023-11-22 21:39:30
Profile : green | Count: 508 | Time: 2023-11-22 21:39:30
Profile : green | Count: 509 | Time: 2023-11-22 21:39:30
Profile : green | Count: 510 | Time: 2023-11-22 21:39:30
Profile : green | Count: 511 | Time: 2023-11-22 21:39:30
Profile : green | Count: 512 | Time: 2023-11-22 21:39:30
Profile : green | Count: 513 | Time: 2023-11-22 21:39:30
Profile : green | Count: 514 | Time: 2023-11-22 21:39:30
Profile : green | Count: 515 | Time: 2023-11-22 21:39:30
Profile : green | Count: 516 | Time: 2023-11-22 21:39:30
Profile : green | Count: 517 | Time: 2023-11-22 21:39:30
Profile : green | Count: 518 | Time: 2023-11-22 21:39:30
Profile : green | Count: 519 | Time: 2023-11-22 21:39:30
Profile : green | Count: 520 | Time: 2023-11-22 21:39:30
Profile : green | Count: 521 | Time: 2023-11-22 21:39:30
Profile : green | Count: 522 | Time: 2023-11-22 21:39:30
Profile : green | Count: 523 | Time: 2023-11-22 21:39:30
Profile : green | Count: 524 | Time: 2023-11-22 21:39:30
Profile : green | Count: 525 | Time: 2023-11-22 21:39:30
Profile : green | Count: 526 | Time: 2023-11-22 21:39:30
Profile : green | Count: 527 | Time: 2023-11-22 21:39:30
Profile : green | Count: 528 | Time: 2023-11-22 21:39:30
Profile : green | Count: 529 | Time: 2023-11-22 21:39:30
Profile : green | Count: 530 | Time: 2023-11-22 21:39:30
Profile : green | Count: 531 | Time: 2023-11-22 21:39:30
Profile : green | Count: 532 | Time: 2023-11-22 21:39:30
Profile : green | Count: 533 | Time: 2023-11-22 21:39:30
Profile : green | Count: 534 | Time: 2023-11-22 21:39:30
Profile : green | Count: 535 | Time: 2023-11-22 21:39:30
Profile : green | Count: 536 | Time: 2023-11-22 21:39:30
Profile : green | Count: 537 | Time: 2023-11-22 21:39:30
Profile : green | Count: 538 | Time: 2023-11-22 21:39:30
Profile : green | Count: 539 | Time: 2023-11-22 21:39:30
Profile : green | Count: 540 | Time: 2023-11-22 21:39:30
Profile : green | Count: 541 | Time: 2023-11-22 21:39:30
Profile : green | Count: 542 | Time: 2023-11-22 21:39:30
Profile : green | Count: 543 | Time: 2023-11-22 21:39:30
Profile : green | Count: 544 | Time: 2023-11-22 21:39:30
Profile : green | Count: 545 | Time: 2023-11-22 21:39:30
Profile : green | Count: 546 | Time: 2023-11-22 21:39:30
Profile : green | Count: 547 | Time: 2023-11-22 21:39:30
Profile : green | Count: 548 | Time: 2023-11-22 21:39:30
Profile : green | Count: 549 | Time: 2023-11-22 21:39:30
Profile : green | Count: 550 | Time: 2023-11-22 21:39:30
Profile : green | Count: 551 | Time: 2023-11-22 21:39:30
Profile : green | Count: 552 | Time: 2023-11-22 21:39:30
Profile : green | Count: 553 | Time: 2023-11-22 21:39:30
Profile : green | Count: 554 | Time: 2023-11-22 21:39:30
Profile : green | Count: 555 | Time: 2023-11-22 21:39:30
- 21시 39분 30초 00에서 31초가 되기 전까지 총 114번의 curl 명령어를 실행을 했고 그중에 딱 한 번 중단이 되었다.
- 중단되는 순간은 0.01초도 안 되는 것 같다.
- 100% 무중단은 아니지만 거의 무중단이라고 볼 수 있지 않을까?
- 이마저도 완벽히 하고 싶다면 nginx가 중단될 때 요청을 가지고 있다가 연결되면 보낸다는 설정을 nginx에 추가하는 기능이 있지 않을까 생각한다. 이건 나중에 nginx를 제대로 공부할 때 알아봐야겠다.
📜 정리
- 배포 스크립트 파일이 작성이 끝났다.
- 스크립트 파일을 작성하며 들었던 "서비스가 중단되는 순간이 어느 정도일까?"에 대한 의문도 간단한 테스트로 어느정도 해결되었다.
- 이제 가장 생소한 작업이었던 Groovy를 사용하여 젠킨스 파일을 작성하는 단계만 남았다.
- 다음 글에 계속된다.
참고
뤼튼
https://hudi.blog/zero-downtime-deployment-with-jenkins-and-nginx/
https://hyunminh.github.io/nonstop-deploy/
728x90
'[DevOps] > CI & CD' 카테고리의 다른 글
로컬에서 젠킨스로 블루/그린 무중단 배포 테스트하기 (3) - 젠킨스 파일 작성, 배포 테스트 진행 (3) | 2023.11.27 |
---|---|
로컬에서 젠킨스로 블루/그린 무중단 배포 테스트하기 (1) - 젠킨스 설치, 스프링 부트 서버 세팅 (0) | 2023.11.24 |
로컬 환경에서 도커 젠킨스(Jenkins) 컨테이너로 깃허브 레파지토리 클론 테스트하기(with Ngrok) | 로컬 젠킨스 설명 포함 (2) | 2023.11.18 |
젠킨스(jenkins)에 대해 알아보고 무중단 배포 계획을 세워보자 (0) | 2023.11.15 |