Khi bạn có một cơ sở mã lớn, các chuỗi phần phụ thuộc có thể trở nên rất phức tạp. Ngay cả các tệp nhị phân đơn giản cũng thường có thể phụ thuộc vào hàng chục nghìn mục tiêu bản dựng. Ở quy mô này, bạn không thể hoàn tất bản dựng trong một khoảng thời gian hợp lý trên một máy duy nhất: không có hệ thống xây dựng nào có thể vượt qua các định luật vật lý cơ bản áp đặt lên phần cứng của máy. Cách duy nhất để thực hiện việc này là sử dụng hệ thống xây dựng hỗ trợ các bản dựng phân tán, trong đó các đơn vị công việc do hệ thống thực hiện được phân bổ trên một số lượng máy tuỳ ý và có thể mở rộng. Giả sử chúng ta đã chia công việc của hệ thống thành các đơn vị đủ nhỏ (sẽ nói thêm về vấn đề này sau), thì điều này sẽ cho phép chúng ta hoàn tất mọi bản dựng có kích thước bất kỳ một cách nhanh nhất có thể. Khả năng mở rộng này là mục tiêu mà chúng ta đã và đang hướng tới bằng cách xác định một hệ thống xây dựng dựa trên cấu phần phần mềm.
Bộ nhớ đệm từ xa
Loại bản dựng phân tán đơn giản nhất là loại chỉ tận dụng bộ nhớ đệm từ xa, như minh hoạ trong Hình 1.
Hình 1. Bản dựng phân tán cho thấy bộ nhớ đệm từ xa
Mọi hệ thống thực hiện bản dựng, bao gồm cả máy trạm của nhà phát triển và hệ thống tích hợp liên tục, đều chia sẻ một tham chiếu đến dịch vụ bộ nhớ đệm từ xa chung. Dịch vụ này có thể là một hệ thống lưu trữ ngắn hạn nhanh và cục bộ như Redis hoặc một dịch vụ đám mây như Google Cloud Storage. Bất cứ khi nào người dùng cần tạo một cấu phần phần mềm, dù là trực tiếp hay dưới dạng phần phụ thuộc, hệ thống sẽ kiểm tra trước với bộ nhớ đệm từ xa để xem cấu phần phần mềm đó đã tồn tại ở đó hay chưa. Nếu có, hệ thống có thể tải cấu phần phần mềm xuống thay vì tạo cấu phần phần mềm đó. Nếu không, hệ thống sẽ tự tạo cấu phần phần mềm và tải kết quả lên lại bộ nhớ đệm. Điều này có nghĩa là các phần phụ thuộc cấp thấp không thay đổi thường xuyên có thể được tạo một lần và chia sẻ giữa những người dùng thay vì phải được tạo lại bởi từng người dùng. Tại Google, nhiều cấu phần phần mềm được phân phát từ bộ nhớ đệm thay vì được tạo từ đầu, giúp giảm đáng kể chi phí chạy hệ thống xây dựng của chúng tôi.
Để hệ thống bộ nhớ đệm từ xa hoạt động, hệ thống xây dựng phải đảm bảo rằng các bản dựng hoàn toàn có thể tái tạo. Tức là đối với bất kỳ mục tiêu bản dựng nào, bạn phải có thể xác định tập hợp các dữ liệu đầu vào cho mục tiêu đó sao cho cùng một tập hợp dữ liệu đầu vào sẽ tạo ra chính xác cùng một dữ liệu đầu ra trên bất kỳ máy nào. Đây là cách duy nhất để đảm bảo rằng kết quả của việc tải cấu phần phần mềm xuống giống như kết quả của việc tự tạo cấu phần phần mềm đó. Xin lưu ý rằng điều này đòi hỏi mỗi cấu phần phần mềm trong bộ nhớ đệm phải được khoá bằng cả mục tiêu và hàm băm của dữ liệu đầu vào. Bằng cách đó, các kỹ sư khác nhau có thể thực hiện các sửa đổi khác nhau đối với cùng một mục tiêu cùng một lúc và bộ nhớ đệm từ xa sẽ lưu trữ tất cả các cấu phần phần mềm kết quả và phân phát chúng một cách thích hợp mà không bị xung đột.
Tất nhiên, để có bất kỳ lợi ích nào từ bộ nhớ đệm từ xa, việc tải cấu phần phần mềm xuống cần phải nhanh hơn việc tạo cấu phần phần mềm đó. Điều này không phải lúc nào cũng đúng, đặc biệt nếu máy chủ bộ nhớ đệm ở xa máy đang thực hiện bản dựng. Mạng và hệ thống xây dựng của Google được điều chỉnh cẩn thận để có thể chia sẻ nhanh chóng kết quả bản dựng.
Thực thi từ xa
Bộ nhớ đệm từ xa không phải là bản dựng phân tán thực sự. Nếu bộ nhớ đệm bị mất hoặc nếu bạn thực hiện thay đổi cấp thấp yêu cầu phải tạo lại mọi thứ, bạn vẫn cần thực hiện toàn bộ bản dựng cục bộ trên máy của mình. Mục tiêu thực sự là hỗ trợ thực thi từ xa, trong đó công việc thực tế của việc thực hiện bản dựng có thể được phân bổ trên bất kỳ số lượng trình thực thi nào. Hình 2 mô tả hệ thống thực thi từ xa.
Hình 2. Hệ thống thực thi từ xa
Công cụ xây dựng chạy trên máy của mỗi người dùng (trong đó người dùng là kỹ sư hoặc hệ thống xây dựng tự động) sẽ gửi yêu cầu đến một trình xây dựng chính trung tâm. Trình xây dựng chính chia các yêu cầu thành các thao tác thành phần và lên lịch thực thi các thao tác đó trên một nhóm trình thực thi có thể mở rộng. Mỗi trình thực thi thực hiện các thao tác được yêu cầu với dữ liệu đầu vào do người dùng chỉ định và ghi ra các cấu phần phần mềm kết quả. Các cấu phần phần mềm này được chia sẻ trên các máy khác thực thi các thao tác yêu cầu chúng cho đến khi có thể tạo và gửi dữ liệu đầu ra cuối cùng cho người dùng.
Phần khó khăn nhất của việc triển khai hệ thống như vậy là quản lý giao tiếp giữa các trình thực thi, trình xây dựng chính và máy cục bộ của người dùng. Trình thực thi có thể phụ thuộc vào các cấu phần phần mềm trung gian do các trình thực thi khác tạo ra và dữ liệu đầu ra cuối cùng cần được gửi lại cho máy cục bộ của người dùng. Để thực hiện việc này, chúng ta có thể xây dựng dựa trên bộ nhớ đệm phân tán được mô tả trước đó bằng cách yêu cầu mỗi trình thực thi ghi kết quả vào và đọc các phần phụ thuộc từ bộ nhớ đệm. Trình xây dựng chính chặn các trình thực thi tiếp tục cho đến khi mọi thứ mà chúng phụ thuộc vào đã hoàn tất, trong trường hợp đó, chúng sẽ có thể đọc dữ liệu đầu vào từ bộ nhớ đệm. Sản phẩm cuối cùng cũng được lưu vào bộ nhớ đệm, cho phép máy cục bộ tải sản phẩm đó xuống. Xin lưu ý rằng chúng ta cũng cần một phương tiện riêng để xuất các thay đổi cục bộ trong cây nguồn của người dùng để trình thực thi có thể áp dụng những thay đổi đó trước khi xây dựng.
Để hoạt động, tất cả các phần của hệ thống xây dựng dựa trên cấu phần phần mềm được mô tả trước đó cần phải kết hợp với nhau. Môi trường xây dựng phải hoàn toàn tự mô tả để chúng ta có thể khởi động trình thực thi mà không cần sự can thiệp của con người. Bản thân quy trình xây dựng phải hoàn toàn độc lập vì mỗi bước có thể được thực thi trên một máy khác. Dữ liệu đầu ra phải hoàn toàn xác định để mỗi trình thực thi có thể tin tưởng vào kết quả mà trình thực thi đó nhận được từ các trình thực thi khác. Rất khó để hệ thống dựa trên tác vụ cung cấp các đảm bảo như vậy, điều này khiến việc xây dựng hệ thống thực thi từ xa đáng tin cậy dựa trên hệ thống đó gần như là không thể.
Bản dựng phân tán tại Google
Kể từ năm 2008, Google đã sử dụng hệ thống xây dựng phân tán sử dụng cả bộ nhớ đệm từ xa và thực thi từ xa, như minh hoạ trong Hình 3.
Hình 3. Hệ thống xây dựng phân tán của Google
Bộ nhớ đệm từ xa của Google có tên là ObjFS. Bộ nhớ đệm này bao gồm một phần phụ trợ lưu trữ dữ liệu đầu ra bản dựng trong Bigtable được phân phối trên toàn bộ nhóm máy sản xuất của chúng tôi và một trình nền FUSE có tên là objfsd chạy trên máy của mỗi nhà phát triển. Trình nền FUSE cho phép các kỹ sư duyệt dữ liệu đầu ra bản dựng như thể đó là các tệp thông thường được lưu trữ trên máy trạm, nhưng nội dung tệp chỉ được tải xuống theo yêu cầu đối với một số ít tệp được người dùng yêu cầu trực tiếp. Việc phân phát nội dung tệp theo yêu cầu giúp giảm đáng kể cả mức sử dụng mạng và ổ đĩa, đồng thời hệ thống có thể xây dựng nhanh gấp đôi so với khi chúng tôi lưu trữ tất cả dữ liệu đầu ra bản dựng trên ổ đĩa cục bộ của nhà phát triển.
Hệ thống thực thi từ xa của Google có tên là Forge. Một ứng dụng Forge trong Blaze (tương đương nội bộ của Bazel) có tên là Trình phân phối sẽ gửi yêu cầu cho từng thao tác đến một công việc đang chạy trong các trung tâm dữ liệu của chúng tôi có tên là Trình lập lịch. Trình lập lịch duy trì bộ nhớ đệm kết quả thao tác, cho phép trả về phản hồi ngay lập tức nếu thao tác đã được tạo bởi bất kỳ người dùng nào khác của hệ thống. Nếu không, trình lập lịch sẽ đặt thao tác vào một hàng đợi. Một nhóm lớn các công việc của Trình thực thi liên tục đọc các thao tác từ hàng đợi này, thực thi các thao tác đó và lưu trữ kết quả trực tiếp trong Bigtable ObjFS. Các kết quả này có sẵn cho trình thực thi cho các thao tác trong tương lai hoặc để người dùng cuối tải xuống thông qua objfsd.
Kết quả cuối cùng là một hệ thống có thể mở rộng để hỗ trợ hiệu quả tất cả các bản dựng được thực hiện tại Google. Và quy mô của các bản dựng của Google thực sự rất lớn: Google chạy hàng triệu bản dựng thực thi hàng triệu trường hợp kiểm thử và tạo ra hàng petabyte dữ liệu đầu ra bản dựng từ hàng tỷ dòng mã nguồn mỗi ngày. Hệ thống như vậy không chỉ cho phép các kỹ sư của chúng tôi xây dựng nhanh chóng các cơ sở mã phức tạp mà còn cho phép chúng tôi triển khai một số lượng lớn các công cụ và hệ thống tự động dựa vào bản dựng của chúng tôi.


