사전 마이크로서비스
패턴

마이크로서비스

gabury1고친 사람 github-actions[bot]

마이크로서비스는 애플리케이션 하나를 작은 서비스 여러 개로 쪼개 만들기로 하는 결정입니다. 서비스마다 따로 배포합니다. 한 서비스를 고쳐도 나머지는 멈추지 않습니다. 대신 한 프로그램 안에서 함수를 부르듯 끝나던 일이 네트워크를 건너가게 됩니다.

쉽고 빠른 이해

마이크로서비스는 주문·결제·배송처럼 하는 일이 다른 부분을 각각 따로 도는 프로그램으로 떼어 놓습니다. 결제 코드를 고쳤다면 결제 서비스만 새로 올립니다.

한 덩어리로 만든 애플리케이션은 한 줄을 고쳐도 전부를 다시 배포해야 합니다. 여러 팀이 같은 덩어리를 건드리면 서로의 배포를 기다리게 됩니다. 그 기다림을 없애려고 배포 단위를 쪼갭니다.

  1. 하는 일을 기준으로 서비스를 나눕니다
  2. 서비스마다 자기 데이터를 따로 가집니다
  3. 서로가 필요하면 네트워크로 부릅니다

대가는 한 프로그램 안에서 공짜였던 것이 전부 값을 갖는다는 것입니다. 호출이 실패할 수 있고, 데이터가 잠깐 어긋나며, 서비스가 몇 개든 한눈에 지켜보는 도구가 따로 필요합니다.

상세

한 덩어리로 배포하는 애플리케이션

주문을 받고 결제하고 배송을 잡는 쇼핑몰을 프로그램 하나로 만들었다고 해 봅시다. 코드가 한 저장소에 있고 빌드 결과물도 하나입니다. 이렇게 한 덩어리로 묶여 한 번에 배포되는 애플리케이션을 모놀리스라고 부릅니다.

덩어리가 작을 때는 이 방식이 편합니다. 주문 코드가 결제 코드를 쓸 때 함수를 부르면 되고, 하나의 데이터베이스 안에서 두 테이블을 같이 고칠 수 있습니다.

불편해지는 것은 사람이 늘 때입니다. 팀 다섯이 같은 덩어리를 고치면 배포도 다섯 팀이 함께 해야 합니다. 결제 팀이 고친 한 줄 때문에 배포가 미뤄지면 주문 팀의 수정도 같이 멈춥니다.

바쁜 부분만 골라 늘리기도 어렵습니다. 조회 요청이 몰려도 덩어리 전체를 여러 대에 복사해야 합니다. 시스템 전체를 어떤 모양으로 지을지 정하는 결정을 소프트웨어 아키텍처라고 하고, 마이크로서비스는 그 결정의 한 갈래입니다.

서비스로 쪼갠 뒤의 모양

마이크로서비스는 그 덩어리를 하는 일 단위로 잘라 각각 따로 도는 프로그램으로 만듭니다. 주문 서비스, 결제 서비스, 배송 서비스가 저마다 자기 프로세스로 뜨고 자기 데이터베이스를 갖습니다.

flowchart TD
    subgraph 한덩어리["모놀리스 · 배포 단위 하나"]
        M["주문 · 결제 · 배송 코드가 한 프로세스"] --> MD[("데이터베이스 하나")]
    end
    subgraph 쪼갠뒤["마이크로서비스 · 배포 단위 셋"]
        A["주문 서비스"] --> AD[("주문 데이터베이스")]
        B["결제 서비스"] --> BD[("결제 데이터베이스")]
        C["배송 서비스"] --> CD[("배송 데이터베이스")]
    end

위쪽은 배포 단위가 하나이고 아래쪽은 셋입니다. 아래쪽에서 결제 코드를 고치면 결제 서비스만 다시 올립니다. 나머지 둘은 돌던 채로 있습니다.

바뀌는 것은 코드의 양이 아니라 경계입니다. 모놀리스에서도 주문과 결제는 서로 다른 패키지로 나뉘어 있었을 것입니다. 마이크로서비스는 그 경계를 프로세스 경계로 바꿉니다.

경계가 프로세스를 가르면 부르는 방법도 달라집니다.

Java
// 한 덩어리 안
Order o = orders.find(id);   // 곧바로 값

// 서비스로 나눈 뒤
Order o = client.get(url);   // 네트워크 한 번

위쪽 호출은 실패할 구석이 거의 없습니다. 아래쪽 호출은 상대가 안 떠 있을 수도, 응답이 늦을 수도, 도중에 끊길 수도 있습니다. 여러 대의 컴퓨터가 네트워크로 협력해 하나의 일을 하는 구조를 분산 시스템이라고 합니다. 마이크로서비스를 고른다는 것은 내 애플리케이션을 분산 시스템으로 만든다는 뜻입니다.

서비스 하나의 조건

프로그램을 여러 개로 나눴다고 전부 마이크로서비스가 되지는 않습니다. 아래 셋을 지켜야 나눈 효과가 납니다.

조건 뜻 안 지키면
따로 배포한다 다른 서비스를 멈추지 않고 이 서비스만 새로 올린다 배포 일정이 다시 묶인다
자기 데이터를 가진다 이 서비스의 데이터는 이 서비스만 직접 읽고 쓴다 테이블 변경이 남의 서비스를 깨뜨린다
약속된 창구로만 부른다 바깥은 공개된 요청 모양으로만 이 서비스를 쓴다 안쪽 구조를 못 바꾼다

셋째 줄의 창구는 API(Application Programming Interface, 응용 프로그램 인터페이스)입니다. 프로그램이 다른 프로그램을 부르는 약속이고, 서비스가 바깥에 열어 둔 주소와 요청 모양이 그 서비스의 API 입니다.

셋 다 묻는 것은 하나입니다. 이 서비스를 남의 사정과 상관없이 바꿀 수 있나. 하나라도 어기면 서비스는 나뉘었는데 배포는 함께 해야 하는 상태가 됩니다. 이렇게 모양만 나뉜 구조를 분산 모놀리스라고 부릅니다. 한 덩어리의 불편함은 남겨 두고 네트워크의 값만 더 낸 셈입니다.

서비스를 가르는 기준

자르는 선은 하는 일을 따라갑니다. 주문·결제·배송처럼 업무가 갈리는 곳이 경계입니다.

기술 계층을 따라 자르면 어긋납니다. 화면 서비스·업무 로직 서비스·데이터 서비스로 나누면 기능 하나를 고칠 때마다 세 서비스를 다 고치게 됩니다. 나누기 전보다 배포가 더 묶입니다.

경계를 어디에 그을지 정하는 설계 방법이 도메인 주도 설계입니다. 같은 낱말이 한 가지 뜻으로 통하는 범위를 경계 컨텍스트로 묶고, 그 묶음을 서비스 단위로 삼습니다. 주문에서 말하는 「상품」과 배송에서 말하는 「상품」이 담는 값이 다르면 둘은 다른 서비스입니다.

경계는 조직과도 붙어 있습니다. 서비스 하나를 한 팀이 끝까지 맡을 수 있어야 배포가 안 묶입니다. 서비스 하나에 세 팀이 달라붙으면 배포 일정이 다시 세 팀의 회의가 됩니다.

이름에 붙은 「마이크로」는 줄 수를 뜻하지 않습니다. 몇 줄 이하여야 한다는 기준은 없습니다. 한 팀이 감당할 수 있고 혼자 배포할 수 있으면 그 크기가 맞는 크기입니다.

데이터베이스를 나눠 갖는 까닭

조건 셋 중에 제일 자주 깨지는 것이 데이터입니다. 서비스는 나눴는데 데이터베이스는 하나를 같이 쓰는 구조가 흔합니다.

같이 쓰면 당장은 편합니다. 주문 서비스가 결제 테이블을 직접 조회하면 호출 한 번이 없어집니다. 대신 결제 팀은 그 테이블의 칼럼 하나를 못 바꾸게 됩니다. 누가 그 칼럼을 읽고 있는지 알 길이 없기 때문입니다.

그래서 서비스마다 저장소를 따로 둡니다. 남의 데이터가 필요하면 그 서비스에 물어봅니다. 대신 조인 한 번으로 끝나던 조회가 호출 두 번이 됩니다.

서비스 경계에서 끊기는 트랜잭션

트랜잭션은 여러 변경을 한 묶음으로 처리해 전부 반영되거나 전부 취소되게 하는 장치입니다. 데이터베이스가 하나일 때는 주문 저장과 재고 차감을 한 트랜잭션에 넣으면 끝납니다.

저장소가 갈리면 그 묶음이 끊깁니다. 주문은 저장됐는데 결제가 실패하는 때가 생깁니다. 두 저장소를 한 트랜잭션으로 묶는 2단계 커밋이 있지만, 참여자 하나가 멈추면 나머지가 결정을 기다리게 되어 서비스 사이에서는 잘 쓰지 않습니다.

대신 사가를 씁니다. 긴 작업을 단계로 쪼개고, 중간에 실패하면 앞 단계를 되돌리는 요청을 따로 보냅니다. 결제가 실패하면 주문을 취소 상태로 바꾸는 식입니다.

되돌리는 요청도 네트워크를 건너므로 같은 요청이 두 번 닿을 수 있습니다. 그래서 각 단계는 여러 번 받아도 결과가 같도록 만들어 둡니다. 이 성질이 멱등성입니다.

이 방식에서는 두 저장소가 잠깐 어긋나 있는 때가 생깁니다. 시간이 지나면 같아지지만 그 사이에 읽으면 다른 값이 나옵니다. 이렇게 뒤늦게 맞춰지는 상태가 결과적 일관성입니다. 마이크로서비스를 고르면 「주문은 됐는데 결제 기록이 아직 없는 몇 초」를 업무 규칙으로 받아들여야 합니다.

한 서비스의 장애가 번지는 길

요청 하나가 서비스 여럿을 거치면 그중 하나만 느려져도 앞쪽이 같이 기다립니다. 클라이언트가 서비스 열 개의 주소를 다 알 수는 없으므로 앞에 창구를 하나 세웁니다. 요청을 받아 안쪽 서비스로 넘기는 그 창구가 API 게이트웨이입니다.

sequenceDiagram
    participant 클라이언트
    participant 게이트웨이
    participant 주문 as 주문 서비스
    participant 결제 as 결제 서비스
    클라이언트->>게이트웨이: 주문 요청
    게이트웨이->>주문: 주문 만들기
    주문->>결제: 결제 요청
    Note over 주문,결제: 이 응답이 늦으면 앞의 둘도 같이 기다린다
    결제-->>주문: 결제 완료
    주문-->>게이트웨이: 주문 번호
    게이트웨이-->>클라이언트: 응답

결제 서비스가 답하지 않으면 주문 서비스의 요청이 그 앞에서 멈춥니다. 주문 서비스가 쓰던 연결은 붙잡힌 채로 남습니다. 연결을 다 쓰고 나면 결제와 상관없는 요청까지 못 받게 됩니다.

flowchart TD
    A["결제 서비스가 응답하지 않는다"] --> B["주문 서비스의 요청이 쌓인다"]
    B --> C["주문 서비스의 연결이 바닥난다"]
    C --> D["게이트웨이까지 응답이 멈춘다"]

한 곳의 멈춤이 이렇게 앞으로 번지는 것을 연쇄 장애라고 합니다. 막는 방법은 기다림을 끊는 것입니다. 정해진 시간이 지나면 포기하는 타임아웃을 걸고, 실패가 잦아지면 아예 호출을 끊었다가 나중에 다시 열어 보는 서킷 브레이커를 둡니다.

모놀리스에서는 이 고민이 없었습니다. 결제 코드가 느리면 그 함수만 느렸습니다. 네트워크를 건너는 순간부터는 남의 장애가 내 장애가 됩니다.

먼저 치러야 하는 운영 비용

서비스가 열 개면 배포도 열 번, 로그도 열 군데, 장애 원인도 열 군데에서 찾습니다. 손으로 하던 일이 열 배가 되므로 자동화가 먼저 있어야 합니다.

컨테이너는 프로그램과 그 실행 환경을 한 덩이로 묶어 어디서든 같은 모양으로 띄우는 방식입니다. 쿠버네티스 같은 도구가 그 덩이를 여러 대에 배치하고 죽은 것을 다시 띄웁니다. 코드가 합쳐지면 사람 손 없이 배포까지 가게 만드는 것이 지속적 배포입니다.

원인을 찾는 도구도 달라집니다. 요청 하나가 서비스 여럿을 거치므로 한 서버의 로그만 봐서는 어디서 늦어졌는지 모릅니다. 요청에 같은 번호를 붙여 지나간 서비스들의 기록을 한 줄로 잇는 것이 트레이스입니다.

이 도구들을 갖추기 전에 서비스부터 쪼개면, 장애가 났을 때 볼 수 있는 것이 나누기 전보다 줄어듭니다.

고를 만한 상황과 미룰 상황

이 결정이 이득이 되는 것은 팀이 여럿이고 그 팀들이 서로의 배포를 기다리고 있을 때입니다. 배포가 묶여서 생기는 손해가 네트워크와 운영에 드는 비용보다 클 때입니다.

팀이 하나이거나 업무 경계가 아직 흐릿하면 손해가 더 큽니다. 경계를 잘못 그으면 서비스 둘이 늘 함께 배포되는데, 그때는 코드를 옮기는 것이 아니라 서비스를 합쳐야 해서 훨씬 비쌉니다.

그래서 한 덩어리로 시작해 모듈 경계를 또렷하게 지키다가, 경계가 굳으면 떼어내는 순서를 많이 씁니다. 모듈로는 나뉘어 있지만 배포는 하나인 이 중간 단계를 모듈러 모놀리스라고 부릅니다.

관련 항목

마이크로서비스와 맞세워지는 구성 방식

모놀리스 · 모듈러 모놀리스 · 분산 모놀리스 · 서비스 지향 아키텍처 · 레이어드 · 헥사고날

서비스 경계를 긋는 데 쓰는 설계 방법

도메인 주도 설계 · 경계 컨텍스트 · 컨웨이의 법칙 · 스트랭글러 무화과 · 모듈화

서비스끼리 주고받을 때 쓰는 수단

REST · gRPC · RPC · 메시지 큐 · 메시지 브로커 · 이벤트 주도 · 서비스 간 통신

서비스 앞뒤에 세우는 기반 장치

API 게이트웨이 · 서비스 메시 · 서비스 디스커버리 · 로드 밸런서 · 사이드카

나뉜 데이터를 다시 맞추는 방법

사가 · 결과적 일관성 · 2단계 커밋 · 이벤트 소싱 · CQRS · 멱등성 · 보상 트랜잭션

장애가 번지는 것을 막는 방법

서킷 브레이커 · 타임아웃 · 재시도 · 벌크헤드 · 레이트 리미팅 · 단일 장애점

서비스를 띄우고 굴리는 실행 기반

컨테이너 · Docker · 쿠버네티스 · 오케스트레이션 · 수평 확장 · 무상태 서비스

나뉜 시스템을 들여다보는 수단

트레이스 · 분산 추적 · 관측 가능성 · 로그 집계 · 지표 · 상관 ID

서비스를 자주 내보내는 배포 방식

지속적 배포 · 지속적 통합 · 카나리 배포 · 블루-그린 배포 · 데브옵스

마이크로서비스가 속하는 상위 분류

소프트웨어 아키텍처 · 분산 시스템 · 아키텍처 양식 · 백엔드

다른 이름: microservices · 마이크로서비스 아키텍처 · microservice architecture