3P by xguru 17일전 | favorite | 댓글과 토론
  • MSA로 2,800개 이상의 서비스를 운영하며 많은 가치를 얻고 있음
  • 하지만 이러한 아키텍처에는 어려움도 있음. 그 중 하나는 모든 서비스에 대한 라이브러리 변경을 수행하는 것임
  • 일반적으로 최신 라이브러리, 일관된 라이브러리 버전, 낮은 업그레이드 노력 중 두 가지를 선택해야 함

중앙 집중식 마이그레이션 전략

  • Monzo에서는 중앙 집중식으로 주도하는 마이그레이션 접근 방식을 통해 위의 세 가지 속성을 높은 수준으로 달성할 수 있다고 믿음
  • 마이그레이션 책임을 서비스 소유자에게 넘기는 대신, 단일 팀이 마이그레이션을 주도하는 것을 선호함
  • 이를 통해 높은 조정 오버헤드(느린 마이그레이션으로 이어짐)와 프로젝트 중단 위험(불일치로 이어짐)을 피할 수 있음
  • 단일 팀이 합리적인 시간 내에 마이그레이션을 완료할 수 있도록 하기 위해 다음과 같은 것에 크게 의존함:
    • 핵심 기술 선택 (예: 높은 수준의 일관성, monorepo 사용)
    • 자동화의 대규모 활용 (예: 대량 서비스 배포 도구, 자동화된 롤백 검사)

OpenTracing SDK에서 OpenTelemetry SDK로 마이그레이션

  • 최근 OpenTracing SDK에서 OpenTelemetry SDK로 전환하는 프로젝트를 통해 Monzo의 전략을 실행에 옮김
  • 모든 서비스는 Jaeger에 추적 데이터를 내보냄. 이전에는 OpenTracing 및 Jaeger Go SDK를 통해 이를 수행했음
  • 이러한 라이브러리는 지금은 더 이상 사용되지 않으며, 커뮤니티는 대신 OpenTelemetry로 통합되었음
  • 추적 시스템을 개선하기 위한 기반을 마련하기 위해 먼저 사용되지 않는 라이브러리를 OpenTelemetry SDK로 교체하고자 함

마이그레이션 원칙

  • 서비스 소유자에게 투명한 중앙 집중식 마이그레이션. 조정 오버헤드를 최소화하고 마이그레이션 중단 위험을 줄이기 위해 단일 팀에서 중앙 집중식으로 수행할 수 있는 전략을 선호함
  • 가동 중지 시간 없음. 대부분의 마이그레이션은 은행의 핵심 기능에 중요한 서비스를 다루므로 가동 중지 시간을 허용할 수 없음
  • 점진적인 롤 포워드, 빠른 롤백. 대규모 변경의 경우 문제가 발생할 경우 폭발 반경을 줄이기 위해 점진적으로 롤 포워드할 수 있어야 함. 하지만 필요한 경우 빠르게 모든 것을 롤백할 수 있어야 함
  • 자동화에 대한 80/20 규칙. 대규모 마이그레이션에서는 일반적으로 공통 템플릿에 맞는 변경 사항의 비율이 높음. 이러한 변경은 쉽게 자동화할 수 있음. 더 독특한 사용 사례의 경우 자동화는 수익 감소를 제공하며, 사례별로 해결하는 것이 더 효율적임. 나쁜 놀라움을 피하고 진행 상황을 추적하기 쉽게 하려면 필요한 변경 사항을 사전에 이 두 가지 범주로 분류하는 것이 좋음

마이그레이션 전략

  • Monzo에서는 체계적으로 사용하는 일련의 마이그레이션 단계가 있음

1. 계획 및 정렬

  • 마이그레이션에는 상당한 위험이 따름. 대규모 서비스에 영향을 미칠 뿐만 아니라 마이그레이션을 실행하는 팀이 마이그레이션하는 서비스에 대해 많은(또는 어떠한) 맥락을 가지고 있지 않을 수 있음
  • 서비스의 일관성을 추구하지만 항상 예외가 있으며, 이러한 놀라움을 가능한 한 빨리 잡고 싶어함
  • 따라서 계획 프로세스가 투명하고 모든 엔지니어가 기여할 기회를 갖는 것이 매우 중요함
  • 이를 위해 두 가지 프로세스가 있음:
    • 제안: Monzo에서는 이를 많이 작성함. 실질적인 모든 것은 최종적으로 회사의 모든 사람이 의견을 말할 수 있는 단일 Slack 채널에서 공유됨
    • 아키텍처 검토: 가장 큰 변경 사항의 경우 더 논란이 있거나 위험한 특정 영역에 대해 더 깊이 있게 다루는 동기식 아키텍처 검토 회의를 가짐. 목표는 승인이나 서명을 받는 것이 아니라 설계 상태를 의미 있게 진행하여 프로젝트를 가속화하는 것임

2. 이전 라이브러리 래핑

  • 새로운 라이브러리를 설치하고 서비스 코드를 업데이트하여 호출하는 대신 먼저 이전 라이브러리를 래핑하기로 결정

    1. 기본 라이브러리에 대한 호출을 가로채고 동적 구성을 기반으로 사용할 구현에 대한 결정을 내릴 수 있음. 이를 통해 모든 서비스를 재배포할 필요 없이 쉽게 롤 포워드 및 롤백할 수 있음
    2. 새로운 라이브러리에서 크게 다른 유형/함수가 있었음. 모든 호출 사이트를 업데이트하려면 많은 노력이 필요했고, 경우에 따라 새로운 API의 이점이 미미했음. 이전 라이브러리를 래핑함으로써 이러한 경우 이전 라이브러리와 유사한 인터페이스를 유지하여 호출 사이트를 더 쉽게 업데이트할 수 있음
  • 라이브러리 래핑의 다른 이점:

    • 자체 원격 측정 라이브러리로 계측할 수 있음
    • 더 독단적인 인터페이스를 제공할 수 있음

3. 호출 사이트 업데이트

  • 이 라이브러리의 사용은 공통 패턴에 맞음:

    • 코드베이스 전체에서 여러 번 참조되는 작은 수의 함수/유형이 있었음
    • 그 다음에는 몇 군데에서만 참조되는 함수/유형의 긴 꼬리가 있었음
  • 이 경우를 각각 다르게 다룸:

    • 많은 곳에서 참조되는 소수의 함수/유형의 경우 가능한 한 자동화함. 이 라이브러리의 경우 주로 goplsgorename에 의존하여 자동화된 리팩토링을 수행함
    • 몇 군데에서만 참조되는 함수/유형의 긴 꼬리를 처리하기 위해 수동 사례별 접근 방식을 취함. 경우에 따라 이를 수동으로 마이그레이션함. 다른 경우에는 더 기존의 API를 사용하여 동일한 작업을 수행할 수 있음을 깨달았고, 따라서 이를 전환함. 이는 더 이상 특별한 경우로 처리할 필요가 없음을 의미했고, 래퍼 라이브러리의 API를 작고 독단적으로 유지하는 부수적인 이점이 있었음
  • 이전 라이브러리를 래핑하는 것 외에도 이전 라이브러리에 대한 새로운 종속성이 생기는 것을 차단함. 이는 semgrep을 사용하여 CI 검사를 추가하여 수행함

4. 새로운 라이브러리 래핑

  • 이전 라이브러리가 래핑되면 래퍼 라이브러리 뒤에 새 라이브러리를 추가하기 시작할 수 있음
  • 처음에는 새로운 구현이 구성을 통해 비활성화되었음. 이는 동작 변경이 예상되지 않고 마스터 브랜치에 대한 변경 사항을 계속 점진적으로 병합할 수 있음을 의미함

5. 대량 서비스 배포

  • 새로운 구현을 활성화하기 시작하기 전에 실행 중인 모든 서비스가 새로운 구현을 지원할 수 있는지 확인해야 함
  • 다른 종류의 라이브러리 변경의 경우 새로운 기능이 있는 서비스의 하위 집합만 한 번에 배포할 수 있음. 그러나 추적 라이브러리의 경우 서비스가 새 라이브러리를 사용하도록 마이그레이션된 경우 (과도적으로) 호출할 수 있는 모든 서비스도 새로운 기능을 지원해야 함
  • 많은 수의 서비스 배포를 관리하기 위해 비동기 일괄 작업으로 모든 서비스에 라이브러리 변경 사항을 푸시할 수 있는 대량 배포 도구를 구축함
  • 잠재적인 잘못된 배포의 영향을 완화하기 위해:
    • 자동화된 롤백 검사 사용
    • 가장 중요하지 않은 서비스를 먼저 배포. 모든 서비스에 "계층" 태그를 지정했으며, 대량 배포 도구는 이를 사용하여 가장 위험하지 않은 배포에 우선 순위를 부여함

6. 구성을 통한 롤아웃 제어

  • 대량 배포 도구의 문제는 상대적으로 느리다는 것. 우리가 정말 피하고 싶은 것은 모든 서비스를 배포했는데 새 라이브러리에 문제가 있고 빠르게 롤백할 수 없는 것을 발견하는 것
  • 따라서 새로운 구현을 활성화하여 배포하는 대신 구성 시스템을 통해 새로운 구현을 활성화할 수 있는 기능을 배포함
  • 일반 배포와 비교할 때 여기에서 구성 시스템을 사용하는 이점은 빠르다는 것. 모든 서비스는 60초마다 구성을 새로 고치므로, 필요한 경우 빠르게 롤백할 수 있음
  • 또한 새로운 구현이 사용되는 시기에 대해 훨씬 더 많은 제어 기능을 제공함. 예를 들어 특정 사용자 집합 또는 요청의 무작위 백분율에 대해서만 활성화할 수 있음
  • 이 경우 팀이 소유한 API 엔드포인트에 대해서만 롤아웃하기로 선택했으며, 점차 증가하는 확률에 따라 활성화함

7. 정리

  • 새로운 구현으로 완전히 전환하면 래퍼 라이브러리에서 이전 구현을 제거하는 만족스러운 작업을 수행함

마이그레이션 슈퍼파워

  • 이러한 종류의 중앙 집중식 마이그레이션은 Monzo에서 내린 기본적인 기술 선택과 계속 투자하고 있는 도구 덕분에 가능했음
  • 일관된 기술: 모든 서비스는 Go로 작성되었고 이전 라이브러리의 동일한 버전을 사용함. 이를 통해 변경 사항을 훨씬 더 쉽게 자동화할 수 있음. 예를 들어 (언어별로 하나가 아닌) 단일 리팩터링 도구만 사용하면 됨
  • Monorepo: 모든 서비스 코드가 단일 monorepo에 있어 대규모 리팩토링을 단일 커밋에서 훨씬 더 쉽게 수행할 수 있음. 또한 CI 검사에서 특정 라이브러리의 사용을 전역적으로 적용할 수 있어 일관성을 유지하는 데 도움이 됨
  • 대량 배포: 배포 가능한 구성 요소가 많은 경우 라이브러리 변경 사항을 푸시하기 위한 자동화된 배포 프로세스가 필요함
  • 경량 및 유연한 구성 서비스: 배포 프로세스는 안전하지만 느림(배포당 몇 분). 새로운 기능을 빠르고 즉시 대규모 서비스에서 활성화/비활성화하기 위한 더 가벼운 유연한 프로세스가 필요함

결론

  • 과거에는 마이그레이션을 분산시키려고 했지만, 이는 불가피하게 완료되지 않은 마이그레이션과 많은 조정 노력으로 이어졌음
  • 이것이 Monzo가 중앙 집중식 마이그레이션을 강력하게 선호하는 이유임. 한 팀이 상대적으로 높은 비용을 지불해야 하지만, 전반적으로 더 적은 노력을 들이고 일관성을 유지할 가능성이 크게 높아짐
  • 이러한 접근 방식은 선순환을 만들어냄:
    • 마이그레이션을 실행하는 팀은 마이그레이션을 자동화하기 위한 도구에 투자할 강력한 동기를 가짐
    • 또한 기술적 일관성을 유지함 (도구 구축을 더 쉽게 만듦)
    • 그러나 자동화 정도에 대해서는 여전히 실용적임 - 80/20 규칙을 적용
  • Monzo가 계속 투자하고 있는 도구 외에도, 이러한 접근 방식은 시작 시점에 내린 몇 가지 핵심 기술 선택 덕분에만 가능함
    • 주로 독단적이고 제한된 기술 집합을 사용한다는 사실 때문