Cơ chế hộp cát

Báo cáo vấn đề Xem nguồn Nightly · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

Bài viết này đề cập đến việc tạo hộp cát trong Bazel và gỡ lỗi môi trường tạo hộp cát của bạn.

Hộp cát là một chiến lược hạn chế quyền, giúp tách biệt các quy trình với nhau hoặc với các tài nguyên trong một hệ thống. Đối với Bazel, điều này có nghĩa là hạn chế quyền truy cập vào hệ thống tệp.

Hộp cát hệ thống tệp của Bazel chạy các quy trình trong một thư mục đang hoạt động chỉ chứa các đầu vào đã biết, sao cho các trình biên dịch và công cụ khác không thấy các tệp nguồn mà chúng không được phép truy cập, trừ phi chúng biết đường dẫn tuyệt đối đến các tệp đó.

Quy trình tạo hộp cát không ẩn môi trường lưu trữ theo bất kỳ cách nào. Các quy trình có thể tự do truy cập vào tất cả các tệp trên hệ thống tệp. Tuy nhiên, trên các nền tảng hỗ trợ không gian tên người dùng, các quy trình không thể sửa đổi bất kỳ tệp nào bên ngoài thư mục làm việc của chúng. Điều này đảm bảo rằng biểu đồ bản dựng không có các phần phụ thuộc ẩn có thể ảnh hưởng đến khả năng tái tạo của bản dựng.

Cụ thể hơn, Bazel tạo một thư mục execroot/ cho mỗi thao tác, đóng vai trò là thư mục công việc của thao tác tại thời gian thực thi. execroot/ chứa tất cả các tệp đầu vào cho thao tác và đóng vai trò là vùng chứa cho mọi đầu ra được tạo. Sau đó, Bazel sử dụng một kỹ thuật do hệ điều hành cung cấp (các vùng chứa trên Linux và sandbox-exec trên macOS) để giới hạn thao tác trong execroot/.

Lý do cần tạo hộp cát

  • Nếu không có tính năng tạo hộp cát cho thao tác, Bazel sẽ không biết liệu một công cụ có sử dụng các tệp đầu vào chưa khai báo (các tệp không được liệt kê rõ ràng trong các phần phụ thuộc của một thao tác) hay không. Khi một trong các tệp đầu vào chưa khai báo thay đổi, Bazel vẫn cho rằng bản dựng đã được cập nhật và sẽ không tạo lại thao tác. Điều này có thể dẫn đến bản dựng gia tăng không chính xác.

  • Việc sử dụng lại các mục trong bộ nhớ đệm không chính xác sẽ gây ra vấn đề trong quá trình lưu vào bộ nhớ đệm từ xa. Một mục bộ nhớ đệm không hợp lệ trong bộ nhớ đệm dùng chung sẽ ảnh hưởng đến mọi nhà phát triển trong dự án và việc xoá toàn bộ bộ nhớ đệm từ xa không phải là một giải pháp khả thi.

  • Quy trình tạo hộp cát mô phỏng hành vi của hoạt động thực thi từ xa – nếu một bản dựng hoạt động tốt với quy trình tạo hộp cát, thì bản dựng đó cũng có thể hoạt động với hoạt động thực thi từ xa. Bằng cách thực hiện tải lên thực thi từ xa tất cả các tệp cần thiết (bao gồm cả các công cụ cục bộ), bạn có thể giảm đáng kể chi phí bảo trì cho các cụm biên dịch so với việc phải cài đặt các công cụ trên mọi máy trong cụm mỗi khi bạn muốn dùng thử một trình biên dịch mới hoặc thực hiện thay đổi đối với một công cụ hiện có.

Chiến lược hộp cát cần sử dụng

Bạn có thể chọn loại hộp cát để sử dụng (nếu có) bằng cờ chiến lược. Khi sử dụng chiến lược sandboxed, Bazel sẽ chọn một trong các cách triển khai hộp cát được liệt kê bên dưới, ưu tiên hộp cát dành riêng cho hệ điều hành hơn là hộp cát chung ít khép kín hơn. Persistent worker chạy trong một hộp cát chung nếu bạn truyền cờ --worker_sandboxing.

Chiến lược local (còn gọi là standalone) không thực hiện bất kỳ loại hộp cát nào. Thao tác này chỉ cần thực thi dòng lệnh của thao tác với thư mục đang hoạt động được đặt thành execroot của không gian làm việc.

processwrapper-sandbox là một chiến lược tạo hộp cát không yêu cầu bất kỳ tính năng "nâng cao" nào – chiến lược này sẽ hoạt động trên mọi hệ thống POSIX ngay khi xuất xưởng. Thao tác này sẽ tạo một thư mục hộp cát bao gồm các đường liên kết tượng trưng trỏ đến các tệp nguồn ban đầu, thực thi dòng lệnh của thao tác với thư mục đang hoạt động được đặt thành thư mục này thay vì execroot, sau đó di chuyển các cấu phần phần mềm đầu ra đã biết ra khỏi hộp cát vào execroot và xoá hộp cát. Điều này giúp ngăn hành động vô tình sử dụng bất kỳ tệp đầu vào nào chưa được khai báo và ngăn việc làm lộn xộn execroot bằng các tệp đầu ra không xác định.

linux-sandbox tiến thêm một bước và được xây dựng dựa trên processwrapper-sandbox. Tương tự như những gì Docker làm nâng cao, nó sử dụng Không gian tên Linux (Không gian tên Người dùng, Gắn kết, PID, Mạng và IPC) để tách biệt hành động khỏi máy chủ lưu trữ. Tức là nó chỉ cho phép đọc toàn bộ hệ thống tệp, ngoại trừ thư mục hộp cát. Do đó, thao tác này không thể vô tình sửa đổi bất cứ nội dung nào trên hệ thống tệp của máy chủ. Điều này giúp ngăn chặn các trường hợp như một thử nghiệm có lỗi vô tình xoá thư mục $HOME của bạn. Bạn cũng có thể ngăn hành động truy cập vào mạng (nếu muốn). linux-sandbox sử dụng không gian tên PID để ngăn hành động này nhìn thấy bất kỳ quy trình nào khác và để huỷ tất cả các quy trình một cách đáng tin cậy (kể cả các trình nền do hành động này tạo ra) vào cuối.

darwin-sandbox cũng tương tự, nhưng dành cho macOS. Công cụ này sử dụng công cụ sandbox-exec của Apple để đạt được kết quả tương tự như hộp cát Linux.

Cả linux-sandboxdarwin-sandbox đều không hoạt động trong trường hợp "lồng nhau" do các hạn chế trong cơ chế do hệ điều hành cung cấp. Vì Docker cũng sử dụng không gian tên Linux cho vùng chứa của mình, nên bạn không thể dễ dàng chạy linux-sandbox bên trong vùng chứa Docker, trừ phi bạn sử dụng docker run --privileged. Trên macOS, bạn không thể chạy sandbox-exec trong một quy trình đã được đưa vào hộp cát. Do đó, trong những trường hợp này, Bazel sẽ tự động chuyển về sử dụng processwrapper-sandbox.

Nếu bạn muốn gặp lỗi bản dựng (chẳng hạn như không vô tình tạo bản dựng bằng chiến lược thực thi ít nghiêm ngặt hơn), hãy sửa đổi rõ ràng danh sách chiến lược thực thi mà Bazel cố gắng sử dụng (ví dụ: bazel build --spawn_strategy=worker,linux-sandbox).

Việc thực thi động thường yêu cầu hộp cát để thực thi cục bộ. Để chọn không sử dụng, hãy truyền cờ --experimental_local_lockfree_output. Thực thi động các persistent worker trong hộp cát một cách âm thầm.

Nhược điểm của cơ chế hộp cát

  • Việc tạo hộp cát sẽ phát sinh thêm chi phí thiết lập và huỷ thiết lập. Mức chi phí này phụ thuộc vào nhiều yếu tố, bao gồm cả hình dạng của bản dựng và hiệu suất của hệ điều hành máy chủ. Đối với Linux, các bản dựng được cách ly hiếm khi chậm hơn vài phần trăm. Việc thiết lập --reuse_sandbox_directories có thể giảm thiểu chi phí thiết lập và tháo dỡ.

  • Tính năng hộp cát sẽ vô hiệu hoá mọi bộ nhớ đệm mà công cụ có thể có. Bạn có thể giảm thiểu vấn đề này bằng cách sử dụng persistent worker (worker liên tục), nhưng phải trả giá bằng việc giảm độ đảm bảo của hộp cát.

  • Các worker ghép kênh cần có sự hỗ trợ rõ ràng của worker để được đưa vào hộp cát. Những worker không hỗ trợ hộp cát ghép kênh sẽ chạy dưới dạng worker đơn kênh trong quá trình thực thi linh động, điều này có thể tốn thêm bộ nhớ.

Gỡ lỗi

Hãy làm theo các chiến lược bên dưới để gỡ lỗi các vấn đề về hộp cát.

Không gian tên đã huỷ kích hoạt

Trên một số nền tảng, chẳng hạn như các nút cụm Google Kubernetes Engine hoặc Debian, không gian tên người dùng sẽ bị vô hiệu hoá theo mặc định do lo ngại về vấn đề bảo mật. Nếu tệp /proc/sys/kernel/unprivileged_userns_clone tồn tại và chứa giá trị 0, bạn có thể kích hoạt không gian tên người dùng bằng cách chạy:

   sudo sysctl kernel.unprivileged_userns_clone=1

Lỗi thực thi quy tắc

Hộp cát có thể không thực thi được các quy tắc do chế độ thiết lập hệ thống. Nếu bạn thấy thông báo như namespace-sandbox.c:633: execvp(argv[0], argv): No such file or directory, hãy thử huỷ kích hoạt hộp cát bằng --strategy=Genrule=local cho genrules và --spawn_strategy=local cho các quy tắc khác.

Gỡ lỗi chi tiết cho các lỗi bản dựng

Nếu bản dựng của bạn không thành công, hãy sử dụng --verbose_failures--sandbox_debug để Bazel cho thấy chính xác lệnh mà Bazel đã chạy khi bản dựng của bạn không thành công, bao gồm cả phần thiết lập hộp cát.

Ví dụ về thông báo lỗi:

ERROR: path/to/your/project/BUILD:1:1: compilation of rule
'//path/to/your/project:all' failed:

Sandboxed execution failed, which may be legitimate (such as a compiler error),
or due to missing dependencies. To enter the sandbox environment for easier
debugging, run the following command in parentheses. On command failure, a bash
shell running inside the sandbox will then automatically be spawned

namespace-sandbox failed: error executing command
  (cd /some/path && \
  exec env - \
    LANG=en_US \
    PATH=/some/path/bin:/bin:/usr/bin \
    PYTHONPATH=/usr/local/some/path \
  /some/path/namespace-sandbox @/sandbox/root/path/this-sandbox-name.params --
  /some/path/to/your/some-compiler --some-params some-target)

Giờ đây, bạn có thể kiểm tra thư mục hộp cát đã tạo và xem Bazel đã tạo những tệp nào, sau đó chạy lại lệnh để xem cách hoạt động của lệnh.

Xin lưu ý rằng Bazel không xoá thư mục hộp cát khi bạn sử dụng --sandbox_debug. Trừ phi đang gỡ lỗi, bạn nên tắt --sandbox_debug vì tính năng này sẽ làm đầy ổ đĩa của bạn theo thời gian.