Bản dựng phân tán

Khi bạn có cơ sở mã lớn, các chuỗi phần phụ thuộc có thể trở nên rất sâu. 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, đơn giản là không thể hoàn thành bản dựng trong một khoảng thời gian hợp lý trên một máy: không hệ thống xây dựng nào có thể xoay quanh các định luật vật lý cơ bản áp dụng cho phần cứng của máy. Cách duy nhất để thực hiện công việc này là dùng một hệ thống xây dựng hỗ trợ các bản dựng được phân phối, trong đó các đơn vị công việc do hệ thống thực hiện được trải rộng 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 rõ hơn về điều này ở phần sau), điều này sẽ cho phép chúng tôi hoàn thành mọi bản dựng thuộc mọi kích thước nhanh chóng trong khả năng chi trả của chúng tôi. Khả năng mở rộng này là mục tiêu tối quan trọng mà chúng tôi đã nỗ lực hướng tới bằng cách xác định hệ thống xây dựng dựa trên cấu phần phần mềm.

Lưu vào bộ nhớ đệm từ xa

Loại bản dựng được phân phối đơn giản nhất là loại bản dựng chỉ tận dụng tính năng lưu vào bộ nhớ đệm từ xa, như minh hoạ trong Hình 1.

Bản dựng được phân phối có chức năng lưu vào bộ nhớ đệm từ xa

Hình 1 Một bản dựng được phân phối cho thấy hoạt động lưu vào bộ nhớ đệm từ xa

Mọi hệ thống thực hiện các 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ẻ thông tin tham chiếu đến một 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 và nhanh cục bộ như Redis hoặc dịch vụ đám mây như Google Cloud Storage. Mỗi khi người dùng cần tạo một cấu phần phần mềm, cho dù là trực tiếp hay dưới dạng phần phụ thuộc, trước tiên, hệ thống sẽ kiểm tra bộ nhớ đệm từ xa để xem cấu phần phần mềm đó đã tồn tại ở đó hay chưa. Nếu có, trình phân tích cú pháp 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ự xây dựng cấu phần phần mềm đó và tải kết quả trở 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ẻ cho nhiều người dùng thay vì phải xây dựng lại cho 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 lưu vào 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 có thể tái tạo hoàn toàn. Điều này nghĩa 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 đầu vào cho mục tiêu đó sao cho cùng một tập hợp đầu vào sẽ tạo ra chính xác cùng một đầu ra trên mọi máy. Đây là cách duy nhất để đảm bảo rằng kết quả tải cấu phần phần mềm xuống giống với kết quả khi tự tạo cấu phần phần mềm. Xin lưu ý rằng điều này yêu cầu mỗi cấu phần phần mềm trong bộ nhớ đệm phải được khoá cả mục tiêu và hàm băm của dữ liệu đầu vào. Nhờ đó, mỗi kỹ sư có thể thực hiện nhiều sửa đổi cho cùng một mục tiêu tại cùng một thời điểm và bộ nhớ đệm từ xa sẽ lưu trữ tất cả cấu phần phần mềm thu được và phân phát các cấu phần phần mềm đó một cách phù hợp mà không có xung đột.

Tất nhiên, để có mọi lợi ích 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 so với 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 xảy ra, đặc biệt nếu máy chủ bộ nhớ đệm ở xa máy đang tạo 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ể nhanh chóng chia sẻ kết quả bản dựng.

Thực thi từ xa

Việc lưu vào bộ nhớ đệm từ xa không phải là một bản dựng được phân phối thực sự. Nếu bộ nhớ đệm bị mất hoặc nếu bạn thực hiện một thay đổi ở cấp thấp đòi hỏi phải xây dựng lại mọi thứ, 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ế tạo bản dựng có thể được trải rộng cho bất kỳ số lượng trình chạy nào. Hình 2 mô tả một hệ thống thực thi từ xa.

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ư con người hoặc hệ thống xây dựng tự động) sẽ gửi yêu cầu đến một bản dựng chính trung tâm. Bản dựng chính chia nhỏ 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 đó qua một nhóm worker có thể mở rộng. Mỗi trình thực thi thực hiện các thao tác yêu cầu với dữ liệu đầu vào do người dùng chỉ định và ghi 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 đang thực thi các hành động yêu cầu cho đến khi có thể tạo và gửi kết quả cuối cùng cho người dùng.

Phần khó khăn nhất trong việc triển khai một hệ thống như vậy là quản lý hoạt động giao tiếp giữa trình thực thi, máy chủ chính và máy cục bộ của người dùng. Worker có thể phụ thuộc vào cấu phần phần mềm trung gian do các trình thực thi khác tạo ra và kết quả cuối cùng cần được gửi trở lại máy cục bộ của người dùng. Để làm điều này, chúng ta có thể xây dựng trên bộ nhớ đệm đã phân phối đượ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 đó. Tệp chính sẽ chặn các trình thực thi tiếp tục cho đến khi mọi thứ chúng phụ thuộc vào đã hoàn tất. Trong trường hợp đó, trình thực thi 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 thức 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 các thay đổi đó trước khi tạo bản dựng.

Để làm được điều này, 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ể xoay vòng worker mà không cần sự can thiệp của con người. Bản thân các 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. Kết quả phải hoàn toàn mang tính xác định để mỗi worker có thể tin tưởng kết quả nhận được từ các worker khác. Những đảm bảo như vậy là cực kỳ khó khăn đối với một hệ thống dựa trên tác vụ trong việc cung cấp, khiến cho hệ thống thực thi từ xa đáng tin cậy gần như không thể xây dựng được một hệ thống thực thi từ xa đáng tin cậy.

Bản dựng được phân phối tại Google

Kể từ năm 2008, Google đã sử dụng một hệ thống xây dựng được phân phối sử dụng cả hoạt động lưu vào bộ nhớ đệm từ xa và thực thi từ xa, như minh hoạ trong Hình 3.

Hệ thống xây dựng cấp cao

Hình 3. Hệ thống bản dựng được phân phối của Google

Bộ nhớ đệm từ xa của Google được gọi là ObjFS. Cấu trúc này bao gồm một phần phụ trợ lưu trữ kết quả của bản dựng trong Bigtables được phân phối trên nhóm máy sản xuất của chúng tôi và một trình nền FUSE giao diện người dùng có tên là objfsd chạy trên máy của từng nhà phát triển. Trình nền FUSE cho phép các kỹ sư duyệt qua đầu ra của bản dựng như thể chúng là các tệp thông thường được lưu trữ trên máy trạm, nhưng với nội dung tệp được tải xuống theo yêu cầu chỉ cho một số tệp mà người dùng trực tiếp yêu cầu. Việc phân phát nội dung tệp theo yêu cầu giúp giảm đáng kể 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ả đầ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 gọi là Forge. Một ứng dụng Forge trong Blaze (ứng dụng tương đương nội bộ của Bazel) được gọi là Nhà phân phối gửi yêu cầu về mỗi hành động đến một công việc đang chạy trong trung tâm dữ liệu của chúng tôi có tên là Trình lập lịch biểu. Trình lập lịch biểu duy trì bộ nhớ đệm các kết quả hành động, cho phép trả về phản hồi ngay lập tức nếu hành động đã đượ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, ứng dụng sẽ đưa hành động đó vào một hàng đợi. Một số lượng lớn các lệnh của Executor liên tục đọc các thao tác trong hàng đợi này, thực thi chúng và lưu trữ kết quả trực tiếp trong ObjFS Bigtables. Các kết quả này có sẵn cho các trình thực thi của các hành động trong tương lai hoặc đượ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 sẽ mở rộng quy mô để hỗ trợ hiệu quả tất cả các bản dựng đã thực hiện tại Google. Và quy mô các bản dựng của Google thực sự vô cùng khổng lồ: 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 tỷ dòng mã nguồn mỗi ngày. Một hệ thống như vậy không chỉ cho phép các kỹ sư của chúng tôi nhanh chóng xây dự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.