Cơ chế hộp cát

Bài viết này đề cập đến hộp cát trong Bazel, cách cài đặt sandboxfs và gỡ lỗi môi trường hộp cát.

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 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 làm việc chỉ chứa các dữ liệu đầu vào đã biết, chẳng hạn như trình biên dịch và các công cụ khác sẽ không thấy 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 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 đ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, để hạn chế 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 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 đó. Điều này có thể khiến bản dựng tăng dần không chính xác.

  • Việc sử dụng lại các mục nhập 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 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à giải pháp khả thi.

  • Hộp cát bắt chước hành vi thực thi từ xa – nếu một 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ả các tệp cần thiết lên (bao gồm cả các công cụ cục bộ) từ xa, bạn có thể giảm đáng kể chi phí bảo trì 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 từng 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 một 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 sử dụng loại hộp cát nào (nếu có) với cờ chiến lược. Việc sử dụng chiến lược sandboxed giúp Bazel chọn một trong các phương thức triển khai hộp cát được 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 chạy liên tục sẽ chạy trong một hộp cát chung nếu bạn chuyể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. Hàm này chỉ 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 executor 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 bất kỳ tính năng "nâng cao" nào – nên hoạt động trên mọi hệ thống POSIX ngay từ đầu. Công cụ này xây dựng 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 hành động bằng thư mục đang hoạt động được đặt vào 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 ngăn thao tác vô tình sử dụng bất kỳ tệp đầu vào nào chưa được khai báo và làm hỏng executor bằng các tệp đầu ra không xác định.

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, Gắn kết, PID, Mạng và IPC) để tách biệt hành động với máy chủ. Điều này nghĩa là 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 lưu trữ. Điều này giúp tránh các tình huống như kiểm thử lỗi vô tình vô tình rm -rf thư mục $HOME của bạn. 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 (ngay cả các trình nền do thao tác đó tạo ra) ở cuối.

darwin-sandbox tương tự như vậy, nhưng dành cho macOS. Hộp cát này sử dụng công cụ sandbox-exec của Apple để đạt được gần giống 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ế về cơ chế do hệ điều hành cung cấp. Vì Docker cũng sử dụng không gian tên Linux làm vùng chứa kỳ diệu, nên bạn sẽ 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 bên trong một quy trình đang được tạo hộp cát. Do đó, trong những trường hợp nà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ằng một chiến lược thực thi ít nghiêm ngặt hơn – hãy sửa đổi 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 chuyển cờ --experimental_local_lockfree_output. Quá trình thực thi động động trong các hộp cát cho trình thực thi liên tục (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 máy chủ. Đối với Linux, các bản dựng trong 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 sẽ 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 thực thi liên tục, với chi phí đả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 chạy không hỗ trợ hộp cát multiplex sẽ chạy dưới dạng trình chạy đơn trong chế độ thực thi động, gây ra vấn đề tốn thêm bộ nhớ.

hộp cát

sandboxfs là một hệ thống tệp FUSE cho thấy khung hiển thị tuỳ ý của hệ thống tệp cơ sở mà không có lỗi thời gian. Bazel sử dụng sandboxfs để tạo execroot/ ngay lập tức cho mỗi hành động, giúp tránh chi phí phát sinh hàng nghìn lệnh gọi hệ thống. Xin lưu ý rằng quá trình I/O tiếp theo trong execroot/ có thể chậm hơn do mức hao tổn FUSE.

Cài đặt hộp cát

Hãy làm theo các bước sau để cài đặt sandboxfs và tạo bản dựng Bazel bằng công cụ này:

Tải xuống

Tải xuống và cài đặt sandboxfs để tệp nhị phân sandboxfs xuất hiện trong PATH.

Chạy sandboxfs

  1. (chỉ dành cho macOS) Cài đặt OSXFUSE.
  2. (chỉ dành cho macOS) Chạy:

    sudo sysctl -w vfs.generic.osxfuse.tunables.allow_other=1
    

    Bạn sẽ cần thực hiện việc này sau khi cài đặt và sau mỗi lần khởi động lại để đảm bảo các dịch vụ hệ thống chính của macOS hoạt động thông qua hộp cát.

  3. Chạy bản dựng Bazel bằng --experimental_use_sandboxfs.

    bazel build target --experimental_use_sandboxfs
    

Khắc phục sự cố

Nếu bạn thấy local thay vì darwin-sandbox hoặc linux-sandbox dưới dạng chú giải cho các thao tác được thực thi, thì điều này có thể có nghĩa là hộp cát đã bị tắt. Truyền --genrule_strategy=sandboxed --spawn_strategy=sandboxed để bật tính năng này.

Gỡ lỗi

Hãy làm theo các chiến lược bên dưới để 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 Google Kubernetes Engine hoặc Debian, không gian tên của người dùng sẽ bị vô hiệu hoá theo mặc định do vấn đề về bảo mật. Nếu tệp /proc/sys/kernel/unprivileged_userns_clone có chứa giá trị 0 và có giá trị 0, bạn có thể kích hoạt không gian tên của người dùng bằng cách chạy:

   sudo sysctl kernel.unprivileged_userns_clone=1

Không thực thi được 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ử huỷ kích hoạ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 các lỗi bản dựng

Nếu bản dựng không thành công, hãy sử dụng --verbose_failures--sandbox_debug để Bazel hiển thị chính xác lệnh mà bản dựng đã 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.

Thông báo lỗi mẫu:

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)

Bạn hiện có thể kiểm tra thư mục hộp cát đã tạo, xem Bazel đã tạo tệp nào rồi chạy lại lệnh để xem hành vi 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ì ứng dụng này sẽ đầy ổ đĩa theo thời gian.