Esta página aborda o armazenamento em cache remoto, a configuração de um servidor para hospedar o cache e a execução de builds usando o cache remoto.
Um cache remoto é usado por uma equipe de desenvolvedores e/ou um sistema de integração contínua (CI) para compartilhar saídas de build. Se o build for reproduzível, as saídas de uma máquina poderão ser reutilizadas com segurança em outra, o que pode tornar os builds significativamente mais rápidos.
Visão geral
O Bazel divide uma build em etapas discretas, chamadas de ações. Cada ação tem entradas, nomes de saída, uma linha de comando e variáveis de ambiente. As entradas obrigatórias e as saídas esperadas são declaradas explicitamente para cada ação.
É possível configurar um servidor como um cache remoto para saídas de build, que são essas saídas de ação. Essas saídas consistem em uma lista de nomes de arquivos de saída e os hashes dos respectivos conteúdos. Com um cache remoto, é possível reutilizar as saídas de compilação de outro usuário em vez de criar cada nova saída localmente.
Para usar o armazenamento em cache remoto:
- Configurar um servidor como o back-end do cache
- Configurar o build do Bazel para usar o cache remoto
- Use o Bazel versão 0.10.0 ou posterior
O cache remoto armazena dois tipos de dados:
- O cache de ações, que é um mapa de hashes de ações para metadados de resultados de ações.
- Um armazenamento endereçável por conteúdo (CAS, na sigla em inglês) de arquivos de saída.
O cache remoto também armazena stdout e stderr para cada ação. Portanto, inspecionar o stdout/stderr do Bazel não é um bom indicador para estimar acertos de cache.
Como um build usa o armazenamento em cache remoto
Depois que um servidor é configurado como o cache remoto, ele pode ser usado de várias maneiras:
- Ler e gravar no cache remoto
- Ler e/ou gravar no cache remoto, exceto para destinos específicos
- Ler apenas do cache remoto
- Não usar o cache remoto
Quando você executa um build do Bazel que pode ler e gravar no cache remoto, ele segue estas etapas:
- O Bazel cria o gráfico de destinos que precisam ser criados e, em seguida, cria uma lista de ações necessárias. Cada uma dessas ações tem entradas e nomes de arquivos de saída declarados.
- O Bazel verifica se há saídas de build na máquina local e reutiliza as que encontrar.
- O Bazel verifica o cache em busca de saídas de build. Se a saída for encontrada, o Bazel a vai recuperar. Isso é uma ocorrência em cache.
- Para ações obrigatórias em que as saídas não foram encontradas, o Bazel executa as ações localmente e cria as saídas de build necessárias.
- Novas saídas de build são enviadas para o cache remoto.
Configurar um servidor como back-end do cache
É necessário configurar um servidor para atuar como back-end do cache. Um servidor HTTP/1.1 pode tratar os dados do Bazel como bytes opacos, e muitos servidores atuais podem ser usados como um back-end de cache remoto. O protocolo de cache HTTP do Bazel é o que oferece suporte ao cache remoto.
Você é responsável por escolher, configurar e manter o servidor de back-end que vai armazenar as saídas em cache. Ao escolher um servidor, considere:
- Velocidade da rede. Por exemplo, se sua equipe estiver no mesmo escritório, talvez você queira executar seu próprio servidor local.
- Segurança. O cache remoto terá seus binários e, portanto, precisa ser seguro.
- Gerenciamento mais fácil. Por exemplo, o Google Cloud Storage é um serviço totalmente gerenciado.
Há muitos back-ends que podem ser usados para um cache remoto. Veja algumas opções:
nginx
O nginx é um servidor da Web de código aberto. Com o [módulo WebDAV], ele pode ser usado como um cache remoto para o Bazel. No Debian e no Ubuntu, é possível instalar o pacote
nginx-extras
. No macOS, o nginx está disponível pelo Homebrew:
brew tap denji/nginx
brew install nginx-full --with-webdav
Confira abaixo um exemplo de configuração para nginx. Mude /path/to/cache/dir
para um diretório válido em que o nginx tenha permissão de leitura e gravação. Talvez seja necessário mudar a opção client_max_body_size
para um valor maior se você tiver arquivos de saída maiores. O servidor vai exigir outra configuração, como autenticação.
Exemplo de configuração para a seção server
em 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;
}
bazel-remote
O bazel-remote é um cache de build remoto de código aberto que pode ser usado na sua infraestrutura. Ele é usado com sucesso na produção em várias empresas desde o início de 2018. O projeto Bazel não oferece suporte técnico para o bazel-remote.
Esse cache armazena conteúdo no disco e também faz coleta de lixo para impor um limite superior de armazenamento e limpar artefatos não usados. O cache está disponível como uma [imagem do Docker] e o código dele está disponível no GitHub. As APIs de cache remoto REST e gRPC são compatíveis.
Consulte a página do GitHub para instruções sobre como usar.
Google Cloud Storage
O [Google Cloud Storage] é um repositório de objetos totalmente gerenciado que fornece uma API HTTP compatível com o protocolo de armazenamento em cache remoto do Bazel. É necessário ter uma conta do Google Cloud com o faturamento ativado.
Para usar o Cloud Storage como cache:
Crie um bucket de armazenamento. Selecione um local de bucket mais próximo de você, já que a largura de banda da rede é importante para o cache remoto.
Crie uma conta de serviço para o Bazel se autenticar no Cloud Storage. Consulte Como criar uma conta de serviço.
Gere uma chave JSON secreta e transmita para o Bazel para autenticação. Armazene a chave com segurança, porque qualquer pessoa com ela pode ler e gravar dados arbitrários no/do seu bucket do GCS.
Conecte-se ao Cloud Storage adicionando as seguintes flags ao comando do Bazel:
- Transmita o seguinte URL para o Bazel usando a flag:
--remote_cache=https://storage.googleapis.com/bucket-name
, em quebucket-name
é o nome do seu bucket de armazenamento. - Transmita a chave de autenticação usando a flag:
--google_credentials=/path/to/your/secret-key.json
ou--google_default_credentials
para usar a autenticação de aplicativo.
- Transmita o seguinte URL para o Bazel usando a flag:
É possível configurar o Cloud Storage para excluir automaticamente arquivos antigos. Para fazer isso, consulte Gerenciar ciclos de vida de objetos.
Outros servidores
Você pode configurar qualquer servidor HTTP/1.1 que ofereça suporte a PUT e GET como back-end do cache. Os usuários tiveram sucesso com back-ends de cache como Hazelcast, Apache httpd e AWS S3.
Autenticação
A partir da versão 0.11.0, o suporte à autenticação HTTP básica foi adicionado ao Bazel.
É possível transmitir um nome de usuário e uma senha para o Bazel usando o URL do cache remoto. A sintaxe é https://username:password@hostname.com:port/path
. A autenticação HTTP básica transmite nome de usuário e senha em texto simples pela rede. Por isso, é fundamental sempre usá-la com HTTPS.
Protocolo de armazenamento em cache HTTP
O Bazel é compatível com o armazenamento em cache remoto via HTTP/1.1. O protocolo é conceitualmente simples: os dados binários (BLOB) são enviados por upload usando solicitações PUT e baixados usando solicitações GET.
Os metadados do resultado da ação são armazenados no caminho /ac/
, e os arquivos de saída são armazenados no caminho /cas/
.
Por exemplo, considere um cache remoto em execução em http://localhost:8080/cache
.
Uma solicitação do Bazel para baixar metadados de resultado de ação para uma ação com o hash SHA256
01ba4719...
será assim:
GET /cache/ac/01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b HTTP/1.1
Host: localhost:8080
Accept: */*
Connection: Keep-Alive
Uma solicitação do Bazel para fazer upload de um arquivo de saída com o hash SHA256 15e2b0d3...
para
o CAS vai ficar assim:
PUT /cache/cas/15e2b0d3c33891ebb0f1ef609ec419420c20e320ce94c65fbc8c3312448eb225 HTTP/1.1
Host: localhost:8080
Accept: */*
Content-Length: 9
Connection: Keep-Alive
0x310x320x330x340x350x360x370x380x39
Executar o Bazel usando o cache remoto
Depois que um servidor é configurado como o cache remoto, para usá-lo, é necessário adicionar flags ao comando do Bazel. Confira abaixo a lista de configurações e flags.
Talvez seja necessário configurar a autenticação, que é específica para o servidor escolhido.
É recomendável adicionar essas flags em um arquivo .bazelrc
para não precisar especificá-las toda vez que executar o Bazel. Dependendo do seu projeto e da dinâmica da equipe, é possível adicionar flags a um arquivo .bazelrc
que seja:
- Na sua máquina local
- No espaço de trabalho do projeto, compartilhado com a equipe
- No sistema de CI
Ler e gravar no cache remoto
Tome cuidado ao conceder permissão para gravar no cache remoto. Talvez você queira que apenas o sistema de CI possa gravar no cache remoto.
Use a seguinte flag para ler e gravar no cache remoto:
build --remote_cache=http://your.host:port
Além do HTTP
, os seguintes protocolos também são compatíveis: HTTPS
, grpc
e grpcs
.
Use a seguinte flag além da acima para ler apenas do cache remoto:
build --remote_upload_local_results=false
Excluir destinos específicos do uso do cache remoto
Para impedir que destinos específicos usem o cache remoto, marque-os com
no-remote-cache
. Exemplo:
java_library(
name = "target",
tags = ["no-remote-cache"],
)
Excluir conteúdo do cache remoto
Excluir conteúdo do cache remoto faz parte do gerenciamento do servidor. A forma de excluir conteúdo do cache remoto depende do servidor configurado como cache. Ao excluir saídas, exclua todo o cache ou as saídas antigas.
As saídas em cache são armazenadas como um conjunto de nomes e hashes. Ao excluir conteúdo, não é possível distinguir qual saída pertence a um build específico.
Você pode excluir conteúdo do cache para:
- Criar um cache limpo depois que um cache foi envenenado
- Reduza a quantidade de armazenamento usado excluindo saídas antigas
Soquetes Unix
O cache HTTP remoto aceita conexões por soquetes de domínio unix. O comportamento
é semelhante à flag --unix-socket
do curl. Use o seguinte para configurar o soquete de domínio Unix:
build --remote_cache=http://your.host:port
build --remote_proxy=unix:/path/to/socket
Esse recurso não está disponível no Windows.
Cache de disco
O Bazel pode usar um diretório no sistema de arquivos como um cache remoto. Isso é útil para compartilhar artefatos de build ao trocar de ramificação e/ou trabalhar em vários espaços de trabalho do mesmo projeto, como vários checkouts. Ative o cache de disco da seguinte maneira:
build --disk_cache=path/to/build/cache
Você pode transmitir um caminho específico do usuário à flag --disk_cache
usando o alias ~
. O Bazel vai substituir o diretório inicial do usuário atual. Isso é útil
ao ativar o cache de disco para todos os desenvolvedores de um projeto usando o arquivo .bazelrc
registrado do projeto.
Coleta de lixo
A partir do Bazel 7.4, é possível usar --experimental_disk_cache_gc_max_size
e
--experimental_disk_cache_gc_max_age
para definir um tamanho máximo para o cache de disco
ou para a idade de entradas de cache individuais. O Bazel coleta automaticamente o lixo do cache de disco enquanto fica ocioso entre os builds. O timer de inatividade pode ser definido com --experimental_disk_cache_gc_idle_delay
(o padrão é 5 minutos).
Como alternativa à coleta automática de lixo, também oferecemos uma ferramenta para executar uma coleta de lixo sob demanda.
Problemas conhecidos
Modificação do arquivo de entrada durante um build
Quando um arquivo de entrada é modificado durante um build, o Bazel pode fazer upload de resultados inválidos para o cache remoto. É possível ativar uma detecção de mudanças com a
sinalização --experimental_guard_against_concurrent_changes
. Não há problemas conhecidos, e ele será ativado por padrão em uma versão futura.
Consulte [problema nº 3360] para atualizações. Em geral, evite modificar arquivos de origem durante um build.
Vazamento de variáveis de ambiente em uma ação
Uma definição de ação contém variáveis de ambiente. Isso pode ser um problema para
compartilhar ocorrências de cache remoto entre máquinas. Por exemplo, ambientes com variáveis $PATH
diferentes não compartilham acertos de cache. Somente as variáveis de ambiente
explicitamente incluídas na lista de permissões via --action_env
são incluídas em uma definição
de ação. O pacote Debian/Ubuntu do Bazel usado para instalar /etc/bazel.bazelrc
com uma lista de permissões de variáveis de ambiente, incluindo $PATH
. Se você estiver recebendo menos acertos de cache do que o esperado, verifique se o ambiente não tem um arquivo /etc/bazel.bazelrc
antigo.
O Bazel não rastreia ferramentas fora de um espaço de trabalho
No momento, o Bazel não rastreia ferramentas fora de um espaço de trabalho. Isso pode ser um
problema se, por exemplo, uma ação usar um compilador de /usr/bin/
. Então, dois usuários com compiladores diferentes instalados vão compartilhar ocorrências em cache de forma incorreta porque as saídas são diferentes, mas têm o mesmo hash de ação. Consulte a
questão #4558 para atualizações.
O estado incremental na memória é perdido ao executar builds em contêineres do Docker O Bazel usa a arquitetura de servidor/cliente mesmo quando executado em um único contêiner do Docker. No lado do servidor, o Bazel mantém um estado na memória que acelera as builds. Ao executar builds em contêineres do Docker, como na CI, o estado na memória é perdido e o Bazel precisa recriá-lo antes de usar o cache remoto.
Links externos
Your Build in a Datacenter:a equipe do Bazel fez uma palestra sobre cache e execução remotos na FOSDEM 2018.
Compilações mais rápidas do Bazel com cache remoto: um comparativo:Nicolò Valigi escreveu uma postagem no blog (link em inglês) em que compara o cache remoto no Bazel.