안녕하세요, Backend Dev 1팀의 Fitz입니다. 이 글에서는 QA 인력의 리소스를 낭비하지 않으면서 인프라 개선 사항을 편하게 테스트할 수 있도록 트래픽을 미러링 하는 Mirror Server를 개발한 경험을 공유합니다.

배경

Azar 프로덕트는 배포 전에 Test 환경에서 통합 QA를 거쳐서 서비스에 배포하고 있습니다.

Azar는 계속 성장하는 프로덕트이기 때문에 인프라 개선도 지속적으로 이루어지고 있습니다. 그리고 이러한 인프라 개선에 대한 QA도 Test 환경에서 진행하고 있습니다.

하지만 인프라를 변경하는 경우 인프라의 특성상 개선된 코드에 문제가 있다면 대부분의 코드에 영향을 미치게 되고, 통합 QA가 진행되고 있는 Test 서버에 영향을 끼치게 됩니다.

그래서 다른 Feature들의 QA까지 영향을 끼치게 되어 문제가 발생한다면 어떤 변경의 영향인지 알아보기 힘들고, 심한 경우는 서버에 장애가 발생하여 앱 동작에 영향을 끼치게 되어 전체 QA를 지연시키는 상황이 발생합니다.

이렇게 통합 QA를 진행하는 Test 서버가 장애에 매우 민감한 구조이기 때문에 개발자의 인프라를 개선하려는 의지를 소극적으로 만들어버릴 수 있어 이를 개선해보고자 마음먹었습니다.

아이디어 구상 및 검증

이리저리 고민해본 결과 Test 환경 서버에 들어오고 있는 QA팀의 엄격한 검수과정에서 생성되는 고품질의 트래픽을 재활용할 수 있으면 어떨까? 라는 생각을 하게 되었습니다.

트래픽은 복사해서 받으면서도, 사용자와 데이터에는 영향을 끼치지 않는 미러링 서버를 만들고, 이곳에 인프라 개선사항을 배포해서 테스트한다면 장애가 발생해도 상관없는 테스트를 진행할 수 있어 많은 재미있는 것들을 실험해볼 수 있다는 생각이 들었습니다.

마침 Azar 프로덕트는 Kubernetes 환경을 사용하고 있었기 때문에 트래픽에 대한 제어가 용이할 거라는 생각이 들었고, Devops팀에 찾아가 상의하였습니다.

그리고 Istio mirroring을 통하여 구현할 수 있다는 것을 확인하였고 작업을 시작하게 되었습니다.

목표

개발의 큰 방향성이 정해졌으므로 다음과 같은 세부 목표를 설정했습니다.

(1) 타겟 서버의 트래픽을 똑같이 받을 수 있어야합니다.

Test API 서버로 들어오는 HTTP traffic을 실시간으로 복제하여 이번에 개발해야 하는 mirror API 서버로 보내야 하며, 동일한 트래픽이 유입 된다는 것을 보장받아야 합니다.


(2) 기존의 테스트 서버에 영향을 주어서는 “절대” 안됩니다.

이 작업을 시작한 이유 중에 하나는 모든 QA가 공통으로 진행되는 Test 서버의 장애를 최소화하기 위함도 있습니다.

만약 트래픽을 복제 받는 새로운 Mirror 서버가 기존의 Test 환경에 영향을 끼친다면 큰 혼란을 초래할 수 있습니다.


(3) 기존의 소스코드에 영향을 주어서는 안 됩니다.

기존의 소스코드에 Mirror 환경에 종속적인 코드가 들어간다면 꽤 많은 상황을 분기 처리해야 하기 때문에 유지보수성이 급격하게 떨어집니다.

예를 들면 특정 기능에 Mirror 환경일 때에만 다른 동작을 하는 코드가 들어간다면 그 기능에 추가나 변경이 일어날 때마다 양 쪽 환경에 대한 고려가 되어야합니다. 그리고 코드에 실행 환경과 관련된 코드가 들어간다면 해당 환경에 종속되어버려 다른 환경에서 코드를 실행시킬 때에 문제가 발생할 가능성이 있습니다.

그러므로 Mirror 환경에 종속적인 코드는 완벽하게 기존 코드와 독립적이어야 합니다. 그리고 독립적으로 돌아간다는 점으로 인해 재사용성이라는 다른 이점도 얻을 수 있습니다.


(4) 동일한 요청에 대해 테스트서버와 미러 서버의 동작이 동일해야 합니다

Mirror 서버에만 적용된 변경사항이 있다면 해당 부분은 양쪽 서버의 동작이 다를 수 있지만, 이외의 요청들은 동일하게 동작함을 보장해야 합니다. 그래야 요청에 대한 결과 비교를 통해 적용된 변경 사항의 영향을 쉽게 파악할 수 있기 때문입니다.

동일한 요청에 대해 동일한 동작을 보장하기 위해서는 사용되는 데이터가 동일해야합니다. 예를 들면 테스트서버에 id가 38인 유저에 대한 요청이 들어와서 처리가 되었지만, Mirror 환경의 DB에는 이 유저가 없다면 해당 요청은 동일하게 처리되지 않고 유저가 존재하지 않는다는 에러가 발생하게 됩니다

하지만 동일한 데이터를 사용하기 위해 양쪽 어플리케이션에서 같은 데이터베이스를 사용하게 된다면 데이터를 중복으로 Insert하거나 Update하는 등의 문제가 발생할 수 있기 때문에 (2) 기존의 테스트 서버에 영향을 주어서는 “절대” 안된다라는 목적에 어긋납니다.

이를 막기 위해 DB의 경우에는 read-only 로 사용할 수 있는 환경을 만들어주기로 했고, Azar는 마이크로서비스 아키텍처를 사용하기 때문에 DB 외에도 여러 마이크로서비스 호출에 대해서도 비슷한 문제를 막을수 있도록 작업해야 했습니다.

위의 목적을 달성하기 위해 기술적으로 요구사항을 풀어나간 이야기는 앞으로 이어질 2편과 3편에서 자세히 설명합니다.

Mirror 서버의 활용

Mirror 서버 완성 후, 내부 리팩토링이나 인프라 작업 등의 장애가 발생할 확률이 있다고 생각되는 변경사항은 해당 브랜치를 mirror 브랜치에 merge 시키는 것으로 미리 Mirror 환경에서 테스트 해볼 수 있게 되었습니다. 이를 통해 관련 장애로 인해 다른 작업들의 QA 절차가 지연되거나 중단되는 리스크를 최소할 수 있었습니다.

Mirror 환경에 유입되는 요청 트래픽들은 뛰어난 QA 팀에서 다양한 상황을 고려해서 작성한 체계적인 테스트 케이스를 기반으로 하기 때문에 개발자 개인이 직접 기능을 점검하는 것보다 훨씬 효율적으로 많은 부분에 대한 검증을 할 수 있으며, 배포시 예상치 못한 문제가 생길 수 있는 부분을 미리 확인 할 수 있습니다.

그리고 두 환경에서의 동작의 차이를 더 쉽게 볼 수 있도록 Test 환경과 Mirror 환경에 각각 Pinpoint APM을 달아서 두 환경의 요청들을 모니터링하고 있습니다.

아래의 이미지는 Pinpoint의 화면입니다. 전체적인 에러 개수와, Mirror의 에러 개수가 다른 것을 확인할 수 있습니다.

.

아래의 그림처럼 Mirror 환경을 통해 1차적으로 문제가 없는지 검증한 다음, Test 환경에 배포하기 때문에 더 세밀한 테스트를 거치고 서비스에 배포될 수 있게 되었고, 인프라 개선뿐이 아니라 기능적인 변경사항도 테스트해볼 수 있습니다.

그리고 Mirror server는 장애가 발생하더라도 다른 곳에 영향을 끼치지 않기 때문에 대규모의 변경이 수반되는 작업이라도 부담 없이 개선을 시도할 수 있는 발판을 마련하게 되었습니다.

.

이어지는 2편과 3편에서는 이 Mirror server를 구현한 내용을 devops의 관점과 개발자의 관점에서 각각 자세하게 설명하겠습니다.