무중단배포 전략과 롤백

공병주(Chris)·2023년 11월 6일
2
post-thumbnail

과거에 무중단 배포를 진행해보았습니다. 하지만, 당시에 많은 고민들을 하지 못했기에, 무중단 배포에 대해서 더 자세히 알아보고 고민해보려 합니다. 여러 무중단 배포의 글들을 서치해본 것들에 더해 저의 개인적인 생각들을 많이 적었으니 비판적으로 글을 읽으시면 좋을 것 같습니다. 비판도 감사히 환영입니다!

무중단 배포 전략

1. 블루그린(Blue-Green)

구버전 그룹과 신버전 그룹을 블루와 그린으로 구분하기 때문에 블루 그린 배포 방식입니다. 구버전과 동일한 환경의 인스턴스들에 신버전을 배포해두고 트래픽을 신버전 그룹으로 변경하는 방식입니다.

장점 1 - 빠른 롤백

일단 롤링에 비해, 빠르게 롤백할 수 있을 것이라고 생각합니다. 구버전 그룹을 종료시키지 않고 신버전으로 트래픽을 보내면서 신버전을 모니터링하고 에러가 많이 발생한다면 빠르게 구버전으로 트래픽을 다시 보내면 되기 때문입니다.

장점 2 - 균일한 서비스를 받는 사용자들

구버전 그룹이 처리하던 트래픽 100%가 모두 신버전 그룹에서 처리되기 때문에, 사용자는 동일한 서비스를 경험할 수 있습니다. 사실 개인적인 생각으로는, 사용자들이 무조건 동일한 응답을 받도록 하는 것이 중요하지 않다면 이것이 큰 장점일까 라는 생각은 듭니다. 오히려, 카나리 방식에서는 의도적으로 일부 트래픽에 대해 신버전으로 트래픽을 처리하기도 하니 말입니다.

단점 1 - 추가적인 인스턴스가 반드시 필요

가장 큰 단점인 것 같습니다. 만약, 구버전에서 10대의 인스턴스로 트래픽을 처리하고 있었다면 신버전 그룹도 동일하게 10대의 인스턴스가 필요합니다. 다른 글에서, 남아있는 구 버전의 인스턴스들을 다음 배포때 활용할 수 있다고 것이 장점이라고 하는데요. 저는 이게 왜 장점인지 모르겠습니다. 블루그린을 구현하기 위해 어쩔 수 없는 제약이 아닐까요? 만약, Cloud 환경이 아니라, 물리적인 환경에서 블루 그린을 통한 무중단배포를 구축해야한다면 인스턴스 10대로 트래픽을 처리할 수 있지만, 블루 그린 배포를 위해 인스턴스 10대를 추가적으로 관리해야합니다.

또한, 저는 과거에 무중단 배포를 진행할 당시에 추가적인 인스턴스를 구축하지 않고 이것을 포트를 통해 해결했습니다. 구버전이 8080포트라면 8081 포트에 신버전을 구축하고 8081 포트로 트래픽을 보내는 방식으로요.

그런데, 이건 사실 트래픽이 많지 않은 서비스에서만 가능하다고 생각합니다. 결국 두 개의 포트는 하나의 컴퓨팅 자원을 공유하기 때문이죠. 구버전에서 메모리를 많이 사용했다고 가정해봅시다. 신버전을 올리고 트래픽이 많이 들어와서 신버전이 사용하는 트래픽이 증가했다면 하드웨어 레벨에서의 메모리가 터져버릴 것입니다. 만약, 가상메모리를 사용해서 Disk와 Swap을 한다고 해도 결국 Swap으로 인한 오버헤드는 남게 되죠.

단점 2 - 모든 사용자가 한번에 에러를 응답받음

만약, 신버전에서 특정 에러가 발생했다고 가정해보겠습니다. 그렇다면, 트래픽 100%가 모두 신버전에 의해 처리되기 때문에 모든 사용자가 에러를 응답 받을 것입니다. 에러는 곧 서비스의 신뢰도와 직결될 수가 있다고 생각하는데요. 따라서, QA나 테스트를 높은 수준으로 진행하고 배포중에는 반드시 모니터링을 통해 언제든지 구버전 그룹으로 트래픽을 이전할 수 있어야 한다고 생각합니다.

주의점(?)

블루 그린의 장점으로 동일한 시간대에 다른 버전의 서비스가 운영되지 않는다고 흔히들 말합니다. 짧게 말해, 호환성 문제가 없다라는 것인데요.
블루 그린 방식으로 무중단배포를 해도, 구버전은 Graceful shutdown으로 요청이 처리중인 상태에서 신버전의 트래픽이 들어올 수 있습니다. 따라서, 호환성 문제가 완벽하게 해결되었다고는 할 수 없을 것 같습니다. 이 부분도 고려를 하시면 좋을 것 같습니다,

2. 카나리(Canary)

카나리라는 이름은 과거의 광부들이 가스에 예민한 카나리아 새를 활용해 가수 누출을 감지했다는 것에서 비롯되었습니다. 이름처럼, 카나리 배포는 특정 사용자 그룹 혹은 랜덤으로 신버전 응답을 받도록 하다가 신버전 응답을 받는 모수를 점점 넓혀나가는 방식입니다. 위 예시에서는 0-100, 10 - 90, 90-10, 100-0과 같은 방식인데요. 정도는 직접 조절 할 수 있습니다.

장점 1 - 신버전에 대해 적은 트래픽 할당을 통한 안정성

카나리의 의도대로, 가장 큰 장점은 원하는 만큼의 트래픽만 신버전으로 보낼 수 있다는 것입니다. 신버전 테스트를 위해 1%만의 트래픽을 신버전으로 보내고 모니터링을 진행할 수 있습니다. 에러가 발생한다면, 에러를 응답 받는 경우는 1%라는 것이 장점입니다. 아마, 신버전을 통한 에러를 최소화 할 수 있다는 것이 카나리의 가장 큰 장점이 아닐까 하는 생각이 듭니다

장점 2 - A/B 테스트

트래픽을 의도적으로 나눌 수 있기 때문에, 사용자 그룹들을 지정해서 신버전과 구버전의 지표 차이를 확인하는 등의 테스트가 가능합니다.

장점 3 - 빠르게 롤백

블루그린과 마찬가지로 빠르게 롤백할 수 있을 것이라 생각합니다. 카나리에서 에러가 발생한다면 빠르게 로드밸런서 설정을 변경해서 카나리 쪽으로의 트래픽을 구버전의 트래픽으로 돌려보낼 수 있다고 생각합니다.

단점 1 - 구현의 어려움

롤링과 블루그린에 비해 구현이 어려울 것으로 예상됩니다. 특정 사용자 그룹에 대해 신버전을 노출시킨다고 한다면, LB에서 사용자 그룹에 대한 식별을 통해 트래픽을 분배해줘야 할 것 같습니다. 이렇게 사용자에 따라 동일한 보전으로 트래픽을 보내주지 않는다면, 사용자는 특정 기능이 있었다가 사라지는 경험을 할 것입니다. 그리고, 트래픽 할당을 특정 값으로 부여해야하기 때문에, 가중치 설정에 대한 필요성도 있을 것입니다.

호환성 문제가 단점인가?

카나리 배포의 단점으로 호환성 문제를 이야기하는 글들이 있습니다. 그런데, 카라니 배포는 의도적으로 트래픽의 일부를 신버전으로 보내는 것이기 때문에 이것이 과연 단점인가에 대한 의문이 듭니다.

카나리에서 고려해볼만 한 인스턴스 마련

만약, 2대의 인스턴스가 라운드로빈 방식으로 트래픽을 처리하고 있고 각각의 CPU 사용률이 50%씩이라고 가정해보겠습니다. 그런데, 이 상황에서 카나리 배포를 위해 한 대의 인스턴스에 신버전을 배포하고 해당 버전의 트래픽을 10%로 지정한다면 어떻게 될까요? 다른 인스턴스는 90%의 트래픽을 감당해야합니다. 50%의 트래픽을 감당할 때, CPU 사용률이 50%였는데 90%의 트래픽을 감당해야한다면 CPU 사용률도 90% 정도로 증가할 것이며 서비스 장애로 이어질 수도 있다고 생각합니다.

따라서, 이런 경우에는 추가적인 인스턴스를 준비하고 해당 인스턴스에 신버전을 배포하고 적은 양의 트래픽을 보내야하지 않을까라는 생각이 듭니다.

2-1. Netflix의 카나리 배포(**Kayenta)**

넷플릭스 테크 블로그에 카라니를 자동으로 분석하는 툴에 대해 알게되었는데, 여기서 에러를 판단하기 위한 환경을 세팅하는 방식이 꽤 흥미로워서 공유해보려고 합니다.

https://netflixtechblog.com/automated-canary-analysis-at-netflix-with-kayenta-3260bc7acc69

여기서 트래픽을 Production(기존 운영 버전), Baseline(기존 운영 버전), Canary(신 버전)으로 분산합니다.

Production과 Canary는 어떤 것인지 바로 파악을 하실텐데, Baseline에 대한 궁금증이 생기실 겁니다.

Production과 Canary를 비교한다면 트래픽의 양이 다를뿐더러, 배포되어 실제로 트래픽을 처리한 시간도 다릅니다. 따라서, 보다 정확한 Canary 모니터링을 위해 Baseline을 Canary 배포 시점에 함께 구축하고 동일한 정도의 트래픽을 받도록 하는 것이죠. 결국, 모니터링 하게 되는 것은 Baseline과 Canary입니다. 이 둘의 차이를 보는 것이죠.

아래와 같은 지표를 통해 Canary와 Baseline의 차이를 확인할 수도 있습니다.

3. 롤링(Rolling)

롤링은 위처럼 인스턴스들의 배포 버전을 순차적으로 변경시키는 것입니다. 처음에 3대의 인스턴스가 트래픽을 받다가, 하나의 인스턴스로의 트래픽을 차단하고 해당 인스턴스에 새로운 버전 배포하고 트래픽을 연결하고, 다음 구버전의 인스턴스로의 트래픽을 차단하고 새로운 버전을 배포하고 트래픽을 연결하고와 같은 방식입니다.

장점 1 - 간편한 구현

일단, 추가적인 인스턴스가 필요하지 않습니다.(하지만, 필요한 상황도 있습니다. 이는 단점에서 살펴보겠습니다.)

또한, 직접 사용해보지 않아서 느껴보지는 못했지만, k8s, elastic beanstalk과 같은 기술에서는 롤링 배포 방식을 지원하여 간편하게 구현할 수 있다는게 장점입니다.

단점 1 - 트래픽

신버전을 배포하고 있는 인스턴스로는 트래픽을 줄 수 없습니다. Spring으로 예시를 들면, java 명령어를 통해 JVM을 구동시킬텐데, 그 과정에서는 트래픽을 줄 수 없다는 것입니다. 따라서, 나머지 인스턴스들이 모든 트래픽을 감당해야합니다.

하지만, 이 문제는 인스턴스를 추가함으로써 해결할 수 있을 것이라 생각합니다. 3개의 인스턴스가 운영중이라면 인스턴스 1대를 더 준비하고, 해당 인스턴스부터 신버전을 배포해나가면 될 것 같습니다. 그렇다면 결과적으로 4대의 인스턴스 중 1대는 배포중이고 3대는 신버전이든, 구버전이든 트래픽을 처리하고 있는 상태가 될 것입니다.

단점 2 - 늦은 롤백(추측)

문제가 발생했을 때, 롤백처리가 늦다는게 단점인 것 같습니다. 만약, 10대의 인스턴스를 롤링 방식으로 업데이트 한다고 가정해보겠습니다. 9번째 인스턴스까지 신버전 배포를 완료하고 10번째 인스턴스를 배포하는 중에, 롤백 결정이 난다면 어떻게 될까요? 신버전이 배포된 인스턴스들을 순차적으로 다운그레이드 시켜야 할 것입니다. 순차적으로 롤백이 되는만큼 에러가 발생하는 신버전이 트래픽을 처리하는 시간들이 길 것입니다. 이것은 서버 인스턴스가 많을 수록, 그리고 Application이 구동되는 시간이 오래 걸릴 수록, 사용자가 에러를 응답 받는 경험이 많아질 것이라 생각합니다.

그렇다고, 신버전을 가진 인스턴스들을 모두 한번에 롤백시킬 수도 없을 것입니다. 그렇다면 구버전을 가진 인스턴스들이 모든 트래픽을 감당해야하기 때문입니다. 그렇게되면, (신버전에서 구버전으로 롤백중 + 구버전이 모든 트래픽을 감당해 구버전 인스턴스들도 다운)으로 인해 서비스가 중단될 수도 있을 것 같다는 생각이 듭니다.

단점 3 - 균일한 서비스를 받지 못하는 사용자들

신버전이 순차적으로 배포되기 때문에, 특정 시간대에 구버전이 처리한 응답을 받는 사용자와 신버전이 처리한 응답을 받는 사용자가 공존합니다.

예를 하나 들어보겠습니다. 사용자에게 선착순 쿠폰을 발급하는 새로운 기능을 가진 버전이 배포된다고 가정하겠습니다. 로드밸런싱에 의해, 신버전의 응답을 받은 사용자가 있으니 구버전의 응답을 받는 사용자는 형평성(?)에 어긋나는 서비스를 제공받을 수 있습니다. 물론 이런 이벤트는 롤링 방식에 의존적이게 운영하지 않을 것입니다. 이해를 돕기 위한 예시정도로만 생각하시면 좋을 것 같습니다.

무중단 배포 롤백

무중단으로 배포하는 것이 중요한 만큼 롤백을 어떻게 처리할지도 매우 중요합니다. 핵심은 모든 무중단배포에는 신버전을 배포하고 구버전으로 향하던 트래픽을 신버전으로 향하게 하는 로드밸런싱이 관여한다는 것입니다.

1. 모니터링

먼저, 신버전에서 에러가 발생했다는 것을 인지하고 롤백 여부를 판단해야 합니다. 따라서, 신버전 배포와 동시에 모니터링을 통해 판단해야한다고 생각합니다. 예를 들어, 프로메테우스와 그라파나를 사용할 수 있을 것입니다. 또한, 로그를 모니터링하는 툴을 이용해서 에러가 발생했을 시에, 어떤 에러가 발생하는지 빠르게 식별할 수 있어야 할 것입니다.

저는 에러가 발생했을시에, 해당 에러를 자동으로 인지하고 롤백 시킬 수 있을 것이라고 생각했는데요. 해당 에러가 새로운 버전에서 발생하는 에러인지, 얼마나 발생하면 롤백시킬지 등에 대한 것을 자동화시키는게 쉽지 않을 것으로 생각했고, 에러에 대한 식별과 인지는 수동으로 해야하지 않을까 라고 생각합니다.

2. 롤링 롤백

먼저, 롤링 롤백입니다. 롤링은 신버전이 동작하고 있는 인스턴스들을 다시 구버전으로 배포시켜야 할 것입니다. 쿠버네티스를 사용하신다면 명령어 1개로 이전 버전으로 롤백을 할 수 있다고 합니다. 하지만, 과정에서 신버전이 구동되고 있는 인스턴스에 구버전을 배포하는 과정을 인스턴스 단위로 차례대로 진행할 것이기에 다른 롤백 방식들에 비해 오랜 시간이 걸리지 않을까라는 생각이 듭니다.

3. 블루 그린 롤백

블루그린 방식을 사용한다면, 신버전을 배포하고 트래픽을 신버전쪽으로 향하게 한 후에 모니터링 하고 있을 것입니다. 만약, 롤백을 해야한다고 판단한다면 로드밸런서의 설정을 신버전에서 구버전으로 빠르게 바꾸면 될 것 같습니다.

저는 Nginx를 로드밸런서로 사용해서 블루 그린 무중단 배포를 사용했었는데요. 신버전으로 바꿀 때, Nginx의 proxy_pass 설정을 변경함으로써 트래픽을 신버전으로 보내주었습니다.

또한, 롤백을 해야한다고 판단했을 때, 빠르게 롤백하기 위해 구버전의 인스턴스들과 Application은 중지하지 않고 롤백을 위한 준비를 시켜두는 것이 좋아보입니다.

4. 카나리 롤백

카나리로 신버전을 배포하고 1%의 트래픽만을 보낸다고 가정해보겠습니다. 이 역시도, 카나리 쪽으로 1%의 트래픽을 보내던 로드밸런서의 설정에 대해 원래의 인스턴스로 모두 100%로 보내게 함으로써 롤백할 수 있을 것입니다.

카나리 방식으로 100% 트래픽을 신버전으로 완료하고 에러가 발생해 롤백해야 한다면?

충분히 모니터링 했다고 판단했지만, 이후에 에러가 발생할 수도 있을 것입니다. 해당 상황에서는 당장 준비되어있는 구버전의 인스턴스도 없을 것입니다. 이런 경우를 대비해서, 버전 단위로 스냅샷을 찍어놓으면 어떨까 하는 생각입니다. 신버전이 100% 배포된 상황에서는 이전의 스냅샷으로 인스턴스들을 구동시키고 빠르게 구버전으로 트래픽을 돌려보낼 수 있을것이라 생각합니다. 이런 상황에서의 롤백은 블루그린과 같은 방식이 될 것 같습니다.

그래서 어떤 배포 방식이 좋을까?

물론, 배포 방식은 서비스의 상황, 인프라적인 요소에 사용할 수 있는 비용, 모니터링의 필요성 등등에 따라 다를 것입니다.
그런데, 개인적으로 가장 괜찮다고 생각하는 방식은 카나리 방식이라고 생각합니다.

  • 블루그린에 비해 추가적인 인스턴스도 덜 필요합니다.
  • 롤백 상황에서도 카나리로의 트래픽만 차단하도록 로드밸런서의 설정을 바꿔주면 됩니다.
  • 처음에 적은 트래픽만을 카나리로 보내도록 설정하면, 에러를 경험하는 사용자의 수를 줄일 수 있다고 생각합니다.

하지만, 특정 사용자의 요청이 카나리에 의해 처리가 된다면, 지속적으로 동일한 버전을 제공해줘야 하기 때문에 해당 사용자의 요청은 지속적으로 카나리에 의해 처리해야하는 구현적인 어려움이 있을 것입니다. 하지만, 구현의 어려움을 감수할만큼 장점들이 충분하다고 생각합니다.

0개의 댓글