9 điểm bởi GN⁺ 2024-11-01 | 1 bình luận | Chia sẻ qua WhatsApp
  • Robinhood là dịch vụ cân bằng tải nội bộ của Dropbox, được triển khai vào năm 2020
  • Nó định tuyến lưu lượng nội bộ giữa các máy chủ để phân phối tải dịch vụ một cách cân bằng
  • Trước Robinhood, phần lớn dịch vụ của Dropbox gặp khó khăn do phân bố tải không đồng đều giữa các backend
  • Sự khác biệt về phần cứng và giới hạn của các thuật toán cân bằng tải trước đây gây ra vấn đề độ tin cậy do các instance bị quá tải
  • Để giải quyết điều này, cần phải cấp phát tài nguyên quá mức cho fleet dịch vụ, kéo theo chi phí phần cứng tăng lên

Tính năng mới của Robinhood

  • Tận dụng bộ điều khiển PID (Proportional-Integral-Derivative) để quản lý mất cân bằng tải nhanh hơn và hiệu quả hơn
  • Điều này giúp cải thiện độ tin cậy của hạ tầng và tiết kiệm đáng kể chi phí phần cứng
  • Khi khối lượng công việc AI vận hành các tính năng thông minh mới nhất tăng lên, việc quản lý nhu cầu đối với tài nguyên GPU một cách hiệu quả trở nên quan trọng hơn bao giờ hết

Thách thức cân bằng tải tại Dropbox

  • Hệ thống service discovery của Dropbox có thể mở rộng tới hàng trăm nghìn host trên nhiều trung tâm dữ liệu toàn cầu
  • Một số dịch vụ của Dropbox có hàng triệu client, nhưng không thể cho phép mỗi client kết nối tới mọi instance máy chủ
    • Vì điều này gây áp lực bộ nhớ quá lớn lên máy chủ, và khi máy chủ khởi động lại, các bắt tay TLS có thể khiến máy chủ bị quá tải
  • Thay vào đó, hệ thống service discovery cung cấp cho mỗi client một tập con các máy chủ để kết nối
  • Chiến lược cân bằng tải tốt nhất mà client có thể sử dụng là round-robin danh sách địa chỉ do hệ thống service discovery cung cấp
    • Tuy nhiên, với cách này, tải trên từng instance máy chủ vẫn có thể rất mất cân bằng
    • Tăng kích thước tập con là một biện pháp giảm nhẹ đơn giản, nhưng không loại bỏ hoàn toàn sự mất cân bằng mà chỉ thêm một tham số nữa cho chủ sở hữu dịch vụ
  • Vấn đề sâu xa hơn là ngay cả khi gửi cùng số lượng request tới mỗi máy chủ, phần cứng nền tảng của từng máy chủ có thể khác nhau
    • Tức là request sẽ tiêu thụ lượng tài nguyên khác nhau trên các lớp phần cứng khác nhau
  • Cốt lõi là client không có khả năng quan sát tải của máy chủ
  • Trước đây, họ đã cố giải quyết vấn đề này bằng cách để máy chủ gắn thông tin tải vào response header
    • Client có thể tự cân bằng tải bằng cách chọn endpoint ít tải nhất trong tập con địa chỉ
    • Kết quả khá hứa hẹn, nhưng vẫn còn một số nhược điểm
      • Cả máy chủ lẫn client đều cần thay đổi mã cho header tải đặc biệt, nên khó áp dụng trên toàn cầu
      • Kết quả tốt, nhưng vẫn chưa đủ tốt

Quyết định xây dựng Robinhood

  • Năm 2019, Dropbox chính thức quyết định xây dựng Robinhood
  • Dịch vụ mới này được xây dựng trên hệ thống service discovery nội bộ hiện có, thu thập thông tin tải từ máy chủ và gắn vào thông tin định tuyến
  • Robinhood tận dụng Endpoint Discovery Service của Envoy để tích hợp thông tin tải vào trọng số endpoint, cho phép client thực hiện weighted round-robin
  • Vì cộng đồng gRPC đang chấp nhận giao thức Envoy xDS, Robinhood tương thích với cả Envoy và client gRPC
  • Thời điểm đó không có giải pháp cân bằng tải sẵn có nào đáp ứng được yêu cầu của Dropbox, nên họ quyết định xây dựng dịch vụ mới

Thành quả của Robinhood

  • Sau vài năm sử dụng trong môi trường production, Robinhood cho thấy kết quả đầy hứa hẹn
  • Đã thành công giảm 25% quy mô fleet của một số dịch vụ lớn, qua đó tiết kiệm đáng kể chi phí phần cứng mỗi năm
  • Độ tin cậy cũng được cải thiện nhờ giảm số lượng process bị sử dụng quá mức

Kiến trúc của Robinhood

  • Mỗi trung tâm dữ liệu triển khai một instance Robinhood, gồm ba phần: dịch vụ cân bằng tải, proxy và cơ sở dữ liệu định tuyến

Dịch vụ cân bằng tải (LBS)

  • Thành phần cốt lõi của Robinhood
  • Có nhiệm vụ thu thập thông tin tải và tạo thông tin định tuyến kèm trọng số endpoint
  • Vì nhiều instance cập nhật đồng thời thông tin định tuyến cho dịch vụ, hệ thống dùng shard manager nội bộ để gán worker chính cho từng dịch vụ
  • Do mỗi dịch vụ là độc lập, có thể phân tách LBS theo từng dịch vụ và mở rộng theo chiều ngang

Proxy

  • Có nhiệm vụ định tuyến thông tin tải của dịch vụ tới phân vùng LBS tương ứng trong trung tâm dữ liệu
  • Việc dùng proxy cũng giúp giảm số lượng kết nối trực tiếp tới process LBS
    • Nếu không có proxy, mọi process LBS sẽ phải kết nối tới mọi node trong hạ tầng
    • Thay vào đó, các process LBS chỉ kết nối tới proxy, nên áp lực bộ nhớ của LBS giảm đáng kể
  • Proxy chỉ kết nối tới các node trong cùng trung tâm dữ liệu, nên có thể mở rộng theo chiều ngang
  • Mô hình này được dùng ở nhiều nơi trong hạ tầng để bảo vệ dịch vụ khỏi phải nhận quá nhiều kết nối TLS

Cơ sở dữ liệu định tuyến

  • Là cơ sở dữ liệu dựa trên ZooKeeper/etcd lưu thông tin định tuyến cho dịch vụ như hostname, địa chỉ IP và trọng số do LBS tạo ra
  • ZooKeeper và etcd có thể thông báo thay đổi node/key theo thời gian thực cho mọi watcher, rất phù hợp với bài toán service discovery thiên về đọc của Dropbox
  • Tính nhất quán cuối cùng mà ZooKeeper/etcd đảm bảo cũng là đủ cho service discovery

Tìm hiểu chi tiết dịch vụ cân bằng tải

  • Mục tiêu của cân bằng tải là làm cho mức sử dụng của mọi node bằng với mức sử dụng trung bình
  • Bộ điều khiển PID được dùng để giữ mức sử dụng của từng node gần như bằng với mức sử dụng trung bình
  • LBS tạo một bộ điều khiển PID cho mỗi node và dùng mức sử dụng trung bình làm giá trị đặt
  • LBS dùng đầu ra của bộ điều khiển PID làm delta cho trọng số endpoint và chuẩn hóa trọng số giữa mọi endpoint của dịch vụ
  • Node mới cần vài lần điều chỉnh để hội tụ về mức sử dụng trung bình, nhưng bộ điều khiển PID rất hiệu quả cho cân bằng tải

Kịch bản hoạt động của LBS

  • LBS được thiết kế để xử lý nhiều kịch bản có thể ảnh hưởng đến cân bằng tải, từ khởi động lại node đến thiếu báo cáo tải
  • Để duy trì hiệu năng tối ưu, LBS triển khai một số chiến lược để xử lý các trường hợp ngoại lệ này

Khởi động LBS

  • LBS giữ thông tin tải và trạng thái bộ điều khiển PID trong bộ nhớ
  • Khi LBS khởi động lại, nó không bắt đầu cập nhật trọng số ngay mà chờ một lúc cho tới khi có báo cáo tải đến
  • Với trọng số của bộ điều khiển PID, LBS đọc trọng số endpoint từ cơ sở dữ liệu định tuyến để khôi phục

Node Cold Start

  • Vì node mới thường xuyên tham gia fleet dịch vụ, việc tránh vấn đề Thundering Herd là rất quan trọng
  • Do mức sử dụng ban đầu của node mới thường là 0, LBS đặt trọng số của node mới thành trọng số endpoint thấp và để bộ điều khiển PID kéo node lên mức sử dụng trung bình

Thiếu báo cáo tải

  • Trong môi trường hệ thống phân tán, sự cố là điều phổ biến
  • Do tắc nghẽn mạng hoặc lỗi phần cứng, báo cáo tải của một số node có thể bị chậm hoặc không đến nơi
  • LBS bỏ qua các node đó trong quá trình cập nhật trọng số, nên trọng số endpoint của chúng không thay đổi
  • Tuy nhiên, nếu thiếu quá nhiều báo cáo tải, việc tính mức sử dụng trung bình có thể trở nên không chính xác
  • Để an toàn, trong trường hợp này LBS sẽ bỏ qua hoàn toàn bước cập nhật trọng số

Chỉ số mức sử dụng

  • Mức sử dụng CPU là chỉ số cân bằng tải được dùng phổ biến nhất tại Dropbox
  • Với các dịch vụ không bị nghẽn ở CPU, số lượng request đang xử lý là một thước đo thay thế tốt
  • LBS được triển khai để hỗ trợ cân bằng tải dựa trên CPU và/hoặc request đang xử lý

Giới hạn

  • Bộ điều khiển PID tạo thành một vòng phản hồi để giữ mức sử dụng của node gần với giá trị mục tiêu (mức sử dụng trung bình)
  • Nếu gần như không có phản hồi, chẳng hạn dịch vụ có rất ít lưu lượng hoặc request có độ trễ rất cao được đo theo phút, thì cân bằng tải sẽ không hiệu quả
  • Các dịch vụ có request độ trễ cao nên là bất đồng bộ

Định tuyến liên trung tâm dữ liệu

  • Các instance LBS xử lý cân bằng tải trong phạm vi trung tâm dữ liệu
  • Định tuyến giữa các trung tâm dữ liệu có những cân nhắc khác
    • Ví dụ, để giảm thời gian khứ hồi của request, cần định tuyến request tới trung tâm dữ liệu gần nhất
  • Để làm điều này, Dropbox đã đưa vào cấu hình locality để xác định cách phân chia lưu lượng giữa các trung tâm dữ liệu đích

Đánh giá hiệu năng của bộ cân bằng tải

  • Hiệu năng cân bằng tải được đo bằng tỷ lệ max/avg
    • Nếu chủ sở hữu dịch vụ chọn cân bằng tải dựa trên CPU, thì dùng maxCPU/avgCPU làm chỉ số hiệu năng
    • Chủ sở hữu dịch vụ thường cấp phát tài nguyên cho dịch vụ dựa trên mức sử dụng tối đa giữa các node, vì mục tiêu chính của cân bằng tải là giảm kích thước fleet
  • Chiến lược cân bằng tải bằng bộ điều khiển PID có thể giữ tỷ lệ max/avg gần với 1

Biểu đồ đánh giá hiệu năng cân bằng tải

  • Biểu đồ cho thấy max/avg CPU và p95/avg CPU của cụm proxy Envoy lớn nhất
    • Sau khi bật cân bằng tải dựa trên bộ điều khiển PID, cả hai chỉ số đều giảm xuống gần 1
    • Tỷ lệ max/avg giảm từ 1.26 xuống 1.01, cho thấy cải thiện 20%
  • Biểu đồ cho thấy phân tích phân vị mức sử dụng CPU theo từng node
    • Sau khi bật cân bằng tải dựa trên bộ điều khiển PID, max, p95, avg và p5 gần như hội tụ thành một đường
  • Một biểu đồ khác cho thấy max/avg CPU và p95/avg CPU của cụm frontend cơ sở dữ liệu lớn nhất
    • Sau khi bật cân bằng tải dựa trên bộ điều khiển PID, cả hai chỉ số đều giảm xuống gần 1
    • Tỷ lệ max/avg giảm từ 1.4 xuống 1.05, cho thấy cải thiện 25%
  • Một biểu đồ khác cho thấy phân tích phân vị mức sử dụng CPU theo từng node
    • Sau khi bật cân bằng tải dựa trên bộ điều khiển PID, max, p95, avg và p5 một lần nữa gần như hội tụ thành một đường

Lý do xây dựng Config Aggregator

  • Robinhood cung cấp nhiều tùy chọn để chủ sở hữu dịch vụ lựa chọn, và cũng có thể áp dụng thay đổi một cách động
  • Chủ sở hữu dịch vụ tạo và cập nhật cấu hình Robinhood cho dịch vụ trong thư mục dịch vụ của codebase
  • Các thiết lập này được lưu trong dịch vụ quản lý cấu hình, một thư viện tiện lợi để nhận thay đổi cấu hình Robinhood theo thời gian thực
  • Tuy nhiên, do một số vấn đề, không thể định kỳ build và push cấu hình mega của Robinhood từ codebase
    • Khi thay đổi được đưa vào thông qua một lần push cấu hình, việc nhấn nút rollback là rủi ro
      • Vì không biết đã có bao nhiêu dịch vụ khác thay đổi kể từ lần push cuối
    • Đội ngũ sở hữu Robinhood cũng phải chịu trách nhiệm cho từng lần push cấu hình mega
      • Điều này có nghĩa là đội Robinhood phải tham gia vào mọi lần push cấu hình thay đổi, gây lãng phí thời gian kỹ sư
      • Vì phần lớn sự cố có thể do chủ sở hữu dịch vụ xử lý
    • Để giảm thiểu rủi ro tiềm ẩn, mỗi lần push mất vài giờ để triển khai tới nhiều trung tâm dữ liệu
  • Để giải quyết các vấn đề này, họ đã xây dựng thêm một dịch vụ nhỏ khác là Config Aggregator

Config Aggregator

  • Config Aggregator thu thập tất cả cấu hình theo từng dịch vụ và tạo cấu hình mega để LBS sử dụng
  • Config Aggregator theo dõi cấu hình theo từng dịch vụ và truyền các thay đổi vào cấu hình mega theo thời gian thực
  • Config Aggregator cũng cung cấp tính năng tombstone để ngăn cấu hình Robinhood của một dịch vụ bị xóa nhầm
    • Nếu chủ sở hữu dịch vụ push một thay đổi xóa dịch vụ khỏi cấu hình Robinhood, Config Aggregator sẽ đánh dấu tombstone thay vì xóa mục dịch vụ ngay lập tức
    • Việc xóa thực tế sẽ diễn ra vài ngày sau
    • Tính năng này cũng giải quyết condition cạnh tranh có thể phát sinh do chu kỳ push khác nhau giữa cấu hình Robinhood và các cấu hình định tuyến khác, như cấu hình Envoy
  • Một nhược điểm của dịch vụ quản lý cấu hình là hiện tại chưa có quản lý phiên bản
  • Họ định kỳ sao lưu cấu hình mega để có thể khôi phục cấu hình LBS về trạng thái tốt đã biết nếu cần

Chiến lược migration

  • Việc chuyển đổi chiến lược cân bằng tải cùng một lúc có thể mang rủi ro
  • Đó là lý do Robinhood cho phép cấu hình nhiều chiến lược cân bằng tải cho một dịch vụ
  • Dropbox có feature gate dựa trên phần trăm, nên họ triển khai chiến lược lai trong đó client dùng tổng có trọng số của các trọng số được tạo từ hai chiến lược cân bằng tải làm trọng số endpoint
  • Nhờ vậy có thể migration dần sang chiến lược cân bằng tải mới trong khi mọi client vẫn thấy cùng một cách gán trọng số cho endpoint

Bài học rút ra

  • Trong quá trình thiết kế và triển khai Robinhood, họ đã rút ra một số bài học quan trọng về điều gì hiệu quả và điều gì không
  • Bằng cách ưu tiên sự đơn giản, giảm thiểu thay đổi ở client và lên kế hoạch migration ngay từ đầu, họ đã có thể đơn giản hóa việc phát triển và triển khai LBS, đồng thời tránh được những cạm bẫy tốn kém

Cấu hình nên đơn giản nhất có thể

  • Robinhood đưa vào nhiều tùy chọn mà chủ sở hữu dịch vụ có thể cấu hình
  • Tuy nhiên, trong đa số trường hợp, thứ họ cần chỉ là thiết lập mặc định được cung cấp
  • Một cấu hình mặc định tốt và đơn giản, hoặc tốt hơn nữa là không cần cấu hình, có thể tiết kiệm rất nhiều thời gian kỹ sư

Hãy giữ thay đổi ở client thật đơn giản

  • Việc rollout thay đổi tới client nội bộ có thể mất hàng tháng
    • Phần lớn deployment được push hằng tuần, nhưng nhiều deployment chỉ được thực hiện mỗi tháng một lần hoặc thậm chí không triển khai trong nhiều năm
  • Càng nhiều thay đổi có thể chuyển sang LBS thì càng tốt
    • Ví dụ, ban đầu họ quyết định dùng weighted round robin trong thiết kế client và từ đó không thay đổi nữa
    • Điều này giúp tăng tốc tiến độ đáng kể
  • Giới hạn phần lớn thay đổi ở LBS cũng làm giảm rủi ro ổn định
    • Vì khi cần có thể rollback các thay đổi của LBS chỉ trong vài phút

Migration nên được lên kế hoạch ngay từ giai đoạn thiết kế dự án

  • Migration tiêu tốn một lượng lớn thời gian kỹ sư
  • Ngoài ra còn có rủi ro ổn định cần cân nhắc
  • Đây không phải công việc thú vị, nhưng lại rất quan trọng
  • Khi thiết kế một dịch vụ mới, cần cân nhắc càng sớm càng tốt cách migration trơn tru các use case hiện có sang dịch vụ mới
  • Càng yêu cầu nhiều từ chủ sở hữu dịch vụ thì migration càng trở thành cơn ác mộng
    • Điều này đặc biệt đúng với các thành phần hạ tầng cốt lõi
  • Quy trình migration của Robinhood không được thiết kế tốt ngay từ đầu, nên họ đã tốn nhiều thời gian hơn dự kiến để triển khai lại quy trình và thiết kế lại cấu hình
  • Thời gian kỹ sư cần cho migration nên là một chỉ số thành công then chốt

Tác động của Robinhood

  • Sau khoảng một năm trong môi trường production, có thể nói rằng phiên bản lặp mới nhất của Robinhood đã giải quyết hiệu quả bài toán cân bằng tải tồn tại lâu nay của Dropbox
  • Thuật toán cốt lõi là bộ điều khiển PID đã cho thấy kết quả đầy hứa hẹn và mang lại cải thiện hiệu năng đáng kể ở các dịch vụ lớn nhất
  • Họ cũng thu được những hiểu biết giá trị về việc thiết kế và vận hành dịch vụ cân bằng tải ở quy mô của Dropbox

Chú thích

  1. Gọi N, M, s lần lượt là số lượng server, số lượng client và kích thước của tập con địa chỉ. Số lượng client kết nối tới một server tuân theo mẫu của phân phối nhị thức B(M, s/n). Như đã đề cập trước đó, client thực hiện round robin đơn giản trên tập địa chỉ do service discovery cung cấp. Vì vậy, nếu mỗi client gửi lượng request xấp xỉ như nhau, phân bố tải ở phía server sẽ tương tự phân phối nhị thức.

  2. Họ mở rộng hệ thống service discovery hiện có để hỗ trợ giao thức gRPC xDS (A27). Tính đến thời điểm bài blog này được viết, client gRPC không hỗ trợ weighted round robin cho trọng số endpoint từ control plane, nên họ đã triển khai một bộ chọn weighted round robin tùy chỉnh dựa trên lập lịch earliest deadline first.

  3. Có một trường hợp thú vị khi dịch vụ đôi lúc bị nghẽn do I/O suy giảm hiệu năng. Trong tình huống đó, CPU của node tương ứng vẫn ở mức thấp, và LBS bắt đầu tăng trọng số của node để đưa CPU lên mức trung bình, dẫn đến một vòng xoáy chết. Để khắc phục, họ sử dụng giá trị lớn hơn giữa CPU và số lượng request đang xử lý làm thước đo tải để cân bằng dịch vụ.

Ý kiến của GN⁺

  • Robinhood có vẻ là một dịch vụ xuất sắc đã giải quyết hiệu quả bài toán cân bằng tải của Dropbox. Việc tận dụng bộ điều khiển PID đặc biệt gây ấn tượng
  • Đây là một ví dụ cho thấy rõ cân bằng tải khó đến mức nào trong hạ tầng toàn cầu ở quy mô rất lớn. Có rất nhiều yếu tố cần cân nhắc như khác biệt phần cứng, phân bố tải không đồng đều, tắc nghẽn mạng
  • Có vẻ việc thiết kế để mọi thành phần hoạt động trơn tru và gắn kết hữu cơ với nhau là rất quan trọng. LBS, proxy và routing DB tuy tách biệt nhưng tương tác chặt chẽ theo thời gian thực
  • Những biểu đồ định lượng hiệu năng cân bằng tải và trực quan hóa các điểm cải tiến rất ấn tượng. Đặc biệt, chúng cho thấy rõ việc giữ tỷ lệ max/avg gần với 1 quan trọng thế nào đối với tối ưu hóa kích thước fleet
  • Việc đưa vào Config Aggregator để tách cấu hình theo từng dịch vụ cũng có vẻ là một ý tưởng hay. Điều này cho phép chủ sở hữu dịch vụ quản lý độc lập các thay đổi của mình
  • Việc chuẩn bị các cơ chế an toàn như tombstone cũng là một chi tiết chỉn chu. Ngăn việc xóa cấu hình do nhầm lẫn là rất quan trọng
  • Những bài học về chiến lược migration cũng có vẻ hữu ích. Nếu không tính đến migration ngay từ đầu thì sau này có thể tốn rất nhiều thời gian
  • Nhìn chung, Robinhood có vẻ là một giải pháp bài bản và tinh tế cho cân bằng tải ở quy mô của Dropbox. Đây là một trường hợp mà các công ty có hạ tầng quy mô lớn khác cũng đáng tham khảo

Các giải pháp tương tự:

  • Elastic Load Balancing (ELB) của AWS hay Cloud Load Balancing của Google Cloud cũng cung cấp dịch vụ quản lý cho cân bằng tải ở quy mô lớn
  • Với Kubernetes, tuy đã có sẵn bộ cân bằng tải riêng (kube-proxy), nhưng nếu sử dụng các giải pháp service mesh như Istio hoặc Linkerd thì có thể dùng các tính năng cân bằng tải và quản lý lưu lượng mạnh hơn
  • Zuul của Netflix hay Envoy của Lyft cũng cung cấp chức năng cân bằng tải dựa trên proxy

Các điểm cần cân nhắc khi triển khai:

  • Cần xác nhận khả năng tương thích với hạ tầng và dịch vụ hiện có. Nếu cần migration thì phải xây dựng chiến lược
  • Cần kiểm thử và giám sát đầy đủ tác động tới hiệu năng và độ ổn định. Bug trong logic cân bằng tải có thể gây hậu quả nghiêm trọng
  • Cần quyết định phạm vi và tốc độ triển khai dựa trên năng lực của đội ngũ. Thay vì áp dụng vội vàng trên toàn bộ hệ thống, triển khai theo từng giai đoạn sẽ tốt hơn
  • Về dài hạn, cần liên tục tối ưu và cải tiến. Những hoạt động như tinh chỉnh thuật toán cân bằng tải cho phù hợp với tình huống và loại bỏ các điểm nghẽn sẽ rất hữu ích

1 bình luận

 
kbumsik 2024-11-01

Lần đầu thấy nhắc đến bộ điều khiển PID ở mảng phần mềm luôn haha