무중단 배포

June·2022년 10월 17일
1

속닥속닥

목록 보기
4/7
post-thumbnail

학습 배경

이번 스프린트 요구 사항에 무중단 배포가 있었다. 실제로 예전에 별 생각 없이 저녁 시간에 배포를 하다가 '속닥속닥 왜 지금 안돼요?'라는 말을 들은적이 있어서 이번 무중단 배포에 관심이 갔다.

무중단 배포

새로운 기능이 추가되거나 버그가 수정되어 배포를 할 시 서비스가 잠깐 멈추는 경우가 생긴다. 아예 서비스 점검 중이다라고 공지를 걸어놓고 새벽에 배포를 할 수도 있지만 24시간 서비스가 가능해야하는 경우 이런 방식은 불편하다.

블루-그린

이미지 출처

이전 버전을 배포하고 있는 그룹을 블루라 하고, 새로운 버전을 배포할 그룹을 그린이라고 하자. 새로운 버전을 배포하고 한꺼번에 새로운 서버들로 연결을 하는 것이다. 만약 새로운 버전에 문제가 있다면 빨리 롤백하여 예전 버전으로 대응이 가능하다. 또 한번에 일괄적으로 바뀌기 때문에 예전 버전과 새버전이 섞여 있는 경우가 없다는 장점이 있다. 다만 미리 여분의 시스템 자원을 준비해야 하기 떄문에 비용이 두배로 많이 든다.

롤링 배포

애플리케이션이 실행중인 서버가 여러대 있을 때를 가정한 것이다. 서버 중 하나에 배포하고, 그 동안은 나머지 서버들이 요청을 처리한다. 이런 방식으로 하나씩 바꾸기 때문에 평소보다 적은 서버로 요청을 처리해야하는 단점이 있다. 또 배포가 한대씩 되기 때문에 구버전과 신버전이 공존하는 순간이 있다.

카나리 배포

특정 서버 또는 사용자들에게만 신버전을 배포하고, 문제가 없으면 전체를 배포하는 방식이다. 미리 새로운 기능을 배포함으로서 A/B테스트도 가능하다고 한다. A/B 테스트 특강 때 들은바에 의하면 법적으로 이렇게 서로 다른 버전이 배포되었을 때 혜택 같은 것에서 차별이 있으면 안된다고 한다. (새로운 버전을 쓰는 사람들에게 할인 쿠폰을 주는 등).

프로젝트 적용

속닥속닥 서비스의 인프라 구조였다. Nginx가 로드밸런싱 역할도 하고 있을 때 어떻게 무중단 배포를 해야할지 생각을 많이 했다. 블로그들을 보면서 자료를 찾아봤는데 많은 자료들이 AWS와 스프링부트로 혼자 구현하는 웹서비스를 참고했는데 우리는 경우가 조금 달랐다. 그래서 조금 완벽하지 않고 투박하더라도 우리 방식대로 해보기로 했다.

우리는 블루-그린 배포 방식을 사용하기로 했다. 큰 문제가 당장은 발생하지 않겠지만 새로운 버전과 옛날 버전이 공존할 때 문제가 생기는 경우가 있을 수도 있다라고 생각했다.

각 WAS에서 8081 포트를 이용해서 서버를 띄우도록 준비해놨다. 8080 포트를 이용하는 서버는 블루 그룹이 되는 것이고, 8081을 이용하는 서버는 그린 그룹이 되는 것이다.

stage('PROD-DEPLOY') {
    ...
    steps {
        script {
            withCredentials([sshUserPrivateKey(credentialsId: "back-key", keyFileVariable: 'my_private_key_file')]) {
                sh '''#!/bin/bash
                    RESPONSE_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://{WAS1 IP}:8080/actuator/health)
                    cd backend/sokdak/build/libs
                    if [ $RESPONSE_CODE -ne 200 ]
                    then
                        echo '############## 8080 LOAD START ##############'
                        scp -o StrictHostKeyChecking=no -i ${my_private_key_file} *.jar ubuntu@{WAS1 IP}:/home/ubuntu/sokdak
                        scp -o StrictHostKeyChecking=no -i ${my_private_key_file} *.jar ubuntu@{WAS2 IP}:/home/ubuntu/sokdak
                        ssh -o StrictHostKeyChecking=no -i ${my_private_key_file} ubuntu@{WAS1 IP} 'cd sokdak && ./deploy-8080.sh\'
                        ssh -o StrictHostKeyChecking=no -i ${my_private_key_file} ubuntu@{WAS2 IP} 'cd sokdak && ./deploy-8080.sh\'
                                
                        for var in {1..100} // 8080 배포가 끝난 후 정상 작동하기를 기다린다. 정상 작동하면 기존의 블루 그룹은 종료한다. 
                        do
                            sleep 1
                            HEALTH_CHECK_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://{WAS1 IP}:8080/actuator/health)
                            if [ $HEALTH_CHECK_CODE -eq 200 ]
                            then
                                ssh -o StrictHostKeyChecking=no -i ${my_private_key_file} ubuntu@{Nginx IP} 'cd /etc/nginx/sites-enabled && sudo rm * && cd ~/zero-down-confs && sudo cp sokdak-8080.conf /etc/nginx/sites-enabled/ && nginx -s reload && sudo kill -9 $(lsof -ti tcp:8081)\'
                                ssh -o StrictHostKeyChecking=no -i ${my_private_key_file} ubuntu@{WAS1 IP} 'sudo kill -9 $(lsof -ti tcp:8081)'
                                ssh -o StrictHostKeyChecking=no -i ${my_private_key_file} ubuntu@{WAS2 IP} 'sudo kill -9 $(lsof -ti tcp:8081)'
                                echo 'SWITCHING FROM 8081 TO 8080 SUCCESS'
                                break
                            fi
                        done
                        
                        echo '############## 8080 LOAD COMPLETE ##############'
                        
                    else
                        echo '############## 8081 LOAD START ##############'
                        scp -o StrictHostKeyChecking=no -i ${my_private_key_file} *.jar ubuntu@{WAS1 IP}:/home/ubuntu/sokdak
                        scp -o StrictHostKeyChecking=no -i ${my_private_key_file} *.jar ubuntu@{WAS2 IP}:/home/ubuntu/sokdak
                        ssh -o StrictHostKeyChecking=no -i ${my_private_key_file} ubuntu@{WAS1 IP} 'cd sokdak && ./deploy-8081.sh\'
                        ssh -o StrictHostKeyChecking=no -i ${my_private_key_file} ubuntu@{WAS2 IP} 'cd sokdak && ./deploy-8081.sh\'
                        
                        for var in {1..100}
                        do
                            sleep 1
                            HEALTH_CHECK_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://{WAS1 IP}:8081/actuator/health)
                            if [ $HEALTH_CHECK_CODE -eq 200 ]
                            then
                                ssh -o StrictHostKeyChecking=no -i ${my_private_key_file} ubuntu@{Nginx IP} 'cd /etc/nginx/sites-enabled && sudo rm * && cd ~/zero-down-confs && sudo cp sokdak-8081.conf /etc/nginx/sites-enabled/ && nginx -s reload\'
                                ssh -o StrictHostKeyChecking=no -i ${my_private_key_file} ubuntu@{WAS1 IP} 'sudo kill -9 $(lsof -ti tcp:8080)'
                                ssh -o StrictHostKeyChecking=no -i ${my_private_key_file} ubuntu@{WAS2 IP} 'sudo kill -9 $(lsof -ti tcp:8080)'
                                echo 'SWITCHING FROM 8080 TO 8081 SUCCESS'
                                break
                            fi
                        done
                        
                        echo '############## 8081 LOAD COMPLETE ##############'
                    fi
                '''
                sh "echo '########### BACK-END DEV COMPLETE ###########'"
            }
        }
    }
}

스크립트가 복잡해보이지만 흐름은 간단하다. 각 WAS에 8080포트로 health check를 하고, 정상적으로 떠있다면 8081 포트에 배포를 하면된다. 그리고 100초 가량 반복문을 돌면서 8081이 정상적으로 뜨고 나면 8080 포트를 죽이면 된다. 반대로 8081 포트에서 현재 코드가 돌고 있다면 8080에 이 방식을 쓰면 된다.

한계

현재 팀에서 만든 무중단 배포가 완벽한 무중단 배포가 아니다. 왜냐면 nginx 설정 값이 바뀌므로 바뀐 설정 값을 적용하기 위해 nginx를 restart 하는 순간이 있기 때문이다. 이 과정이 1초도 걸리지 않지만 그 시간에 요청을 날린 사람이 있다면 그 요청을 올바르게 처리하지 못할 것이기 때문이다. 이건 어떻게 개선할 수 있을지 좀 더 고민을 해봐야한다.

승팡이 블로그를 보고 restart가 아닌 reload가 있다고 알려주었습니다. 감사합니다 승팡팡!

사실 제대로 하려면 한 EC2에서 포트로 나누는 것이 아니라 EC2를 따로 띄우고 배포를 해야할 것 같다. 또 블루-그린의 장점을 제대로 누리기 위해서는 롤백 하는 스크립트도 작성해야 한다.

참고

https://www.samsungsds.com/kr/insights/1256264_4627.html

0개의 댓글