แซนด์บ็อกซ์

รายงานปัญหา ดูซอร์สโค้ด รุ่น Nightly · 8.0 7.4 . 7.3 · 7.2 · 7.1 · 7.0 · 6.5

บทความนี้กล่าวถึงแซนด์บ็อกซ์ใน Bazel และการแก้ไขข้อบกพร่องของสภาพแวดล้อมแซนด์บ็อกซ์

แซนด์บ็อกซ์เป็นกลยุทธ์การจํากัดสิทธิ์ที่แยกกระบวนการออกจากกันหรือแยกออกจากทรัพยากรในระบบ สําหรับ Bazel การดำเนินการนี้หมายถึงการจํากัดการเข้าถึงระบบไฟล์

แซนด์บ็อกซ์ระบบไฟล์ของ Bazel จะเรียกใช้กระบวนการในไดเรกทอรีการทำงานที่มีเฉพาะอินพุตที่รู้จักเท่านั้น เพื่อให้คอมไพเลอร์และเครื่องมืออื่นๆ ไม่เห็นไฟล์ต้นฉบับที่ไม่ควรเข้าถึง เว้นแต่ว่าเครื่องมือเหล่านั้นจะทราบเส้นทางแบบสัมบูรณ์ของไฟล์

Sandboxing ไม่ได้ซ่อนสภาพแวดล้อมของโฮสต์ไม่ว่าในทางใดก็ตาม กระบวนการเข้าถึงไฟล์ทั้งหมดในระบบไฟล์ได้อย่างอิสระ อย่างไรก็ตาม ในแพลตฟอร์มที่รองรับเนมสเปซของผู้ใช้ กระบวนการจะแก้ไขไฟล์ที่อยู่นอกไดเรกทอรีการทำงานไม่ได้ ซึ่งทำให้มั่นใจได้ว่ากราฟบิลด์จะไม่มีทรัพยากรภายนอกที่ซ่อนอยู่ซึ่งอาจส่งผลต่อความสามารถในการสร้างบิลด์ซ้ำ

กล่าวอย่างเจาะจงคือ Bazel จะสร้างไดเรกทอรี execroot/ สำหรับการดำเนินการแต่ละรายการ ซึ่งทำหน้าที่เป็นไดเรกทอรีทํางานของการดำเนินการ ณ เวลาเรียกใช้ execroot/ มีไฟล์อินพุตทั้งหมดสำหรับการดำเนินการและทำหน้าที่เป็นคอนเทนเนอร์สำหรับเอาต์พุตที่สร้างขึ้น จากนั้น Bazel จะใช้เทคนิคที่ระบบปฏิบัติการมีให้ ซึ่งได้แก่ คอนเทนเนอร์ใน Linux และ sandbox-exec ใน macOS เพื่อจำกัดการดำเนินการภายใน execroot/

เหตุผลในการใช้แซนด์บ็อกซ์

  • หากไม่มีแซนด์บ็อกซ์การดำเนินการ Bazel จะไม่ทราบว่าเครื่องมือใช้ไฟล์อินพุตที่ไม่ได้ประกาศหรือไม่ (ไฟล์ที่ไม่ได้ระบุไว้ในทรัพยากร Dependency ของการดำเนินการอย่างชัดเจน) เมื่อไฟล์อินพุตที่ไม่มีการประกาศไฟล์ใดไฟล์หนึ่งมีการเปลี่ยนแปลง Bazel จะยังคงเชื่อว่าบิลด์เป็นเวอร์ชันล่าสุดและจะไม่สร้างการดำเนินการอีกครั้ง ซึ่งอาจส่งผลให้บิลด์ที่เพิ่มขึ้นไม่ถูกต้อง

  • การใช้รายการแคชซ้ำอย่างไม่ถูกต้องจะทำให้เกิดปัญหาระหว่างการแคชระยะไกล รายการแคชที่ไม่ถูกต้องในแคชที่แชร์จะส่งผลต่อนักพัฒนาแอปทุกคนในโปรเจ็กต์ และการล้างแคชระยะไกลทั้งหมดไม่ใช่วิธีแก้ปัญหาที่ทำได้จริง

  • Sandboxing จะเลียนแบบลักษณะการทํางานของการดำเนินการจากระยะไกล หากบิลด์ทํางานได้ดีกับ Sandboxing ก็น่าจะทํางานกับการดำเนินการจากระยะไกลด้วย การทำให้การดําเนินการจากระยะไกลอัปโหลดไฟล์ที่จําเป็นทั้งหมด (รวมถึงเครื่องมือในเครื่อง) จะช่วยให้คุณลดค่าใช้จ่ายในการบํารุงรักษาคลัสเตอร์การคอมไพล์ได้อย่างมากเมื่อเทียบกับการต้องติดตั้งเครื่องมือในเครื่องทุกเครื่องในคลัสเตอร์ทุกครั้งที่ต้องการลองใช้คอมไพเลอร์ใหม่หรือทําการเปลี่ยนแปลงเครื่องมือที่มีอยู่

กลยุทธ์แซนด์บ็อกซ์ที่จะใช้

คุณเลือกประเภทแซนด์บ็อกซ์ที่จะใช้ (หากมี) ได้ด้วยFlag กลยุทธ์ การใช้กลยุทธ์ sandboxed จะทำให้ Bazel เลือกการติดตั้งใช้งานแซนด์บ็อกซ์รายการใดรายการหนึ่งตามที่ระบุไว้ด้านล่าง โดยเลือกแซนด์บ็อกซ์เฉพาะระบบปฏิบัติการมากกว่าแซนด์บ็อกซ์ทั่วไปที่ปิดทึบน้อยกว่า Worker แบบถาวรจะทำงานในแซนด์บ็อกซ์ทั่วไปหากคุณส่งผ่าน Flag --worker_sandboxing

กลยุทธ์ local (หรือที่เรียกว่า standalone) จะไม่ใช้แซนด์บ็อกซ์ประเภทใดๆ การดำเนินการนี้จะเรียกใช้บรรทัดคำสั่งของการดำเนินการโดยตั้งค่าไดเรกทอรีที่ทำงานเป็น execroot ของพื้นที่ทำงาน

processwrapper-sandbox เป็นกลยุทธ์แซนด์บ็อกซ์ที่ไม่ต้องใช้ฟีเจอร์ "ขั้นสูง" ใดๆ และควรใช้งานได้ในระบบ POSIX ทุกระบบโดยทันที โดยจะสร้างไดเรกทอรีแซนด์บ็อกซ์ที่ประกอบด้วยลิงก์สัญลักษณ์ซึ่งชี้ไปยังไฟล์ต้นฉบับ เรียกใช้บรรทัดคำสั่งของการดำเนินการโดยตั้งค่าไดเรกทอรีที่ทำงานเป็นไดเรกทอรีนี้แทน execroot จากนั้นย้ายอาร์ติแฟกต์เอาต์พุตที่รู้จักออกจากแซนด์บ็อกซ์ไปยัง execroot และลบแซนด์บ็อกซ์ ซึ่งจะช่วยป้องกันไม่ให้การดำเนินการใช้ไฟล์อินพุตที่ไม่ได้ประกาศโดยไม่ตั้งใจ และไม่ให้ไฟล์เอาต์พุตที่ไม่รู้จักกระจายอยู่ใน execroot

linux-sandbox ก้าวไปอีกขั้นและต่อยอดจาก processwrapper-sandbox ซึ่งคล้ายกับสิ่งที่ Docker ทําอยู่เบื้องหลัง โดยจะใช้เนมสเปซของ Linux (เนมสเปซผู้ใช้ การมาสก์ PID เครือข่าย และ IPC) เพื่อแยกการดำเนินการออกจากโฮสต์ กล่าวคือ จะทำให้ทั้งระบบไฟล์เป็นแบบอ่านอย่างเดียวยกเว้นไดเรกทอรีแซนด์บ็อกซ์ เพื่อให้การดำเนินการดังกล่าวแก้ไขข้อมูลในระบบไฟล์ของโฮสต์โดยไม่ตั้งใจไม่ได้ วิธีนี้จะช่วยป้องกันไม่ให้เกิดสถานการณ์อย่างเช่นการทดสอบที่มีข้อบกพร่องลบไดเรกทอรี $HOME ด้วยคำสั่ง rm -rf โดยไม่ได้ตั้งใจ นอกจากนี้ คุณยังป้องกันไม่ให้การดำเนินการเข้าถึงเครือข่ายได้ด้วย linux-sandbox ใช้เนมสเปซ PID เพื่อป้องกันไม่ให้การดำเนินการมองเห็นกระบวนการอื่นๆ และเพื่อฆ่ากระบวนการทั้งหมด (แม้แต่เดมอนที่การดำเนินการสร้างขึ้น) อย่างน่าเชื่อถือในตอนท้าย

darwin-sandbox คล้ายกับ แต่ใช้กับ macOS โดยจะใช้เครื่องมือ sandbox-exec ของ Apple เพื่อทำงานให้ใกล้เคียงกับแซนด์บ็อกซ์ Linux

ทั้ง linux-sandbox และ darwin-sandbox ไม่ทำงานในสถานการณ์ "ที่ซ้อนกัน" เนื่องจากข้อจำกัดในกลไกที่ระบบปฏิบัติการระบุ เนื่องจาก Docker ใช้เนมสเปซของ Linux สำหรับคอนเทนเนอร์ด้วย คุณจึงไม่สามารถเรียกใช้ linux-sandbox ในคอนเทนเนอร์ Docker ได้โดยง่าย เว้นแต่จะใช้ docker run --privileged ใน macOS คุณจะไม่สามารถเรียกใช้ sandbox-exec ในกระบวนการที่อยู่ในแซนด์บ็อกซ์อยู่แล้ว ในกรณีเหล่านี้ Bazel จะเปลี่ยนไปใช้ processwrapper-sandbox โดยอัตโนมัติ

หากต้องการรับข้อผิดพลาดในการสร้าง เช่น เพื่อไม่ให้สร้างด้วยกลยุทธ์การดำเนินการที่เข้มงวดน้อยกว่าโดยไม่ตั้งใจ ให้แก้ไขรายการกลยุทธ์การดำเนินการที่ Bazel พยายามใช้อย่างชัดเจน (เช่น bazel build --spawn_strategy=worker,linux-sandbox)

โดยทั่วไปแล้ว การดำเนินการแบบไดนามิกต้องใช้แซนด์บ็อกซ์สําหรับการดําเนินการในเครื่อง หากต้องการเลือกไม่ใช้ ให้ส่งผ่าน Flag --experimental_local_lockfree_output การดำเนินการแบบไดนามิกจะดำเนินการในเบื้องหลังโดยที่ผู้ปฏิบัติงานแบบถาวรในแซนด์บ็อกซ์

ข้อเสียของการใช้แซนด์บ็อกซ์

  • การใช้แซนด์บ็อกซ์จะมีค่าใช้จ่ายเพิ่มเติมในการตั้งค่าและการเลิกใช้งาน ค่าใช้จ่ายนี้ขึ้นอยู่กับหลายปัจจัย ซึ่งรวมถึงรูปแบบของบิลด์และประสิทธิภาพของระบบปฏิบัติการโฮสต์ สำหรับ Linux บิลด์ที่ใช้แซนด์บ็อกซ์จะช้ากว่าเพียงไม่กี่เปอร์เซ็นต์เท่านั้น การตั้งค่า --reuse_sandbox_directories สามารถช่วยลดค่าใช้จ่ายในการตั้งค่าและการรื้อถอน

  • Sandboxing จะปิดใช้แคชทั้งหมดที่เครื่องมืออาจมี คุณสามารถบรรเทาปัญหานี้ได้โดยใช้ผู้ปฏิบัติงานแบบถาวร แต่การรับประกันแซนด์บ็อกซ์จะลดลง

  • ผู้ปฏิบัติงานแบบหลายรายการต้องได้รับการรองรับผู้ปฏิบัติงานอย่างชัดเจนจึงจะใช้แซนด์บ็อกซ์ได้ เวิร์กเกอร์ที่ไม่รองรับแซนด์บ็อกซ์แบบหลายช่องจะทำงานเป็นเวิร์กเกอร์แบบช่องเดียวภายใต้การดำเนินการแบบไดนามิก ซึ่งอาจใช้หน่วยความจำเพิ่ม

การแก้ไขข้อบกพร่อง

ทําตามกลยุทธ์ด้านล่างเพื่อแก้ไขข้อบกพร่องเกี่ยวกับแซนด์บ็อกซ์

เนมสเปซที่ปิดใช้งาน

ในบางแพลตฟอร์ม เช่น โหนดคลัสเตอร์ของ Google Kubernetes Engine หรือ Debian ระบบจะปิดใช้งานเนมสเปซของผู้ใช้โดยค่าเริ่มต้นเนื่องจากข้อกังวลด้านความปลอดภัย หากไฟล์ /proc/sys/kernel/unprivileged_userns_cloneมีอยู่และมี 0 อยู่ คุณจะเปิดใช้งานเนมสเปซของผู้ใช้ได้โดยเรียกใช้คำสั่งต่อไปนี้

   sudo sysctl kernel.unprivileged_userns_clone=1

การดำเนินการตามกฎไม่สำเร็จ

Sandbox อาจไม่สามารถเรียกใช้กฎได้เนื่องจากการตั้งค่าระบบ หากเห็นข้อความอย่าง namespace-sandbox.c:633: execvp(argv[0], argv): No such file or directory ให้ลองปิดใช้งานแซนด์บ็อกซ์ด้วย --strategy=Genrule=local สำหรับ genrules และ --spawn_strategy=local สำหรับกฎอื่นๆ

การแก้ไขข้อบกพร่องโดยละเอียดสำหรับการบิลด์ที่ไม่สําเร็จ

หากการบิลด์ไม่สำเร็จ ให้ใช้ --verbose_failures และ --sandbox_debug เพื่อทำให้ Bazel แสดงคำสั่งที่แน่นอนซึ่งทำงานเมื่อการบิลด์ไม่สำเร็จ รวมถึงส่วนที่ตั้งค่าแซนด์บ็อกซ์

ตัวอย่างข้อความแสดงข้อผิดพลาด

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)

ตอนนี้คุณสามารถตรวจสอบไดเรกทอรีแซนด์บ็อกซ์ที่สร้างขึ้นเพื่อดูว่า Bazel สร้างไฟล์ใดบ้าง และเรียกใช้คำสั่งอีกครั้งเพื่อดูลักษณะการทำงาน

โปรดทราบว่า Bazel จะไม่ลบไดเรกทอรี Sandbox เมื่อคุณใช้ --sandbox_debug คุณควรปิดใช้ --sandbox_debug เว้นแต่คุณจะกำลังแก้ไขข้อบกพร่องอยู่ เนื่องจากจะทําให้ดิสก์เต็มเมื่อเวลาผ่านไป