1, 2가 정상 동작한다면 Nginx가 새로운 버전의 WAS를 바라보도록 변경하고 기존의 WAS를 종료한다.
1. 배포 스크립트 작성
이제 배포 시 실행할 쉘 스크립트를 작성할 것이다.
기존에 리버스 프록시로 이용하고 있던 Nginx를 여기서도 써먹어서 무중단 배포를 구현한다.
배포 스크립트의 각 스탭에 주석을 달아두었으니 리눅스 커맨드를 어느정도 알고 있다면 쉽게 이해할 수 있을 것이다.
1-1. run_new_was
업데이트 된 버전의 새로운 WAS를 띄운다.
# run_new_was.sh
#!/bin/bash
# 환경 변수 설정
PROJECT_ROOT="/home/ubuntu/app" # 프로젝트 루트
JAR_FILE="$PROJECT_ROOT/build/libs/team-0.0.1-SNAPSHOT.jar" # 빌드해서 생성된 jar 파일명
# service_url.inc 에서 현재 서비스 중인 WAS의 포트 번호 확인
CURRENT_PORT=$(cat /home/ubuntu/service_url.inc | grep -Po '[0-9]+' | tail -1)
TARGET_PORT=0
echo "> Current port of running WAS is ${CURRENT_PORT}."
# 서비스 중인 포트가 8081이면 8082 포트로 배포
# 서비스 중인 포트가 8082이면 8081 포트로 배포
if [ ${CURRENT_PORT} -eq 8081 ]; then
TARGET_PORT=8082
elif [ ${CURRENT_PORT} -eq 8082 ]; then
TARGET_PORT=8081
else
echo "> Any WAS is connected to nginx" # 애플리케이션이 실행되고 있지 않음
fi
# 타겟 포트 번호로 실행 중인 프로세스가 있는지 확인
TARGET_PID=$(lsof -Fp -i TCP:${TARGET_PORT} | grep -Po 'p[0-9]+' | grep -Po '[0-9]+')
# PID를 이용해 타겟 포트로 실행 중인 프로세스 Kill
if [ ! -z ${TARGET_PID} ]; then
echo "> Kill ${TARGET_PORT}."
sudo kill ${TARGET_PID}
fi
# 타켓 포트로 업데이트 된 버전의 새로운 서버 실행
nohup java -jar -Dserver.port=${TARGET_PORT} ${JAR_FILE} > /home/ubuntu/nohup.out 2>&1 &
echo "> Now new WAS runs at ${TARGET_PORT}."
exit 0
1-2. health_check
새로 띄운 WAS가 정상 동작하는지 헬스 체크한다.
# health_check.sh
#!/bin/bash
# 환경 변수 설정
# service_url.inc에서 현재 서비스 중인 WAS의 포트 번호 확인
# 해당 포트 번호로 health_check 실행
CURRENT_PORT=$(cat /home/ubuntu/service_url.inc | grep -Po '[0-9]+' | tail -1)
TARGET_PORT=0
if [ ${CURRENT_PORT} -eq 8081 ]; then
TARGET_PORT=8082
elif [ ${CURRENT_PORT} -eq 8082 ]; then
TARGET_PORT=8081
else
echo "> Any WAS is connected to nginx" # 헬스체크 시 Nginx에 어떤 WAS도 연결돼있지 않으면 에러 코드
exit 1
fi
echo "> Start health check of WAS at 'http://127.0.0.1:${TARGET_PORT}' ..."
# "/" 경로로 헬스 체크하여 응답 코드를 보고 서버가 정상적으로 작동하는지 확인
# 최대 10번까지 테스트 해서 그 안에 성공하면 통과(WAS가 늦게 뜨는 경우를 대비한 안전 장치)
for RETRY_COUNT in 1 2 3 4 5 6 7 8 9 10
do
echo "> #${RETRY_COUNT} trying..."
# 테스트할 API 주소를 통해 http 상태 코드 확인
RESPONSE_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:${TARGET_PORT})
# RESPONSE_CODE의 http 상태가 200번인 경우 성공
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 5 # 각 시도 마다 5초간 대기
done
이 때, 본인은 "/" 경로로 헬스 체크를 수행하였다.
따라서 "/" 경로로 GET 요청 시 상태코드 200을 반환하는 컨트롤러를 하나 만들어줘야 한다.
포트번호까지 확인하고 싶어서 아래처럼 해주었다.
@Slf4j
@RestController
public class HealthChecktController implements ApplicationListener<WebServerInitializedEvent> {
private int serverPort;
@Override
public void onApplicationEvent(WebServerInitializedEvent event) {
this.serverPort = event.getWebServer().getPort();
}
@GetMapping
public String health() {
log.info("[헬스체크] 포트번호: {} 호출", serverPort);
return "hi" + serverPort;
}
}
1-3. switch
위 두 단계가 정상적으로 진행됐다면 Nginx가 바라보는 애플리케이션을 스위칭한다.
그리고 이전 버전의 WAS는 종료한다.
# switch.sh
#!/bin/bash
# service_url.inc에서 현재 서비스 중인 WAS의 포트 번호 확인
CURRENT_PORT=$(cat /home/ubuntu/service_url.inc | grep -Po '[0-9]+' | tail -1)
TARGET_PORT=0
echo "> Nginx currently proxies to ${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"
exit 1
fi
# service_url.inc 파일에 적힌 서비스 주소를 새로 띄운 서버의 주소로 변경
echo "set \$service_url http://127.0.0.1:${TARGET_PORT};" | tee /home/ubuntu/service_url.inc
echo "> Now Nginx proxies to ${TARGET_PORT}."
# service_url이 변경됐으므로 nginx를 reload 해줌
sudo service nginx reload
echo "> Nginx reloaded."
# -9 SIGKILL 은 서버를 바로 종료하므로
# -15 SIGTERM 안전 종료인 SIGTERM을 사용하여 이전 포트 프로세스를 제거한다.
CURRENT_PID=$(lsof -Fp -i TCP:${CURRENT_PORT} | grep -Po 'p[0-9]+' | grep -Po '[0-9]+')
if [ -z "$CURRENT_PID" ]; then
echo "> No process found on port ${CURRENT_PORT}."
else
echo "> Killing process ${CURRENT_PID} on port ${CURRENT_PORT}."
sudo kill -15 ${CURRENT_PID}
# kill 명령이 실패했는지 확인
if [ $? -eq 0 ]; then
echo "> Process ${CURRENT_PID} successfully terminated."
else
echo "> Failed to terminate process ${CURRENT_PID}."
exit 1
fi
fi
2. 무중단 배포를 위한 Nginx 설정
8081, 8082 두개의 포트를 이용하여 애플리케이션을 띄우는 방식으로 무중단 배포를 구현한다.
기존 서비스가 8081이면 8082 포트로 새로운 버전의 애플리케이션을 띄우고, 정상 동작 시 기존 8081 포트를 내린다.
Nginx로 도메인으로의 요청을 포트포워딩 해주고 있으므로 Nginx 설정을 배포 시에 자동으로 수정하도록 변경해줘야 한다.
1에서배포 스크립트를 작성하면서service_url.inc 를 보았을 것이다.
service_url.inc의 내용은 아래와 같다.
# service_url.inc
set $service_url http://127.0.0.1:8081;
배포 시 매번 $service_url이 변경되면서 Nginx가 바라볼 포트 번호를 지정해준다.
/etc/nginx/sites-available/default 파일이 service_url.inc의 $service_url를 이용할 수 있도록 아래처럼 수정해주자.
include /home/ubuntu/service_url.inc; 구문으로/home/ubuntu/service_url.inc 를 참조할 수 있게 해준다.
다른 포트로 접근 시 막히거나 리다이렉트 되기 때문에 443 포트로 접근할 때만 설정해주면 된다.
3. AppSpec 파일 작성
CodeDeploy Agent는 배포 시appspec.yml파일을 참고하여 배포 프로세스를 진행한다.
(참고로 파일 이름은 꼭 appspec.yml이어야 한다!)
본인은 EC2의 /home/ubuntu/app 폴더에서 배포를 진행할 것이다.
아래처럼 설정하면 CodeDeploy Agent가 동작할 때 S3 버킷의 zip 파일을 /home/ubuntu/app 로 압축해제한다.
AppSpec 파일은 기본적으로 배포를 실행할 폴더의 루트 디렉터리에 위치해야 하기 때문에
압축 시 루트 디렉터리에 위치시켜주면 된다.
appspec.yml
files의 overwrite: yes 로 설정하면 /home/ubunut/app 경로가 없을 시 자동으로 생성한다.
해당 옵션이 없을 때 /home/ubunut/app 경로가 없으면 배포에 실패하므로 참고하자.
# 4. certbot 명령어가 실행될 수 있게 세팅한다 sudo ln -s /snap/bin/certbot /usr/bin/certbot
# 5. 아래 명령어를 입력하면 certbot이 nginx 구성을 자동으로 수정한다. # nginx가 아닌 apache를 웹서버로 사용할 경우, sudo certbot --apache 가 된다 sudo certbot --nginx -d midcon.store -d www.midcon.store
# certbot은 CLI몇 줄로 SSL을 적용해줄 뿐 아니라 자동 리뉴얼까지 해준다 # 처음 설치할 때부터 이러한 cron job 처리를 위한 내용을 site-available/default 에 자동으로 설정해준다 # 아래 명령어로 자동 리뉴얼이 적용되고 있는지 확인할 수 있다 sudo certbot renew --dry-run
5번 커맨드를 입력하면 아래처럼 이메일, 약관 동의 여부 등등을 입력하는 부분이 있다.
당황하지말고 적당히 읽어보고 입력하면 된다.
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Enter email address (used for urgent renewal and security notices)
(Enter 'c' to cancel): hyukkind@naver.com # 이메일 입력
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.4-April-3-2024.pdf. You must agree in
order to register with the ACME server. Do you agree?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: y # ACME 약관에 동의하는지 N선택시 진행불가
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing, once your first certificate is successfully issued, to
share your email address with the Electronic Frontier Foundation, a founding
partner of the Let's Encrypt project and the non-profit organization that
develops Certbot? We'd like to send you email about our work encrypting the web,
EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: y # 이메일을 통해 Let's Encrypt 프로젝트 정보를 받아볼지
Account registered.
Requesting a certificate for midcon.store and www.midcon.store
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/midcon.store/fullchain.pem
Key is saved at: /etc/letsencrypt/live/midcon.store/privkey.pem
This certificate expires on 2024-08-15.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.
Deploying certificate
Successfully deployed certificate for midcon.store to /etc/nginx/sites-enabled/default
Successfully deployed certificate for www.midcon.store to /etc/nginx/sites-enabled/default
Congratulations! You have successfully enabled HTTPS on https://midcon.store and https://www.midcon.store
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
If you like Certbot, please consider supporting our work by:
* Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
* Donating to EFF: https://eff.org/donate-le
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2. 결과 확인
2-1./etc/nginx/sites-available/default 확인
아래 사진은/etc/nginx/sites-available/default로 들어가본 결과이다.
localtion /ws 부분은 본인이 웹소켓 설정하느라 추가한 부분이고 아래 부분부터 보면 되는데
아래처럼 certbot이 nginx 설정까지 자동으로 변경한 알 수 있다.
HTTP로 요청하면 HTTPS로 리다이렉트 시키는 설정도 certbot이 자동으로 해두었다.
2-2. HTTP/HTTPS 요청 시
이제 HTTP 로 요청하면 자동으로 HTTPS 로 리다이렉트 되고, HTTPS 요청도 잘 되는걸 확인할 수 있다.
3. 추가
만약 인증서를 삭제하고 싶다면 아래 명령어를 입력하면 된다.
sudo certbot delete
그럼 아래와 같은 창을 확인할 수 있고, 여기서 삭제하고 싶은 인증서의 번호를 입력하면 된다.