마이크로서비스 아키텍처는 기본적으로 '해결하려는 문제'를 기준으로 서비스를 분리하는 아키텍처입니다. 소위 말하는 DDD(Domain Driven Design; 도메인 주도 설계)에서 사용하는 도메인이라는 개념과 비슷한 기준으로 서비스를 분리한다고 볼 수도 있습니다.
이렇게만 이야기하면 잘 와닿지 않으니 예시를 하나 들어보겠습니다.
앞서서 해결하려는 문제를 기준으로 각 서비스를 분리한다고 이야기 드렸습니다. 쇼핑몰 앱이 있다고 한다면, 이 쇼핑몰을 운영하기 위해선 계정을 관리해야 한다는 문제가 있고, 결제도 관리 해야하고, 주문도 관리를 해야 합니다.
기존의 모놀리틱 아키텍처(Monolith Architecture)에서는 하나의 프로젝트, 하나의 웹 서버에서 이걸 다 처리했다면, 마이크로서비스 아키텍처에서는 이것들을 다 별도의 마이크로서비스로 분리해서 개발하고 운영한다는 의미가 됩니다.
그리고 마이크로서비스 아키텍처에서 조금 더 상세히 구조를 살펴보게 된다면, 아래 보시는 구조가 될 것 같습니다.
마이크로서비스란, 서비스를 해결하려는 문제를 기준으로 세분화 하고, 서비스 간 통신은 네트워크 호출을 통해 진행하여 확장 가능하고 회복적이며 유연한 애플리케이션을 구성하는 것
마이크로서비스 아키텍처는 결국 각각의 서비스들이 쪼개져서 관리가 되구요, 각각의 서비스들에 대한 호출은 API를 이용하게 됩니다. REST API가 될 수도 있고 gRPC 같은걸 사용할 수도 있겠죠.
그리고 사용자의 웹 브라우저와 앱에는 각각의 서비스를 직접 노출하지 않고, API Gateway를 통해서 통신을 하게 됩니다. 이 API Gateway에서는 사용자의 인증 관련된 처리라던가 API에 대한 정책적인 부분을 검증하는 역할도 함께 수행합니다.
데이터베이스도 모놀리틱 아키텍처와는 달리 서비스별로 데이터베이스를 독립적으로 가져가는 구조가 되게 됩니다.
마이크로서비스 아키텍처를 이용한다면, 위 그림에서 payment 마이크로서비스에 장애가 발생하더라도 다른 서버와 마이크로서비스들은 모두 정상이기 때문에 사용자는 로그인과 회원가입을 여전히 할 수 있고, 상품을 둘러보는 행동도 할 수 있게 됩니다. 단지 결제를 수행할 때만 현재는 결제 요청을 처리할 수 없다는 메시지를 보여주는 것만으로도 대처를 할 수 있겠죠.
얼핏 봐도 신경 써야 하는 게 많아 보이죠? 이런 마이크로서비스 아키텍처를 구성하기 위한 기술에는 정말 다양한 것들이 있는데요, 그 중에서 필수적인 것에 가깝고 꼭 고려해봐야 하는 것들을 소개해보겠습니다.
Component in MSA
보시는 것과 같이 마이크로서비스 아키텍처를 구성하신다 했을 때는 아무래도 마이크로서비스들이 잘게 쪼개져 있는 구조이기 때문에, 흩어진 서비스들을 통합 모니터링한다거나 환경 변수를 공통으로 관리한다던지 그런 부분들이 많이 존재하게 됩니다.
1. Config Management
Netflix Archaius, Kubernetes Configmap
첫 번째는 Config Management라고 해서, 마이크로서비스에서 어떤 환경 변수를 사용한다고 했을 때 그 변수의 값이 변경될 때마다 새롭게 빌드하고 배포하는 게 아니라 이런 환경 변수를 별도의 원격 저장소에 두고 값을 가져다 쓸 수 있는 환경을 제공해주는 구성요소입니다.
2. Service Discovery
Netflix Eureka, Kubernetes Service, Istio
두 번째로 중요한 것 중 하나로, Service Discovery입니다. 여러 마이크로서비스가 각각 분산되어 있고, 각 서비스가 다른 서비스를 호출할 때에는 그 서비스의 아이피 주소나 포트 번호 같은 걸 알아야 하는데, 호출하는 서비스 쪽에서 이를 구체적으로 모르더라도 서비스의 위치 정보를 관리하고 제공해주는 Service Discovery에 대한 고민도 필요합니다.
3. API Management
Netflix Zuul, Kubernetes Ingress
세 번째로는 앞서 잠깐 언급한 것처럼 마이크로서비스 자체를 외부에 오픈하지는 않습니다. 앞선 예시에서는 API Gateway라는 게 외부의 요청을 허용, 차단 그리고 중계하는 형태로 되어 있었는데 API Management가 이런 역할을 해준다고 보시면 될 것 같습니다.
4. Centralized Logging
ELK Stack
네 번째로는 중앙화 된 로깅입니다. 분산되어 있는 마이크로서비스 아키텍처에서 정말 중요한게 중앙화된 관리인데, 로그도 중앙에서 수집하고 이를 시각화해줄 수 있는 그런 요소가 필요합니다.
5. Distributed Tracing & 6. Centralized Monitoring
Distributed Tracing: Spring Cloud Sleuth, Zipkin
Centralized Monitoring: Netflix Spectator, Heapster
다섯 번째와 여섯 번째도 마찬가지로 각 서비스 간의 호출을 추적하고 더 나아가 로깅하고, 이를 모니터링하는 것들이 각 서비스가 분산되어 있더라도 잘 수행될 수 있도록 하는 요소도 중요합니다.
7. Resilience & Fault Tolerance
Netflix Hystrix, Resilience4j, Kubernetes Health check
일곱 번째 Resilience & Fault Tolerance는 뭐냐면, 예를 들어 서비스 A에서 서비스 B를 호출하는데 서비스 B가 어떤 이유로 응답하지 않는다고 하면 쓰레드가 대기 상태로 유지되게 될 것이고, 계속해서 많은 개수의 쓰레드가 대기 상태에 빠져 있다면 서비스 A 자체의 요청 처리 능력이 감소해서 사용자들은 "서비스가 느려졌다"라는 경험을 할 수도 있습니다.
MSA 환경에서는 이런 상황이 언제든 발생할 수 있습니다. 그래서 서비스 A와 서비스 B 사이에 서킷 브레이커라는 것들 둬서 서비스 B가 응답하지 않는다고 한다면 다른 메시지를 응답해서 특정 서비스의 장애가 다른 서비스로 전파되지 않도록 하는 구조를 지향 해야만 합니다.
8. Auto-Scaling & Self-Healing
마지막 여덟 번째는 마이크로서비스를 배포를 해뒀는데 갑자기 특정 서비스에 많은 요청이 들어온 겁니다. 그런데 이 요청의 양이 기존의 머신/인스턴스로는 제대로 처리할 수 없는 경우 처리 속도가 지연되고, 어떤 경우에는 아예 Network Time-out이 발생하는 상황이 될 수도 있습니다.
그래서 이런 문제가 될 수 있는 부분을 자동으로 감지해서 Scale-out을 통해 더 많은 요청을 처리할 수 있도록 환경을 재구성하는 것을 Auto-Scaling & Self-Healing이라고 부릅니다.
이 외에도 마이크로서비스 아키텍처를 구성하기 위해서 고려해야 하는 요소들이 정말 많은데요. 그러다 보니 이런 MSA는 도입하려는 시도 자체가 쉽지가 않습니다.
Don’t even consider microservices unless you have a system
that’s too complex to manage as a monolith. - By MARTIN FOWLER
실제로 디자인 패턴으로 유명하신 마틴 파울러라는 분의 말씀을 가져왔는데, 그분은 어지간히 복잡한 시스템이 아니고서는 모놀리틱 아키텍처가 더 맞다.라고 이야기하시기도 했습니다. 위 그래프에서 보시듯이 시스템의 복잡성이 꽤 높아야만 모놀리틱 아키텍처보다 마이크로서비스 아키텍처가 생산성이 더 높은 모습을 보실 수도 있습니다.
이런 것을 봤을 때 무작정 MSA가 좋다, 무조건 MSA를 해야 한다고 보기엔 어려움이 있을 것 같습니다.
더 많은 MSA와 관련된 이야기가 궁금하시다면 아래 두 책을 읽어보시는 것도 추천드립니다.
https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=103397768
https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=268179012
☕️ Networking
기술 직군의 기술적인 교류, 커리어 이야기, 직군 무관 네트워킹 모두 환영합니다!
위클리 아카데미 오픈 채팅방(비밀번호: 9323)
kakaotalk: https://open.kakao.com/o/gyvuT5Yd