Zona de pruebas

En este artículo, se explica la zona de pruebas en Bazel, la instalación de sandboxfs y la depuración de tu entorno de zona de pruebas.

La zona de pruebas es una estrategia de restricción de permisos que aísla los procesos entre sí o de los recursos en un sistema. Para Bazel, esto significa restringir el acceso al sistema de archivos.

La zona de pruebas del sistema de archivos de Bazel ejecuta procesos en un directorio de trabajo que solo contiene entradas conocidas, de modo que los compiladores y otras herramientas no vean archivos de origen a los que no deberían acceder, a menos que conozcan las rutas de acceso absolutas a ellos.

La zona de pruebas no oculta el entorno de host de ninguna manera. Los procesos pueden acceder libremente a todos los archivos del sistema. Sin embargo, en las plataformas que admiten espacios de nombres de usuarios, los procesos no pueden modificar ningún archivo fuera de su directorio de trabajo. Esto garantiza que el gráfico de compilación no tenga dependencias ocultas que puedan afectar la reproducibilidad de la compilación.

Específicamente, Bazel construye un directorio execroot/ para cada acción, que actúa como el directorio de trabajo de la acción en el momento de la ejecución. execroot/ contiene todos los archivos de entrada a la acción y sirve como contenedor para cualquier salida generada. Luego, Bazel usa una técnica proporcionada por el sistema operativo, contenedores en Linux y sandbox-exec en macOS, para restringir la acción dentro de execroot/.

Motivos de la zona de pruebas

  • Sin la zona de pruebas de acciones, Bazel no sabe si una herramienta usa archivos de entrada no declarados (archivos que no se enumeran de forma explícita en las dependencias de una acción). Cuando cambia uno de los archivos de entrada no declarados, Bazel aún cree que la compilación está actualizada y no volverá a compilar la acción. Esto puede generar una compilación incremental incorrecta.

  • La reutilización incorrecta de entradas de caché crea problemas durante el almacenamiento en caché remoto. Una entrada de caché incorrecta en una caché compartida afecta a todos los desarrolladores del proyecto, y limpiar toda la caché remota no es una solución factible.

  • La zona de pruebas imita el comportamiento de la ejecución remota. Si una compilación funciona bien con la zona de pruebas, es probable que también funcione con la ejecución remota. Si haces que la ejecución remota suba todos los archivos necesarios (incluidas las herramientas locales), puedes reducir significativamente los costos de mantenimiento de los clústeres de compilación, en comparación con tener que instalar las herramientas en cada máquina del clúster cada vez que quieras probar un compilador nuevo o realizar un cambio en una herramienta existente.

Qué estrategia de zona de pruebas usar

Con las marcas de estrategia, puedes elegir qué tipo de zona de pruebas usar, si corresponde. El uso de la estrategia sandboxed hace que Bazel elija una de las implementaciones de zona de pruebas que se enumeran a continuación y prefiere una zona de pruebas específica del SO a la menos hermética genérica. Los trabajadores persistentes se ejecutan en una zona de pruebas genérica si pasas la marca --worker_sandboxing.

La estrategia local (también conocida como standalone) no realiza ningún tipo de zona de pruebas. Solo ejecuta la línea de comandos de la acción con el directorio de trabajo establecido en la execroot de tu lugar de trabajo.

processwrapper-sandbox es una estrategia de zona de pruebas que no requiere ninguna función "avanzada". Debería funcionar en cualquier sistema POSIX de inmediato. Crea un directorio de zona de pruebas con symlinks que apuntan a los archivos fuente originales, ejecuta la línea de comandos de la acción con el directorio de trabajo establecido en este directorio en lugar de en el execroot, y luego mueve los artefactos de salida conocidos fuera de la zona de pruebas a execroot y borra la zona de pruebas. Esto evita que la acción use accidentalmente cualquier archivo de entrada que no esté declarado y desperdicie el execroot con archivos de salida desconocidos.

linux-sandbox va un paso más allá y se basa en processwrapper-sandbox. De manera similar a lo que hace Docker de forma interna, usa espacios de nombres de Linux (espacios de nombres de Usuario, Activación, PID, IPC y Red) para aislar la acción del host. Es decir, hace que todo el sistema de archivos sea de solo lectura, excepto el directorio de la zona de pruebas, por lo que la acción no puede modificar nada por accidente en el sistema de archivos del host. De esta manera, se evitan situaciones como una prueba con errores que restablece por accidente tu directorio $HOME. De manera opcional, también puedes impedir que la acción acceda a la red. linux-sandbox usa espacios de nombres de PID para evitar que la acción vea otros procesos y finalizar de manera confiable todos los procesos (incluso daemons generados por la acción) al final.

darwin-sandbox es similar, pero para macOS. Usa la herramienta sandbox-exec de Apple para lograr casi lo mismo que la zona de pruebas de Linux.

Tanto linux-sandbox como darwin-sandbox no funcionan en una situación "anidada" debido a restricciones en los mecanismos que proporcionan los sistemas operativos. Debido a que Docker también usa espacios de nombres de Linux para su comando mágico de contenedor, no puedes ejecutar linux-sandbox con facilidad dentro de un contenedor de Docker, a menos que uses docker run --privileged. En macOS, no puedes ejecutar sandbox-exec dentro de un proceso que ya está en zona de pruebas. Por lo tanto, en estos casos, Bazel vuelve a usar automáticamente processwrapper-sandbox.

Si prefieres obtener un error de compilación, como no compilar accidentalmente con una estrategia de ejecución menos estricta, modifica de forma explícita la lista de estrategias de ejecución que Bazel intenta usar (por ejemplo, bazel build --spawn_strategy=worker,linux-sandbox).

Por lo general, la ejecución dinámica requiere una zona de pruebas para ejecutarse de forma local. Para inhabilitar esta opción, pasa la marca --experimental_local_lockfree_output. La ejecución dinámica crea una zona de pruebas silenciosa para los trabajadores persistentes.

Desventajas de la zona de pruebas

  • La zona de pruebas genera costos adicionales de configuración y desmontaje. El tamaño de este costo depende de muchos factores, como la forma de la compilación y el rendimiento del SO host. Para Linux, las compilaciones de zona de pruebas rara vez son más de un pequeño porcentaje más lentas. Configurar --reuse_sandbox_directories puede mitigar los costos de configuración y desmontaje.

  • La zona de pruebas inhabilita de forma eficaz cualquier caché que pueda tener la herramienta. Puedes mitigar esto si usas trabajadores persistentes, a costa de garantías más débiles de la zona de pruebas.

  • Los trabajadores multiplex requieren compatibilidad explícita para los trabajadores en la zona de pruebas. Los trabajadores que no admiten la zona de pruebas multiplex se ejecutan como trabajadores de un soloplex en ejecución dinámica, lo que puede consumir memoria adicional.

zonas de pruebas

sandboxfs es un sistema de archivos FUSE que expone una vista arbitraria del sistema de archivos subyacente sin penalizaciones de tiempo. Bazel usa sandboxfs a fin de generar execroot/ instantáneamente para cada acción, lo que evita el costo de emitir miles de llamadas al sistema. Ten en cuenta que las operaciones de E/S adicionales dentro de execroot/ pueden ser más lentas debido a la sobrecarga de FUSE.

Instalar zonas de pruebas

Sigue estos pasos para instalar sandboxfs y realizar una compilación de Bazel con él:

Descargar

Descarga e instala sandboxfs para que el objeto binario sandboxfs termine en tu PATH.

Ejecuta sandboxfs

  1. (Solo en macOS) Instala OSXFUSE.
  2. Solo para macOS: Ejecuta el siguiente comando:

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

    Deberás hacerlo después de la instalación y después de cada reinicio para asegurarte de que los servicios principales del sistema macOS funcionen a través de zonas de pruebas.

  3. Ejecuta una compilación de Bazel con --experimental_use_sandboxfs.

    bazel build target --experimental_use_sandboxfs
    

Solución de problemas

Si ves local en lugar de darwin-sandbox o linux-sandbox como una anotación para las acciones que se ejecutan, esto puede significar que la zona de pruebas está inhabilitada. Pasa --genrule_strategy=sandboxed --spawn_strategy=sandboxed para habilitarlo.

Depuración

Sigue las estrategias que se indican a continuación para depurar problemas relacionados con la zona de pruebas.

Espacios de nombres desactivados

En algunas plataformas, como los nodos del clúster de Google Kubernetes Engine o Debian, los espacios de nombres de los usuarios se desactivan de forma predeterminada debido a problemas de seguridad. Si el archivo /proc/sys/kernel/unprivileged_userns_clone existe y contiene un 0, puedes ejecutar lo siguiente para activar los espacios de nombres del usuario:

   sudo sysctl kernel.unprivileged_userns_clone=1

Fallas en la ejecución de reglas

Es posible que la zona de pruebas no ejecute reglas debido a la configuración del sistema. Si ves un mensaje como namespace-sandbox.c:633: execvp(argv[0], argv): No such file or directory, intenta desactivar la zona de pruebas con --strategy=Genrule=local para genrules y --spawn_strategy=local para otras.

Depuración detallada de fallas de compilación

Si tu compilación falló, usa --verbose_failures y --sandbox_debug para que Bazel muestre el comando exacto que ejecutó cuando falló la compilación, incluida la parte que configuró la zona de pruebas.

Ejemplo de mensaje de error:

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)

Ahora, puedes inspeccionar el directorio de la zona de pruebas generado y ver qué archivos creó Bazel y volver a ejecutar el comando para ver cómo se comporta.

Ten en cuenta que Bazel no borra el directorio de la zona de pruebas cuando usas --sandbox_debug. A menos que realices una depuración de forma activa, debes inhabilitar --sandbox_debug, ya que llena el disco con el tiempo.