How we designed our continuous integration system to be more than 50% faster
Bài tóm tắt Pinterest 2021 3. Quy mô lớn Độ khó: Trung bình
Pinterest · 2021 · 3. Quy mô lớn · Độ khó: Trung bình — bài gốc
Bối cảnh
- Đội Engineering Productivity của Pinterest làm CI cho các repo lớn, phục vụ hơn 1.000 engineer (2020). Repo được nói tới là Optimus: monorepo Java hơn 120 service, gồm nhiều nền tảng dữ liệu quan trọng, khoảng 300 người đóng góp mỗi tháng.
- Đầu 2020, CI của Optimus chạy mọi target của service có thay đổi, và release rule chia rất nhỏ. P50 của một build CI là 52 phút, P90 là 69 phút.
Thay đổi chính
- Bazel làm build tool chung cho mọi ngôn ngữ: vì Bazel hermetic, CI chạy target mà không cần cài phụ thuộc lên máy chủ; cùng một workflow CI dùng được cho mọi repo dùng Bazel.
- Monorepo theo ngôn ngữ: mỗi ngôn ngữ một repo, để số pipeline CI và số repo phải quản lý ít đi.
- Chỉ test và release phần bị đổi: CLI Go tên build-collector nhận một tập commit, xem file nào đổi rồi chạy Bazel query (
rdepsđể tìm phụ thuộc ngược, lọckindđể lấy target test), xuất danh sách target ra JSON. Theo Pinterest, kể cả có cache của Bazel,bazel test //...vẫn tốn thời gian tải và nạp phụ thuộc cho từng target; chỉ chạy tập tối thiểu thì bỏ hẳn được các lượt gọi mạng đó. Lý do thứ hai: tần suất commit của các service rất khác nhau. - File BUILD là hợp đồng giữa CI và developer: dùng rule test chuẩn, dùng release rule riêng của Pinterest để khai báo artifact, gắn tag (
no-ci) cho thứ không chạy trong pipeline. Developer không cần học cách CI được dựng. - Release rule tự viết (ví dụ
container_release): developer chỉ khai báo muốn phát hành gì; rule đẩy image vào registry nội bộ và đưa artifact cho nền tảng CD (Teletraan cho EC2, Hermez cho Kubernetes). Mỗi rule là một macro sinh hai rule ẩn, bản CI và bản dev, nên artifact dev đi nơi khác. Thứ tự phát hành được cố định trong rule (image trước, YAML tham chiếu image sau), nhờ vậy query của build-collector đơn giản. - Dispatcher Model: tính xong danh sách target thì phân cho nhiều worker chạy song song; target release chạy độc lập nên ai chỉ quan tâm vài service sẽ nhận artifact ngay khi nó xong, không chờ cả build.
- Build cho PR dùng lại đúng đường đó: vá thay đổi thành một commit tạm đưa cho build-collector, chỉ khác là không phát hành mà chỉ
bazel buildcác target release để kiểm tra biên dịch được.
Kết quả (theo Pinterest)
- Trong một tuần sau khi chuyển: P50 từ 52 xuống 19 phút (nhanh gấp 2,7 lần), P90 từ 69 xuống 49 phút (gấp 1,4 lần).
- Việc tiếp theo họ nêu: remote execution, profiling, tự phát hiện và loại test flaky.
Vì sao
- Chạy hết target là chi phí cố định trả cho mọi thay đổi, kể cả thay đổi nhỏ; tính tập ảnh hưởng từ đồ thị phụ thuộc đã có sẵn trong Bazel thì cắt được phần đó (CI và build ở quy mô lớn).
- CI là nền tảng: giao diện cho developer là file BUILD và vài rule, còn hạ tầng phát hành nằm sau abstraction (Nền tảng nội bộ cho service (đường ray chung)).
Bài học cho SWE
- P50 cải thiện nhiều hơn hẳn P90: chọn target theo phụ thuộc giúp nhiều nhất cho thay đổi nhỏ; thay đổi rộng vẫn phải chạy nhiều.
- Đặt hợp đồng CI ở nơi developer đã khai báo sẵn (file BUILD) thay vì một file cấu hình CI riêng.
- Tách luồng PR và luồng merge chỉ ở bước cuối (phát hành hay chỉ build) để một bộ logic dùng được cho cả hai.
Liên quan
- Uber 2022 (cũng chọn target theo đồ thị Bazel) · Riot Games 2021 · Stripe 2026 · Grab 2022
- CI và build ở quy mô lớn · Nền tảng nội bộ cho service (đường ray chung)
#CI#Bazel#Monorepo theo ngôn ngữ#rdeps query#build-collector#Custom release rule#Dispatcher#Teletraan#Hermez
Được nhắc tới trong
Công ty (1)
Bài tóm tắt (1)