◆ MSA 구성 요소 및 MSA 패턴
▶ Micro Service 운영과 관리를 위한 플랫폼 패턴
● MSA 기술 변화 흐름
⊙ 앞에서 언급한 패턴들은 모두 모노리스 시스템이 여러 조각의 Micro Service로 나눠져서 발생하는 문제들을 해결하는데 추적, 모니터링, 로깅, 인증, 탐색, 유연성, 탄력성 등이 여기에 해당
⊙ 이러한 문제를 해결하기 위해 초기 MSA 생태계에서는 넷플릭스 OSS나 스프링 클라우드를 이용해 각각의 서비스를 별도로 만들어서 해결하거나 유연성처럼 수 평 확장이 필요한 요소는 AWS laaS 서비스를 이용해 해결했는데 문제마다 상이한 기술로 해결할 수 밖에 없었음
⊙ 이후 여러 문제의 해결책을 한꺼번에 제공하는 솔루션들이 등장했는데, 바로 쿠버네티스 나 오픈시프트(OpenShift) 같은 제품으로 기존에 넷플릭스 OSS 기반의 여러 서비스로 처리했던 문제들을 묶어서 하나의 쿠버네티스 또는 오픈시프트로 해결할 수 있게 됨
⊙ 특히 인프라 유연성을 보장하기 위해 AWS laaS의 인프라 차원에서 해결했던 역할을 쿠버네티스가 소프트웨어 차원, 즉 컨테이너의 레플리카 기술로 탐색, 호출 문제와 함께 통합해서 지원하면서 쿠버네티스가 각광받고 있음다.
⊙ 최근 동향은 쿠버네티스에 덧붙여 이스티오 기술이 함께 사용되고 있음
● 서비스 매시 패턴
⊙ 초창기 MSA 기술인 넷플릭스 OSS나 스프링 클라우드 기반의 서비스를 구축 및 운용할 때의 문제점은 API 게이트웨이, 서비스 레지스트리. 컨피그 서비스와 같이 운영 관리를 위한 여러 개 의 기반 서비스를 별도로 각각 만들어야 한다는 번거로움과 더불어 업무 처리 Micro Service에 스프링 클라우드 서비스를 사용하기 위한 라이브러리를 비즈니스 로직과 함께 탑재해야 한다는 점이었음
⊙ 기능 구현에 집중해야 하는 Micro Service 입장에서 이러한 코드까지 관리 및 운영해야 한다면 번거로울 수밖에 없음
⊙ 스프링 클라우드는 자바 기반이기 때문에 Micro Service가 자바 외의 다른 언어로 폴리글랏하게 구현된 경우에는 스프링 클라우드 서비스를 아예 사용할 수조차 없음
⊙ 최근에는 MSA 문제 영역 해결을 위한 기능(서비스 탐색, 서킷 브레이크, 추적, 로드 밸런싱 등)을 비즈니스 로직과 분리해서 네트워크 인프라 계층에서 수행하게 하는 서비스 메시(service mesh) 패턴이 선호되고 있음
⊙ 서비스 메시는 인프라 레이어로서 서비스 간의 통신을 처리하며 앞에서 언급한 여러 문제 해결 패턴을 포괄
⊙ 돛단배 모양의 아이콘이 서비스 메시 패턴의 대표적 구현체인 구글의 이스티오(Istio)를 나타냄
⊙ 이스티오는 애플리케이션이 배포되는 컨테이너에 완전히 격리되어 별도의 컨테이너로 배포되는 사이드카(Sidecar) 패턴을 적용해서 서비스 디스커버리, 라우팅, 로드 밸런싱, 로깅, 모니터링, 보안, 트레이싱 등의 기능을 제공
⊙ 기본적으로 이스티오는 쿠버네티스에 탑재되어 이러한 서비스 메시 기능을 지원하며 스프링 클라우드와 넷플릭스 OSS를 이용한 경우에는 스프링 클라우드로 각 서비스를 먼저 구축하고 Micro Service 애플리케이션 자체도 코드 내부에 스프링 클라우드 사용을 위한 클라이언트 코드가 탑재돼야 하지만 서비스 메시를 적용하는 경우에는 오른쪽 그림처럼 Micro Service마다 함께 배포되는 사이드카 프락시에 운영 관리를 위한 기능이 별도로 담겨있기 때문에 Micro Service는 순수 비즈니스 로직에 집중할 수 있음
⊙ 컨트롤 플레인(Control Plain) 기능에 의해 중앙에서 통제되며 사이드카끼리 통 신해서 관련 운영 관리 기능을 제공하는 모습을 보여주는데 이를 통해 Micro Service의 비즈니스 로직과는 완벽하게 독립적으로 운영
⊙ 쿠버네티스의 컨테이너 단위인 파드(pod)에 서비스 컨테이너와 사이드카 구현체인 엔보이(Envoy) 컨테이너가 함께 배포된 것을 볼 수 있음
⊙ 이스티오는 다음과 같이 스프링 클라우드와 넷플릭스 OSS에서 제공했던 대부분의 기능을 모두 제공하면서 동시에 차별점도 있음
→ 주요기능
∵ 트래픽 관리(Traffic Management): 동적 라우팅. 로드 밸런싱
∵ 보안: 보안 통신 채널(TLS), 인증/인가/암호화
∵ 관측성(Observability): 메트릭, 분산 트레이싱, 로깅
→ 스프링 클라우드 및 넷플릭스 OSS와의 차별점
∵ 애플리케이션 코드의 변경이 거의 없는데 스프링 클라우드나 넷플릭스 OSS 기반은 비즈니스 로직과 함께 코드로 표현돼야 하지만 이스티오는 완전히 사이드카로 격리되며 yaml 파일과 같은 설정 파일에 의해 정의됨
∵ 폴리글랏 애플리케이션도 지원하는데 스프링 클라우드나 넷플릭스 기반은 자바 언어만 지원하나 이스티오는 각 Micro Service를 다른 언어(자바, Node.js, C#)로 작성한 경우에도 지원 가능
∵ 이스티오는 쿠버네티스와 완벽하게 통합된 환경을 지원
⊙ 쿠버네티스와 이스티오는 최근에 나온 기술이지만 모든 상황에 항상 적합하고 옳은 것은 아니며 각 문제 영역을 정확히 이해하고 시스템 상황에 맞게 적절한 기술을 적용하는 것이 중요
▶애플리케이션 패턴
● 모노리스 Front End
⊙ 여러 API를 호출하고 조합한 후 화면으로 구성해서 보여주는 방식으로 이 경우 고민은 Front End가 한 덩어리일 경우 과연 Micro Service 기반 시스템의 장점인 서비스의 독립적인 변경과 배포가 가능하겠느냐는 것
⊙ 하나의 업무 기능은 보통 Front End와 Back End의 연계로 구현
⊙ 업무 기능 하나가 변경되어 재배포해야 할 상황을 생각해 보면 Back End는 수정해서 하나의 서비스로 독립적으로 배포 가능하지만 Front End는 하나의 덩어리이기 때문에 변경되지 않은 다른 기능들도 함께 빌드되고 배포해야 하기 때문에 이전의 Back End가 모노리스였을 때 겪었던 문제(독립적인 기능 변경 및 배포 불가, 독립적인 기능 확장 불가)를 Front End의 모노리스 서비스도 동일하게 겪을 수밖에 없는 것
● UI 컴포지트 패턴 또는 마이크로 Front End
⊙ Front End도 Back End Micro Service처럼 기능별로 분리하고 이를 조합하기 위한 프레임(frame) 형태의 부모 창을 통해 각 Front End를 조합해서 동작하게 작성
⊙ 부모 서비스는 틀만 가지고 있고 실제 각 기능 표현은 마이크로 Front End 조각이 구현하게 하는 방식으로 마이크로 Front End들은 비즈니스 구현을 위해 여 러 개의 Back End Micro Service API를 호출
⊙ 고객 서비스 팀은 별도의 독립된 소스 리포지토리에 Back End Micro Service와 마이크로 프런트를 관리하고 이를 독립적으로 빌드 및 배포할 수 있기 때문에 자율적으로 서비스를 개선할 수 있음
⊙ 아마존의 메인 화면은 여러 개의 조각으로 구성 돼 있고 각 조각은 여러 개의 마이크로 Front End의 조합으로 서비스를 제공
⊙ 하나의 기능을 변경했을 때 이를 제공하는 마이크로 Front End 와 Back End를 구성하는 Micro Service가 모두 변경되고 배포되는데 배포 시 잠시 반응이 없는 경우나 일부 Micro Service에 장애가 발생한 경우에도 메인 화면을 제공하는 부모 창은 사용자가 알아채지 못하게 화면 구성을 재배열하는 역할까지 수행
⊙ 아마존닷컴의 서비스 개선 속도는 이 같은 유연한 UI 구성을 통해서도 보장
● Micro Service 통신 패턴
⊙ 동기 통신 방식
→ 동기 호출 방식은 클라이언트에서 서버 측에 존재하는 Micro Service REST API를 호출할 때 사용되는 기본 통신 방법이며 다양한 클라이언트 채널 연계나 라우팅 및 로드 밸런싱을 원활하게 하기 위한 방법으로 중간에 API 게이트웨이를 둘 수 있음
→ 웹이나 앱에서 API 게이트웨이를 통해 상품, 추천, 리뷰 Micro Service를 동기 호출하는 구성
→ Back End Micro Service 간의 호출에서도 REST API 같은 동기식 호출을 사용할 수 있는데 모바일 UI 고객의 주문 내역을 확인하기 위해 주문 서비스에 HTTP GET 방식의 요청을 보내면 주문 서비스는 고객 정보를 확인하기 위해 고객 서비스에 GET 방식의 동기 호출을 수행하고 그에 따라 바로 응답이 발생하고 성공 시 200 OK라는 성공 코드를 받아오도록 만들 수 있는데 이처럼 요청하면 응답이 오는 직관적인 방식이기 때문에 가장 많이 쓰이고 구현하기 쉽지만 호출을 받은 Micro Service에 장애가 생긴다면 요청을 보낸 서비스는 반응이 올 때까지 기다리게 되고. 반응이 오지 않으면 계속 기다리면서 재호출하게 됨
⊙ 비동기 통신 방식
→ 메시지 기반의 비동기(asynchronous) 호출 방식은 동기 호출처럼 응답을 기다리지 않음
→ 메시지를 보낸 다음 응답을 기다리지 않고 다음 일을 처리하는데 보낸 결과가 어떻게 됐는지 응답을 받지 않으므로 동기식처럼 완결성을 보장할 수는 없기 때문에 이를 보장하기 위한 메커니즘이 필요한데 보통 아파치 카프카{Apache Kafka), 래빗엠큐(RabbitMQ), 액티브엠큐(ActiveMQ) 같은 메시지 브로커(message broker)를 활용
→ 이러한 메커니즘에서는 메시지를 보내는 생산자(producer)와 메시지를 가져다가 처리하는 소비자(consumer)가 서로 직접 접속하지 않고 메시지 브로커에 연결됨
→ 메시지 브로커에 메시지를 전달하고 자신의 일을 처리하면 메시지 브로커가 전송을 보장하게 되는데 여러 서비스에서 전달한 메시지를 처리하는 메시지 브로커 자체에 부하가 생길 수가 있으므로 이 경우 메시지 브로커는 메시지 처리 규모에 따라 확장이 가능해야 함
→ 이 방식은 메시지 브로커에 의해 중계되기 때문에 서로 통신하는 서비스들이 물리적으로 동일한 시스템에 위치할 필요도 없고 서로 프로세스를 공유할 필요도 없으며 심지어 동일한 시간 대에 동시에 동작하지 않아도 되기 때문에 서비스 요구에 따라 늘어나거나 줄어들 수 있는 탄력성이 높은 클라우드 플랫폼 환경에서 서비스가 다운됐을 때 또는 시스템을 더 확장해야 할 때 사용할 수 있는 매우 효과적인 방법
→ 앞클라우드 벤더에서 완전관리형으로 제공하는 AWS의 SQS나 SNS, Azure Event Hub, Azure Event Grid 등도 많이 사용
⊙ 비동기 방식의 이벤트 기반 아키텍쳐
→ 비동기 통신 방식을 이용해 느슨한 연계를 지향하는 아키텍처가 있는데 이벤트에 반응한다는 의미로 이벤트 기반 아키텍처(event driven architecture)라고 부르는데 이벤트 기반 아키텍처는 예전부터 사용된 개념으로 분산 시스템 간에 발신자가 이벤트를 생성 및 발행(publish)하고 해당 이벤트를 필요로 하는 수신자에게 전송하면 이벤트를 구독하고(subscribe) 있던 수신자가 이벤트를 받아 처리하는 형태의 시스템 아키텍처
→ 이벤트는 상태의 변화를 의미하며 기존의 순차적 방식의 아키텍처와 달리 특정 행동이 자동으로 순서에 따라 발생하는 것이 아닌 어떤 상태의 변경에 대한 반응으로 동작한다는 점이 차이점
→ 이러한 방식은 일반 실생활에서도 작업의 효율성을 위해 이미 많이 사용하는 방식
→ 커피숍에서 손님이 주문을 하면 주문 접수자는 주문을 받고 바리스타에게 커피 제작을 의뢰하는데 이때 주문 접수자는 하나의 주문이 바리스타에 의해 완료될 때까지 마냥 주문을 받지 않고 기다리지는 않는데 주문 접수 - 커피 제작 - 고객 전달이라는 하나의 무결하고 완결된 단위로 다루지 않음
→ 대신 주문이 들어오는 대로 꾸준히 주문 목록에 적고 동시에 커피 주문이 들어왔다는 이벤트를 바리스타에게 계속 전달하면 바리스타도 마찬가지로 커피 주문 이벤트를 순차적으로 제작 목록에 기입하고 주문에 해당하는 커피를 만들고 커피 제작이 완료되면 커피 제작 완료 이벤트를 보내고 이것이 진동벨을 통해 손님에게 통보되는 것
→ 주문은 많이 들어오는데 바리스타가 부족하다면 추가로 더 투입될 수도 있음
→ 이벤트 기반 방식은 여러 개의 주문을 받아 여러 개의 커피를 동시에 제작할 수 있는 효율성을 높이는 반면 병렬 처리를 하지 않고 하나의 커피 주문을 받고 커피가 제작될 때까지 다른 주문을 접수할 수 없다면 매우 비효율적일 것
→ 이벤트 기반 아키텍처는 이벤트를 생산하는 모듈과 이벤트에 대응하는 모듈을 분리하고 상호 독립적으로 동작하게 함으로써 병렬 처리를 촉진
→ 이벤트 기반 아키텍처의 전달 메커니즘으로 앞에서 논의한 비동기 메시지 메커니즘을 선택하면 더욱 더 효과적
→ 이벤트 기반의 아키텍처와 비동기 통신 메커니즘을 함께 사용하는 Micro Service를 이벤트 기반 Micro Service(event-driven microservice)라고도 함
→ 이벤트 메시지를 사용하면 발신자와 수신자를 장소와 시간에서 쉽게 분리할 수 있으며 Micro Service가 추구하는 느슨한 결합으로 확장성, 탄력성 측면에서 이점이 많음
● 저장소 분리 패턴
⊙ 기존 모노리스 시스템의 저장소는 통합 저장소로 애플리케이션 모듈은 분리하되 저장 처리는 모듈 별로 격리하지 않고 다른 모듈에서의 호출을 허용하는 구조
⊙ 국내 엔터프라이즈 애플리케이션의 내부를 보면 모든 비즈니스 로직이 데이터베이스의 SQL 처리에 몰려있는 경우가 대부분인데 단순한 조사 방법이긴 하지만 소스코드 라인 수와 데이터 처리를 수행하는 SQL 구문의 라인 수를 세어봐도 알 수 있음
⊙ 업무 규칙 및 흐름 처리를 수행하는 애플리케이션의 코드 라인 수보다 SQL 코드의 라인 수가 몇 배 이상
⊙ 이러한 구조를 데이터 중심 애플리케이션이라 하는데 특정 관계형 데이터베이스 벤더에 구속되고 복잡해져 유지보수가 어려워지고 성능 문제가 발생했을 때 SQL 구문 튜닝이나 저장소 증설(스케일 업)에 의존할 수 밖에 없음
⊙ 이러한 구조의 애플리케이션은 아무리 여러 개의 Micro Service로 분리하더라도 요청이 증가할 경우 서비스는 한가하고 여러 서비스에서 호출되는 통합 데이터베이스만 여전히 바쁜 상황이 되어 Micro Service의 자동 확장(스케일 아웃) 기능이 별 소용이 없어질 수 있음
⊙ 저장소 분리 패턴은 각 Micro Service는 각자의 비즈니스를 처리하기 위한 데이터를 직접 소유해야 한다는 것을 의미하기 때문에 자신이 소유한 데이터는 다른 서비스에 직접 노출하지 않고 각자가 공개한 API를 통해서만 접근할 수 있으며(정보 은닉) 저장소가 격리돼 있기 때문에 각 저장소를 자율적으로 선택할 수 있는데(폴리글랏 저장소) 궁극적으로 이 같은 제약이 데이터를 통한 변경의 파급 효과(영향도)를 줄여 서비스를 독립적으로 만듬
⊙ 주문 서비스가 주문 수행을 위해 고객 정보를 필요로 하더라도 바로 고객 테이블에 질의할 수 없고 반드시 고객 서비스의 API를 통해서만 호출할 수 있음
⊙ Micro Service별로 기능을 분리하고 저장소를 격리함에 따라 이전에는 불거지 지 않았던 문제가 생기는데 여러 개의 분산된 서비스에 걸쳐 비즈니스 처리를 수행해야 하는 경우 비즈니스 정합성 및 데이터 일관성을 어떻게 보장할 것인가에 대한 문제
● 분산 트랜잭션 처리 패턴
⊙ 손쉽게 적용할 수 있는 한 가지 방법은 여러 개의 분산된 서비스를 하나의 일관된 트랜잭션으로 묶는 것
⊙ 분산 트랜잭션 처리에서는 여러 서비스 간의 비즈니스 및 데이터 일관성을 유지할 필요가 있는데 분산 트랜잭션 처리를 위한 전통적인 방법으로 2단계 커밋 같은 기법이 있음
⊙ 2단계 커밋은 분산 데이터베이스 환경에서 원자성(atomicity)을 보장하기 위해 분산 트랜잭션에 포함돼 있는 모든 노드가 커밋(commit)되거나 롤백(rollback)하는 메커니즘인데 각 서비스에 잠금(lock in)이 걸려 발생하는 성능 문제 탓에 효율적인 방법이 아닌데 특히 각 서비스가 다른 인스턴스로 로딩되기 때문에 통제하기 어렵고 서비스의 저장소가 각각 다를 경우 문제가 있으며 특히 MongoDB 같은 NoSQL 저장소는 2단계 커밋 자체를 지원하지 않고 클라우드의 가장 큰 장애는 네트워크 장애인 경우가 많은데 네트워크 장애 등으로 특정 서비스의 트랜잭션이 처리되지 않을 경우 트랜잭션에 묶인 서비스가 즉시 영향을 받기도 함
⊙ 2단계 커밋을 통한 분산 트랜잭션 처리는 독립적이지 않고 비자율적
⊙ Micro Service의 독립적인 분산 트랜잭션 처리를 지원하는 패턴이 사가(Saga) 패턴
⊙ 사가 패턴은 각 서비스의 로컬 트랜잭션을 순차적으로 처리하는 패턴
⊙ 사가 패턴은 여러 개의 분산된 서비스를 하나의 트랜잭션으로 묶지 않고 각 로컬 트랜잭션과 보상 트랜잭션을 이용해 비즈니스 및 데이터의 정합성을 맞추는데 각 로컬 트랜잭션은 자신의 데이터베이스를 업데이트한 다음 사가 내에 있는 다음 로컬 트랜잭션을 트리거하는 메시지 또는 이벤트를 게시해서 데이터의 일관성을 맞추는 방식
⊙ 롤백이 필요하다면 보상 트랜잭션의 개념을 이용
⊙ 보상 트랜잭션은 어떤 서비스에서 트랜잭션 처리에 실패할 경우 그 서비스의 앞선 다른 서비스에서 처리된 트랜잭션을 되돌리게 하는 트랜잭션
⊙ 사가는 일관성 유지가 필요한 트랜잭션을 모두 묶어 하나의 트랜잭션으로 처리하지 않고 각 로컬 트랜잭션으로 분리해서 순차적으로 처리하는 방법으로 트랜잭션이 실패한 경우 이전 로컬 트랜잭션이 작성한 변경 사항을 취소하는 일련의 보상 트랜잭션을 통해 비즈니스 처리의 일관성을 유지
⊙ 주문을 처리할 때 고객의 신용 한도 정보에 따라 최종 주문을 승인하는 업무가 있는데 이 두 서비스의 트랜잭션을 하나로 묶지 않고 보상 트랜잭션과 이벤트를 활용해 처리할 수 있음
⊙ 주문을 처리할 때 고객의 신용 한도 정보에 따라 최종 주문을 승인하는 업무가 있는데 이 두 서비스의 트랜잭션을 하나로 묶지 않고 보상 트랜잭션과 이벤트를 활용해 처리할 수 있음
→ 주문 처리가 시작되면 주문 서비스는 가주문을 생성하고 주문자 정보가 담긴 주문 생성됨 이벤트를 발행하고 트랜잭션을 종료
→ 고객 서비스가 주문 생성됨 이벤트를 확인한 뒤 다음 처리를 수행
∵ 이벤트에 존재하는 주문자 정보로 고객의 신용한도를 조회해서 신용한도가 충족되면 신용 승인됨 이벤트를 발행
∵ 신용한도가 충족되지 않는다면 신용한도 초과됨 이벤트를 발행
→ 주문 서비스는 고객 서비스가 발행한 이벤트를 확인해 다음 처리를 수행
∵ 고객 서비스가 발행한 이벤트가 신용 승인됨 인 경우에는 주문 승인 처리를 수행
∵ 신용한도 초과됨 이벤트인 경우에는 보상 트랜잭션인 주문 처리 취소를 수행
→ 이처럼 하나의 큰 트랜잭션으로 묶지 않고 4개의 분리된 로컬 트랜잭션으로 비즈니스의 정합성을 맞출 수 있음
댓글