Cơ chế hộp cát

Báo cáo sự cố Xem nguồn

Bài viết này đề cập đến hộp cát trong Bazel và cách gỡ lỗi môi trường 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 hệ thống. Đối với Bazel, điều này có nghĩa là việc 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 làm việc chỉ chứa dữ liệu đầu vào đã biết, để trình biên dịch và các công cụ khác không xem được các tệp nguồn mà chúng không được truy cập, trừ phi chúng biết đường dẫn tuyệt đối đến các tệp đó.

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 mọi 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 đang hoạt độ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 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 điểm thực thi. execroot/ chứa tất cả tệp đầu vào cho hành động 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, để ràng buộc hành động trong execroot/.

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

  • Nếu không có hộp cát 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 hay không (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 hành động). Khi một trong các tệp đầu vào chưa được khai báo thay đổi, Bazel vẫn tin rằng bản dựng đã được cập nhật và sẽ không tạo lại hành động này. Điều này có thể dẫn đến việc 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 sự cố 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 ả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à giải pháp khả thi.

  • Hộp cát bắt chước hành vi thực thi từ xa – nếu bản dựng hoạt động tốt với hộp cát, thì bản dựng đó cũng có thể hoạt động với chế độ thực thi từ xa. Bằng cách tải tất cả tệp cần thiết lên (bao gồm cả công cụ cục bộ) lên, 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 muốn dùng thử trình biên dịch mới hoặc thay đổi công cụ hiện có.

Nên sử dụng chiến lược hộp cát nào

Bạn có thể chọn loại hộp cát sẽ sử dụng (nếu có) với cờ chiến lược. Việc sử dụng chiến lược sandboxed khiến Bazel chọn một trong những phương thức triển khai hộp cát liệt kê dưới đây, ưu tiên hộp cát dành riêng cho hệ điều hành sang hộp cát chung ít khép kín hơn. Trình thực thi liên tục sẽ chạy trong một hộp cát chung nếu bạn vượt qua 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. Lệnh này chỉ thực thi dòng lệnh của thao tác với thư mục đang làm việc được đặt thành execroot của không gian làm việc.

processwrapper-sandbox là một chiến lược hộp cát không yêu cầu 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 từ đầu. Thư mục này xây dựng một thư mục hộp cát bao gồm các 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 hành động với thư mục đang hoạt động được đặt thành thư mục này thay vì tệp thực thi, 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 tệp thực thi và xoá hộp cát. Điều này ngăn hành động vô tình sử dụng bất kỳ tệp đầu vào nào không được khai báo và ngăn việc lãng phí các tệp đầu ra không xác định cho execroot.

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

darwin-sandbox cũng tương tự như vậy, nhưng dành cho macOS. Công cụ này sử dụng công cụ sandbox-exec của Apple để đạt được gần giống với hộp cát của 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ác 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 ma thuật của nó, nên bạn không thể dễ dàng chạy linux-sandbox bên trong vùng chứa Docker, trừ khi bạn sử dụng docker run --privileged. Trên macOS, bạn không thể chạy sandbox-exec bên trong một quy trình đang ở trong hộp cát. Do đó, trong những trường hợp như vậy, Bazel sẽ tự động quay lại 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 một 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 các chiến lược thực thi mà Bazel cố gắng sử dụng (ví dụ: bazel build --spawn_strategy=worker,linux-sandbox).

Quá trình 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. Quá trình thực thi động sẽ ngầm chuyển sang hộp cát cho trình thực thi lâu dài (persistent worker).

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

  • Hộp cát làm phát sinh thêm chi phí thiết lập và chia nhỏ. 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 lưu trữ. Đối với Linux, các bản dựng hộp cát hiếm khi chậm hơn vài phần trăm. Việc đặt --reuse_sandbox_directories có thể giảm thiểu chi phí thiết lập và chia nhỏ.

  • Hộp cát vô hiệu hoá hiệu quả mọi bộ nhớ đệm mà công cụ này có thể có. Bạn có thể giảm thiểu điều này bằng cách sử dụng trình chạy liên tục, nhưng với các đảm bảo hộp cát yếu hơn.

  • Trình thực thi Multiplex yêu cầu hỗ trợ trình thực thi rõ ràng để tạo hộp cát. Các trình thực thi không hỗ trợ hộp cát multiplex sẽ chạy dưới dạng các trình thực thi đơn tác trong chế độ thực thi động, điều này có thể gây tốn thêm bộ nhớ.

Gỡ lỗi

Hãy làm theo các chiến lược dưới đây để khắc phục sự cố về hộp cát.

Không gian tên bị 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 trên Google Kubernetes Engine hoặc Debian, không gian tên người dùng bị vô hiệu hoá theo mặc định do 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 quá trình thiết lập hệ thống. Nếu bạn thấy một thông báo như namespace-sandbox.c:633: execvp(argv[0], argv): No such file or directory, hãy thử tắt hộp cát bằng --strategy=Genrule=local đối với quy tắc tạo và --spawn_strategy=local đối với các quy tắc khác.

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

Nếu không thể tạo bản dựng, hãy sử dụng --verbose_failures--sandbox_debug để Bazel hiển thị chính xác lệnh mà nó đã chạy khi bản dựng 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 những tệp mà Bazel đã tạo rồi chạy lại lệnh này để xem cách thức hoạt động của tệp.

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