TIL
1장 모놀리식 지옥에서 벗어나라
1.1 서서히 모놀리식 지옥에 빠져들다
FTGO는 미국 온라인 음식 배달 업계를 선도하는 회사
하나의 WAR 파일로 구성된 모놀리스 시스템은 점점 커지면서 복잡해졌다.
1.1.1 FTGO 애플리케이션 아키텍처
FTGO 전체 구조는 코어가 비즈니스 로직으로 구성된 육각형 아키텍처 (hexagonal architecture)
UI 구현 및 외부 시스템 통합을 담당하는 다양한 어댑터가 코어를 감싼 형태
비즈니스 로직은 도메인 객체 컬렉션인 모듈로 구성
외부 시스템과 연계하는 어댑터가 여러 존재
REST API, 웹 UI 등의 인바운드 어댑터
DB, 클라우드 서비스를 호출하는 아운바운드 어댑터
논리적으로 모듈화했지만 애플리케이션은 하나의 WAR 파일로 패키징
1.1.2 모놀리식 아키텍처 장점
개발이 간단
IDE 등 개발 툴은 단일 이플리케이션 구축에 초점이 맞추어져 있다.
변경이 용이
코드, DB 스키마를 변경해서 빌드/배포하기 용이
테스트하기 쉽다.
애플리케이션을 띄우고, REST API를 호출하는 등 종단 간 테스트 작성
배포하기 쉽다.
서버에 접속하여 톰캣 설치 경로에 WAR 파일 복사하면 그만
이제는 JAR만 있으면 된다.
확장하기 쉽다.
부하 분산기 뒤에 인스턴스 여러 개를 실행
1.1.3 모놀리식 지옥의 실상
개발 팀이 스프린트를 할 때마다 구현 스토리가 늘어나고 오버헤드도 증가
개발 팀 인원도 계속 증가
팀별로 특화된 스크럼 팀만 여럿
너무 복잡해서 개발자가 주눅 든다.
FTGO 애플리케이션은 너무 복잡하다.
개발자가 완전히 이해할 수 없을 정도
버그 픽스, 기능 추가가 갈수록 힘들고 오래 걸린다.
기능 별 팀은 여러개인데 소스 코드 저장소는 하나
대규모 개발 팀이 단일 소스 코드 저장소에 커밋
여러 팀의 커밋 → CI → 백로그 → 수동 테스트 → 프로덕션 배포까지의 과정이 험난
단일 코드베이스는 소통/조정 오버헤드를 유발
개발이 더디다.
애플리케이션이 커지면 일상적 개발 업무도 더디게 진행된다.
IDE 실행 속도 느려짐
빌드 시간 오래 걸림
한 번 시동하는 것도 적잖은 시간이 걸린다.
즉 코드 변경 → 빌드/실행 → 테스트까지 너무 많은 시간이 낭비되어 생산성이 떨어진다.
커밋부터 배포에 이르는 길고 험난한 여정
애플리케이션 최신 트렌드는 지속적 배포 (continuous deployment)
몇 번이고 원하는만큼 변경분을 배포
아마존은 2011년 사용자에게 아무 영향 없이 11.6초마다 배포했다고 한다.
너무 방대한 모놀리식 애플리케이션은 한 달 2회 이상 배포도 힘든 일이었다.
확장하기 어렵다.
FIGO 애플리케이션은 모듈마다 리소스 요건이 맞지 않아 확장하기 어렵다.
용량이 큰 음식점 데이터는 인 메모리 DB 형태로 저장되기에 메모리 칩이 많은 서버에 배포하는 것이 좋다.
이미지 처리 모듈은 CPU 코어 수가 많은 서버에 배포하는 것이 최적
같은 애플리케이션이라도 리소스 조건이 상이해 서버 구성 시 리소스 배분을 신경 써야 한다.
모놀리스는 확실히 전달하기 어렵다.
신뢰성이 부족한 것도 문제
덩치가 워낙 커서 테스트하기 어렵다.
테스트성 부족은 곧 버그 발생 확률을 높인다.
갈수록 한물간 기술 스택
모놀리식 아키텍처에선 어쩔 수 없이 한물간 기술 스택을 쓸 수밖에 없다.
최신 기술 사용 때문에 전체 모놀리식 애플리케이션을 재작성할 수 없기 때문
비용과 리스크가 높다.
1.4 마이크로서비스 아키텍처가 답이다
어떤 아키텍처든 유스 케이스는 구현할 수 있다.
다만 FIGO 애플리케이션은 덩치가 커지며 여러 품질 속성이 악화되었다.
소프트웨어 전달 속도
확장성
테스트성
그렇다면 마이크로서비스의 정의는 무엇인가?
1.4.1 확장 큐브와 마이크로서비스
확장 큐브라는 3차원 확장 모델이 있다.
애플리케이션을 확장하는 세 가지 방법을 정의
X축은 확장은 동일한 다중 인스턴스에 들어온 요청을 부하 분산 (스케일 아웃)
Z축 확장은 요청 속성에 따라 요청을 라우팅 (데이터 분할, 다중 파티션)
Y축 확장은 애플리케이션을 기능에 따라 서비스로 분해 (기능 분해, 마이크로서비스)
X축 확장: 다중 인스턴스에 고루 요청 분산
X축 확장은 일반적인 모놀리식 애플리케이션의 확장 수단
부하 분산기에 들어온 요청을 인스턴스들에 고루 분배
Z축 확장: 요청 속성별 라우팅
인스턴스별로 주어진 데이터 하위 집합만 처리하도록 설정하는 방법
인스턴스 앞면의 라우터가 요청 속성에 알맞은 인스턴스로 요청을 라우팅
ex) userId에 따라 라우팅할 인스턴스를 선택하여 요청을 전송
증가하는 트랜잭션 및 데이터 볼륨을 처리하기 좋은 수단
Y축 확장: 기능에 따라 애플리케이션을 서비스로 분해
X, Z축 확장으로 애플리케이션 능력과 가용성은 개선되지만 복잡성은 해결되지 않는다.
Y축 확장, 즉 기능 분해가 필요
ex) 주문 서비스, 고객 서비스, 리뷰 서비스
각 서비스는 필요에 따라 X, Z축 확장을 진행
중요한 것은 각 서비스의 크기가 아니라 각 서비스가 집중된/응집된 책임을 맡고 있다는 사실
1.4.2 마이크로서비스는 모듈성을 갖고 있다.
모듈성은 크고 복잡한 애플리케이션 개발 시 필요한 특성
여러 사람이 이해하고 개발할 수 있게 애플리케이션을 여러 모듈로 분해
마이크로서비스 아키텍처는 서비스가 모듈의 단위
서비스들 사이엔 API라는 경계선을 갖기에 다른 서비스의 내부에 침투할 수 없다.
각 서비스를 독립적으로 배포/확장 가능
1.4.3 서비스마다 DB가 따로 있다.
마이크로서비스는 각각 자체 DB를 가진다.
다른 서비스 개발자와 일일이 협의하지 않고도 본인이 담당한 서비스 스키마 변경 가능
다른 서비스가 내 서비스의 DB 락을 획득해 블로킹하는 일도 없다.
1.4.4 FTGO 마이크로서비스 아키텍처
FTGO 애플리케이션 비즈니스 로직은 다양한 백엔드 서비스로 구성된다.
주문 서비스
배달 서비스
음식점 서비스
주방 서비스
회계 서비스
API 게이트웨이는 각 서비스로 요청을 라우팅한다.
1.4.5 마이크로서비스 아키텍처와 SOA
SOA (Service Oriented Architecture, 서비스 지향 아키텍처)
고수준에선 SOA와 MSA는 비슷해보이지만 근본적인 차이점이 존재한다.
구분
SOA
MSA
서비스 간 통신
SOAP, WS 표준처럼 무거운 프로토콜을 응용한 엔터프라이즈 서비스 버스 중심의 스마트 파이프
REST나 GRPC처럼 가벼운 프로토콜을 응용한 메시지 브로커 또는 서비스 간 통신 중심의 덤 파이프
데이터
전역 데이터 모델 및 공유 DB
서비스 별 데이터 모델 및 DB
주요 사례
대규모 모놀리식 애플리케이션
소규모 서비스
1.5 마이크로서비스 아키텍처의 장단점
1.5.1 마이크로서비스 아키텍처의 장점
크고 복잡한 애플리케이션을 지속적 전달/배포 가능
테스트성: 마이크로서비스는 크키가 작아 자동화 테스트를 작성하기 쉽고 더 빨리 실행된다.
배포성: 다른 팀의 개발자와 협의 없이 독립적으로 배포 가능
자율성, 느슨한 결합: 팀이 독립적으로 개발, 배포, 확장할 수 있어 생산성이 향상된다.
서비스 규모가 작아 관리하기 쉽다
비교적 크기가 작아 코드를 이해하기 쉽다.
코드베이스가 작아 IDE도 느려지지 않고 생산성이 향상된다.
배포 과정 역시 빠르다.
서비스를 독립적으로 배포/확장 가능
각 서비스를 독립적으로 X/Z축 확장이 가능
서비스마다 상이한 리소스 요건에 맞추어 하드웨어에 배포 가능
팀이 자율적으로 움직인다
결함 격리가 잘 된다
한 서비스에서 장애가 발생해도 다른 서비스는 정상 가동
새로운 기술을 실험하고 도입하기 쉽다
서비스 규모가 작아 더 나은 언어와 기술로 얼마든지 재작성 가능
1.5.2 마이크로서비스 아키텍처의 단점
딱 맞는 서비스를 찾기 쉽지 않다.
시스템을 잘못 분해할 경우 결합도가 높은 서비스로 이루어진 시스템이 탄생할 수도 있다.
모놀리식/마이크로서비스의 단점만 있는 분산 모놀리스를 구축할 가능성이 존재
분산 시스템은 너무 복잡해서 개발, 테스트, 배포가 어렵다.
서비스 간 통신이 복잡하며 부분 실패를 처리할 수 있어야 한다.
다중 DB에 접속, 조회하고 트랜잭션을 구현하는 일이 복잡하다.
여러 서비스가 연관된 테스트를 자동화하는 것도 쉽지 않다.
여러 종류의 많은 인스턴스가 떠 있으니 운영 복잡도 역시 가중된다.
여러 서비스에 걸친 기능을 배포할 때는 잘 조정해야 한다.
마이크로서비스 아키텍처 도입 시점을 결정하기 어렵다.
초기 버전 개발 시에는 굳이 마이크로서비스를 사용할 필요가 없다. (오히려 생산성 저하)
복잡성을 다루는 문제가 중요해지는 시점에 기능 분해 하는 것이 바람직
1.6 마이크로서비스 아키텍처 패턴 언어
아키텍처와 설계는 전부 결정하기 나름
모놀리식이 잘 맞을 수도 있다.
마이크로서비스를 도입하려면 수많은 트레이드 오프를 검토해야 한다.
1.6.1 마이크로서비스 아키텍처도 만병통치약은 아니다.
소프트웨어 공학에 만병통치약 따위는 없다.
마이크로서비스가 어떤 서비스에 적합할지는 여러 요인에 따라 달라지고 상황에 따라 다르다.
이성적으로 기술을 논할 방법이 필요
패턴 포맷으로 기술을 객관적으로 기술하는 것
1.6.2 패턴 및 패턴 언어
패턴 - 특정 상황에서 발생한 문제에 대한 재사용 가능한 해법
ex) 전략 패턴
패턴은 자신이 적용되는 맥락을 반드시 기술해야 한다는 점에서 가치가 크다.
어떤 맥락에선 통하지만 어떤 맥락에서는 통하지 않을 수 있다.
상용 패턴의 구조는 대략 다음 세 부분으로 구성된다.
강제 조항
결과 맥락
연관 패턴
강제 조항: 문제 해결을 위해 반드시 처리해야 할 이슈
주어진 맥락에서 문제를 해결하고자 할 때 반드시 처리해야 할 조항
상충하는 조항도 있기에 맥락에 따라 우선순위를 정해야 한다.
ex) 이해하기 쉬운 코드 vs 성능이 우수한 코드
결과 맥락: 패턴 적용 결과
패턴 적용 결과를 다음 세 부분으로 기술하는 영역
장점: (해결된 강제 조항 등) 패턴의 좋은 점
단점: (미해결 강제 조항 등) 패턴의 나쁜 점
이슈: 패턴 적용 시 발생한 새로운 문제점
연관 패턴: 다섯 가지 관계 유형
한 패턴과 다른 패턴의 관계를 기술하는 영역
선행자: 이 패턴을 필요하게 만든 선형 패턴
ex) MSA패턴은 모놀리식을 제외한 나머지 패턴들의 선행자
후행자: 이 패턴으로 야기된 이슈를 해결하는 패턴
ex) MSA 패턴 적용을 위해선 서비스 디스커버리 패턴, 회로 차단기 패턴 등 후행자 패턴이 필요
대안: 이 패턴의 대체 솔루션을 제공하는 패턴
ex) MSA와 모놀리식 패턴은 서로를 대신할 수 있다.
일반화: 문제를 해결하는 일반적인 솔루션에 해당하는 패턴
세분화: 특정 패턴을 더 세부적으로 나타난 형태
1.6.3 마이크로서비스 아키텍처 패턴 언어 개요
마이크로서비스 아키텍처 패턴 언어
전체 애플리케이션을 MSA로 구성할 때 유용한 패턴의 모음집
MSA 도입이 옳은지 결정할 때 요긴하다.
패턴은 세 계층으로 분류된다.
인프라 패턴: 개발 영역 밖 인프라 문제 해결
애플리케이션 인프라: 개발에도 영향을 미치는 인프라 문제 해결
애플리케이션 패턴: 개발자가 맞닥뜨리는 문제를 해결
애플리케이션을 여러 서비스로 분해하는 패턴
한 서비스를 여러 서비스로 분해하는 방법을 결정하는 것
두 가지 주요 분해 패턴 (각각이 서로의 대안)
비즈니스 능력에 따른 분해
(DDD) 하위 도메인에 따라 분해
통신 패턴
MSA는 기본적으로 분산 시스템이기에 프로세스 간 통신(IPC)이 아주 중요
통신 패턴의 다섯 그룹
통신 스타일: 어떤 종류의 IPC를 사용하는지
디스커버리: 서비스 클라이언트는 서비스 인스턴스의 IP 주소를 어떻게 가져오는지
신뢰성: 서비스 통신 불능 시 서비스 간 통신 신뢰성은 어떻게 보장되는지
트랜잭셔널 메시징: 비즈니스 데이터 업데이트하는 DB 트랜잭션에 메시지를 송신하고 이벤트를 발행하는 행위를 어떻게 통합하는지
외부 API: 애플리케이션 클라이언트는 서비스와 어떻게 통신하는지
트랜잭션 관리를 위한 데이터 일관성 패턴
마이크로서비스는 각자 DB를 갖고 서로 느슨하게 결합
기존의 분산 트랜잭션은 요즘 애플리케이션에는 안 맞는 방법
사가 패턴(saga pattern)에 따라 데이터 일관성을 유지해야 한다.
데이터 쿼리 패턴
DB가 분리되어 있으면 조인 쿼리도 문제다.
서비스 데이터는 오직 그 서비스 API를 통해서만 접근 가능
DB에 분산 쿼리를 사용할 수 없다.
API 조합 패턴
하나 이상의 서비스를 호출해서 그 결과를 조합
CQRS
하나 이상의 데이터 레플리카를 유지해 쉽게 쿼리하는 방식
서비스 배포 패턴
마이크로서비스 애플리케이션은 다양한 언어와 프레임워크로 구성되어 있기에 배포가 훨씬 복잡하고 가동부가 상당히 많다.
고도로 자동화한 배포 인프라가 필요
가상 머신, 컨테이너, 서버리스 기술을 응용한 배포 플랫폼이 바람직
단순한 UI로 서비스를 배포/관리할 수 있도록
관측성 패턴: 애플리케이션 동작 파악
운영자의 주 임무는 애프리케이션 런타임 동작을 이해하고 갖가지 문제를 진단/조치하는 일
마이크로서비스는 문제를 진단하기 매우 까다롭다.
모놀리식과 달리 로그 파일 하나로는 알 수 없는 문제가 많다.
관측 가능한 서비스를 설계하려면 다음 패턴들이 필요
헬스 체크 API: 서비스 가동 상태를 반환하는 엔드 포인트 표출
로그 수집: 서비스 내역을 중앙 로깅 서버에 출력하여 검색/경고 기능 제공
분산 추적: 각 외부 요청마다 ID를 부여하여 서비스 통과 과정 추적
예외 추적: 예외 발생 시 추적 서비스에 보고
애플리케이션 지표: 카운터, 게이지 등 메트릭 측정, 지표 서버에 표출
감사 로깅: 사용자가 한 일을 로깅
서비스 테스트 자동화 패턴
마이크로서비스는 단위 서비스 크기가 작아 테스트는 쉽다.
하지만 서로 다른 여러 서비스가 조화롭게 잘 작동되는지 테스트하는 일이 더 즁요
느리고 복잡한, 취약한 E2E 테스트는 가급적 피하는 것이 상책
서비스를 분리해서 테스트하는 단순화 패턴이 필요
컨슈머 주도 계약 테스트: 클라이언트가 의도한대로 서비스가 동작하는지
컨슈머 쪽 계약 테스트: 클라이언트와 서비스가 상호 통신 가능한지 확인
서비스 컴포넌트 테스트: 서비스를 따로따로 테스트
횡단 관심사 처리 패턴
마이크로서비스는 모든 서비스가 반드시 구현해야 할 관심사가 한두 가지가 아니다.
관측성 패턴, 디스커버리 패턴, DB 자격증명 등
횡단 관심사를 처리하는 프레임워크에서 마이크로서비스 섀시(Chassis) 패턴을 적용하는 편이 바람직
보안 패턴
MSA에선 일반적으로 API 게이트웨이가 신원, 역할 등을 인증한 후 호출할 서비스에 관련 정보를 전달한다.
JWT와 같은 토큰을 적용하는 것이 일반적
1.7 마이크로서비스 너머: 프로세스와 조직
올바른 아키텍처뿐만 아니라 올바른 조직, 개발/배포 프로세스도 수립되어야 소프트웨어 개발에 성공할 수 있다.
1.7.1 소프트웨어 개발/전달 조직
사업이 성공하면 기술 팀 규모도 커지기 마련
팀의 크기가 커지면 소통 오버헤드도 커짐
규모가 큰 팀을 여러 팀으로 나누는 것이 좋다.
MSA는 팀이 자율적으로 움직이게 만드는 핵심 역할을 담당
다른 팀과 협의 없이 독립적으로 서비스를 개발, 배포, 확장 가능
개발 조직 확장성도 좋다.
1.7.2 소프트웨어 개발/전달 프로세스
MSA는 애자일 개발 프로세스를 도입하고 스크럼, 칸반 등을 실천해야 한다.
데브옵스 일부인 지속적 전달/배포를 실천하자
지속적 전달을 위해선 자동화 테스트 등의 높은 수준의 자동화는 필수
지속적 배포는 릴리스 가능한 코드를 프로덕션에 자동 배포하는
1.7.3 마이크로서비스를 받아들이는 인간적 요소
MSA를 도입하면 조직, 개발 프로세스가 모두 변화한다.
인간의 근무 환경도 달라짐