Open MinJunKweon opened 2 years ago
새 인스턴스를 추가하려면 수동으로 로드밸런서를 구성해야한다
=> 클라우드 환경에서 지원하는 Auto Scaling을 통해 해결 가능. k8s의 Service
한 컴포넌트에서 오류가 발생했을 때 전체적인 시스템에 장애가 발생할 수 있다.
어떠한 이유로 응답 처리가 늦어지게되면, 물고있는 커넥션 수가 너무 많아질 수 있다.
더 최악으로 timeout에 대한 failover를 하기 위해 retry를 하다보면 그에 대한 처리도 가중되면서 네트워크 혼잡도가 너무 높아지게 된다. 이는 결국 시스템 전체의 장애로 번진다.
처리를 위한 스레드들이 전부 running 상태가 되어 OS 스레드가 고갈되어 컴포넌트가 뻗어버린다 (...)
=> Circuit Breaker로 해결할 수 있다. 스프링 클라우드 넷플릭스 OSS의 Hystrix 등등..
=> 또한, Failover를 위해 사용하는 retry backoff를 최대한 넉넉하게 잡아서 Retry storm을 제거한다. Exponential backoff 참조
코드 재활용이 힘들고, 최신 상태로 유지하는 작업이 많이 힘들다.
=> 코드 재활용은 멀티모듈로 해결 가능
=> 컴포넌트를 유지보수하는 일이 어렵다는 점은 MSA의 가장 치명적인 단점이라고 많이 지적받는 부분.
=> MSA 도입을 꺼리게 되는 이유가 '요구사항이 변경되어서 컴포넌트를 수정해야하는데 개발자가 퇴사해버려서 히스토리 파악이 너무 어려운 상황' 같은 것 때문인데, MSA를 적극적으로 도입한 회사의 사례를 들어보면 '남은 개발자들이 기능을 다시 새로 짜면 되는 것'으로 해결한다고 한다(...) 결국 사람 뽑아서 해결한다는 마인드
로깅 및 모니터링이 힘들다.
=> 초창기에는 몰라도 요즘은 로그를 수집하는 인스턴스를 따로 둬서 ES로 검색할 수도 있고 Grafana와 같이 metric으로 비주얼라이징할 수 있는 툴이 많이 있다.
트랜잭션 추적 및 관리가 힘들다. 즉, 사용자가 요청을 보냈을 때, 내부적으로 어떤 어떤 컴포넌트들을 거쳤는지 추적하기가 많이 어렵다.
=> 모든 컴포넌트 간의 통신에 트랜잭션 ID를 부여해서 각 요청들을 Elastic Search에 넣고 추적하면 가능하긴 하다. 하지만, 컴포넌트 간의 통신 프로토콜을 HTTP로 통일해야하거나 하는 제약조건이 붙게된다.
MSA는 위 두 가지를 목표로 한다
독립 컴포넌트들을 활용해서 아키텍처를 구성하면 MSA는 분산 애플리케이션과 같은 형태를 띄게 된다.
피터 도이치Peter Deutsch가 언급한 분산 애플리케이션을 처음 구축할 때 가정하는 8가지. 고통스러운 학습 경험을 통해 8가지가 다 틀렸다는 걸 깨닫게 된다고 한다(...)
1977년, 크리스토퍼 알렉산더Christopher Alexander가 MSA의 특정 상황에서 발생하는 문제에 대한 해결책으로 제시한 디자인 패턴
참고로 이 외에도 더 많지만, 책에서는 최소한 필요한 패턴들만 나열했다.
클라이언트가 마이크로서비스와 그 인스턴스를 찾을 수 있어야 한다.
현재 사용가능한 인스턴스를 추적하는 새 컴포넌트(서비스 검색 서비스)를 시스템 환경에 추가한다.
공개된 마이크로서비스는 악의적인 클라이언트의 요청으로부터 보호해야 한다.
클라이언트로부터 들어오는 모든 요청이 거치는 컴포넌트(에지 서버)를 추가한다.
동시 요청 수가 증가하거나 연산량이 많아질 때 가용 스레드가 부족해서 응답이 늦어지거나 서버가 중단되는 문제를 해결해야 한다.
논블로킹 I/O를 사용해 데이터베이스나 다른 마이크로서비스가 처리하길 기다리는 동안 스레드가 할당되지 않도록 한다.
현재 배포된 모든 인스턴스의 구성 정보를 한눈에 관리할 수 있어야한다. 또한, 구성 변경 시 한번에 자동화가 되어야 한다.
구성 정보를 저장하고 동기화하는 컴포넌트(구성 서버)를 추가한다.
인스턴스들이 기록하는 로그들을 한번에 모아서 분석이 필요하다. 사용자의 요청이 어떤 인스턴스들을 거쳤는지 트랜잭션 추적이 필요하다.
로그를 중앙화 해서 관리한다. 로그를 수집하는 컴포넌트를 하나 추가한다.
시스템 환경에 대한 외부 호출을 처리하는 동안 서비스 사이에서 흐르는 요청 및 메시지를 추적할 수 있어야 한다.
모든 요청 및 메시지에 상관 IDCorrelation ID를 넣고 로그에 기록한다.
인스턴스가 응답하지 않거나 처리가 느려지는 경우, 다른 서비스들에도 장애가 전파될 위험이 있어서 이를 차단해야한다.
문제점
대상 서비스에 문제가 있다는 것을 감지해서 더이상 요청을 보내지 않도록 차단한다.
여러 서버에 분산되어 있는 시스템에서 중단되거나 지연된 인스턴스를 자동으로 감지하고 자동으로 조치할 필요가 있다.
문제점
분산되어 있는 시스템에서 중단되거나 지연되는 인스턴스를 수동으로 파악하기 매우 어렵다.
시스템 환경의 상태를 관찰하는 컴포넌트(제어 루프)를 추가한다.
응답시간이 너무 느리거나 하드웨어 사용량이 너무 높은 경우 근본 원인을 찾는 것이 매우 어렵다. 따라서, 자원 사용량을 모니터링할 수 있어야한다.
하드웨어 사용량이 지나치게 많거나 응답시간이 느릴 때 파악을 위해 모니터링이 필요하다.
인스턴스의 하드웨어 리소스 사용량에 대한 메트릭 수집을 하는 컴포넌트(모니터 서비스)를 추가한다.
위에서 소개된 디자인 패턴들에 대한 해결책을 각 플랫폼별로 정리한 시트다.
디자인 패턴 | 스프링 부트 | 스프링 클라우드 | 쿠버네티스 | 이스티오(Istio) |
---|---|---|---|---|
서비스 검색 | Netflix Eureka, Netflix Ribbon |
kube-proxy ,Service |
||
에지 서버 | Spring Cloud, Spring Security OAuth |
Ingress Controller | Ingress Gateway | |
리액티브 마이크로서비스 | Spring Reactor, Spring WebFlux |
|||
구성 중앙화 | Spring Config Server | ConfigMap, Secret | ||
로그 분석 중앙화 | EFK 스택 (ElasticSearch, Fluentd, Kibana) |
|||
분산 추적 | Spring Cloud Sleuth, Zipkin |
Jaeger | ||
서킷 브레이커 | Resilience4j, Hystrix |
Outlier detection | ||
제어 루프 | Controller Manager | |||
모니터링 및 경고 중앙화 | Grafana, Prometheus | Kiali, Grafana, Prometheus |
데브옵스DevOps : 개발과 운영조직이 긴밀하게 협업해서 새 버전을 지속적으로 전달할 수 있게 해야한다.
조직 구성과 콘웨이 법칙
소프트웨어 구조는 그 소프트웨어를 개발한 조직의 의사소통 구조를 반영한다. - 멜빈 콘웨이Melvyn Conway, 1967
일체형 애플리케이션을 마이크로서비스로 분해
가장 어렵고 비용이 많이 드는 방식이다.
제대로 분해되지 않으면 생기는 문제
느린 전달 : 비즈니스 요구사항이 변경되면 서비스에 많은 영향을 미치기 때문에 추가작업이 발생한다.
성능 저하 : 여러 서비스 간에 많은 요청이 오가야하므로 느려진다.
일관성 없는 데이터 : 데이터가 나뉘기 때문에 시간이 지남에 따라 보유한 데이터와 다른 마이크로서비스가 관리하는 데이터가 어긋나게 된다.
적절한 경계를 찾기 위해서는 도메인 주도 설계Domain-Driven Design와 경계가 있는 콘텍스트Bounded Context를 적용하는 게 좋다.
경계가 있는 콘텍스트란? 서브 시스템이나 특정 팀의 담당 업무와 같은, 특정 모델을 정의하고 적용할 수 있는 경계를 명시한 것 - 에릭 에반스Eric Evans
API 설계 : 외부에서 마이크로서비스에 접근하는 공통 API를 공개하는 경우 쉽게 이해할 수 있도록 설계해야한다.
온프레미스에서 클라우드로 마이그레이션
마이크로서비스를 위한 설계 원칙
이번 회사 신규 프로젝트에 Spring Cloud를 도입하며 찾아본 내용이 많긴 했지만, 체계적으로 공부해본 적이 없어 스터디에 합류하였다. 확실히 책으로 공부를 하니 MSA의 장단점, 모놀리틱의 장단점, MSA를 이루기 위해 필요한 구성 요소, 디자인패턴 등을 체계적으로 공부할 수 있어 좋은 것 같다
분산 컴퓨팅의 여덟가지 오류
에서 1. 네트워크는 안전하다
와 4.네트워크는 안전하다
가 같아서 문서를 찾아보니, 1번은 reliable
(신뢰할 만한)이고, 4번은 secure
(보안상 안전한)이었음구성 중앙화
의 한 예시로, 우리 회사에서는 보안상의 이유로 주기적으로 DB 비밀번호를 교체함. 이 때 구성 중앙화를 하지 않으면 서버의 코드마다 DB 비밀번호를 변경하고 재배포해야하는 이슈가 있음. 구성 중앙화를 이룬다면 훨씬 간단하게 변경 가능.동기 서비스를 사용하거나 API를 이용한 메시징 방식
?문제점
해결책
해결책의 필요 조건
문제점
해결책
해결책의 필요 조건
문제점
해결책
해결책의 필요 조건
문제점
해결책
해결책의 필요 조건
문제점
해결책
문제점
해결책
해결책의 필요 조건
문제점
해결책
해결책의 필요 조건
문제점
해결책
해결책의 필요 조건
문제점
해결책
해결책의 필요 조건
Resillience4j의 CircuitBreaker
- 요청 마이크로서비스와 제공 마이크로서비스 사이에 resillience4j 를 통해 통신
- {최소요청횟수} 이후 {timeout}시간 기준으로 {최근 통계시간}동안 또는 {최근 통계건수}로 평가했을때, {실패율}이상이 되면 Circuit Breaker가 Open되고, {Circuit Breaker지속시간}동안 유지
- 유지되는 동안 Backend service를 호출하지 않는다.
- 그 시간이 경과하면 Circuit Breaker는 Half Open상태가 되고, Backend service를 1번 호출한다.
- 요청이 성공하면 Circuit Breaker는 Close되고, 실패하면 다시 Open된다.
- CircuitBreaker 환경설정을 하드 코딩하면 옵션을 바꿀 때 마다 재배포해야하므로 config server에서 설정한다. → 구성 중앙화
들으랴 말하랴 서기하는게 쉽지 않네요... 서기하는거 까먹고 있다가 뒤늦게 적어서 내용이 부실한 점 죄송합니다. 😢
챕터 1을 공부하며 느낀 점
역시 챕터 1은 날먹이다.
대부분 실무에서도 적용하고 있는 기법들이었으나 서킷 브레이커의 경우 사용해본적이 없어서 상당히 기대가 됨.
회사에서 당연하게 하고 있는 것들을 용어로 접하니까 새로운 느낌임. 선대 개발자들이 구축해놓은 시스템들이 다 이런 이유를 갖고 있었구나~ 라고 다시 한 번 느끼게 되는 계기가 되었음.
마이크로서비스 소개
독립된 소프트웨어 컴포넌트의 장점
MSA에서의 각 컴포넌트는 독립적으로 배포, 업그레이드할 수 있다.
다른 컴포넌트와 상관 없이 여러 인스턴스로
Scale-Out
할 수 있다. (앞단에 보통 LB를 둔다.)독립된 소프트웨어 컴포넌트의 단점
컴포넌트의 새 인스턴스를 추가하면 수동으로 로드밸런서를 구성하고 새 노드를 수동으로 설정해주어야한다. ( k8s service )
다른 시스템에서 문제가 발생할 경우 오류가 전이된다. 이런 현상을
Chain of Failure
라고 부른다. ( Circuit Breaker )각 컴포넌트들의 최신화를 위한 많은 유지보수 비용이 들어간다. ( 개발자 많이 뽑으셈 )
모니터링이 더 어렵다. ( Grafana )
분산된 여러 컴포넌트에서의 로그 파일을 수집하고 로그 컴포넌트를 상호 연결시키는 것이 어렵다. ( EFK )
마이크로서비스의 정의
빠르게 개발해 지속적으로 배포할 수 있어야 한다.
수동 혹은 자동으로 쉽게 스케일링할 수 있어야 한다.
MSA에서 각 컴포넌트는 아무것도 공유하지 않는 아키텍처를 유지해야한다. 즉 MSA의 각 컴포넌트들은 DB를 공유하지 않는다.
명확한 인터페이스를 통해서만 통신한다. 동기식 서비스를 사용하거나, API를 이용한 메시징 방식을 사용할 수 있는데, 버전 관리 전략에 따라 문서화되어야한다.
런타임으로 배포되어야 한다.
MSA 인스턴스는 Stateless 해야한다. 모든 마이크로서비스 인스턴스가 마이크로서비스로 들어오는 요청을 처리할 수 있다.
마이크로서비스의 문제
동기식 통신을 사용하는 다수의 소형 컴포넌트들은
Chain of Failure
를 일으킬 수 있다.다수의 소형 컴포넌트를 최신 상태로 유지하는건 어렵다. (아 개발자 많이 뽑으라고 ㅋㅋ)
로그를 수집하고 분석하기가 어렵다.
하드웨어 자원 사용량 분석도 어렵다.
소형 컴포넌트들을 수동으로 관리하는건 비용이 많이 들고 오류도 발생하기 쉽다.
MSA 디자인 패턴
디자인 패턴은 오래된 개념이다. 특정 상황에 발생하는 문제에 대해 재사용 가능한 해결책을 정리한 것이다.
Service Discovery
Edge Server
Reactive Microservice
Central Configuration
Centralized Log Analysis
Distributed Tracing
Circuit Breaker
Control Loop
Centralized Monitoring And Alarm
Service Discovery
선행 문제점
Service Discovery의 해결책
Reverse Proxy
를 노출시켜서 클라이언트의 요청을 처리할 적절한 인스턴스로 요청을 전달한다.Edge Server
선행 문제점
MSA의 컴포넌트는 일부만 외부에 공개하고 그 외의 MSA는 외부에서 접근하지 못하도록 숨기는게 일반적이다.
그 이유는 악의적인 클라이언트의 요청으로부터 보호하기 위함이다.
Edge Server의 해결책
Reactive Microservice
선행 문제점
Reactive Microservice의 해결책
Central Configuration
선행 문제점
Central Configuration의 해결책
Central Configuration에 대한 여담
application.properties
를 확인하고 싶은데 방법이 없음. 어떻게 하면 좋으려나? ( API로 만들어놔야 하나..? )Centralized Log Analysis
선행 문제점
Centralized Log Analysis의 해결책
Distributed Tracing
선행 문제점
Distributed Tracing의 해결책
Correlation ID
를 넣어야하고 모든 로그 이벤트에 이 ID가 있어야한다.Circuit Breaker
선행 문제점
동기 방식으로 상호 통신하는 MSA는 연쇄 장애가 발생할 여지가 있다.
하나의 MSA가 응답하지 않으면 MSA의 클라이언트에게 응답하지 않게 된다.
Circuit Breaker의 해결책
서비스에 문제가 감지되면 시간 초과를 무시하고 바로 실패하도록 서킷을 연다.
half-open-circuit
반열림 서킷이라고도 하는 장애 복구형probe
를 사용한다. 즉 서비스가 정상 동작하는지 주기적으로 요청을 보낸다.프로브가 서비스의 정상 동작을 감지하면 서킷을 닫는다.
이런 기능은 시스템 환경을 탄력적으로 만들어 자가 치유를 가능케 하는 매우 중요한 기능이다.
Control Loop
선행 문제점
Control Loop의 해결책
Control Loop
를 시스템 환경에 추가하여 관찰하게 한다.Centralized Monitoring And Alarm
선행 문제점
응답 시간이나 하드웨어 자원 사용량이 지나치게 높은 경우 문제의 근본 원인을 찾는 것이 매우 어렵다.
MSA별 하드웨어 자원 사용량을 분석할 수 있어야 한다.
Centralized Monitoring And Alarm의 해결책
Metric
을 수집하는 새로운 컴포넌트를 시스템 환경에 추가한다.