본문 바로가기

Infra

[우당탕탕 개발 일지] Spring Boot 배포하기: Nginx 로 CICD

반응형
SMALL

안녕하세요. 방금 우당탕탕 어찌저찌 무중단 배포로 CICD 세팅 완료하고 왔읍니다 !

 

[CHORE] CICD 세팅 - CD by thguss · Pull Request #39 · Team-Smeme/Smeme-server-renewal

Related issue 🚀 closed #37 Work Description 💚 CICD 세팅: CD 과정 진행 과정 🔥 S3 업로드 Code Deploy 연결 CD

github.com

 

 

지난 번 CICD에는 성공했지만,, 매번 코드가 바뀌고 배포할 때마다 그 배포 중은 서버가 502로 내려가는 것이 조금 불편쓰 했다.

그래서 이번에는 Nginx를 이용하여 무중단 배포 CICD를 세팅하기로 했다! 우분투 20.04 기준입니다!

 

플로우는 Spring Boot로 CICD 세팅하기 포스팅(https://soso-hyeon.tistory.com/53)와 비슷하게 흘러가되, Nginx와 몇 가지 파일을 추가함으로써 무중단 특성을 추가한다. (즉, 몇 가지 과정은 생략될 것이라는 마...ㄹ..)

 

 

전체적인 흐름도는 다음과 같다.

(1) 빌드된 JAR 파일을 S3에 업로드 한다.

(2) S3에 업로드 된 파일을 가지고 Code Deploy에 전달한다.

(3) Code Deploy에서 Code Deploy Agent에게 배포 명령을 내리고,

(4) S3에서 JAR 파일을 받아와 EC2에 배포한다.

 

(1)~(4) 과정은 CICD 과정과 같다. 여기서 Nginx를 사용하여 포트를 2개로 나눠 번갈아가며 WAS를 띄움으로써 배포 중 서버가 끊기지 않도록 한다. 처음에 8081 포트로 배포했다면, 다음 배포에서는 8082로 배포한다. 8082번이 배포될 동안은 8081번 배포는 살아있으니 배포 중에도 서버가 끊기지 않을 수 있다. 또한, proxy_pass을 사용하여 특정 도메인으로 요청을 날렸을 때 현재 배포된 포트 번호로 redirect 되도록 함으로써 무중단 배포를 설정할 수 있었다.

 

 

이제 직접 해보자!

CI, jdk17, codedeploy-agent, S3, IAM 설정 등등 과정은 생략하도록 하겠다. 해당 과정은 아래 포스팅을 참고해주세요!

jar 파일을 압축하여 S3 업로드까지는 필수로 해주세요!

 

[우당탕탕 개발 일지] Github Action과 CodeDeploy로 SpringBoot CICD 세팅하기

새로운 프로젝트를 슬슬 시작하는 과정에 있다. 편리함을 위해 이번 프로젝트에서는 CICD 도입을 맡게 되었다. 스프링 부트 기반의 프로젝트를 CICD 연결했던 과정을 기록해보겠다! 더보기 준비물

soso-hyeon.tistory.com

 

Nginx 설치

아래와 같이 설치해주세요!

 

[우당탕탕 개발 일지] 우분투 Nginx 설치

$ sudo apt update $ sudo apt upgrade $ sudo apt autoremove $ sudo apt install nginx $ sudo nginx -v 업데이트 후, nginx를 설치하고 버전을 확인한다. $ sudo service nginx start $ sudo service nginx status nginx를 시작하고, 상태를 확

soso-hyeon.tistory.com

 

AWS ~ deploy.yml

AWS 설정부터 deploy.yml 파일까지는 위 포스팅에서 확인할 수 있는 과정이므로 생략하겠다.

 

appspec.yml 파일

version: 0.0
os: linux

files:
  - source: /
    destination: /home/ubuntu/smeme/
    overwrite: yes

permissions:
  - object: /home/ubuntu
    pattern: '**'
    owner: ubuntu
    group: ubuntu

hooks:
  ApplicationStart:
    - location: scripts/run_new_was.sh
      timeout: 180
      runas: ubuntu
    - location: scripts/health_check.sh
      timeout: 180
      runas: ubuntu
    - location: scripts/switch.sh
      timeout: 180
      runas: ubuntu

본 CICD 과정에서 hooks 과정이 다르다. 무중단 배포에서는 3가지 스크립트 파일을 실행시킨다.

 

 

run_new_was.sh 파일

scripts 파일 아래에 run_new_was.sh 파일을 추가하자.

#!/bin/bash

CURRENT_PORT=$(cat /etc/nginx/conf.d/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]+')

if [ ! -z ${TARGET_PID} ]; then
  echo "> Kill WAS running at ${TARGET_PORT}."
  sudo kill ${TARGET_PID}
fi

nohup java -jar -Dserver.port=${TARGET_PORT} -Dspring.profiles.active=prod /home/ubuntu/smeme/build/libs/server-0.0.1-SNAPSHOT.jar > /dev/null 2> /dev/null < /dev/null &
echo "> Now new WAS runs at ${TARGET_PORT}."
exit 0

- 현재 배포 중인 포트가 8081이라면 8082를, 반대라면 8081을 TARGET_PORT로 지정한다. (그 외는 연결된 것이 없음)

- TARGET_PORT를 사용 중인 PID가 있다면 kill 한다.

- nohup으로 TARGET_PORT로 실행시킨다. (TARGET_PORT로 배포)

 

-Dspring.profiles.active는 프로필 지정으로, 낯설다면 아래 포스팅을 참고해주세요!

 

[Spring Boot] Profile.active 을 이용하여 다른 설정 정보(application.properties) 불러오기

application.properties 파일 우리는 application.yml 이나 application.properties 파일을 이용해서 설정 정보를 추가하곤 한다. server: port: 56237 spring: application: name: user-service datasource: url: 232.221.123.33 driver: h2.Driver

wonit.tistory.com

 

health_check.sh 파일

파일명 그대로 잘 배포되었는지 heath check 하는 용도의 파일이다. 마찬가지로 scripts 폴더 내부에 추가한다.

#!/bin/bash

# Crawl current connected port of WAS
CURRENT_PORT=$(cat /etc/nginx/conf.d/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}/)

    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

- 이전 yml 파일에서와 같이 TARTGET_PORT를 가져온다.

- 10번의 카운트 동안 테스트 경로 요청에서 200을 받으면 패스한다. (카운트 안에 요청 못하면 실패)

 

그러므로 health check 할 API 경로를 미리 만들어주도록 하자.

    @GetMapping("/")
    public String test() {
        return "CD TEST V5";
    }

"/" 경로에 요청 테스트 API를 추가했다. (Spring Security를 사용한다면 permitAll() 해주는 것도 잊지 말아야 한다.)

 

switch.sh

EC2 내에 TARGET_PORT를 저장해주는 용도이다. (CURRENT_PORT > TARGET_PORT) 역시 scripts 폴더 내에 추가한다.

#!/bin/bash

# Crawl current connected port of WAS
CURRENT_PORT=$(cat /etc/nginx/conf.d/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};" |sudo tee /etc/nginx/conf.d/service-url.inc

echo "> Now Nginx proxies to ${TARGET_PORT}."

# Reload nginx
sudo service nginx reload

echo "> Nginx reloaded."

CURRENT_PID=$(lsof -Fp -i TCP:${CURRENT_PORT} | grep -Po 'p[0-9]+' | grep -Po '[0-9]+')

sudo kill ${CURRENT_PID}

- TARGET_PORT를 구한다.

- service_url 환경변수 값의 포트번호를 TARGET_PORT로 바꾼다.

- nginx를 재시작 시킨다.

- 재배포 전에 사용하던 포트번호는 해당 PID를 찾아서 kill 한다.

 

EC2 설정

sudo vi /etc/nginx/sites-available/default 경로로 들어가서 파일을 편집해야 한다.

location / 부분을 찾아서 위에 include 부분과, 아래 proxy_set_header ~ send_timeout 까지 7줄 추가해준다.

해당 ip로 API 요청이 들어왔을 때, include된 파일에서 설정한 환경변수 $service_url로 redirect 된다.

 

이제 sudo vi /etc/nginx/conf.d/service-url.inc 경로로 들어가서 환경변수를 설정해주어야 한다.

set $service_url http://127.0.0.1:8081;

처음에 8081번으로 설정했다.

 

배포를 시킬 때, 이미 8081번 8082번 중 하나로 배포가 되어있는 상태여야 하므로 처음에 수동으로 한 번 배포시켜주어야 한다.

nohup java -jar -Dserver.port=8081 -Dspring.profiles.active=prod /home/ubuntu/smeme/build/libs/server-0.0.1-SNAPSHOT.jar &

위 명령어를 EC2 내에서 실행하여 백그라운드로 jar 파일을 배포시켜주어야 한다.

 

 

배포

그럼 이제 health check 할 부분의 API를 수정하여 develop에 push 해보자.

1. 깃허브 액션 성공

 

2. CodeDeploy 배포 성공

 

3. 위에서 설정한 service-url.inc 파일을 확인하면 포트번호가 8082로 변경되었음을 확인할 수 있다.

 

4. 포트번호 없이 ip 주소(또는 도메인)으로 접속하면 API가 정상적으로 호출되는 것을 확인할 수 있다.

 

 

무중단 배포를 설정해준 후에는 배포 중에도 서버가 502로 내려가지 않았다. 끊기지 말고 편하게 개발하자! 야호~!

 

 

Refereces

https://wbluke.tistory.com/39

https://mumomu.tistory.com/126

https://velog.io/@dirn0568/NGINX-%EB%AC%B4%EC%A4%91%EB%8B%A8-%EB%B0%B0%ED%8F%AC

https://dazbee.tistory.com/94

https://wonit.tistory.com/501

반응형
LIST