Skip to content

Latest commit

 

History

History
331 lines (192 loc) · 16.3 KB

1.6 마이크로서비스 아키텍쳐 패턴 언어.md

File metadata and controls

331 lines (192 loc) · 16.3 KB

1.6.1 마이크로서비스 아키텍쳐는 만병통치약이 아니다.

1986년 Frederick Brooks는 소프트웨어 공학에 만병통치약 따위는 없다 라고 말했다.

아무런 Trade-off 없이 생산성을 갑자기 10배로 끌어올려 주는 기술이나 기법은 없다는 의미이다.


하나의 기술을 쓰레기인지 소프트웨어 공학을 뒤흔들만한 기술인지 이분법 적으로 생각하는 논쟁은 아직도 현재 진행중 이다.

Neal Ford가 처음 쓴 말(The suck/rock dichotomy)로, 예를 들면 다음과 같다.

  • 동기/리액티브
  • 객체 지향/함수형
  • Rest/메세징

하지만 현실은 그렇게 간단하지 않다.

모든 기술에는 장단점이 있기 때문에 어떤 기술이든 Gartner hype cycle을 거쳐야 한다.

Gartner hype cycle이란 미국의 정보 기술 연구 및 자문 회사인 Gartner에서 만든 기술의 성숙도를 표현하는 시각 도구이다.

기술들은 다음 5단계로 이루어진다.

단계 명칭 설명
1 기술 촉발 (Technology Trigger) 잠재적 기술이 관심을 받기 시작하는 시기. 초기 단계의 개념적 모델과 미디어의 관심이 대중의 관심을 불러 일으킨다. 상용화된 제품은 없고 상업적 가치도 아직 증명되지 않은 상태이다.
2 부풀려진 기대의 정점 (Peak of Inflated Expectations) 초기의 대중성이 일부의 성공적 사례와 다수의 실패 사례를 양산해 낸다. 일부 기업이 실제 사업에 착수하지만, 대부분의 기업들은 관망한다.
3 환멸 단계 (Trough of Disillusionment) 실험 및 구현이 결과물을 내놓는 데 실패함에 따라 관심이 시들해진다. 제품화를 시도한 주체들은 포기하거나 실패한다. 살아 남은 사업 주체들이 소비자들을 만족시킬만한 제품의 향상에 성공한 경우에만 투자가 지속된다.
4 계몽 단계 (Slope of Enlightenment) 기술의 수익 모델을 보여 주는 좋은 사례들이 늘어나고 더 잘 이해되기 시작한다. 2-3세대 제품들이 출시된다. 더 많은 기업들이 사업에 투자하기 시작한다. 보수적인 기업들은 여전히 유보적인 입장을 취한다.
5 생산성 안정 단계 (Plateau of Productivity) 기술이 시장의 주류로 자리잡기 시작한다. 사업자의 생존 가능성을 평가하기 위한 기준이 명확해진다. 시장에서 성과를 거두기 시작한다.

기술의 특징을 고려하지 않았다는 비판을 받고있긴 하다

결국 기술들은 관심을 받아 기대의 정점 상태를 거쳐 환멸이라는 구렁텅이로 빠지게 되고, 결국 생산성이 정체되는 5단계에 이르러서야 기술의 단점, Trade-off에 대해 이해할 수 있다.


철학에 가까운 이야기

Jonathan Haidt는 The Righteous Mind: Why Good People Are Divided by Politics and Religion이라는 책에서 인간의 마음이 움직이는 것을 코끼리와 코끼리를 탄 사람에 비유했다.

코끼리는 감정, 인간은 이성을 담당하는데 코끼리를 탄 사람은 코끼리에게 영향을 줄수도 있지만 거의 영향을 끼칠 수 없고 코끼리의 행동에 대한 결과를 정당화 하기 위한 근거를 제공할 뿐이다.

이렇듯 인간의 의사결정은 대부분 감정에 따른다.

감정을 극복한 채로 기술에 대한 토론이 필요하기 때문에 Pattern Format으로 기술을 객관적으로 설명하는 것이다.


1.6.2 패턴 및 패턴 언어

패턴은 특정 상황에 발생한 문제에 대한 재사용 가능한 해법으로, 이미 소프트웨어 아키텍쳐나 설계 분야에서 검증된 아이디어이다.

패턴 언어는 특정 영역 내부에서 문제를 연결하는 연관 패턴의 집합을 말한다.

지금은 이해하기 힘들겠지만 뒤에 설명을 보고 나면 이해하기 좀 더 수월하다.

패턴과 패턴 언어는 Christopher Alexander라는 사람이 정립한 개념이다.

이 사람의 글은 소프트웨어 커뮤니티에 큰 반응을 이끌어 냈고, 결국 패턴과 패턴 언어 개념을 가져다 사용하기에 이르렀다.

Design Patterns: Elements of Resuable Object-Oriented Software 이라는 책이 출간되었는데, 객체 지향에서 사용할 수 있는 패턴들이 정립되어 있어 추천한다.


패턴에 대해 예를 들어 보자면, 마이너스 통장을 지원하는 은행 시스템을 구축해야 한다고 가정해 보자.

솔루션은 다음 3가지로 구성될 수 있다.

  • 초과 인출 알고리즘을 캡슐화 한 Overdraft라는 전략 인터페이스

    전략 인터페이스는 Strategy Pattern이라는 디자인 패턴에서 인터페이스를 의미하며, 하나의 인터페이스를 구현하는 구현체를 여러개 만들어서 정적으로 행위를 수정하는게 아니라 동적으로 전략(구현체)를 바꿔주는 패턴

  • 하나 이상의 전략 Concrete 클래스(각각의 클래스들은 특정 기능을 구현)

    Concrete 클래스란 new 연산자로 객체 생성이 가능한 클래스를 의미한다.

    즉 추상 메소드 없이 모든 메소드를 구현완전한 클래스를 의미한다.

    전략 클래스란 전략 인터페이스를 구현한 클래스를 의미한다.

  • 알고리즘을 사용하는 Account 클래스


전략 패턴은 훌륭한 디자인 패턴 중 하나이므로 솔루션의 Element는 클래스 이다.


패턴은 자신이 적용되는 맥락을 반드시 기술해야 한다.

예를 들어 넷플릭스에서 사용하던 디자인 패턴이 우리에게도 항상 Best Solution이거나 잘 작동할거라는 보장은 없다.

자신들의 상황을 잘 이해하고 사용하고, 솔루션 측면도 함께 기술을 강제한다는 점에서 호용성이 뛰어나다.


상용 패턴은 다음 3가지로 구성된다.

  • 강제 조항
  • 결과 컨텍스트
  • 연관 패턴

강제 조항

주어진 상황을 해결하기 위해 반드시 선택해야 하는 조항이다.

강제 조항 끼리 충돌이 발생할수도 있기 때문에, 상황에 따라 둘 사이의 우선순위를 정해야 한다.

예를 들어 성능이 좋고 러닝커브가 높은 Reactive 스타일의 코드와 러닝커브가 낮고 성능이 부족한 동기 방식의 코드 사이에서 하나만 택해야 하는 상황이 올 수 있다.

맞을지 틀릴지는 모르겠지만, 코드를 짤 때 필수적인 부분을 선택한다고 보면 될 것 같다.

위에서 든 예제로 Reactive와 동기라는 예제는 꼭 어느걸 사용할지 선택해야 하지만, 성능과 러닝커브 둘 다 잡을 순 없어서 선택해야 한다.

물론 강제 조항은 반드시 선택해야 하는 조항이지, 반드시 충돌이 났을 때를 의미하진 않는다.


결과 컨텍스트

패턴을 적용한 결과를 나타내는 영역으로, 3가지로 분류된다.

  • 장점: 해결된 강제 조항을 포함해서, 해당 디자인 패턴을 사용함으로써 얻는 장점
  • 단점: 해결되지 않은 강제 조항을 포함해서, 해당 디자인 패턴을 사용함으로써 갖게되는 단점
  • 문제점: 해당 패턴을 적용함으로써 새롭게 생기는 문제점

결과 컨텍스트는 솔루션에 대한 더 객관적인 시야로 바라볼 수 있게 해준다.


연관 패턴

한 패턴과 다른 패턴들 간의 관계를 기술하는 영역으로, 5가지가 있다.

  • 선행자: 이 패턴을 넣게 된 선행 패턴. MSA를 예로 들면, SAGA 패턴의 선행자는 MSA이다.
  • 후행자: 이 패턴으로 인해 발생하게 된 문제를 해결하는 패턴. MSA를 예로 들면 SAGA패턴이나 서비스 디스커버리 패턴 등이 있다.
  • 대안: 이 패턴의 대체제. MSA는 Monolithic Architecture를 서로 대체할 수 있는 패턴으로, 하나를 선택하면 된다.
  • 일반화: 문제를 해결하는데에 더 일반화된 패턴.
  • 세분화: 특정 패턴을 더 세분화 해서 나타낸 형태.

특정 영역의 문제를 해결하는 패턴끼리 묶어 명시적으로 기술하면 어떤 패턴을 사용하는게 좋을지 효과적으로 나타낼 수 있다.


img

Monolithic Architecture는 MSA의 대안이 될 수 있고, MSA로 인해 호스트별 단일 서비스 라는 패턴을 사용하게 되었다.

호스트별 단일 서비스는 컨테이너별 서비스 라는 세분화된 패턴을 가지고 있다.


관계를 표현하는 데에도 몇가지 유형이 있다.

  • 선행자 - 후행자를 표시한다.
  • 같은 문제를 해결할 수 있는 여러 솔루션을 제공한다.
  • 한 패턴에 대한 구체화된 패턴을 제시한다.
  • 패턴들은 특정 문제 영역(배포)에 적용된다.

1.6.3 마이크로서비스 아키텍쳐 패턴 언어

MSA 패턴 언어는 전체 Application을 MSA로 구성할 때 유용한 패턴들의 모음이다.

아키텍쳐의 장단점을 기술할 수 있기 때문에, MSA를 사용하는게 적합한지 확인할 수 있고 패턴 언어를 통해 다양한 이슈들을 해결할 수 있다.

img

왼쪽은 아키텍쳐 패턴 그룹, 오른쪽은 마이크로서비스 아키텍쳐 패턴 사용 시 각종 이슈들을 해결해 주는 솔루션 패턴들이다.


그림에서 볼 수 있다싶이 3가지 구분으로 나뉘게 된다.

  • 인프라 패턴: 주로 개발 영역 밖의 인프라 문제 해결
  • 애플리케이션 인프라: 개발에 영향을 주는 인프라 문제 해결
  • 애플리케이션 패턴: 개발자가 맞닥뜨리는 문제 해결

위와 같이 패턴들은 종류별로 묶어줄 수 있다.

이제 주요 패턴 그룹들을 살펴보자


Application을 서비스로 분해하는 패턴

Application을 서비스로 분해하는지 결정하는 것은 예술에 가깝다.

하지만 이것을 도울 여러가지 패턴들은 존재한다.

img

비지니스 능력에 따라 분해하는 패턴과 DDD의 하위 도메인에 따라 분해하는 방법 2가지가 있다.

자세한건 2장에서 다룬다.


통신 패턴

MSA는 기본적으로 분산 시스템이기 때문에 프로세스간의 통신이 중요하다.

그렇기 때문에 서비스끼리, 그리고 외부와 어떻게 통신해야 할지 아키텍쳐/설계 관점에서 충분한 의논 후 자신에게 맞는 방법을 선택해야 한다.

총 5개로 나눌 수 있다.

img

  • Communication Style: 어떤 IPC를 사용할 것인가?
  • Discovery: 서비스 클라이언트는 서비스 인스턴스의 IP주로를 어떻게 가져올 것인가?
  • Reliability: 서비스가 불능일 때 서비스간 통신에서 신뢰성은 어떻게 보장할 수 있는가?
  • Transactional Messaging: DB 트랜잭션을 어떻게 관리할 것인가?
  • External API: 외부 API와 어떻게 통신할 것인가?

DB의 분산으로 인한 문제점

MSA는 각각의 DB를 가지고 느슨한 결합을 유지한다고 했는데, DB가 모두 따로 있으면 몇가지 문제가 있다.

데이터 일관성을 위한 패턴

가장 큰 문제는 트랜잭션 문제인데, 여러 서비스에 걸친 트랜잭션을 관리하기 힘들다.

따라서 SAGA 패턴을 통해 데이터 일관성을 유지해야 한다.

자세한 내용은 4~6장

img


데이터 쿼리 패턴

서비스마다 DB를 두면 여러 서비스에 걸친 Join도 문제가 될 수 있다.

서비스는 오직 API를 통해서만 접근할 수 있기 때문에 분산 쿼리를 사용할 수 없다.

분산 쿼리는 여러 DB 사이에서 실행 계획을 세우는 것이다.

따라서 훨씬 최적화된 실행 계획으로 쿼리를 동작시킬 수 있지만, 직접 DB에 접근해야 해서 API로만 통신하는 지금은 사용할 수 없다.

따라서 Composition Pattern(API 조합 패턴) 이란것을 사용해야 한다.

이 패턴은 API를 호출해 결과를 조합하고, CQRS는 데이터 복제본을 유지해서 간단하게 쿼리할 수 있는 방법이다.


서비스 배포 배턴

Application의 배포는 분명 Monolith에서도 어려운 작업 이었지만, MSA보단 훨씬 직관적이었다.

하지만 MSA는 다양한 언어와 프레임워크로 구현된 수십개가 넘는 서비스로 이루어져 있기 때문에 배포 작업이 훨씬 복잡하고 유지를 하는데에도 많은 사람들이 필요하다.

그래서 다음 배포 패턴을 이용해야 한다.


Monolithic Architecture에서는 그냥 JAR 파일을 build해서 수동으로 배포했다.

하지만 MSA는 그런 방식으로 하기 힘들고, 높은 수준의 자동 배포 인프라를 구축해야 한다.

img

자세한 내용은 12장에서


관측 패턴

관리자의 주 업무는 애플리케이션의 요청 실패, 높은 지연시간이 발생했을 때 이를 진단하고 조치하는 일 이다.

Monolithic Application도 운영은 어렵겠지만, 요청의 처리가 단순하기 때문에 트러블 슈팅(문제점 검색)이 훨씬 쉽다.

요청이 분산을 위해 라우팅 되고, 애플리케이션은 DB에 쿼리를 날려 결과를 반환한다. 클라이언트의 요청은 그냥 로그를 보면 된다.


하지만 MSA에선 반환되기까지 어떤 서비스를 어떻게 오고 갈지 알기 힘들기 때문에 로그만으로는 문제 파악이 힘들다.

거기다가 문제의 원인도 찾고 진단하는것은 굉장히 복잡한 일 이고, 지연시간은 원인이 너무 많아 특정하기 힘들다.


관측을 위해선 다음 패턴들을 필요로 한다.

  • Heath Check API: 서비스의 상태를 반환하는 Endpoint를 노출 시킨다.
  • Log Aggregation: 서비스 내역을 기록, 중앙의 로깅 서버에 로그를 저장해서 로그의 검색이나 문제 발생 시 경고를 해주도록 한다.
  • Distributed Tracing: 외부 요청마다 ID를 부여해서 서비스 통과 과정을 추적한다.
  • Exception Tracking: 예외가 발생하면 예외 추적 서비스에 보고해 준다. 중복된 예외는 걸러주고 개발자에게 경고를 해준다.
  • Application Metrics: 지표를 측정해서 특정 서버에서 출력해 준다.
  • Audit Logging: 사용자가 한 일을 기록한다.

테스트 자동화 패턴

MSA에선 테스트의 규모가 작긴 하지만, 여러 서비스들이 조화롭게 작동하는지 테스트 하는게 중요하다.

느리고 복잡한 end-to-end test는 가급적 피해야 하기 때문에 다음 테스트 단순화 패턴이 필요하다.

E2E test란 GUI를 통해 실제 사용자처럼 테스트를 진행하는 것이다.

  • Consumer-Driven contract test: 클라이언트의 의도대로 서비스가 동작하는지 테스트
  • Consumer-Side contract test: 클라이언트와 서비스가 서로 통신이 가능한지 테스트
  • Service Component test: 서비스들끼리 따로따로 테스트 한다.

Cross-Cutting 처리 패턴

횡단 관심사(Cross-Cutting concern)란 AOP에서 공통된 코드들을 추출하는 것 처럼 전 서비스에 걸친 공통된 부분을 의미한다.

MSA는 위에서 나온 여러 패턴들 외에도 여러 서비스들이 반드시 구현해야 할 패턴들이 많다.

각각의 마이크로서비스마다 이런 Cross-Cutting을 처리하는건 시간이 오래 걸리기 때문에 Cross-Cutting을 처리하는 서비스를 두고, 그 서비스에서 Microservice Chassis를 적용해서 구축하는게 바람직하다.


보안 패턴

MSA에선 주로 API Gateway가 인증과 인가 역할을 맞고, 각각 서비스에 정보를 전달한다.

일반적으로는 JWT를 적용하는 것인데, API Gateway는 AccessToken을 각각 서비스에 전달하고 서비스는 토큰을 확인한다.