Lessons Learned From Scaling Uber To 2000 Engineers, 1000 Services, And 8000 Git Repositories
Bài tóm tắt Uber 2016 3. Quy mô lớn Độ khó: Dễ
Uber · 2016 · 3. Quy mô lớn · Độ khó: Dễ — bài gốc
Nguồn thứ cấp: đây là bản tóm tắt của HighScalability cho bài talk "What I Wish I Had Known Before Scaling Uber to 1000 Services" của Matt Ranney (Chief Systems Architect của Uber). Các nhận định bên dưới là theo Matt Ranney qua lời kể của HighScalability.
Bối cảnh (4/2016)
- Uber có mặt ở 400 thành phố, 70 quốc gia, 6.000+ nhân viên, 2.000 kỹ sư (1,5 năm trước chỉ có 200), khoảng 1.000 microservice, hơn 8.000 repo git (tháng trước đó là 7.000). Một nửa đội kỹ sư mới vào chưa tới 6 tháng.
- Bối cảnh kinh doanh: thị trường "người thắng lấy hết", nên phải lớn nhanh.
- Lịch sử: bản đầu tiên được thuê ngoài viết; dispatch viết bằng Node.js (đang chuyển sang Go); core viết bằng Python (đang chuyển sang Go); Maps và data engineering dùng Python, Java; hệ thống metric dùng Go.
Thay đổi chính (các bài học "ước gì biết sớm")
- Lý do có nhiều service là con người, không phải kỹ thuật. "Scale traffic không phải vấn đề, scale đội ngũ và tốc độ ra tính năng mới là động lực chính." Microservices thay giao tiếp giữa người bằng phối hợp qua API.
- Chi phí ít ai thấy:
- xây service mới thay vì sửa cái hỏng;
- "đổi độ phức tạp lấy chính trị" (viết thêm code để khỏi phải nói chuyện);
- mỗi team giữ ngôn ngữ ưa thích, dẫn tới văn hoá bị chia thành các "phe" Node, Go…, khó chia sẻ code, khó chuyển team.
- Theo bài, Uber ổn định nhất vào cuối tuần, lúc đông khách nhất nhưng ít ai thay đổi gì.
- RPC: HTTP/REST + JSON không có kiểu gây ra những lỗi kiểu "chuỗi rỗng hay null" khi đi qua vài hop. "Server không phải là trình duyệt": nên coi mỗi lời gọi như một lời gọi hàm có kiểu. Đây cũng là lý do chọn Thrift năm 2015 (Uber 2015).
- Fan-out: một service p99 = 1 giây, gọi 100 service thì ~63% request chạm ít nhất một lần 1 giây. Cần distributed tracing (Zipkin/OpenTracing, lấy mẫu ~1%) và truyền context xuyên ngôn ngữ (user, xác thực, geo-fence), điều Matt tiếc là không làm sớm.
- Hiệu năng: "không cần nhanh, nhưng phải biết mình đang ở đâu": mỗi service nên có một SLA hiệu năng, dashboard chuẩn tự sinh, cùng một định dạng flame graph cho mọi ngôn ngữ.
- Logging: cần log có cấu trúc thống nhất (uber-go/zap), back-pressure trong logging (log quá nhiều làm sự cố tệ hơn), và "tính tiền" theo lượng log để mỗi service biết mình đang tạo ra bao nhiêu.
- Load test trên production giờ thấp điểm, phải đánh dấu traffic test xuyên suốt hệ thống (lại là truyền context). Lý tưởng: luôn giữ tải gần đỉnh và lùi dần khi traffic thật tăng.
- Failure testing (kiểu Chaos Monkey) nên là mặc định của production ngay từ đầu, vì thêm vào sau thì gặp kháng cự.
- Migration: "Mọi thứ đều là legacy, ai cũng đang migrate thứ gì đó". Không còn cửa sổ bảo trì, chỉ còn mức downtime chấp nhận được.
- "Chỉ dùng củ cà rốt, không dùng cây gậy": đừng ép dùng hệ thống mới; hãy làm công cụ dễ tới mức không ai muốn làm cách khác. Ngoại lệ: bảo mật và tuân thủ.
Vì sao
Theo Matt Ranney: tốc độ tăng trưởng 10 lần trong 1,5 năm buộc phải đánh đổi; điều tiếc nhất là đã không đưa ra các đánh đổi đó một cách có chủ đích.
Bài học cho SWE
- Bổ sung cho Uber 2015 (lý do tách) và Uber 2020 (gom lại theo domain): bài này nằm ở đỉnh của sự bùng nổ, giữa hai bài kia.
- Các tính năng xuyên suốt (context propagation, tracing, log có cấu trúc, đánh dấu traffic test) càng làm muộn càng đắt. Xem Nền tảng nội bộ cho service (đường ray chung).
- Đa ngôn ngữ có chi phí văn hoá thật. Xem Đổi ngôn ngữ và runtime.
- Cùng tinh thần với Shopify 2020: thay đổi hành vi bằng cách làm công cụ dễ dùng, không bằng mệnh lệnh (Shopify 2020).
Liên quan
Monolith và microservices — đi và quay lạiNền tảng nội bộ cho service (đường ray chung)Đổi ngôn ngữ và runtimeDeploy và release an toàn
Được nhắc tới trong
Chủ đề (4)
Công ty (1)