2021. 12. 17. 04:29ㆍ항해99/실전프로젝트
앞에서 Github Action을 통해서 S3에 빌드된 zip 파일을 업로드 하고
CodeDeploy에 이를 알리는 것 까지 하였다.
AWS에 Code Deploy를 만들고 배포 그룹만들고 EC2까지 연결해서
Code Deploy가 S3에 있는 빌드된 zip 파일을 가져오는 작업은 너무 길어서
아래 포스팅으로 대체하겠다.
https://wbluke.tistory.com/40?category=418851
Github Actions + CodeDeploy + Nginx 로 무중단 배포하기 (2)
CodeDeploy 소개 전 시간에 이어 다음으로는 Github Actions 에서 CodeDeploy 에게 S3에 있는 jar 파일을 가져가서 담당한 배포 그룹의 EC2에 배포해 줘! 라는 명령을 내릴 수 있도록 구성해 보겠습니다. 먼저
wbluke.tistory.com
사실 이 포스팅보다 더 이해하기 쉽게 작성할 자신이 없다...
02. NGINX
Code deploy를 통해서 star-project라는 이름으로 백엔드 프로젝트를 받아왔다.
이제 이 프로젝트를 배포 해야한다.
그 전에 무중단배포의 방식을 보면
위와 같은 방식으로 동작한다.
nginx가 여기서 4번의 역활을 맡게되고 Ec2 내의 CodeDeploy Agent가 1, 2, 3의 역활을 맡게된다.
일단 nginx 설정부터 하자면
listen 80;
listen [::] 80;
80번 포트를 보고있으며 프록시 서버로 설정한 포트번호로 라우팅 해준다.
include /home/ec2-user/service_url.inc;
다른 곳에 존재하는 설정 파일 등을 불러올 수 있다.
/home/ec2-user/service_url.inc 파일의 설정 변수들을 불러온다.
location / {
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $http_Host;
proxy_pass $service_url;
}
우리가 지정할 $service_url로 요청을 보낼 수 있도록 하는 프록시 설정이다.
만약 $service_url 이 8001번 포트라면 80번 포트를 8001 포트로 라우팅 해준다.
$service_url
이제 service_url 설정 파일을 만들어보자
service_url은 간편하게 현재 라우팅할 포트번호만을 적어준다.
이제 nginx를 이용한 라우팅은 모두 마무리 되었다.
※ 라우팅 포트를 설정 파일로 뺀 이유
무중단 배포의 nginx의 라우팅 포트 번호 자체가 상황에 따라서 포트 번호가 바뀌게 된다.
만약 8001번 포트로 서비스를 하고 있고 새로 배포를 요청하게 된다면
8001번이 아닌 8002번으로 서버를 띄우고 8002번으로 라우팅해야 한다.
마치 service_url 설정파일을 변수처럼 사용해서 값을 그때마다 바꾸어서
라우팅한다.
03. 배포 스크립트 추가
이제 EC2에서 서버를 띄울 준비만 하면 된다.
이 작업은 EC2 내의 CodeDeploy Agent가 맡게 된다.
# appspec.yml
version: 0.0
os: linux
files:
- source: /
destination: /home/ec2-user/star-project/ # 프로젝트 이름
overwrite: yes
permissions:
- object: /
pattern: "**"
owner: ec2-user
group: ec2-user
hooks:
ApplicationStart:
- location: scripts/run_new_was.sh
timeout: 180
runas: ec2-user
- location: scripts/health_check.sh
timeout: 180
runas: ec2-user
- location: scripts/switch.sh
timeout: 180
runas: ec2-user
CodeDeploy Agent가 ApplicationStart라는 수명 주기에 세 가지 스크립트를 차례로 실행시킨다.
run_new_was.sh
# run_new_was.sh
CURRENT_PORT=$(cat /home/ec2-user/service_url.inc | grep -Po '[0-9]+' | tail -1)
TARGET_PORT=0
echo "> Current port of running WAS is ${CURRENT_PORT}."
if [ ${CURRENT_PORT} -eq 8081 ]; then
TARGET_PORT=8082
elif [ ${CURRENT_PORT} -eq 8082 ]; then
TARGET_PORT=8081
else
echo "> No WAS is connected to nginx"
fi
TARGET_PID=$(lsof -Fp -i TCP:${TARGET_PORT} | grep -Po 'p[0-9]+' | grep -Po '[0-9]+')
MONITORING_PID=$(lsof -Fp -i TCP:8000 | grep -Po 'p[0-9]+' | grep -Po '[0-9]+')
if [ ! -z "${TARGET_PID}" ]; then
echo "> Kill WAS running at ${TARGET_PORT}."
sudo kill ${TARGET_PID}
fi
nohup java -jar -Dserver.port=${TARGET_PORT} /home/ec2-user/star-project/build/libs/* > /home/ec2-user/nohup.out 2>&1 &
echo "> Now new WAS runs at ${TARGET_PORT}."
exit 0
run_new_was.sh 는 새로운 서버를 띄우는 스크립트 명령어들이다.
CURRENT_PORT=$(cat /home/ec2-user/service_url.inc | grep -Po '[0-9]+' | tail -1)
TARGET_PORT=0
echo "> Current port of running WAS is ${CURRENT_PORT}."
if [ ${CURRENT_PORT} -eq 8081 ]; then
TARGET_PORT=8082
elif [ ${CURRENT_PORT} -eq 8082 ]; then
TARGET_PORT=8081
else
echo "> No WAS is connected to nginx"
fi
현재 서비스하고 있는 포트번호를 service_url 설정파일을 통해서 가져온다.
그 후 if 문을 통해서 새로운 서버를 배포할 포트번호를 정하게 된다.
TARGET_PID=$(lsof -Fp -i TCP:${TARGET_PORT} | grep -Po 'p[0-9]+' | grep -Po '[0-9]+')
MONITORING_PID=$(lsof -Fp -i TCP:8000 | grep -Po 'p[0-9]+' | grep -Po '[0-9]+')
if [ ! -z "${TARGET_PID}" ]; then
echo "> Kill WAS running at ${TARGET_PORT}."
sudo kill ${TARGET_PID}
fi
정한 포트번호를 토대로 서버를 키기 전에 그 포트번호에 서버가 켜있으면 안되므로
해당 포트번호의 프로세스가 존재한다면 그 프로세스를 종료한다.
nohup java -jar -Dserver.port=${TARGET_PORT} /home/ec2-user/star-project/build/libs/* > /home/ec2-user/nohup.out 2>&1 &
echo "> Now new WAS runs at ${TARGET_PORT}."
exit 0
이제 nuhup으로 서버를 실행하게된다.
switch.sh
# switch.sh
# Crawl current connected port of WAS
CURRENT_PORT=$(cat /home/ec2-user/service_url.inc | grep -Po '[0-9]+' | tail -1)
TARGET_PORT=0
echo "> Nginx currently proxies to ${CURRENT_PORT}."
# Toggle port number
if [ ${CURRENT_PORT} -eq 8081 ]; then
TARGET_PORT=8082
elif [ ${CURRENT_PORT} -eq 8082 ]; then
TARGET_PORT=8081
else
echo "> No WAS is connected to nginx"
exit 1
fi
# Change proxying port into target port
echo "set \$service_url http://127.0.0.1:${TARGET_PORT};" | tee /home/ec2-user/service_url.inc
echo "> Now Nginx proxies to ${TARGET_PORT}."
# Reload nginx
sudo service nginx reload
echo "> Nginx reloaded."
switch.sh 스크립트는 service_url 설정파일의 포트번호를 바꾸어서
궁극적으로는 nginx가 라우팅해주는 포트번호를 바꾸게한다.
# Crawl current connected port of WAS
CURRENT_PORT=$(cat /home/ec2-user/service_url.inc | grep -Po '[0-9]+' | tail -1)
TARGET_PORT=0
현재 서비스 중인 포트번호를 가져온다. (새로 띄운 포트번호가 아니다. 이전에 라우팅하고 있는 포트번호)
# Toggle port number
if [ ${CURRENT_PORT} -eq 8081 ]; then
TARGET_PORT=8082
elif [ ${CURRENT_PORT} -eq 8082 ]; then
TARGET_PORT=8081
else
echo "> No WAS is connected to nginx"
exit 1
fi
if문을 통해서 이번에 띄운 포트번호를 가져온다.
# Change proxying port into target port
echo "set \$service_url http://127.0.0.1:${TARGET_PORT};" | tee /home/ec2-user/service_url.inc
echo "> Now Nginx proxies to ${TARGET_PORT}."
# Reload nginx
sudo service nginx reload
echo "> Nginx reloaded."
service_url 설정파일을 수정하고 nginx를 재가동한다.
health_check.sh
# health_check.sh
# Crawl current connected port of WAS
CURRENT_PORT=$(cat /home/ec2-user/service_url.inc | grep -Po '[0-9]+' | tail -1)
TARGET_PORT=0
# Toggle port Number
if [ ${CURRENT_PORT} -eq 8081 ]; then
TARGET_PORT=8082
elif [ ${CURRENT_PORT} -eq 8082 ]; then
TARGET_PORT=8081
else
echo "> No WAS is connected to nginx"
exit 1
fi
echo "> Start health check of WAS at 'http://127.0.0.1:${TARGET_PORT}' ..."
for RETRY_COUNT in 1 2 3 4 5 6 7 8 9 10
do
echo "> #${RETRY_COUNT} trying..."
RESPONSE_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:${TARGET_PORT}/health)
if [ ${RESPONSE_CODE} -eq 200 ]; then
echo "> New WAS successfully running"
exit 0
elif [ ${RETRY_COUNT} -eq 10 ]; then
echo "> Health check failed."
exit 1
fi
sleep 10
done
run_new_was 스크립트를 통해서 새로운 서버를 띄우게 했으며 위 서버가 제대로
서버를 운영하고 있는지 체크하는 스크립트이다.
for 문을 통해서 일정 주기 동안 실제로 메시지를 보내고 reponse 메시지를 통해서 확인한다.
for RETRY_COUNT in 1 2 3 4 5 6 7 8 9 10
do
echo "> #${RETRY_COUNT} trying..."
RESPONSE_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:${TARGET_PORT}/health)
if [ ${RESPONSE_CODE} -eq 200 ]; then
echo "> New WAS successfully running"
exit 0
elif [ ${RETRY_COUNT} -eq 10 ]; then
echo "> Health check failed."
exit 1
fi
sleep 10
done
GET /health 요청을 보내고 상태 코드를 확인한다.
@RestController
public class LoggingController {
@Value("${logging-module.version}")
private String version;
@GetMapping("/") public String version() {
return String.format("Project Version : %s", version);
}
@GetMapping("/health")
public String checkHealth() {
return "healthy";
}
}
당연히 응답을 보내기 위해서 위의 Controller를 추가하였다.
자 이렇게 스크립트까지 잘 작성하고 workflow를 실행시키면 aws의 codedeploy 페이지에서
codedeploy agent가 실행되는 것을 볼 수 있다.
04. 에러 해결
한번 씩 codedeploy Agent가 에러를 띄워서 많이 곤혹스러웠는데
에러 로그를 통해서 확인할 수 있다.
일단 Codedeploy Agent의 로그는
vim /opt/codedeploy-agent/deployment-root/deployment-logs/codedeploy-agent-deployments.log
에서 확인이 가능하고
추가로 서버를 실행했을 때의 로그도 확인하는게 좋다.
앞서 run_new_was 스크립트에서 nohup 옵션으로
> /home/ec2-user/nohup.out 2>&1 &
이건 리눅스의 꺽쇠(>)명령어로 꺽쇠(>)는 좌측 명령의 결과를 우측 파일명에 저장하게 한다.
즉 스프링에서 로그로 찍은 모든 로그가 nohup.out이라는 이름으로 찍히게 된다.
이 로그 또한 확인하는게 좋다.
앞서 위의 에러는 그 때 당시 logback을 통해서 프로젝트가 실행하게 되면logback.log 라는 파일을 생성하게 되었다.
그러나!!
zip 파일을 풀고 만들어진 폴더에는 파일을 생성하는 권한이 없다.
그래서 서버가 실행되다가 logback.log 파일을 생성하는 순간 뻗어버린 것 이었다.
그래서 run_new_was.sh 스크립트에
sudo chmode -R 777 /home/ec2-user/star-project
문구를 추가해서 해결하였다.
'항해99 > 실전프로젝트' 카테고리의 다른 글
항해99 - 실전프로젝트 10(모니터링, 설정파일 암호화) (0) | 2021.12.17 |
---|---|
항해99 - 실전 프로젝트 09(Nginx + Let's Encrypt를 통한 https 구축) (0) | 2021.12.17 |
항해99 - 실전 프로젝트 08(무중단 배포 1) (0) | 2021.12.17 |
항해99 - 실전 프로젝트 07(API 제작 - QueryDsl) (0) | 2021.12.17 |
항해99 - 실전 프로젝트 06(API 제작 - JPA) (0) | 2021.12.15 |