-
[Infra] Blue-Green 배포 전략을 활용한 무중단 서비스프로젝트/뉴스타 2024. 8. 9. 12:40
Blue-Green 배포 전략을 활용한 무중단 서비스 왜 궁금했을까❓
뉴스타 프로젝트에 Blue-Green 배포 전략을 도입하여 무중단 서비스를 구축한 과정에 대해 작성해보려고 한다. 또한, 배포 전략 중에 Blue-Green을 선택한 이유도 작성해보겠다.
[Infra] 무중단 배포 전략 (Rolling / Blue-Green / Canary)
무중단 배포 전략 (Rolling / Blue-Green / Canary) 왜 궁금했을까❓뉴스타 서비스 CI/CD 과정에서 서비스가 중단되는 문제점으로 인해 사용자가 일정 시간동안 서비스를 이용하지 못하는 문제점이 있었
pslog.co.kr
위 포스팅을 통해 무중단 배포 전략에 대한 개념을 학습할 수 있다.
1. 왜 Blue-Green 배포 전략인가?
- 1개의 서버와 어플리케이션당 하나씩 Docker를 이용해서 띄우기 때문에 롤링과 카나리 방식은 적합하지 않았다.
- 프로젝트 기간이 짧아 기능의 품질을 보장하기 쉽지 않았고 이로 인해 잦은 백업이 필요했다. 보다 쉽게 롤백을 이뤄낼 수 있기 때문에 Blue-Green을 선택했다.
- Blue-Green의 경우 리소스가 많이 사용된다는 점이 있지만 어플리케이션을 3개 밖에 구동중이지 않아 전환되는 과정에서 부하가 많지 않을 것이라 예상되었다.
2. 프로젝트 구축 과정
2.1. Blue-Green 무중단 서비스 Flow
- 개발자가 코드 변경 사항을 GitLab에 PUSH
- Webhook을 통해서 신버전 코드 빌드 및 이미지 배포
- Blue-Green 전환 배포 스크립트 Deploy.sh 실행
- Nginx 라우팅을 통해서 구버전 -> 신버전 전환
[CI/CD] Jenkins / GitLab / Docker / EC2 연동 (1/2)
SSAFY 2학기 프로젝트 "Archiview"를 Jenkins, GitLab, Docker, EC2를 이용해서 CI/CD를 구축해보겠다.처음에는 Jenkins를 Docker에 올렸는데 Jenkins에서 docker를 실행하기 위해서는 docker를 또 설치해야 하는 문제점
pslog.co.kr
위 포스팅을 통해서 Jenkins, GitLab을 활용한 CI/CD 과정을 볼 수 있다.
2.2. Nginx
service-url.inc
set $spring_url http://172.17.0.1:8080; set $react_url http://172.17.0.1:3000; set $fastapi_url http://172.17.0.1:8000;
- 서버 IP를 각 변수에 저장하고 해당 IP를 변경하여 Blue-Green 전환이 이뤄지게 된다.
default.conf
server { ... location /api { proxy_pass $spring_url; proxy_set_header X-Forwarded-Host $server_name; } location /api/data/ { proxy_pass $fastapi_url; proxy_set_header X-Forwarded-Host $server_name; } location / { proxy_pass $react_url; } }
- proxy_pass에 각 서버에 해당되는 변수를 입력하여 서버 IP를 지정하여 라우팅한다.
2.3. Docker
docker-compose.blue.yaml
version: '3' services: spring: container_name: spring-blue image: newstar_back ports: - 8080:8080 restart: always env_file: - /env/.env environment: TZ: Asia/Seoul react: container_name: react-blue image: newstar_front ports: - 3000:3000 fastapi: container_name: fastapi-blue image: fastapi_back ports: - 8000:8000 restart: always env_file: - /env/.env environment: TZ: Asia/Seoul networks: default: name: app-net external: true
docker-compose.green.yaml
version: '3' services: spring: container_name: spring-green image: newstar_back ports: - 8081:8080 restart: always env_file: - /env/.env environment: TZ: Asia/Seoul react: container_name: react-green image: newstar_front ports: - 3001:3000 fastapi: container_name: fastapi-green image: fastapi_back ports: - 8001:8000 restart: always env_file: - /env/.env environment: TZ: Asia/Seoul networks: default: name: app-net external: true
2.4. Jenkins Pipeline Script
pipeline { agent any stages { stage('clone') { steps { echo 'git clone' git branch: 'master', credentialsId: '[비밀키]', url: 'https://lab.ssafy.com/s10-bigdata-recom-sub2/S10P22B302.git' } } stage('API Server build') { steps { dir('back') { sh 'docker build -t newstar_back .' } } } stage('GPU Server build') { steps { dir('pydata') { sh 'docker build -t fastapi_back .' } } } stage('Web Server build') { steps { dir('front/newstar') { sh 'docker build -t newstar_front .' } } } stage('Blue-Green Deploy'){ steps{ dir('exec/deploy'){ sh 'chmod +x deploy.sh' sh './deploy.sh' } } } } }
- WebHook에 의해 파이프라인이 실행되는데 GitLab에 있는 Repo로부터 코드를 Clone 받아온다.
- API, GPU, Web Server를 순으로 신버전의 도커 이미지를 빌드한다.
- deploy.sh를 통해 Blue-Green 배포를 실시한다.
2.5. deploy.sh
#!/bin/bash echo 'CI/CD Deploy Start' cd ../exec/deploy # Working container check EXIST_BLUE=$(docker compose -p deploy-blue -f docker-compose.blue.yaml ps | grep Up) if [ -z "$EXIST_BLUE" ]; then # blue docker compose -p deploy-blue -f docker-compose.blue.yaml up -d BEFORE_COLOR="green" AFTER_COLOR="blue" BEFORE_SPRING_PORT=8081 BEFORE_REACT_PORT=3001 BEFORE_FASTAPI_PORT=8001 AFTER_SPRING_PORT=8080 AFTER_REACT_PORT=3000 AFTER_FASTAPI_PORT=8000 else # green docker compose -p deploy-green -f docker-compose.green.yaml up -d BEFORE_COLOR="blue" AFTER_COLOR="green" BEFORE_SPRING_PORT=8080 BEFORE_REACT_PORT=3000 BEFORE_FASTAPI_PORT=8000 AFTER_SPRING_PORT=8081 AFTER_REACT_PORT=3001 AFTER_FASTAPI_PORT=8001 fi # Spring Server health checking for retry_count in {1..60} do response=$(curl -s http://172.17.0.1:${AFTER_SPRING_PORT}/api/actuator/health) up_count=$(echo $response | grep 'UP' | wc -l) if [ $up_count -ge 1 ] then echo "==========================" echo "> Spring Server is working" echo "==========================" break else echo "> Spring Health is not working: ${response}" fi # about 10 minuetes if [ $retry_count -eq 60 ] then echo "> Spring Server working failed" docker compose -p deploy-${AFTER_COLOR} -f docker-compose.${AFTER_COLOR}.yaml down exit 1; fi # wating 10 seconds sleep 10 done # Fastapi Server health checking for retry_count in {1..60} do response=$(curl -s http://172.17.0.1:${AFTER_FASTAPI_PORT}/api/data/health) up_count=$(echo $response | grep 'UP' | wc -l) if [ $up_count -ge 1 ] then echo "==========================" echo "> Fastapi Server is working" echo "==========================" break else echo "> Fastapi Health is not working: ${response}" fi # about 10 minuetes if [ $retry_count -eq 60 ] then echo "> Fastapi Server working failed" docker compose -p deploy-${AFTER_COLOR} -f docker-compose.${AFTER_COLOR}.yaml down exit 1; fi # wating 10 seconds sleep 20 done echo "${AFTER_COLOR} server up(spring_port:${AFTER_SPRING_PORT}, react_port:${AFTER_REACT_PORT}, fastapi_port:${AFTER_FASTAPI_PORT})" EXIST_AFTER=$(docker compose -p deploy-${AFTER_COLOR} -f docker-compose.${AFTER_COLOR}.yaml ps | grep Up) if [ -n "$EXIST_AFTER" ]; then echo "nginx Setting" docker exec -i nginx /bin/bash -c "echo -e 'set \$spring_url http://172.17.0.1:${AFTER_SPRING_PORT};\nset \$react_url http://172.17.0.1:${AFTER_REACT_PORT};\nset \$fastapi_url http://172.17.0.1:${AFTER_FASTAPI_PORT};' | tee /etc/nginx/conf.d/service-url.inc && nginx -s reload" echo "Completed Deploy!" echo "$BEFORE_COLOR server down(spring_port:${BEFORE_SPRING_PORT}, react_port:${BEFORE_REACT_PORT}, fastapi_port:${BEFORE_FASTAPI_PORT})" docker compose -p deploy-${BEFORE_COLOR} -f docker-compose.${BEFORE_COLOR}.yaml down fi
- 현재 실행중인 Docker-compose의 색깔을 확인(Blue or Green)
- 만약, Blue일 경우 Green의 Docker-compose를 띄우고 port번호를 Green에 맞게 저장
- Green일 경우 Blue의 Docker-compose를 띄우고 port번호를 Blue에 맞게 저장
- 각 어플리케이션 API, GPU Server의 Health Check API를 통해 요청을 날려보며 신버전의 코드가 정상적으로 운영되고 있는지 확인
- 만약, 정상적으로 운영되지 않으면 신버전의 서버 다운 후 스크립트 종료
- 신버전의 Docker-compose가 정상적으로 띄워져 있다면 service-url.inc에 있는 각 어플리케이션 port를 신버전으로 변경 후 Nginx 재시작
- 기존에 운영되던 구버전의 docker compose 다운
'프로젝트 > 뉴스타' 카테고리의 다른 글
[React] React Tour를 활용한 서비스 가이드 (0) 2024.08.12 [FastAPI] ElasticSearch를 활용한 검색 속도 향상 (0) 2024.07.25 [Infra] 무중단 배포 전략 (Rolling / Blue-Green / Canary) (0) 2024.07.21 [React] Axios API 모듈화 (0) 2024.07.02 [Spring Boot / FastAPI] 프로젝트 환경 분리 (0) 2024.06.30