안녕하세요. 주간SaaS 입니다. 오늘 소개하는 글은 Uber 엔지니어링 블로그의 과거 글 가운데 Why We Leverage Multi-tenancy in Uber’s Microservice Architecture 라는 제목의 글을 소개 합니다. Uber는 큰 규모의 복잡한 마이크로 서비스 아키텍처 구조이며 여기에 새로운 코드 변경을 안정적이고 빠르게 배포하기 위해 멀티테넌시 개념을 적용했다고 합니다. 가령 테스트 단위를 하나의 테넌트 객체로 보고 이에 대한 격리, 테넌시를 보장해 안정적이고 빠른 배포 및 소프트웨어 운영이 가능했다고 합니다.
저희가 SaaS에서 말하는 멀티테넌시와는 달라 보일 수 있지만 내용을 보시면 SaaS 아키텍처에 멀티테넌시를 주입하는 패턴과 유사함을 알 수 있습니다. Uber가 선택한 접근 방법을 따라 고객을 마이크로 서비스 단위로 테넌시를 부여해 서비스 운영하거나, 특정 tier에 대한 premium 서비스를 마이크로 서비스로 분리해 격리 하는 것도 고려해볼 수 있습니다. 모쪼록 SaaS 아키텍처에 관한 좋은 힌트를 주는 내용이라 생각 됩니다. 그럼 좋은 하루 되십쇼!
Uber 서비스의 성능은 구성 서비스들이 우리 기술 스택의 어느 위치에 있든 관계없이 플랫폼에서 새로운 기능을 빠르고 안정적으로 출시할 수 있는 능력에 달려 있습니다. 플랫폼 성능의 근간은 애플리케이션간에 상호 작용 구조를 갖는 마이크로서비스 기반 아키텍처입니다.
안정적인 배포와 모듈화를 지원할 수 있는 마이크로서비스 아키텍처는 확장성이 뛰어난 것으로 잘 알려져 있습니다. Uber의 다양한 엔지니어링 팀이 상호간에 연결된 서비스를 운용하고 있기 때문에 새로운 변경 사항을 안전하게 배포하고 아키텍처의 일부를 모듈 방식으로 안정적으로 재사용할 수 있는지 확인하는 것이 중요합니다. 이를 통해 개발 속도가 빨라지고 릴리스 처리 시간이 단축되어 서비스 수준 계약(SLA)을 충족하면서 독립적인 일정에 따라 유연하게 빌드할 수 있습니다.
이러한 안정성과 모듈성을 촉진하는 가장 효과적인 방법 중 하나는 멀티테넌시라고 하는 개념을 통해 마이크로서비스 아키텍처에서 여러 시스템이 공존할 수 있도록 허용하는 것입니다. 테스트, 카나리아 릴리스, 섀도 시스템, 심지어 서비스 티어 또는 제품 라인까지 테넌트 개념을 적용해 사용하면 코드 격리를 보장하고 트래픽 테넌트를 기반으로 라우팅 결정을 내릴 수 있습니다. 전송 중인 데이터(예: 메시징 대기열의 요청 또는 메시지)와 미사용 데이터(예: 스토리지 또는 영구 캐시) 모두에 대한 테넌시를 통해 격리 및 공정성을 보장하고 테넌시 기반 라우팅 기회를 제공할 수 있습니다. 멀티 테넌시를 사용하면 향상된 통합 테스트 프레임워크, Shadow 트래픽 라우팅, 트래픽 기록 및 재생, 실험을 위한 실시간 트래픽의 숨김 재생, 용량 계획, 현실적인 성능 예측, 여러 제품 라인을 한 번에 실행하는 등 간단한 마이크로서비스 스택에서 다양한 기능을 구현할 수 있습니다.
이러한 멀티 테넌시의 이점을 종합하면, 보다 유연하고 확장 가능한 마이크로서비스 아키텍처를 통해 생산성을 높이고 애플리케이션 성능을 개선하여 엔지니어와 플랫폼 사용자 모두에게 이점을 제공합니다.
마이크로서비스 환경
마이크로서비스 아키텍처를 사용하면 팀이 다른 서비스와 독립적으로 서비스에 대한 새로운 기능과 버그 수정을 배포할 수 있으므로 개발 속도를 높일 수 있습니다. 예를 들어, 한 팀이 합의된 SLA를 가진 4개의 서비스(시스템 1이라고 함께 지칭)를 소유하고 있으며, 각자의 SLA를 가진 여러 다른 서비스와 정기적으로 상호 작용한다고 가정해 보세요.
아래 그림 1에서는 팀의 4개의 마이크로 서비스인 서비스 A, B, C, D가 상호 작용하는 방식을 보여줍니다. 이 다이어그램에서 서비스 A는 시스템 2로부터 요청을 받습니다. 시스템 1은 서비스 B에 연결하여 요청을 처리하고, 서비스 B는 다시 서비스 C 및 서비스 D에 연결합니다.
이 예에서 서비스 B를 변경해도 서비스 A, C, D와 여전히 잘 상호 운용되는지 확인해야 합니다. 마이크로서비스 아키텍처에서는 이러한 통합 테스트 시나리오를 수행하여 서비스의 시스템 내 다른 서비스와의 상호 작용을 테스트해야 합니다. 일반적으로 마이크로서비스 아키텍처에 대한 통합 테스트를 수행하는 기본적인 방법에는 병렬 테스트와 프로덕션 테스트의 두 가지가 있습니다.
병렬 테스트
첫 번째 접근 방식인 병렬 테스트는 프로덕션 스택처럼 보이고 느껴지지만 테스트 트래픽을 처리하는 데만 사용되는 스테이징 환경을 만들어야 합니다. 아래 그림 2에서 볼 수 있듯이 이 스택은 프로덕션 스택과 분리되어 있으며 소규모로 운영되는 경우가 많지만 항상 라이브 상태의 프로덕션 코드를 실행합니다:
병렬 테스트 시, 프로덕션 서비스에 대한 변경 사항을 구현하는 엔지니어링 팀은 먼저 테스트 스택에 새 코드가 포함된 서비스를 배포합니다. 이 접근 방식을 사용하면 개발자가 프로덕션에 영향을 주지 않고 모든 서비스를 안정적으로 테스트할 수 있으므로 릴리스 전에 버그 및 기타 문제를 더 쉽게 식별하고 억제할 수 있습니다.
병렬 테스트는 테스트 트래픽이 프로덕션 스택으로 유출되지 않도록 해야 하며, 이는 두 스택을 별도의 네트워크로 물리적으로 분리하고 테스트 도구가 테스트 스택에서만 작동하도록 함으로써 달성할 수 있습니다.
병렬 테스트는 매우 효율적인 통합 테스트 수단이 될 수 있지만, 마이크로서비스 아키텍처의 성공에 영향을 미칠 수 있는 몇 가지 과제를 안고 있습니다:
- 추가 하드웨어 비용: 모든 데이터 저장소, 메시지 대기열 및 기타 인프라 구성 요소와 함께 테스트를 위해 전체 스택을 프로비저닝해야 한다는 것은 추가적인 하드웨어 및 유지 관리 비용이 발생한다는 것을 의미합니다.
- 동기화 문제: 테스트 스택은 해당 프로덕션 스택과 동일한 경우에만 유용합니다. 두 스택이 서로 다르면 테스트 스택이 프로덕션 스택을 미러링하기가 점점 더 어려워지고, 스택을 동기화하기 위해 인프라 구성 요소에 추가적인 부담이 발생합니다.
- 신뢰할 수 없는 테스트: 팀이 실험적이고 버그가 있을 수 있는 코드를 테스트 스택에 배포할 때 해당 서비스가 제대로 작동하지 않을 수 있으며, 이로 인해 테스트가 자주 실패하게 됩니다. 예를 들어, 서비스 A를 소유한 팀이 새 코드가 작동하는지 확인하기 위해 병렬 테스트를 트리거했지만 서비스 B의 버그로 인해 테스트가 실패했다고 가정해 보겠습니다. 프로덕션 빌드와 완전히 다른 빌드에서 테스트하기 때문에 버그가 어디에 있는지 진단하기 어려울 뿐 아니라 테스트가 전체 흐름을 통과할 때까지는 서비스 A에 적용된 변경 사항이 안전한지 알 수 없으므로 서비스 B를 소유한 팀이 깨끗한 코드를 테스트 스택에 다시 배포할 때까지 테스트 진행이 중단 됩니다. 테스트 중인 서비스가 인스턴스화되는 또 다른 샌드박스 환경으로 트래픽을 라우팅하는 라우팅 프레임워크를 사용하면 이러한 단점을 완화할 수 있습니다.
- 부정확한 용량 테스트: 전체 스택 또는 하위 스택의 용량을 평가하려면 테스트 스택에 테스트 부하를 주입해야 합니다. 특정 용량을 테스트하려면 테스트 스택의 용량을 늘려야만 현재 프로덕션 부하보다 목표 용량의 증가를 의미하는 델타 부하를 테스트 스택에 적용할 수 있습니다. 이 델타 부하로 인해 테스트 스택이 포화 상태가 되지 않을 수 있으므로 목표 용량을 달성하기 위해 프로덕션 스택에 용량을 얼마나 더 추가해야 하는지 불분명해질 수 있습니다.
프로덕션 환경에서의 테스트
마이크로서비스 아키텍처에서 통합 테스트에 대한 또 다른 접근 방식은 현재 프로덕션 스택을 멀티테넌트로 만들고 테스트 트래픽과 프로덕션 트래픽이 모두 이 스택을 통과하도록 허용하는 것입니다. 아래 그림 3은 이러한 예 중 하나를 보여줍니다:
이 다소 야심찬 접근 방식은 스택의 모든 서비스가 테스트 요청과 함께 프로덕션 요청을 동시에 처리할 수 있는지 확인해야 한다는 것을 의미합니다.
이 접근 방식에서는 서비스 B를 테스트하기 때문에 프로덕션 서비스 C와 D에 액세스할 수 있는 격리된 샌드박스 영역에서 테스트 빌드를 인스턴스화합니다. 프로덕션 트래픽은 프로덕션 인스턴스를 통해 평소처럼 흐르고 테스트 트래픽은 테스트 빌드인 서비스 B'로 라우팅합니다. 서비스 B'는 테스트 트래픽만 서비스하고 프로덕션 트래픽은 서비스하지 않습니다. 또한 프로덕션 트래픽이 테스트 트래픽에 의해 어떤 식으로든 영향을 받지 않도록 하는 것이 중요합니다.
이것은 단순화된 예시 이지만 멀티 테넌시가 통합 테스트를 해결하는 데 어떻게 도움이 되는지 설명하는 데 도움이 됩니다. 프로덕션 테스트에서 나타나는 두 가지 기본 요구 사항이 있으며, 이는 멀티 테넌트 아키텍처의 기초를 형성하기도 합니다:
- 트래픽 라우팅: 스택을 통해 흐르는 트래픽의 종류에 따라 트래픽을 라우팅할 수 있어야 합니다.
- 격리: 테스트와 프로덕션 간에 리소스를 안정적으로 격리하여 비즈니스 크리티컬 마이크로서비스에 부작용을 일으키지 않도록 하는 것입니다.
여기서 격리 요건은 구성, 로그, 메트릭, 스토리지(비공개 또는 공개), 메시지 대기열 등 가능한 모든 미사용 데이터를 격리해야 하므로 특히 광범위합니다. 이 격리 요구 사항은 테스트 중인 서비스뿐만 아니라 전체 스택에도 적용됩니다.
멀티테넌시는 단계적 배포 및 스택의 트래픽 재생과 같은 통합 테스트 이외의 다른 사용 사례를 위한 기반을 마련합니다.
카나리아 배포
개발자가 서비스를 변경할 때, 변경 사항을 잘 검토하고 테스트했더라도 실행 중인 모든 서비스 인스턴스에 변경 사항을 한 번에 배포하고 싶지 않을 수 있습니다. 이는 변경 사항으로 인해 문제나 버그가 발생할 경우 전체 사용자 기반이 취약해지지 않도록 하기 위함입니다. 카나리아라고 하는 소규모 인스턴스 집합에 먼저 변경 사항을 롤아웃하는 것이 좋습니다. 그런 다음 피드백 루프를 통해 카나리아를 모니터링하고 코드 변경 사항을 점진적으로 광범위하게 배포합니다.
카나리아는 멀티테넌트 아키텍처에서 또 다른 테넌트로 취급될 수 있으며, 카나리아는 라우팅 결정을 내리는 속성 값이 됩니다. 카나리아를 사용하면 배포 중에 리소스가 격리됩니다. 특정 시점에 서비스에 모든 카나리아라는 테넌트의 트래픽이 라우팅되는 카나리아가 배포될 수 있습니다. 요청을 카나리아로 샘플링할지 여부는 사용자 유형, 제품 유형, 사용자 위치 등 요청 자체의 속성을 기반으로 아키텍처의 에지에 가깝게 결정할 수 있습니다.
캡처/재생 및 그림자(shadow) 트래픽
실제 프로덕션 트래픽을 전송하는 동안 서비스 요금 변경이 어떤 영향을 미치는지 확인할 수 있으면 변경 사항의 안전성을 확인하는 데 효과적인 수단입니다. 이전에 캡처한 라이브 트래픽을 재생하거나 안전한 환경에서 라이브 프로덕션 트래픽의 섀도 복사본을 재생하는 것도 멀티 테넌시의 또 다른 사용 사례입니다.
이 경우 테스트 중인 인스턴스가 수행하는 모든 아웃바운드 호출에 대한 응답을 미리 준비된 응답을 하도록 만듭니다(stub). 이러한 사용 사례는 테스트 및 실험 영역에 속하므로 프로덕션 트래픽 캡처 및 재생은 통합 테스트의 하위 범주로 취급할 수 있습니다.
리플레이 트래픽은 기술적으로 테스트 트래픽이며 테스트 테넌시의 일부가 될 수 있으므로 다른 테넌시로부터 격리할 수 있습니다. 리플레이 트래픽의 경우 별도의 테넌시를 할당하여 다른 테스트 트래픽과 더욱 격리할 수 있는 유연성이 있습니다.
이처럼 멀티 테넌트 아키텍처의 중요한 특징은 여러 비즈니스 크리티컬 제품 라인 또는 다양한 계층의 사용자 기반을 보호하고 격리할 수 있다는 점입니다.
테넌시 지향 아키텍처
테넌시 지향 마이크로서비스 아키텍처에서 테넌시는 일급 객체로 취급됩니다. 테넌시 개념은 사용 중인 데이터와 미사용 데이터 모두에 적용됩니다. 멀티테넌트 마이크로서비스 아키텍처를 만들려면 수신 요청에 컨텍스트를 첨부하고 요청의 수명 주기 동안 해당 컨텍스트를 전파하여 사용자가 해당 컨텍스트를 기반으로 요청을 라우팅할 수 있도록 해야 합니다.
테넌시 컨텍스트
마이크로서비스 아키텍처는 상호 연결된 네트워크에서 실행되는 서로 다른 서비스 그룹이므로 실행 시퀀스에 테넌시 컨텍스트를 첨부할 수 있는 기능이 필요합니다. 요청이 엣지 게이트웨이에 들어올 때 요청에 첨부할 수 있는 이 컨텍스트는 요청의 테넌시에 대해 알려줍니다. 이 컨텍스트가 요청의 수명 동안 요청과 함께 유지되고 동일한 비즈니스 로직 컨텍스트에서 생성되는 모든 새 요청에 전파되어 요청 시퀀스에 대한 테넌시를 유지하기를 원합니다.
다음은 간단한 테넌시 컨텍스트 형식과 몇 가지 예시입니다:
컨텍스트 전파
일반적으로 콜 체인의 모든 서비스가 요청을 받으면 해당 서비스가 테넌시 컨텍스트를 비즈니스 로직의 일부로 활용할 수 있으므로 테넌시 컨텍스트를 함께 사용할 수 있기를 원합니다. 그러나 서비스가 동일한 수신 요청을 처리하는 과정에서 추가 요청을 할 때 컨텍스트를 추가로 전파해야 합니다.
대부분의 서비스는 테넌시 컨텍스트를 볼 필요가 없지만, 일부 서비스는 일부 비즈니스 로직을 우회하기 위해 요청 컨텍스트를 평가할 수 있습니다. 예를 들어, 사용자의 전화번호를 확인하는 감사 서비스는 테스트 요청에 관련된 사용자가 테스트 사용자이므로 테스트 트래픽에 대한 확인을 우회할 수 있습니다. 또한 은행 게이트웨이와 대화하는 트랜잭션 처리 서비스를 통해 테스트 트래픽을 실행하여 사용자의 자금을 이체하는 경우, 은행 게이트웨이를 차단하거나 은행의 스테이징 게이트웨이(테스트에 사용할 수 있는 경우)와 대화하여 실제 자금 이체를 방지할 수 있습니다. 테넌시 컨텍스트 전파는 언어와 전송 방식에 구애받지 않고 분산된 컨텍스트 전파를 가능하게 하는 OpenTracing 및 Jaeger와 같은 오픈 소스 도구를 사용하여 달성할 수 있습니다.
테넌시 컨텍스트는 Apache Kafka 메시징 대기열의 메시지와 같은 전송 중인 데이터 객체에도 전파되어야 합니다. 최신 버전의 Kafka는 헤더 추가를 지원하며 오픈 소스 추적 도구를 사용하여 메시지에 컨텍스트를 추가할 수 있습니다.
또한 테넌시 컨텍스트가 서비스에서 영구 데이터를 저장하는 데 사용하는 모든 데이터 스토리지 시스템(예: MySQL, Apache Cassandra, AWS)을 포함하여 저장된 데이터로 전파되기를 원할 수도 있습니다. Redis 및 Memcached와 같은 분산 캐시도 미사용 데이터로 분류할 수 있습니다. 아키텍처에서 활용하는 모든 스토리지 시스템과 캐시는 테넌시 컨텍스트에 따라 데이터를 검색하고 저장할 수 있도록 데이터와 함께 컨텍스트 저장을 합리적인 세부 수준으로 지원해야 합니다. 높은 수준에서 볼 때, 미사용 데이터 구성 요소의 유일한 요구 사항은 테넌시를 기반으로 데이터와 트래픽을 격리할 수 있는 기능입니다.
데이터가 격리되는 정확한 방법과 테넌시 컨텍스트가 데이터와 함께 저장되는 방법은 스토리지 시스템에 따라 구현 세부 사항이 달라집니다.
테넌시 기반 라우팅
요청에 테넌시를 태그할 수 있게 되면 테넌시를 기반으로 요청을 라우팅할 수 있습니다. 이러한 라우팅은 프로덕션 테스트, 레코드/재생, 섀도 트래픽에 매우 중요합니다. 또한 카나리아를 배포하려면 카나리아 요청을 격리된 카나리아 환경에서 실행되는 특정 서비스 인스턴스로 라우팅해야 합니다.
오버헤드 없이 원활하게 작동하는 라우팅 솔루션을 식별할 때는 배포 및 서비스 기술 스택을 고려하는 것이 중요합니다. 차량 전체 라우팅 솔루션을 선택할 때는 서비스가 작성되는 언어와 서비스가 서로 통신하는 데 사용하는 전송 및 인코딩을 고려해야 할 수 있습니다. Envoy 또는 Istio와 같은 오픈 소스 서비스 메시 도구는 서비스 언어와 사용되는 전송 또는 인코딩에 관계없이 작동하는 테넌시 기반 라우팅을 제공하는 데에도 매우 적합합니다.
일반적으로 테넌시 기반 라우팅은 서비스의 송신 또는 수신에서 구현할 수 있습니다. 송신 시에는 서비스 검색 계층이 요청의 테넌시에 따라 어떤 서비스 인스턴스와 통신할지 결정하는 데 도움을 줄 수 있습니다. 또는 아래 그림 5와 같이 인그레스에서 라우팅 결정을 내린 다음 요청을 올바른 인스턴스로 재라우팅할 수 있습니다:
그림 5. 위의 예제에서 프로덕션 서비스 Ap에서 테스트 인스턴스 A1으로 테스트 트래픽을 전송하는 것처럼, 서비스에 대한 인그레스에서 테넌시 라우팅을 적용할 수 있습니다.
그림 5에 표시된 예에서 요청 테넌시가 테스트인 경우 사이드카를 사용하여 요청을 테스트 인스턴스로 전달할 수 있습니다. 사이드카는 서비스에 들어오는 모든 트래픽의 프록시 역할을 하는 프로세스가 될 수 있으며 서비스와 함께 배치됩니다. 트래픽은 먼저 서비스의 사이드카에서 수신되며, 여기서 요청의 테넌시 컨텍스트를 검사하고 컨텍스트에 따라 라우팅 결정을 내릴 수 있습니다.
해결하고자 하는 사용 사례에 따라 테넌시 컨텍스트에 추가 메타데이터가 필요합니다. 예를 들어, 프로덕션 테스트의 경우 서비스가 테스트 중인 경우 테스트 트래픽을 서비스의 테스트 인스턴스로 리디렉션하고 싶습니다. 아래와 같이 이 동작을 허용하는 추가 정보를 컨텍스트에 추가할 수 있습니다:
라우팅 결정을 내릴 때 요청 테넌시가 테스트 테넌시인지, 요청 수신자가 서비스_언더_테스트 중 하나인지 확인할 수 있습니다. 이러한 조건이 충족되면 요청을 <test instance Id>로 라우팅합니다.
데이터 격리
우리는 모든 인프라 구성 요소가 테넌시를 이해하고 테넌시 라우팅을 기반으로 트래픽을 격리할 수 있는 아키텍처를 구축하여 플랫폼 내에서 메트릭 및 로그와 같은 다양한 마이크로서비스를 실행하기 위한 제어를 강화하고자 합니다. 마이크로서비스 아키텍처에 사용되는 일반적인 인프라 구성 요소로는 로깅, 메트릭, 스토리지, 메시지 대기열, 캐시, 구성 등이 있습니다. 테넌시를 기반으로 데이터를 격리하려면 인프라 구성 요소를 개별적으로 처리해야 합니다. 예를 들어, 서비스에서 생성되는 모든 로그와 메트릭의 일부로 테넌시 컨텍스트를 방출하기 시작할 수 있습니다. 이렇게 하면 개발자가 테넌시를 기반으로 필터링할 수 있으므로 잘못된 경고를 방지하거나 휴리스틱 또는 학습 데이터가 왜곡되는 것을 방지하는 데 도움이 될 수 있습니다.
마찬가지로 스토리지 서비스를 고려할 때는 테넌트 간의 격리를 효율적으로 생성하기 위해 기본 스토리지 아키텍처를 고려해야 합니다. 일부 스토리지 아키텍처는 멀티 테넌시를 쉽게 채택합니다. 두 가지 높은 수준의 접근 방식은 아래 그림 6과 같이 테넌시 개념을 데이터와 함께 명시적으로 포함시키고 서로 다른 테넌시를 가진 데이터를 함께 배치하거나 테넌시에 따라 데이터를 명시적으로 분리하는 것입니다:
후자의 접근 방식은 더 나은 격리를 보장하는 반면, 전자는 운영 오버헤드가 적은 경우가 많습니다. Kafka와 같은 메시징 대기열 시스템의 경우, 테넌시에 대한 새 토픽을 투명하게 롤아웃하거나 해당 테넌시를 위해 별도의 Kafka 클러스터를 완전히 전용으로 사용할 수 있습니다.
데이터 격리를 위해서는 컨텍스트가 인프라 구성 요소까지 전파되어야 합니다. 데이터 격리와 관련하여 서비스가 최소한의 오버헤드를 갖도록 하는 것이 중요합니다. 서비스가 테넌시를 명시적으로 처리하지 않는 것이 이상적입니다. 또한 격리 로직을 모든 데이터가 흐르는 중앙 초크 포인트에 배치하는 것이 이상적입니다. 게이트웨이는 격리 로직을 구현할 수 있는 이러한 초크 포인트 중 하나이며, 저희가 선호하는 접근 방식입니다. 클라이언트 라이브러리는 테넌시 기반 격리를 구현하는 또 다른 대안이 될 수 있지만, 코딩 언어의 다양성으로 인해 모든 언어별 클라이언트 라이브러리 간에 로직을 동기화하기가 조금 더 어렵습니다.
구성 격리의 경우에도 마찬가지로 서비스의 구성 데이터를 테넌시별로 분리하여 한 테넌시의 구성 변경이 다른 테넌시에 영향을 미치지 않도록 해야 합니다.
앞으로 나아가기
마이크로서비스 기반 아키텍처는 계속 발전하고 있으며 개발자와 조직 전반의 민첩성을 촉진하는 도구가 되고 있습니다. 신중하게 계획된 멀티테넌트 아키텍처는 개발자의 생산성을 높이고 진화하는 비즈니스 라인을 지원할 수 있습니다.
Uber는 멀티테넌시를 구현하여 코드 및 구성의 자동화된 롤아웃을 안전하게 수행하여 개발자 속도를 향상하는 등 다양한 이점을 얻었습니다. 멀티테넌트 아키텍처의 격리 보장 덕분에 Uber는 테스트 트래픽을 비롯한 다양한 목적으로 동일한 마이크로서비스 스택을 재활용할 수 있었습니다.
의견을 남겨주세요