April 04, 2021
AWS Blue/Green 무중단 배포방식은 ELB와 EC2 인스턴스 하나 더 필요합니다. 비용이 많이 필요하다보니 또다른 방법인 Nginx를 이용한 무중단 배포 방식을 사용하기로 했습니다.
Nginx를 이용한 무중단 배포는 AWS EC2 인스턴스가 하나 더 필요하지 않고, 꼭 AWS와 같은 클라우드 인프라가 필요하지 않고 사용할 수 있는 방법 중 하나입니다.
구조는 간단하게 EC2 인스턴스 1대, Nginx 1대, 스프링 부트 jar를 2대 사용하면 됩니다.
Github Actions를 이용한 AWS CodeBuild
EC2에 접속하여 Nginx를 설치합니다.
# nginx 설치
sudo apt-get install nginx
# nginx 실행
sudo service nginx start
# nginx 확인
ps -ef | grep nginx
# nginx 설정폴더로 이동
cd /etc/nginx/sites-available
# nginx 설정 수정하기
sudo vi default
proxy_pass http://localhost:8080;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
http://localhost:8080
proxysetheader XXX : 실제 요청 데이터를 header의 각 항목에 할당
proxy_set_header X-Real-IP $remote_addr
: Request Header의 X-Real-IP에 요청자의 IP를 저장합니다.설정을 마쳤으면 다음 명령어를 내려 Nginx를 재시작합니다.
sudo service nginx restart
실제 스프링부트 프로젝트를 실행할 때
nohup java -jar -Dspring.profiles.active={프로퍼티이름}
사용하여 프로퍼티를 선택하여 실행이 가능합니다.
예시
nohup java -jar -Dspring.profiles.active=dev
각 properties마다 port번호를 다르게 부여합니다.
# dev
server.port=8081
# dev2
server.port=8082
실행하고 있는 프로젝트의 Profile을 확인할 수 있는 API를 만듭니다.
@RestController
public class HelloController {
private final Environment env;
public HelloController(Environment env) {
this.env = env;
}
@GetMapping("/profile")
public String getProfile () {
return Arrays.stream(env.getActiveProfiles())
.findFirst()
.orElse("");
}
#!/usr/bin/env bash
BASE_PATH=/home/ubuntu/
BUILD_PATH=$(ls $BASE_PATH/myapp/*.jar)
JAR_NAME=$(basename $BUILD_PATH)
echo "> build 파일명: $JAR_NAME"
echo "> build 파일 복사"
DEPLOY_PATH=/home/ubuntu/temp/
cp $BUILD_PATH $DEPLOY_PATH
echo "> 현재 구동중인 Set 확인"
CURRENT_PROFILE=$(curl -s http://localhost/profile)
echo "> $CURRENT_PROFILE"
# 쉬고 있는 dev 찾기: dev이 사용중이면 dev2가 쉬고 있고, 반대면 dev이 쉬고 있음
if [ $CURRENT_PROFILE == dev ]
then
IDLE_PROFILE=dev2
IDLE_PORT=8082
elif [ $CURRENT_PROFILE == dev ]
then
IDLE_PROFILE=dev
IDLE_PORT=8081
else
echo "> 일치하는 Profile이 없습니다. Profile: $CURRENT_PROFILE"
echo "> dev 할당합니다. IDLE_PROFILE: dev"
IDLE_PROFILE=dev
IDLE_PORT=8081
fi
echo "> application.jar 교체"
IDLE_APPLICATION=$IDLE_PROFILE-demo.jar
IDLE_APPLICATION_PATH=$DEPLOY_PATH$IDLE_APPLICATION
# 미연결된 Jar로 신규 Jar 심볼릭 링크 (ln)
ln -Tfs $DEPLOY_PATH$JAR_NAME $IDLE_APPLICATION_PATH
echo "> $IDLE_PROFILE 에서 구동중인 애플리케이션 pid 확인"
IDLE_PID=$(pgrep -f $IDLE_APPLICATION)
if [ -z $IDLE_PID ]
then
echo "> 현재 구동중인 애플리케이션이 없으므로 종료하지 않습니다."
else
echo "> kill -15 $IDLE_PID"
kill -15 $IDLE_PID
sleep 5
fi
echo "> $IDLE_PROFILE 배포"
nohup java -jar -Dspring.profiles.active=$IDLE_PROFILE $IDLE_APPLICATION_PATH > $DEPLOY_PATH/nohup.out 2>&1 &
# Nginx Port 스위칭을 위한 스크립트
echo "> 스위칭"
sleep 10
/home/ubuntu/myapp/switch.sh
#!/usr/bin/env bash
echo "> 현재 구동중인 Port 확인"
CURRENT_PROFILE=$(curl -s http://localhost/profile)
if [ $CURRENT_PROFILE == dev ]
then
IDLE_PORT=8082
elif [ $CURRENT_PROFILE == dev2 ]
then
IDLE_PORT=8081
else
echo "> 일치하는 Profile이 없습니다. Profile: $CURRENT_PROFILE"
echo "> 8081을 할당합니다."
IDLE_PORT=8081
fi
echo "> 전환할 Port: $IDLE_PORT"
echo "> Port 전환"
echo "set \$service_url http://127.0.0.1:${IDLE_PORT};" |sudo tee /etc/nginx/conf.d/service-url.inc
PROXY_PORT=$(curl -s http://localhost/profile)
echo "> Nginx Current Proxy Port: $PROXY_PORT"
echo "> Nginx Reload"
sudo service nginx reload # reload는 설정만 재적용하기 때문에 바로 적용이 가능합니다.
7) 스프링부트로 웹 서비스 출시하기 - 7. Nginx를 활용한 무중단 배포 구축하기
cd /etc/nginx
conf.d
폴더에서 service-url.inc
를 생성합니다.sudo vi service-url.inc
## service-url.inc
set $service_url http://127.0.0.1:8081;
설정을 추가합니다. (sites-available/default)
include /etc/nginx/conf.d/service-url.inc;
proxy_pass $service_url;
저장하셨으면 다시 Nginx를 재시작합니다.
sudo service nginx restart
테스트를 위해 curl
을 수행합니다.
curl -s localhost/profile
Nginx가 port:8081로 Proxy가 가는 것을 확인할 수 있습니다.
이전 포스트참고 하셔도 됩니다!
Github Actions, AWS Deploy로 CI/CD해보자!
CodeDeploy 애플리케이션 생성
환경구성
name: CD with Gradle
on:
push:
branches: [master]
jobs:
buildAndTest:
name: Github actions CD
runs-on: ubuntu-18.04
defaults:
run:
shell: bash
steps:
- name: 체크아웃 Github-Action
uses: actions/checkout@v2
- name: AWS 설정
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-northeast-2
- name: CodeBuild 실행
uses: aws-actions/aws-codebuild-run-build@v1.0.3
with:
project-name: cicdtest
- name: Code Deploy
run: aws deploy create-deployment --application-name cicdtest --deployment-config-name CodeDeployDefault.OneAtATime --deployment-group-name nginx-bluegreen --s3-location bucket=cicdtestsunny,bundleType=zip,key=build.zip
--deployment-config-name: 배포설정
--deployment-group-name: 배포그룹 이름
--s3-location : bucket=버킷이름, bundleType=파일 타입, key=파일 이름
key는 build.zip가 아닌 server/build.zip로 설정해야합니다.
version: 0.0
os: linux
files:
- source: /
destination: /home/ubuntu/myapp
permissions:
- object: /home/ubuntu/
owner: ubuntu
group: ubuntu
mode: 755
hooks:
BeforeInstall:
- location: beforeInstall.sh
timeout: 60
runas: ubuntu
AfterInstall:
- location: deploy.sh
timeout: 60
runas: ubuntu
overwrite에러가 발생할 가능성이 있기 때문에 BeforeInstall
단계를 거칩니다.
#!/usr/bin/env bash
REPOSITORY=/home/ubuntu/
if [ -d $REPOSITORY/myapp ]; then
rm -rf $REPOSITORY/myapp
fi
mkdir -vp $REPOSITORY/myapp
여기까지 설정하셨으면 CodeDeploy + Github Actions CI/CD 파이프라인은 모두 갖췄습니다. 이제 테스트를 실행합니다.
배포 성공했습니다!
다음에는 로드밸런서를 이용하여 Blue/Green 배포를 해보겠습니다! 😆
# dev 인스턴스
export DEFAULT_PROFILE=dev
echo $DEFAULT_PROFILE
dev
# live 인스턴스
export DEFAULT_PROFILE=prod
echo $DEFAULT_PR
쉘스크립트 실행 전에 디렉토리를 미리 만들어야합니다.