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

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 sâu. Ngay cả các tệp nhị phân đơn giản cũng có thể thường 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 một 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ể vượt qua được 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à sử 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 tôi chia công việc của hệ thống thành các đơn vị đủ nhỏ (sẽ tìm hiểu thêm về nội dung 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 ở mọi kích thước nhanh chóng nhất có thể trong khả năng của chúng tôi. Khả năng mở rộng này chính là giải pháp mà chúng tôi đang 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 phân phối đơn giản nhất là loại 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 chức nă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 dành cho 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 phổ biến. 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 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, dù 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 thông qua 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ả 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 tự xây dựng lại theo 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ì tạo từ đầu, giúp giảm đáng kể chi phí chạy hệ thống xây dựng.

Để 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. Tức là đối với mọi mục tiêu bản dựng, bạn phải có khả năng 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 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 xuống cấu phần phần mềm giống với 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á cả mục tiêu và hàm băm của dữ liệu đầu vào. Nhờ đó, các kỹ sư có thể cùng lúc thực hiện nhiều sửa đổi cho cùng một mục tiêu. Khi đó, 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ó 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 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 đúng, đặc biệt là khi máy chủ bộ nhớ đệm ở xa máy chủ 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 yêu cầu 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 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 số lượng trình thực thi bất kỳ. 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 các yêu cầu thành các thao tác thành phần và lên lịch thực hiện 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 đượ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ả. Những cấu phần phần mềm này được chia sẻ giữa các máy khác thực thi các hành động cần đến chúng cho đến khi kết quả cuối cùng có thể được tạo và gửi đến 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 các worker, chủ thể 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 worker khác tạo ra và kết quả cuối cùng cần được gửi 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 được 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 chặn 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 đó, 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 có 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 để worker 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. Đầu ra phải hoàn toàn mang tính quyết định để mỗi worker đều 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 hệ thống dựa trên tác vụ cung cấp, điều này khiến 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 có cả chức nă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. Hệ thống này bao gồm một phần phụ trợ lưu trữ dữ liệu đầu ra của bản dựng trong Bigtables đượ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 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 các đầu ra của bản dựng như thể chúng là các tệp bình 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 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ể 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ả đầ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 (công cụ tương đương nội bộ của Bazel) được gọi là Nhà phân phối sẽ gửi yêu cầu về từng hành động cho một công việc đang chạy trong trung tâm dữ liệu của chúng tôi được gọi là Trình lập lịch biểu. Trình lập lịch biểu duy trì bộ nhớ đệm kết quả của hành động, cho phép bộ nhớ này 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, hệ thống sẽ đặt hành động vào hàng đợi. Một nhóm lớn các công việc của Executor liên tục đọc các hành động từ 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 executor cho 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 có thể mở rộng để hỗ trợ hiệu quả tất cả các bản dựng do Google thực hiện. Và quy mô các bản dựng của Google thực sự rất 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 số petabyte kết quả bản dựng từ hàng tỷ dòng mã nguồn mỗi ngày. Một hệ thống như vậy không chỉ giúp các kỹ sư của chúng tôi nhanh chóng xây dựng cơ sở mã phức tạp, mà còn cho phép chúng tôi triển khai số lượng lớn công cụ và hệ thống tự động dựa trên bản dựng của chúng tôi.