Almacenamiento en caché remota

Informa un problema Ver código fuente

En esta página, se abarca el almacenamiento en caché remoto, la configuración de un servidor para alojar la caché y la ejecución de compilaciones con la caché remota.

Un equipo de desarrolladores o un sistema de integración continua (CI) usan una caché remota para compartir los resultados de la compilación. Si tu compilación es reproducible, los resultados de una máquina se pueden volver a usar de forma segura en otra máquina, lo que puede hacer que las compilaciones sean mucho más rápidas.

Descripción general

Bazel divide una compilación en pasos discretos, que se llaman acciones. Cada acción tiene entradas, nombres de salida, una línea de comandos y variables de entorno. Las entradas obligatorias y las esperadas se declaran de forma explícita para cada acción.

Puedes configurar un servidor a fin de que sea una caché remota para los resultados de la compilación, que son estas salidas de acción. Estos resultados contienen una lista de nombres de archivos de salida y los hashes de su contenido. Con una caché remota, puedes volver a usar los resultados de compilación de la compilación de otro usuario en lugar de compilar cada resultado nuevo de forma local.

Para usar el almacenamiento en caché remoto, haz lo siguiente:

  • Configura un servidor como backend de la caché
  • Configura la compilación de Bazel para usar la caché remota
  • Usa Bazel 0.10.0 o una versión posterior

La caché remota almacena dos tipos de datos:

  • La caché de acciones, que es un mapa de hash de acción para los metadatos de resultados de acciones
  • Es un almacén de archivos de salida con acceso a contenido (CAS).

Ten en cuenta que la caché remota también almacena stdout y stderr para cada acción. Inspeccionar el stdout/stderr de Bazel no es una buena señal para estimar los aciertos de caché.

Cómo una compilación usa el almacenamiento en caché remoto

Una vez que se configura un servidor como la caché remota, puedes usar la caché de varias maneras:

  • Lee y escribe en la caché remota
  • Lee o escribe en la caché remota, excepto para destinos específicos.
  • Solo leer de la caché remota
  • No usar la caché remota en absoluto

Cuando ejecutas una compilación de Bazel que puede leer y escribir en la caché remota, la compilación sigue estos pasos:

  1. Bazel crea el grafo de destinos que se debe compilar y, luego, crea una lista de acciones requeridas. Cada una de estas acciones declaró entradas y nombres de archivo de salida.
  2. Bazel comprueba las salidas de compilación existentes de la máquina local y reutiliza las que encuentre.
  3. Bazel revisa la caché en busca de resultados de compilación existentes. Si se encuentra el resultado, Bazel lo recupera. Este es un acierto de caché.
  4. Para las acciones requeridas en las que no se encontraron los resultados, Bazel ejecuta las acciones de forma local y crea los resultados de compilación necesarios.
  5. Los nuevos resultados de compilación se suben a la caché remota.

Configura un servidor como backend de la caché

Debes configurar un servidor para que actúe como backend de la caché. Un servidor HTTP/1.1 puede tratar los datos de Bazel como bytes opacos, y muchos servidores existentes se pueden usar como un backend de almacenamiento en caché remoto. El protocolo de almacenamiento en caché HTTP de Bazel es lo que admite el almacenamiento en caché remoto.

Eres responsable de elegir, configurar y mantener el servidor de backend que almacenará los resultados almacenados en caché. Cuando elijas un servidor, ten en cuenta lo siguiente:

  • Velocidad de red. Por ejemplo, si tu equipo está en la misma oficina, es posible que desees ejecutar tu propio servidor local.
  • Seguridad La caché remota incluirá los objetos binarios, por lo que debe ser segura.
  • Administración sencilla. Por ejemplo, Google Cloud Storage es un servicio completamente administrado.

Hay muchos backends que se pueden usar para una caché remota. Estas son algunas opciones:

nginx

nginx es un servidor web de código abierto. Con su [módulo webDAV], se puede usar como caché remota para Bazel. En Debian y Ubuntu, puedes instalar el paquete nginx-extras. En macOS, nginx está disponible a través de Homebrew:

brew tap denji/nginx
brew install nginx-full --with-webdav

A continuación, se muestra un ejemplo de configuración de nginx. Ten en cuenta que deberás cambiar /path/to/cache/dir a un directorio válido en el que nginx tenga permiso para escribir y leer. Es posible que debas cambiar la opción client_max_body_size a un valor mayor si tienes archivos de salida más grandes. El servidor requerirá otra configuración, como la autenticación.

Configuración de ejemplo para la sección server en nginx.conf:

location /cache/ {
  # The path to the directory where nginx should store the cache contents.
  root /path/to/cache/dir;
  # Allow PUT
  dav_methods PUT;
  # Allow nginx to create the /ac and /cas subdirectories.
  create_full_put_path on;
  # The maximum size of a single file.
  client_max_body_size 1G;
  allow all;
}

control remoto de bazel

bazel-remote es una caché de compilación remota de código abierto que puede usar en la infraestructura. Se usa con éxito en producción en varias empresas desde principios de 2018. Ten en cuenta que el proyecto de Bazel no proporciona asistencia técnica para Bazel-remote.

Esta caché almacena contenido en el disco y también proporciona la recolección de elementos no utilizados para aplicar un límite superior de almacenamiento y limpiar los artefactos sin usar. La caché está disponible como [imagen de Docker] y su código está disponible en GitHub. Se admiten las API de caché remota de REST y gRPC.

Consulta la página de GitHub a fin de obtener instrucciones para usarla.

Google Cloud Storage

[Google Cloud Storage] es un almacén de objetos completamente administrado que proporciona una API de HTTP compatible con el protocolo de almacenamiento en caché remoto de Bazel. Requiere que tengas una cuenta de Google Cloud con la facturación habilitada.

Para usar Cloud Storage como caché, haz lo siguiente:

  1. Crea un bucket de almacenamiento. Asegúrate de seleccionar la ubicación del bucket más cercana, ya que el ancho de banda de red es importante para la caché remota.

  2. Crea una cuenta de servicio para que Bazel se autentique en Cloud Storage. Consulta Crea una cuenta de servicio.

  3. Genera una clave JSON secreta y, luego, pásala a Bazel para su autenticación. Almacena la clave de forma segura, ya que cualquier persona que la tenga podrá leer y escribir datos arbitrarios en tu bucket de GCS o desde él.

  4. Para conectarte a Cloud Storage, agrega las siguientes marcas al comando de Bazel:

    • Pasa la siguiente URL a Bazel con la marca --remote_cache=https://storage.googleapis.com/bucket-name, en la que bucket-name es el nombre de tu bucket de almacenamiento.
    • Pasa la clave de autenticación con la marca --google_credentials=/path/to/your/secret-key.json o --google_default_credentials para usar la autenticación de aplicaciones.
  5. Puedes configurar Cloud Storage para que borre automáticamente los archivos antiguos. Para hacerlo, consulta Administra ciclos de vida de objetos.

Otros servidores

Puedes configurar cualquier servidor HTTP/1.1 que admita PUT y GET como backend de la caché. Los usuarios informaron que son exitosos con backends de almacenamiento en caché como Hazelcast, Apache httpd y AWS S3.

Autenticación

A partir de la versión 0.11.0, se agregó a Bazel compatibilidad con la autenticación básica HTTP. Puedes pasar un nombre de usuario y una contraseña a Bazel a través de la URL de caché remota. La sintaxis es https://username:password@hostname.com:port/path. Ten en cuenta que la autenticación básica de HTTP transmite el nombre de usuario y la contraseña en texto simple a través de la red y, por lo tanto, es fundamental usarlo siempre con HTTPS.

Protocolo de almacenamiento en caché HTTP

Bazel admite el almacenamiento en caché remoto a través de HTTP/1.1. El protocolo es conceptualmente simple: los datos binarios (BLOB) se suben a través de solicitudes PUT y se descargan mediante solicitudes GET. Los metadatos del resultado de acción se almacenan en la ruta de acceso /ac/ y los archivos de salida se almacenan en la ruta de acceso /cas/.

Por ejemplo, considera una caché remota que se ejecuta en http://localhost:8080/cache. Una solicitud de Bazel para descargar metadatos de resultados de acción de una acción con el hash SHA256 01ba4719... se verá de la siguiente manera:

GET /cache/ac/01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b HTTP/1.1
Host: localhost:8080
Accept: */*
Connection: Keep-Alive

Una solicitud de Bazel para subir un archivo de salida con el hash SHA256 15e2b0d3... al CAS tendrá el siguiente aspecto:

PUT /cache/cas/15e2b0d3c33891ebb0f1ef609ec419420c20e320ce94c65fbc8c3312448eb225 HTTP/1.1
Host: localhost:8080
Accept: */*
Content-Length: 9
Connection: Keep-Alive

0x310x320x330x340x350x360x370x380x39

Ejecuta Bazel con la caché remota

Una vez que se configura un servidor como la caché remota, para agregar la caché remota, debes agregar marcas al comando de Bazel. Consulta la lista de configuraciones y sus marcas a continuación.

Es posible que también necesites configurar la autenticación, que es específica del servidor que elegiste.

Te recomendamos que agregues estas marcas en un archivo .bazelrc para que no tengas que especificarlas cada vez que ejecutes Bazel. Según la dinámica de tu proyecto y tu equipo, puedes agregar marcas a un archivo .bazelrc que tenga las siguientes características:

  • En tu máquina local
  • En el lugar de trabajo de tu proyecto, compartido con el equipo
  • En el sistema de CI

Lee la caché remota y escribe en ella

Determina quién tiene la capacidad de escribir en la caché remota. Es posible que solo tu sistema de CI pueda escribir en la caché remota.

Usa la siguiente marca para leer la caché remota y escribir en ella:

build --remote_cache=http://your.host:port

Además de HTTP, también se admiten los siguientes protocolos: HTTPS, grpc y grpcs.

Usa la siguiente marca además de la anterior para leer solo desde la caché remota:

build --remote_upload_local_results=false

Excluir destinos específicos para que no usen la caché remota

Para excluir destinos específicos del uso de la caché remota, etiqueta el destino con no-remote-cache. Por ejemplo:

java_library(
    name = "target",
    tags = ["no-remote-cache"],
)

Cómo borrar contenido de la caché remota

Borrar contenido de la caché remota forma parte de la administración de tu servidor. La forma de borrar contenido de la caché remota depende del servidor que hayas configurado como caché. Cuando borres los resultados, borra toda la caché o los resultados antiguos.

Los resultados almacenados en caché se almacenan como un conjunto de nombres y hashes. Cuando se borra contenido, no hay forma de distinguir qué resultado pertenece a una compilación específica.

Te recomendamos que borres contenido de la memoria caché para lo siguiente:

  • Crea una caché limpia después de que se haya envenenado una caché
  • Borra los resultados antiguos para reducir la cantidad de almacenamiento que se usa

Sockets Unix

La caché HTTP remota admite la conexión a través de los sockets de dominio de Unix. El comportamiento es similar a la marca --unix-socket de curl. Usa lo siguiente para configurar el socket de dominio Unix:

   build --remote_cache=http://your.host:port
   build --remote_cache_proxy=unix:/path/to/socket

Esta función no es compatible con Windows.

Caché de disco

Bazel puede usar un directorio del sistema de archivos como caché remota. Esto es útil para compartir artefactos de compilación cuando se cambian ramas o se trabaja en varios lugares de trabajo del mismo proyecto, como varias confirmaciones de compras. Dado que Bazel no recolecta los elementos no utilizados del directorio, es posible que desees automatizar la limpieza periódica de este directorio. Habilita la caché de disco de la siguiente manera:

build --disk_cache=path/to/build/cache

Puedes pasar una ruta específica del usuario a la marca --disk_cache con el alias ~ (Bazel reemplazará el directorio principal del usuario actual). Esto es útil cuando se habilita la caché de disco para todos los desarrolladores de un proyecto a través del archivo registrado del proyecto .bazelrc.

Errores conocidos

Modificación del archivo de entrada durante una compilación

Cuando se modifica un archivo de entrada durante una compilación, es posible que Bazel suba resultados no válidos a la caché remota. Puedes habilitar una detección de cambios con la marca --experimental_guard_against_concurrent_changes. No hay problemas conocidos y se habilitará de forma predeterminada en una versión futura. Consulta el [problema 3360] para conocer las actualizaciones. En general, evita modificar los archivos de origen durante una compilación.

Variables de entorno que se filtran en una acción

Una definición de acción contiene variables de entorno. Esto puede ser un problema para compartir aciertos de caché remota entre máquinas. Por ejemplo, los entornos con diferentes variables $PATH no compartirán los aciertos de caché. En una definición de acción, solo se incluyen las variables de entorno que se incluyen de forma explícita en la lista blanca a través de --action_env. Paquete de Debian/Ubuntu de Bazel que se usa para instalar /etc/bazel.bazelrc con una lista blanca de variables de entorno, incluida $PATH. Si recibes menos aciertos de caché de lo esperado, verifica que tu entorno no tenga un archivo /etc/bazel.bazelrc antiguo.

Bazel no registra las herramientas fuera de un lugar de trabajo

Por el momento, Bazel no realiza un seguimiento de las herramientas fuera de un lugar de trabajo. Esto puede ser un problema si, por ejemplo, una acción usa un compilador de /usr/bin/. Luego, dos usuarios con diferentes compiladores instalados compartirán incorrectamente aciertos de caché porque los resultados son diferentes, pero tienen el mismo hash de acción. Consulta el problema 4558 para obtener actualizaciones.

El estado incremental en la memoria se pierde cuando se ejecutan compilaciones dentro de contenedores de Docker Bazel usa la arquitectura de servidor/cliente, incluso cuando se ejecuta en un solo contenedor de Docker. En el servidor, Bazel mantiene un estado en la memoria que acelera las compilaciones. Cuando se ejecutan compilaciones dentro de contenedores de Docker, como en CI, se pierde el estado en la memoria y Bazel debe volver a compilarlo antes de usar la caché remota.