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 máquina, o que pode tornar os builds significativamente mais rápidos.
Visão geral
O Bazel divide uma versão 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 necessárias e as saídas esperadas são declaradas explicitamente para cada ação.
É possível configurar um servidor para ser 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 do conteúdo deles. Com um cache remoto, é possível reutilizar as saídas de build de outro usuário em vez de criar cada nova saída localmente.
Para usar o armazenamento em cache remoto:
- Configurar um servidor como back-end do cache
- Configurar o build do Bazel para usar o cache remoto
- Use o Bazel versão 0.10.0 ou mais recente
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.
- Uma loja endereçável por conteúdo (CAS, na sigla em inglês) de arquivos de saída.
O cache remoto também armazena o stdout e o stderr para cada ação. A inspeção do 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, você pode usar o cache de várias maneiras:
- Ler e gravar no cache remoto
- Ler e/ou gravar no cache remoto, exceto para segmentos específicos
- Somente leitura do cache remoto
- Não usar o cache remoto
Quando você executa um build do Bazel que pode ler e gravar no cache remoto, o build 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 forem encontradas.
- O Bazel verifica o cache em busca de saídas de build. Se a saída for encontrada, o Bazel vai recuperá-la. 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.
- As novas saídas de build são enviadas para o cache remoto.
Configurar um servidor como back-end do cache
Você precisa configurar um servidor para atuar como o back-end do cache. Um servidor HTTP/1.1 pode tratar os dados do Bazel como bytes opacos, e muitos servidores existentes podem ser usados como um back-end de cache remoto. O protocolo de armazenamento em cache HTTP do Bazel oferece suporte ao armazenamento em 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 a equipe estiver no mesmo escritório, talvez seja melhor 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 do nginx. Você vai precisar
mudar /path/to/cache/dir
para um diretório válido em que o Nginx tenha permissão
para gravar e ler. 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 outras
configurações, 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 tem sido 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 fornece coleta de lixo para aplicar um limite máximo de armazenamento e limpar artefatos não utilizados. O cache está disponível como uma [imagem do Docker] e o código está disponível no GitHub. As APIs REST e gRPC de cache remoto são compatíveis.
Consulte a página do GitHub para instruções sobre como usá-lo.
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ê, porque a largura de banda da rede é importante para o cache remoto.
Crie uma conta de serviço para que o Bazel se autentique no Cloud Storage. Consulte Como criar uma conta de serviço.
Gere uma chave JSON secreta e transmita-a ao Bazel para autenticação. Armazene a chave com segurança, porque qualquer pessoa com a chave pode ler e gravar dados arbitrários no seu bucket do GCS.
Conecte-se ao Cloud Storage adicionando as seguintes flags ao comando do Bazel:
- Transmita o URL a seguir 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 URL a seguir para o Bazel usando a flag:
É possível configurar o Cloud Storage para excluir arquivos antigos automaticamente. Para fazer isso, consulte Gerenciamento do ciclo de vida de objetos.
Outros servidores
Você pode configurar qualquer servidor HTTP/1.1 compatível com PUT e GET como o back-end do cache. Os usuários relataram sucesso com back-ends de cache, como o Hazelcast, Apache httpd e AWS S3.
Autenticação
A partir da versão 0.11.0, o suporte à autenticação básica HTTP foi adicionado ao Bazel.
É possível transmitir um nome de usuário e uma senha para o Bazel pelo URL do cache remoto. A
sintaxe é https://username:password@hostname.com:port/path
. A autenticação básica HTTP transmite o nome de usuário e a senha em texto simples pela rede. Portanto, é fundamental sempre usá-la com HTTPS.
Protocolo de armazenamento em cache HTTP
O Bazel oferece suporte a armazenamento em cache remoto pelo HTTP/1.1. O protocolo é conceitualmente simples:
os dados binários (BLOB) são enviados por solicitações PUT e transferidos por 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 fazer o download de metadados de resultado de ação para uma ação com o hash SHA256
01ba4719...
será semelhante a esta:
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 será semelhante a esta:
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 for configurado como o cache remoto, para usá-lo, é necessário adicionar flags ao comando do Bazel. Confira a lista de configurações e as flags abaixo.
Talvez seja necessário configurar a autenticação, que é específica para o servidor escolhido.
É possível adicionar essas flags em um arquivo .bazelrc
para que não seja necessário
especificá-las sempre que você executar o Bazel. Dependendo do projeto e da
dinâmica da equipe, é possível adicionar flags a um arquivo .bazelrc
que:
- Na sua máquina local
- No espaço de trabalho do seu projeto, compartilhado com a equipe
- No sistema de CI
Ler e gravar no cache remoto
Cuidado com quem tem permissão para gravar no cache remoto. Talvez você queira que apenas o sistema de CI grave no cache remoto.
Use a flag a seguir para ler e gravar no cache remoto:
build --remote_cache=http://your.host:port
Além de HTTP
, os seguintes protocolos também são aceitos: HTTPS
, grpc
e grpcs
.
Use a flag a seguir, além da anterior, 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 o destino com
no-remote-cache
. Exemplo:
java_library(
name = "target",
tags = ["no-remote-cache"],
)
Excluir conteúdo do cache remoto
Fazer isso faz parte do gerenciamento do servidor. A maneira de excluir conteúdo do cache remoto depende do servidor que você configurou 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
- Reduzir a quantidade de armazenamento usada excluindo saídas antigas
Soquetes Unix
O cache HTTP remoto oferece suporte à conexão por soquetes de domínio Unix. O comportamento
é semelhante à flag --unix-socket
do curl. Use o seguinte para configurar o socket de domínio
Unix:
build --remote_cache=http://your.host:port
build --remote_proxy=unix:/path/to/socket
Esse recurso não é compatível com o 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 alternar entre filiais 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
É possível transmitir um caminho específico do usuário para a flag --disk_cache
usando o alias ~
.
O Bazel vai substituir o diretório inicial do usuário atual. Isso é útil
ao ativar o cache do disco para todos os desenvolvedores de um projeto usando o arquivo
.bazelrc
verificado 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 do disco
ou para a idade de entradas de cache individuais. O Bazel coleta automaticamente o lixo
do cache do disco enquanto fica ocioso entre os builds. O timer inativo pode ser definido
com --experimental_disk_cache_gc_idle_delay
(padrão de 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 de arquivos de entrada durante um build
Quando um arquivo de entrada é modificado durante um build, o Bazel pode fazer o upload de resultados
inválidos para o cache remoto. É possível ativar uma detecção de mudança com
a flag --experimental_guard_against_concurrent_changes
. Não
há problemas conhecidos, e ele será ativado por padrão em uma versão futura.
Consulte [issue #3360] para atualizações. Em geral, evite modificar arquivos de origem durante um
build.
Variáveis de ambiente que vazam para 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 por --action_env
são incluídas em uma definição
de ação. O pacote Debian/Ubuntu do Bazel era usado para instalar /etc/bazel.bazelrc
com uma lista branca 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/
. Em seguida,
dois usuários com compiladores diferentes instalados vão compartilhar ocorrências de cache incorretamente,
porque as saídas são diferentes, mas têm o mesmo hash de ação. Consulte
o problema 4558 para atualizações.
O estado incremental na memória é perdido ao executar builds em contêineres do Docker O Bazel usa a arquitetura 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 os builds. Ao executar builds em contêineres do Docker, como no CI, o estado na memória é perdido e o Bazel precisa ser recriado antes de usar o cache remoto.
Links externos
Seu build em um datacenter:a equipe do Bazel fez uma palestra sobre cache e execução remota na FOSDEM 2018.
Builds do Bazel mais rápidos com o armazenamento em cache remoto: um comparativo:Nicolò Valigi escreveu uma postagem de blog em que ele compara o armazenamento em cache remoto no Bazel.
Buildbarn (em inglês)