Evolving Reddit's ML Model Deployment and Serving Architecture

Bài tóm tắt Reddit 2021 4. Điều chỉnh Độ khó: Trung bình

Reddit · 2021 · 4. Điều chỉnh · Độ khó: Trung bình — bài gốc

Bối cảnh

  • Hệ ML của Reddit (2021) phục vụ hàng nghìn request inference mỗi giây cho feed, video, push, email. Inference nằm trong Minsky, một service Thrift Python (baseplate.py) vốn phục vụ dữ liệu relevance (độ giống giữa subreddit, xu hướng của user) từ Cassandra và cache trong process. Phần ML gắn thêm vào đó tên là Gazette.
  • Triển khai trên hạ tầng cũ: Puppet dựng máy, công cụ rollingpin để rollout, mỗi EC2 chạy 4 process Minsky/Gazette chung CPU và RAM, Einhorn chia request. Feature lấy từ request hoặc từ Cassandra/Memcached, có memcached cục bộ trên mỗi máy (qua mcrouter) để giảm tail latency.
  • Model nhúng vào ứng dụng: thêm model là viết code ứng dụng (tải model, nạp vào RAM lúc start, sửa schema, viết class biến đổi dữ liệu); mọi model chạy trên mọi instance.

Thay đổi chính

  • Các giới hạn của thiết kế cũ, theo Reddit:
    • Hiệu năng: endpoint inference nặng CPU nằm chung với endpoint đọc dữ liệu nặng I/O, làm event loop của Minsky bị nghẽn; nhiều model trên cùng máy tranh nhau core.
    • Scale: mọi model phải vừa RAM của mọi máy nên cần nhiều máy rất lớn; model đông traffic không scale riêng được.
    • Bảo trì: chung dependency nên model mới muốn dùng thư viện mới phải giữ tương thích với mọi model cũ; mỗi model tự viết code nên cùng việc mà nhiều cách làm.
    • Tin cậy: một model ném exception là sập cả process, kéo theo mọi model khác.
    • Quan sát: khó biết trạng thái "model đáng ra được deploy" so với "model thật sự đang chạy".
  • Gazette Inference Service: service Go (baseplate.go) trên Kubernetes, chỉ làm inference, một endpoint Predict nhận tên model, phiên bản, danh sách record và feature. Service tự resolve schema của model từ registry cục bộ, lấy feature từ kho, đóng gói thành FeatureFrame rồi gửi sang model server pool của model đó.
  • Mỗi model một pool riêng: model server là service Python bọc một model (bản đầu chỉ hỗ trợ TensorFlow savedmodel), đóng container, deploy bằng Helm. Engineer ML deploy model mới bằng cách commit một file cấu hình: metadata, đường dẫn artifact, schema (giá trị mặc định, nguồn feature), phiên bản image của model server, tài nguyên và autoscaling. Chính các file này dựng registry model và schema của Inference Service.
  • Giữ cache cục bộ trên hạ tầng mới: memcached chạy dạng DaemonSet trên mỗi node, bật topology aware routing để request ưu tiên tới pod cùng node; cùng cơ chế áp cho model server pool để bù cho bước mạng mới thêm.
  • Lúc viết đang chuyển dần các model khỏi Minsky; hướng tiếp: hỗ trợ framework khác, tách deploy model server khỏi deploy Inference Service bằng registry từ xa, làm lại pipeline/kho/phục vụ feature cho quy mô 1 tỉ user.

Vì sao

  • Cách đầu tiên (nhúng model vào một service có sẵn) giúp đưa ML vào sản phẩm nhanh, nhưng khi số model và số người viết model tăng thì mọi thứ dùng chung (CPU, RAM, dependency, process, deploy) thành giới hạn chung.

Bài học cho SWE

  • Tách workload theo hình dạng tài nguyên: CPU-bound (inference) và I/O-bound (đọc dữ liệu) chung một event loop thì cái nặng làm hỏng cái nhẹ.
  • Đơn vị cô lập nên trùng với đơn vị thay đổi: mỗi model một deployment thì scale, dependency, sự cố và rollout đều theo từng model.
  • Cấu hình khai báo vừa là cách deploy vừa là "trạng thái mong muốn" để so với trạng thái thật trong Kubernetes.
  • Thêm một bước mạng thì bù bằng định tuyến theo topology, không bỏ hẳn cache cục bộ.

Liên quan