ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • incident.io에서 모놀리식 유지하면서 역할은 분리하는 방법
    개발하면서/타인글보면서 2023. 12. 16. 20:20
    반응형

    꽤 오래된 얘기지만 Monorepo로 코드 유지하면서 MSA 운영하는 게 심심치 않게 언급된다.

    괜찮은 방법인가 보다.

     

    Monorepo로 운영하는 회사 다닌적은 없지만 Monorepo의 장점을 상상해봤다.

    1. 회사에서 공통적으로 쓰는 라이브러리(DB 커넥션, 캐시, 유틸성) 

    2. 하나의 Repo로 유지를 하니 코드 검색이 쉽고 빠를 듯

     

    MSA

    네트워크 비용과 모니터링, 회사 내 코드 컨벤션 통일 어려움, 추적이 쉽지 않다는 점이 단점이 있고,

    장점은 타 팀에 의존이 덜해서 각 팀만의 개발 및 운영이 용이하고
    특정 서비스에 오류가 발생하면 해당 서비스만 격리하기 쉽다는 장점이 있다.

     

    Monorepo 하면 가장 먼저 생각나는 회사는 구글이다.

    https://qeunit.com/blog/how-google-does-monorepo/

     

    How Google Does Monorepo - QE Unit

    Google has made structuring choices in relation of its repositories since 1999. Choices that have proven to be the right ones.

    qeunit.com

     

     

    우리가 알고 있는 MSA(서비스 단위로 분리)가 아닌 workload 단위로 분리하는 방법을

    소개하는 글이 있어 정리했다.

     

    Keep the monolith, but split the workloads | incident.io

    Everybody loves a monolith, but you can hit issues as you scale. This post is about a technique – splitting your workloads – that can significantly reduce that pain, costs little, and can be applied early.

    incident.io

     

     

     


    모노리식 설계를 굉장히 좋아하지만
    반년 사이에 20명에서 200명의 엔지니어가 되고, 10GB였던 Postgres 용량이 5TB가 되면서
    기존 모노리식 설계는 이득보다 손해가 컸다.

     

    그러던 와중에 사고 발생!!

    2022년 11월에 약 32분 동안 반복적으로 크래시가 발생하는 문제가 있었다.

     

    회고 해보자면

    • Go monolith 설계의 application이 Heroku에서 돌고 있었고, 비동기 메시지큐로 GCP Pub/Sub을 사용했다.
    • 그리고 application은 한 개의 실행파일로 여러 서버로 실행되고 있었는데
      하나의 applicaiton은 web과 cron 그리고 worker 기능을 했다.
    • 잘못된 Pub/Sub 메시지가 입력되면서 처리할 수 없는 패닉이 발생했고
      전체 application(web, cron, worker)에 크래시로 이어졌다.

    최악이었다. MSA였다면 문제 되는 부분만 격리했다면 전체 크래시 되는 일은 없었을 텐데... 그렇죠?

     

    MSA로 했다면

    잘못된 Pub/Sub 메시지에 영향을 받는 서비스에만 문제를 격리시킬 수 있었고

    하나의 서비스에만 온전히 자원 할당되어 문제가 전파되는 것을 막을 수 있었다.

     

    하지만 추가 작업(분산 시스템 문제, RPC, 기타 등등)이 필요해서
    MSA의 이점은 살리되 MSA의 추가 작업은 하지 않는 방향으로 진행했다.

     

    Rule 1: workload를 절대 섞지 않는다.

    incident.io application은 크게 3개의 workload로 구성되었다.

    1. 사용자 요청을 담당하는 Web
    2. 비동기 작업을 담당하는 Pub/Sub
    3. 스케줄링 작업을 담당하는 Cron

    3개의 workload가 하나의 process에 돌면서 특정 코드 문제가 전체로 퍼지는 문제와,

    특정 workload가 리소스를 많이 잡아먹으면 다른 workload는 적은 리소스로 실행되는 문제가 있었다.

     

    같은 코드, 같은 Docker Image와 같은 환경 변수지만 실행할 때 argument를 다르게 줘서
    web, cron, worker를 각각 따로 실행하도록 분리했다.

    개발환경 설정의 복잡함도 없고 RPC 프레임워크도 필요하지 않으며 빠르게 적용 가능한 방법이었다.

     

    Rule 2: workload마다 guardrail을 세운다.

    Application들은 단순히 코드만 실행하는 게 아니라 캐시나 디비 같은 리소스를 사용한다.

     

    workload의 역할에 맞게 Connection Pool을 만들어 준다.

    예를 들어 Pub/Sub 메시지를 받아 BigQuery에 저장하는 Pub/Sub workload는

    DB Pool을 두 개로 설정한다.

    늦게 처리해도 되지만 DB에 영향을 주면 안 되기 때문에 적게 할당한다.
    (마치 Kafka partiton 할당 작게 하는 느낌)

     

    어라? monolith 설계가 문제네? MSA 적용해야겠다가 아니라
    문제의 본질을 파악하는게 중요하다는 마지막 문단이 나를 뜨끔하게 했다.

     

    So let’s not throw the baby out with the bathwater.

    ※ 목욕물과 함께 아기를 내던지지 말라.(중요하지 않은 것을 없애려다 소중한 것을 잃지 마라)

    반응형

    댓글

Designed by Tistory.