3 điểm bởi xguru 2024-09-02 | Chưa có bình luận nào. | Chia sẻ qua WhatsApp
  • Vận hành hơn 2.800 dịch vụ theo MSA và đang thu được rất nhiều giá trị
  • Tuy nhiên, kiến trúc này cũng có những khó khăn. Một trong số đó là thực hiện thay đổi thư viện trên mọi dịch vụ
  • Thông thường, bạn phải chọn hai trong ba yếu tố: thư viện mới nhất, phiên bản thư viện nhất quán, và nỗ lực nâng cấp thấp

Chiến lược migration tập trung

  • Tại Monzo, họ tin rằng có thể đạt được cả ba thuộc tính trên ở mức cao thông qua cách tiếp cận migration được dẫn dắt tập trung
  • Thay vì chuyển trách nhiệm migration cho chủ sở hữu dịch vụ, họ ưu tiên để một đội duy nhất dẫn dắt migration
  • Nhờ đó có thể tránh được chi phí điều phối cao (dẫn đến migration chậm) và rủi ro dự án bị đình trệ (dẫn đến thiếu nhất quán)
  • Để một đội duy nhất có thể hoàn tất migration trong thời gian hợp lý, họ phụ thuộc nhiều vào các yếu tố như:
    • Các lựa chọn công nghệ cốt lõi (ví dụ: mức độ nhất quán cao, sử dụng monorepo)
    • Tận dụng tự động hóa ở quy mô lớn (ví dụ: công cụ triển khai hàng loạt dịch vụ, kiểm tra rollback tự động)

Migration từ OpenTracing SDK sang OpenTelemetry SDK

  • Gần đây, Monzo đã hiện thực hóa chiến lược này qua một dự án chuyển từ OpenTracing SDK sang OpenTelemetry SDK
  • Mọi dịch vụ đều xuất dữ liệu tracing sang Jaeger. Trước đây, việc này được thực hiện qua OpenTracing và Jaeger Go SDK
  • Các thư viện này hiện đã bị deprecate, và cộng đồng đã hợp nhất sang OpenTelemetry
  • Để tạo nền tảng cho việc cải thiện hệ thống tracing, trước tiên họ muốn thay các thư viện không còn được dùng bằng OpenTelemetry SDK

Nguyên tắc migration

  • Migration tập trung nhưng minh bạch với chủ sở hữu dịch vụ. Họ ưu tiên chiến lược có thể được thực hiện tập trung bởi một đội duy nhất để giảm tối đa chi phí điều phối và giảm rủi ro migration bị đình trệ
  • Không có downtime. Phần lớn migration liên quan đến các dịch vụ quan trọng với chức năng cốt lõi của ngân hàng, nên không thể chấp nhận downtime
  • Roll forward dần dần, rollback nhanh. Với các thay đổi lớn, cần có khả năng roll forward theo từng bước để giảm blast radius khi có sự cố. Nhưng đồng thời cũng phải có thể rollback nhanh toàn bộ nếu cần
  • Quy tắc 80/20 cho tự động hóa. Trong migration quy mô lớn, thường có tỷ lệ cao các thay đổi khớp với một mẫu chung. Những thay đổi này có thể dễ dàng tự động hóa. Với các trường hợp đặc thù hơn, tự động hóa cho lợi ích giảm dần, và xử lý theo từng case sẽ hiệu quả hơn. Để tránh các bất ngờ khó chịu và dễ theo dõi tiến độ, nên phân loại trước các thay đổi cần thiết vào hai nhóm này

Chiến lược migration

  • Tại Monzo, có một chuỗi bước migration được sử dụng một cách có hệ thống

1. Lập kế hoạch và đồng thuận

  • Migration đi kèm rủi ro đáng kể. Không chỉ vì nó ảnh hưởng đến rất nhiều dịch vụ, mà còn vì đội thực hiện migration có thể không có nhiều (hoặc bất kỳ) bối cảnh nào về các dịch vụ đang được migration
  • Dù hướng tới tính nhất quán giữa các dịch vụ, luôn có ngoại lệ, và họ muốn phát hiện những bất ngờ đó càng sớm càng tốt
  • Vì vậy, điều rất quan trọng là quy trình lập kế hoạch phải minh bạch và mọi kỹ sư đều có cơ hội đóng góp
  • Để làm điều đó, họ có hai quy trình:
    • Đề xuất: Monzo viết các tài liệu này rất nhiều. Hầu như mọi việc quan trọng cuối cùng đều được chia sẻ trong một kênh Slack chung nơi mọi người trong công ty có thể đưa ý kiến
    • Rà soát kiến trúc: Với các thay đổi lớn nhất, họ tổ chức các cuộc họp rà soát kiến trúc đồng bộ để đi sâu hơn vào các khu vực cụ thể có nhiều tranh luận hơn hoặc rủi ro cao hơn. Mục tiêu không phải là xin phê duyệt hay chữ ký, mà là thúc đẩy trạng thái thiết kế tiến triển một cách có ý nghĩa để tăng tốc dự án

2. Bọc thư viện cũ

  • Thay vì cài thư viện mới và cập nhật mã dịch vụ để gọi nó ngay, họ quyết định trước tiên bọc thư viện cũ

    1. Có thể chặn các lời gọi tới thư viện nền tảng và quyết định triển khai nào sẽ được dùng dựa trên cấu hình động. Nhờ đó có thể dễ dàng roll forward và rollback mà không cần triển khai lại mọi dịch vụ
    2. Thư viện mới có các kiểu/hàm khác biệt đáng kể. Việc cập nhật mọi call site đòi hỏi nhiều công sức, và trong một số trường hợp lợi ích từ API mới là không đáng kể. Bằng cách bọc thư viện cũ, trong các trường hợp này có thể giữ giao diện tương tự thư viện cũ để việc cập nhật call site dễ dàng hơn
  • Các lợi ích khác của việc bọc thư viện:

    • Có thể instrument bằng thư viện telemetry riêng
    • Có thể cung cấp một interface mang tính định hướng hơn

3. Cập nhật call site

  • Việc sử dụng thư viện này khớp với một mẫu phổ biến:

    • Có một số lượng nhỏ hàm/kiểu được tham chiếu nhiều lần trên toàn bộ codebase
    • Sau đó là một long tail gồm các hàm/kiểu chỉ được tham chiếu ở vài nơi
  • Họ xử lý hai trường hợp này theo cách khác nhau:

    • Với số ít hàm/kiểu được tham chiếu ở nhiều nơi, họ tự động hóa tối đa có thể. Với thư viện này, họ chủ yếu dựa vào goplsgorename để thực hiện refactor tự động
    • Để xử lý long tail các hàm/kiểu chỉ được tham chiếu ở vài nơi, họ dùng cách tiếp cận thủ công theo từng case. Trong một số trường hợp, họ migration thủ công. Ở các trường hợp khác, họ nhận ra có thể dùng API hiện có phổ biến hơn để làm cùng công việc, nên chuyển sang dùng các API đó. Điều này đồng nghĩa không còn cần xử lý chúng như các case đặc biệt nữa, đồng thời có thêm lợi ích phụ là giữ API của thư viện wrapper nhỏ gọn và mang tính định hướng
  • Ngoài việc bọc thư viện cũ, họ còn chặn việc phát sinh phụ thuộc mới vào thư viện cũ. Việc này được thực hiện bằng cách thêm kiểm tra CI sử dụng semgrep

4. Bọc thư viện mới

  • Khi thư viện cũ đã được bọc, họ có thể bắt đầu thêm thư viện mới phía sau thư viện wrapper
  • Ban đầu, triển khai mới bị vô hiệu hóa qua cấu hình. Điều này có nghĩa là chưa dự kiến có thay đổi hành vi nào và họ có thể tiếp tục hợp nhất dần các thay đổi vào nhánh master

5. Triển khai hàng loạt dịch vụ

  • Trước khi bắt đầu bật triển khai mới, cần bảo đảm rằng mọi dịch vụ đang chạy đều có thể hỗ trợ triển khai mới
  • Với các loại thay đổi thư viện khác, có thể chỉ triển khai một tập con dịch vụ có tính năng mới tại một thời điểm. Nhưng với thư viện tracing, nếu một dịch vụ đã được migration để dùng thư viện mới thì mọi dịch vụ mà nó có thể gọi tới trong giai đoạn chuyển tiếp cũng phải hỗ trợ tính năng mới
  • Để quản lý việc triển khai số lượng lớn dịch vụ, họ xây dựng một công cụ triển khai hàng loạt có thể đẩy thay đổi thư viện tới mọi dịch vụ dưới dạng tác vụ batch bất đồng bộ
  • Để giảm thiểu tác động của các lần triển khai sai tiềm ẩn:
    • Sử dụng kiểm tra rollback tự động
    • Triển khai trước cho các dịch vụ ít quan trọng nhất. Họ gắn thẻ "tier" cho mọi dịch vụ, và công cụ triển khai hàng loạt dùng thông tin này để ưu tiên các lần triển khai ít rủi ro nhất

6. Kiểm soát rollout bằng cấu hình

  • Vấn đề của công cụ triển khai hàng loạt là nó tương đối chậm. Điều họ thực sự muốn tránh là triển khai mọi dịch vụ xong rồi mới phát hiện thư viện mới có vấn đề và không thể rollback nhanh
  • Vì vậy, thay vì triển khai với triển khai mới đã bật sẵn, họ triển khai khả năng bật triển khai mới thông qua hệ thống cấu hình
  • So với triển khai thông thường, lợi ích của việc dùng hệ thống cấu hình ở đây là tốc độ. Mọi dịch vụ làm mới cấu hình mỗi 60 giây, nên khi cần có thể rollback nhanh
  • Cách này cũng cho phép kiểm soát nhiều hơn rất nhiều về thời điểm triển khai mới được sử dụng. Ví dụ, chỉ bật cho một tập người dùng cụ thể hoặc cho một tỷ lệ phần trăm ngẫu nhiên của request
  • Trong trường hợp này, họ chọn rollout chỉ cho các API endpoint do đội sở hữu, và kích hoạt theo xác suất tăng dần

7. Dọn dẹp

  • Khi đã chuyển hoàn toàn sang triển khai mới, họ thực hiện bước thỏa mãn là loại bỏ triển khai cũ khỏi thư viện wrapper

Siêu năng lực migration

  • Kiểu migration tập trung này khả thi tại Monzo nhờ các lựa chọn công nghệ nền tảng mà họ đã đưa ra và các công cụ mà họ tiếp tục đầu tư
  • Công nghệ nhất quán: Mọi dịch vụ đều được viết bằng Go và dùng cùng một phiên bản của thư viện cũ. Điều này giúp việc tự động hóa thay đổi dễ hơn rất nhiều. Ví dụ, chỉ cần một công cụ refactor duy nhất thay vì một công cụ cho mỗi ngôn ngữ
  • Monorepo: Toàn bộ mã dịch vụ nằm trong một monorepo duy nhất, giúp việc refactor quy mô lớn trong một commit đơn trở nên dễ dàng hơn nhiều. Điều này cũng cho phép áp dụng việc sử dụng một thư viện cụ thể trên phạm vi toàn cục trong kiểm tra CI, qua đó giúp duy trì tính nhất quán
  • Triển khai hàng loạt: Khi có nhiều thành phần có thể triển khai, cần một quy trình triển khai tự động để đẩy thay đổi thư viện
  • Dịch vụ cấu hình nhẹ và linh hoạt: Quy trình triển khai an toàn nhưng chậm (mỗi lần triển khai mất vài phút). Cần một quy trình nhẹ hơn và linh hoạt hơn để bật/tắt nhanh các tính năng mới trên quy mô lớn dịch vụ

Kết luận

  • Trước đây, họ từng cố gắng phân tán migration, nhưng điều này cuối cùng luôn dẫn đến các migration chưa hoàn thành và rất nhiều nỗ lực điều phối
  • Đó là lý do Monzo rất ưu tiên migration tập trung. Một đội sẽ phải trả chi phí tương đối cao, nhưng xét tổng thể sẽ tốn ít công sức hơn và khả năng duy trì tính nhất quán cũng cao hơn đáng kể
  • Cách tiếp cận này tạo ra một vòng lặp tích cực:
    • Đội thực hiện migration có động lực mạnh để đầu tư vào công cụ tự động hóa migration
    • Đồng thời cũng duy trì tính nhất quán kỹ thuật (giúp việc xây công cụ dễ hơn)
    • Tuy nhiên, họ vẫn thực dụng về mức độ tự động hóa - áp dụng quy tắc 80/20
  • Bên cạnh các công cụ mà Monzo tiếp tục đầu tư, cách tiếp cận này cũng chỉ khả thi nhờ một số lựa chọn công nghệ cốt lõi được đưa ra từ đầu
    • Chủ yếu là vì họ sử dụng một tập công nghệ có tính định hướng và bị giới hạn

Chưa có bình luận nào.

Chưa có bình luận nào.