LODY/정리

클라우드 네이티브 / 마이크로서비스와 오케스트레이션

마이크로서비스와 오케스트레이션

🟣 하나로 만들던 것을 쪼개는 이유

전통적인 방식은 애플리케이션 하나에 모든 기능을 담는 것이었다. 회원, 주문, 결제, 알림이 한 코드베이스에 있고 한 프로세스로 배포된다. 이걸 모놀리식이라 부른다. 작을 때는 이게 가장 단순하고 빠르다. 배포도 하나, 관리도 하나다.

문제는 커졌을 때다. 알림 기능 한 줄을 고쳐도 전체를 다시 배포해야 하고, 주문 트래픽만 몰려도 앱 전체를 늘려야 한다. 결제 코드의 버그가 회원 기능까지 멈추게 한다. 규모가 커질수록 이 한 덩어리가 발목을 잡는다.

마이크로서비스는 이 덩어리를 기능별 독립 서비스로 쪼갠다. 회원 서비스, 주문 서비스, 결제 서비스가 각각 배포되고 각각 확장된다. 주문 트래픽이 몰리면 주문 서비스만 늘리면 되고, 알림을 고쳐도 알림만 다시 배포한다. 앞 책의 Spring으로 만든 서버가 이런 서비스 하나에 해당한다.


🟣 쪼갠 대가를 먼저 말해야 한다

마이크로서비스를 이점만 말하면 얕게 읽힌다. 면접에서도 대가를 함께 말할 수 있느냐로 이해의 깊이가 갈린다. 쪼개는 순간 새로운 문제들이 생긴다.

모놀리식에서 함수 호출 한 번이던 것이, 서비스가 나뉘면 네트워크 통신이 된다. 네트워크는 함수 호출과 달리 느리고, 실패하고, 순서가 뒤바뀔 수 있다. 하나의 트랜잭션으로 처리되던 주문과 결제가 이제 두 서비스에 나뉘어, 데이터 정합성을 맞추기가 훨씬 어려워진다. 앞 책 데이터베이스에서 본 ACID 트랜잭션이 서비스 경계를 넘으면 그대로 성립하지 않는다.

그래서 판단이 필요하다. 작은 서비스는 모놀리식으로 시작하는 게 옳다. 통신 비용과 정합성 복잡도를 감당할 만큼 규모가 크고 팀이 나뉘어 있을 때 마이크로서비스가 값을 한다. 도구가 좋아서가 아니라 문제가 그걸 요구할 때 쪼개는 것이다.


🟣 컨테이너가 수백 개가 되면

마이크로서비스를 컨테이너로 배포하면, 서비스 종류마다 여러 개씩 떠서 컨테이너가 금세 수십, 수백 개가 된다. 이걸 사람이 관리할 수 없다. 어느 서버에 몇 개를 띄울지, 하나가 죽으면 어떻게 다시 띄울지, 트래픽에 따라 몇 개로 늘릴지를 일일이 손으로 할 수 없다.

이 관리를 대신하는 것이 오케스트레이션이고, 사실상 표준이 쿠버네티스다. 쿠버네티스에 "주문 서비스를 항상 3개 띄워 둬라"라고 원하는 상태를 선언하면, 쿠버네티스가 그 상태를 유지한다. 하나가 죽으면 알아서 새로 띄우고, 서버 한 대가 꺼지면 다른 서버로 옮긴다. 앞 장의 systemd가 한 서버 안에서 프로세스를 살려 두었다면, 쿠버네티스는 여러 서버에 걸쳐 그 일을 한다.

원하는 상태 선언주문 서비스 3개쿠버네티스(현재를 맞춘다)파드 1파드 2파드 3

핵심은 선언형이라는 점이다. "이렇게 해라"라는 절차가 아니라 "이 상태였으면 좋겠다"라는 목표를 준다. 나머지는 쿠버네티스가 현재와 목표의 차이를 계속 좁힌다. Flutter의 선언형 UI에서 "화면이 이래야 한다"고 선언하면 프레임워크가 맞춰 그리던 그 사고방식이, 인프라 관리에서도 똑같이 나타난다.


🟣 파드, 디플로이먼트, 서비스

쿠버네티스의 기본 개념은 세 가지만 잡으면 된다.

파드는 배포의 최소 단위다. 보통 컨테이너 하나가 파드 하나에 담긴다. 쿠버네티스는 컨테이너를 직접 다루지 않고 이 파드 단위로 배치한다.

디플로이먼트는 "이 파드를 몇 개 유지하라"를 선언하는 것이다. 앞 그림의 "주문 서비스 3개"가 디플로이먼트다. 파드가 죽으면 디플로이먼트가 새로 만들어 개수를 맞춘다. 배포할 새 버전이 있으면 파드를 하나씩 교체해 무중단으로 롤아웃한다.

서비스는 파드들의 고정된 접속 지점이다. 파드는 죽고 새로 뜨면서 IP가 계속 바뀐다. 그래서 파드 IP로 직접 부르면 안 된다. 서비스가 그 앞에 서서 변하지 않는 이름을 제공하고, 뒤의 파드들로 요청을 분산한다. 앞 책 Nginx의 로드 밸런싱과 같은 역할이 클러스터 안에서 이뤄지는 것이다.


🟣 정리

  • 모놀리식은 작을 때 가장 단순하다. 규모가 커져 부분 배포와 부분 확장이 필요할 때 마이크로서비스로 쪼갠다.
  • 쪼개는 대가가 있다. 함수 호출이 네트워크 통신이 되고, 트랜잭션이 서비스 경계를 넘으면 정합성이 어려워진다. 작으면 모놀리식으로 시작하는 게 옳다.
  • 컨테이너가 수백 개가 되면 사람이 관리할 수 없다. 쿠버네티스가 원하는 상태를 선언받아 유지한다. systemd의 역할이 여러 서버로 확장된 것이다.
  • 파드(배포 단위), 디플로이먼트(개수 유지·롤아웃), 서비스(고정 접속 지점) 세 개념이 기본이다.
  • 선언형 사고는 Flutter의 UI, 쿠버네티스의 인프라에서 똑같이 나타난다. "이 상태였으면"을 주면 나머지를 프레임워크가 맞춘다.

컨테이너를 만들고 무리를 관리하는 데까지 왔다. 남은 것은 코드 한 줄이 실제로 실행 중인 서비스가 되기까지를 자동으로 잇는 일이다. 마지막 장은 그 자동화, CI/CD 파이프라인이다.