1. 무중단 배포란?
프로젝트를 진행하며 기존의 배포방식에서는 새로운 서비스를 개발한 후 배포하게되면 서버가 중단되는 다운타임이 존재하는 단점이 있었습니다. 다운타임이 10초가량 걸린다 생각하면 그 10초동안은 유저들이 해당 서비스를 이용하지 못하는 상황이 생기게 됩니다. 이러한 중간에 서버가 다운되며 발생하는 다운타임을 무중단 배포를 통해 해결할 수 있습니다.
무중단 배포는 서비스의 버전이 변경되어 배포될 때 운영중인 서비스가 중단되지 않으면서 새로운 버전을 배포하는 방식으로 로드밸런서를 통해 연결된 두 개 이상의 인스턴스에 트래픽을 제어해 배포하게 됩니다.
- Rolling 방식
- Canary 방식
- Blue / Green 방식
무중단 배포는 위와같은 3가지의 방식이 존재하는데. 지금부터는 각 3가지의 방식에 대해 알아보도록 하겠습니다.
2. Rolling
Rolling 방식은는 사용중인 인스턴스에서 새로운 버전의 서비스의 배포를 점진적으로 진행하는 방식입니다.
인스턴스마다 배포를 순차적으로 진행하기 때문에 상황에 따라 쉽게 롤백을할 수 있는 장점이 있습니다. 하지만 서비스 도중 배포되는 서버로 향하는 트래픽을 멈추고 다른 서버로 트래픽을 향하게 하기 때문에 각각의 서버가 부담하는 트래픽의 양이 늘어나게 됩니다. 또한 구버전과 신버전이 공존하는 상황이 발생해 호환성 문제가 발생할 수 있습니다.
장점
- 서버 별로 배포를 진행하게 되서 상황에 따라 롤백이 간편하다.
단점
- 배포되는 서버로 향하는 트래픽을 멈추고 다른 서비스로 분산시켜야 하기 때문에 트래픽 처리 용량을 잘 판단해 진행해야 한다.
- 신버전과 구버전이 공존하는 상황이 벌어지 수 있어 호환성 문제에 대해 고려해야한다.
3. Canary
카나리 방식은 새로운 버전을 점진적으로 배포해 안정성을 검증하는 배포 전략으로 새로운 버전의 애플리케이션이 프로덕션 환경에서 예상대로 동작하는지 확인한 후 전체 트래픽으로 확장하는 방식입니다. 새로운 버전을 점진적으로 배포하기 때문에 문제가 발생해도 영향을 받는 사용자가 제한적이고, 문제를 빠르게 발견하고 대응할 수 있습니다. 하지만 구버전과 신버전이 공존하는 상황이 벌어져 호환성 문제가 발생할 수 있습니다.
장점
- 새로운 버전으로 인한 위험을 최소화할 수 있다
- A/B 테스트를 진행하기 적합하다.
단점
- 신버전과 구버전이 공존해 호환성 문제가 발생할 수 있다.
4. Blue / Green
블루 그린 배포는 구버전과 동일한 구조의 신버전 인스턴스를 생성한 후 로드밸런서를 이용해 트래픽을 신버전으로 전환하는 배포방식입니다.
1. 두개의 독립된 환경
• Blue 환경: 현재 프로덕션에서 사용되고 있는 환경입니다. 기존 버전의 애플리케이션이 실행 중입니다.
• Green 환경: 새로운 버전의 애플리케이션이 배포될 환경입니다. 이 환경은 Blue 환경과 동일한 설정을 갖고 있지만, 새로운 버전이 실행됩니다.
2. 배포 과정:
• 새로운 애플리케이션 버전은 Green 환경에 배포되고, 이 과정에서 기존 Blue 환경은 계속 사용자가 접속할 수 있도록 유지됩니다.
• Green 환경에서 새 버전의 애플리케이션이 정상적으로 동작하는지 테스트하고 검증합니다.
3. 트래픽 전환:
• Green 환경이 충분히 검증되면, 전체 트래픽을 Blue 환경에서 Green 환경으로 전환합니다. 이때 서비스는 중단 없이 새로운 버전으로 업데이트됩니다.
• 엔진엑스(Nginx)와 같은 로드 밸런서를 사용해 트래픽을 Green 환경으로 전환할 수 있습니다.
4. 롤백 전략:
• 만약 Green 환경에 문제가 발생하면, 트래픽을 다시 Blue 환경으로 되돌려 빠르게 롤백할 수 있습니다.
• Green 환경에서 문제가 발생하더라도 Blue 환경은 영향을 받지 않으므로, 서비스의 안정성을 유지할 수 있습니다.
장점
- 트래픽 전환이 이루어지는 동안에도 서비스 중단 없이 새로운 버전을 배포할 수 있다.
- 구버전의 서버를 지우는 작업을 하지 않는다면 빠른 롤백이 가능하다.
단점
- 실제 운영에 필요한 서버 리소스가 2배로 필요하다.
- 새로운 환경에 대한 테스트가 전제되어야 한다.
5. Blue / Green 방식으로 결정!
지금까지 무중단 배포의 3가지 방식을 알아보았습니다. 저는 그 중 Blue/Green 방식을 선택하게 되었는데요.
구버전과 신버전의 공존상황이 일어나지 않고, 도커환경에 스프링 애플리케이션을 올려놨기 때문에 추가적인 인스턴스 없이도 port 기준으로 하나의 인스턴스 안에서 배포할 수 있기 때문에 최종적으로 Blue/Green 배포를 선택하게 되었습니다.
6. Blue-Green 배포 진행을 위한 스크립트 작성하기
본격적으로 Blue-Green 배포를 위한 배포 스크립트를 작성해보도록 하겠습니다.
6-1. 서버의 포트를 확인해 어떤 포트로 배포할지 결정한다.
RESPONSE_CODE=$(curl -o /dev/null -w "%{http_code}" http://localhost:8080/actuator/health)
if [ ${RESPONSE_CODE} == 200 ];
then
CURRENT_PORT=8080
TARGET_PORT=8081
else
CURRENT_PORT=8081
TARGET_PORT=8080
fi
echo "현재 활성 포트는 $CURRENT_PORT 입니다."
echo "$TARGET_PORT에 새 버전을 배포합니다."
서버 헬스 체크를 통해 새로운 버전을 8080포트로 배포할지, 8081 배포로 배포할지 판단합니다.
6-2. 컨테이너를 생성한다.
docker run -d -p $TARGET_PORT:8080 --name backend-${TARGET_PORT} alswns7984/doridos:latest
echo "컨테이너 생성중"
sleep 30
RESPONSE_CODE=$(curl -o /dev/null -w "%{http_code}" http://localhost:$TARGET_PORT/actuator/health)
if [ ${RESPONSE_CODE} != 200 ];
then
IDLE_CONTAINER_ID = $(docker ps -q --filter "publish=${TARGET_PORT}")
docker stop ${IDLE_CONTAINER_ID}
docker rm ${IDLE_CONTAINER_ID}
echo "컨테이너 제거 완료"
exit 1
fi
배포하는데 대략 20~25초 사이의 시간이 걸리기 때문에 넉넉히 30초가 지난 후 헬스체크를 통해 Spring 서버가 정상적으로 실행되었는지 확인해줍니다.
6-3. Nginx의 설정을 변경한다.
echo "Spring 서버 확인 완료. Nginx 설정 변경을 시작합니다."
REMOTE_SERVER="ssh접속을 위한 계정과 IP"
NGINX_CONF="nginx 설정파일 경로!"
ssh -o StrictHostKeyChecking=no ${REMOTE_SERVER} << EOF
sudo sed -i 's/:808[01]/:$TARGET_PORT/g' $NGINX_CONF
sudo systemctl reload nginx
EOF
nginx의 설정을 변경해줍니다
ex) proxy_pass가 127.0.0.1:8080으로 되어있다면 127.0.0.1:8081로 변경
6-4. 이전 컨테이너를 제거한다.
echo "이전 컨테이너 제거"
PRE_CONTAINER_ID=$(docker ps -q --filter "publish=${CURRENT_PORT}")
docker stop ${PRE_CONTAINER_ID}
docker rm ${PRE_CONTAINER_ID}
echo "컨테이너 제거 완료"
새로운 포트에 Spring 서버를 띄우고 Nginx의 포트 변경이 완료되었으면 기존에 사용하던 컨테이너를 삭제해줍니다.
6-5. 전체 스크립트
RESPONSE_CODE=$(curl -o /dev/null -w "%{http_code}" http://localhost:8080/actuator/health)
if [ ${RESPONSE_CODE} == 200 ];
then
CURRENT_PORT=8080
TARGET_PORT=8081
else
CURRENT_PORT=8081
TARGET_PORT=8080
fi
echo "현재 활성 포트는 $CURRENT_PORT 입니다."
echo "$TARGET_PORT에 새 버전을 배포합니다."
docker run -d -p $TARGET_PORT:8080 --name backend-${TARGET_PORT} alswns7984/doridos:latest
echo "컨테이너 생성중"
sleep 30
RESPONSE_CODE=$(curl -o /dev/null -w "%{http_code}" http://localhost:$TARGET_PORT/actuator/health)
if [ ${RESPONSE_CODE} != 200 ];
then
IDLE_CONTAINER_ID = $(docker ps -q --filter "publish=${TARGET_PORT}")
docker stop ${IDLE_CONTAINER_ID}
docker rm ${IDLE_CONTAINER_ID}
echo "컨테이너 제거 완료"
exit 1
fi
echo "Spring 서버 확인 완료. Nginx 설정 변경을 시작합니다."
REMOTE_SERVER="ssh접속을 위한 계정과 IP"
NGINX_CONF="nginx 설정파일 경로!"
ssh -o StrictHostKeyChecking=no ${REMOTE_SERVER} << EOF
sudo sed -i 's/:808[01]/:$TARGET_PORT/g' $NGINX_CONF
sudo systemctl reload nginx
EOF
echo "이전 컨테이너 제거"
PRE_CONTAINER_ID=$(docker ps -q --filter "publish=${CURRENT_PORT}")
docker stop ${PRE_CONTAINER_ID}
docker rm ${PRE_CONTAINER_ID}
echo "컨테이너 제거 완료"
7. 실행 결과 확인하기!
해당 스크립트를 실행하면 기존 8081에서 실행되고 있던 컨테이너가 삭제되고 8080포트의 새로운 컨테이너가 실행중인 것을 확인할 수 있습니다. 또한 Nginx의 설정도 올바르게 변경된 것을 확인할 수 있습니다.
참고
'Backend > 프로젝트' 카테고리의 다른 글
Elasticsearch 활용한 검색서비스 만들기 2편(feat. Spring Boot) (0) | 2025.03.12 |
---|---|
Elasticsearch 활용한 검색 서비스 만들기 1편 (Docker로 Elasticsearch + Kibana 구축) (0) | 2025.03.10 |
[Spring] 커버링 인덱스를 통한 페이징 성능 개선하기 (0) | 2024.12.12 |
[Spring] Redis 캐시 적용하기 (0) | 2024.10.31 |
nGrinder Docker 설치 및 사용방법 (0) | 2024.10.25 |