Nesta página, abordaremos o armazenamento em cache remoto, a configuração de um servidor para hospedar o cache e a execução de versões 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 a versão 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 as versões significativamente mais rápidas.
Visão geral
Ele divide um build em etapas distintas, que são 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 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 saídas de compilação da 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 back-end do cache
- Configurar a compilação do Bazel para usar o cache remoto
- Usar 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ção para metadados de resultados de ações.
- Um armazenamento endereçável de conteúdo (CAS) de arquivos de saída.
Observe que o cache remoto também armazena stdout e stderr para cada ação. Portanto, inspecionar o stdout/stderr do Bazel não é um bom sinal para estimar ocorrências em cache.
Como um build usa o armazenamento em cache remoto
Depois que um servidor é configurado como o cache remoto, você usa o cache de várias maneiras:
- Ler e gravar no cache remoto
- Ler e/ou gravar no cache remoto, exceto destinos específicos
- Ler apenas do cache remoto
- Não usar o cache remoto
Quando você executa uma compilação do Bazel que pode ler e gravar no cache remoto, a compilação segue estas etapas:
- O Bazel cria o gráfico de destinos que precisam ser compilados e uma lista de ações necessárias. Cada uma dessas ações declarou nomes de arquivos de entrada e saída.
- O Bazel verifica se há saídas de build na máquina local e reutiliza todas as que encontrar.
- O Bazel verifica o cache para saídas de build atuais. Se a saída for encontrada, o Bazel vai recuperá-la. Isso é uma ocorrência em cache.
- No caso das ações necessárias em que as saídas não foram encontradas, o Bazel executa as ações localmente e cria as saídas de compilação necessárias.
- Novas saídas de compilação são enviadas para o cache remoto.
Configurar um servidor como back-end do cache
Você precisa configurar um servidor para atuar como back-end do cache. Um servidor HTTP/1.1 pode tratar os dados do Bazel como bytes opacos. Portanto, muitos servidores atuais podem ser usados como um back-end de armazenamento em cache remoto. O protocolo de armazenamento em cache HTTP do Bazel é compatível com o armazenamento em cache remoto.
Você é responsável por escolher, configurar e manter o servidor de back-end que 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 o próprio servidor local.
- e na segurança. O cache remoto terá seus binários e, portanto, precisa ser seguro.
- Facilidade de gerenciamento. 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
- bazel-remote (em inglês)
- Google Cloud Storage
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 via Homebrew:
brew tap denji/nginx
brew install nginx-full --with-webdav
Veja abaixo um exemplo de configuração para o nginx. Altere /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 alterar 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 compilação remota 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. Observe que o projeto Bazel não oferece suporte técnico para o bazel-remote.
Esse cache armazena conteúdo no disco e também fornece a coleta de lixo para impor um limite de armazenamento superior 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 (em inglês). As APIs de cache remoto REST e gRPC 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 armazenamento 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 faturamento ativado.
Para usar o Cloud Storage como cache:
Crie um bucket de armazenamento. Selecione o local do bucket mais próximo de você, já que a largura de banda da rede é importante para o cache remoto.
Criar uma conta de serviço para o Bazel autenticar 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 ela pode ler e gravar dados arbitrários no bucket do GCS e a partir dele.
Conecte-se ao Cloud Storage adicionando as seguintes sinalizações ao comando do Bazel:
- Transmita o seguinte URL para o Bazel usando a sinalização:
--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 sinalização:
--google_credentials=/path/to/your/secret-key.json
ou--google_default_credentials
para usar a autenticação de aplicativos.
- Transmita o seguinte URL para o Bazel usando a sinalização:
É possível configurar o Cloud Storage para excluir arquivos antigos automaticamente. Para fazer isso, consulte Como gerenciar ciclos de vida de objetos.
Outros servidores
É possível configurar qualquer servidor HTTP/1.1 compatível com PUT e GET como back-end do cache. Os usuários relataram sucesso com back-ends de armazenamento em cache, como Hazelcast, Apache httpd e AWS S3 (links em inglês).
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 por meio do URL do cache remoto. A
sintaxe é https://username:password@hostname.com:port/path
. A autenticação HTTP básica 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 ao armazenamento em cache remoto via HTTP/1.1. O protocolo é conceitualmente simples:
o upload de dados binários (BLOB) é feito por meio de solicitações PUT e o download é feito por meio de 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 executado em http://localhost:8080/cache
.
Uma solicitação do Bazel para fazer o download de metadados de resultado de uma ação com o hash
SHA256 01ba4719...
terá a seguinte aparência:
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 terá a seguinte aparência:
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
Após configurar um servidor como cache remoto, é necessário adicionar sinalizações ao comando do Bazel para usá-lo. Consulte abaixo a lista de configurações e as sinalizações correspondentes.
Talvez você também precise configurar a autenticação, que é específica para o servidor escolhido.
É possível adicionar essas sinalizações em um arquivo .bazelrc
para não
precisar especificá-las toda vez que executar o Bazel. Dependendo do projeto e da dinâmica da equipe, é possível adicionar sinalizações a um arquivo .bazelrc
que esteja:
- 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
Confira quem pode fazer gravações no cache remoto. Talvez você queira que apenas o sistema de CI possa gravar 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 compatíveis: HTTPS
, grpc
e grpcs
.
Use a sinalização a seguir, além da acima, para ler apenas o cache remoto:
build --remote_upload_local_results=false
Impedir que destinos específicos usem o 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 seu servidor. A maneira como você exclui conteúdo do cache remoto depende do servidor configurado como o 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 será possível distinguir qual saída pertence a um build específico.
Você pode excluir conteúdo do cache para:
- Criar um cache limpo após um cache ser envenenado
- Reduza 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 ao da sinalização --unix-socket
do curl. Use o código a seguir para configurar o soquete de domínio Unix:
build --remote_cache=http://your.host:port
build --remote_cache_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 ramificações e/ou trabalhar em vários espaços de trabalho do mesmo projeto, como várias finalizações de compra. Como o Bazel não faz a coleta de lixo do diretório, convém automatizar uma limpeza periódica desse diretório. Ative o cache de disco da seguinte forma:
build --disk_cache=path/to/build/cache
É possível transmitir um caminho específico do usuário para a sinalização --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 pelo arquivo
.bazelrc
verificado.
Problemas conhecidos
Modificação do arquivo de entrada durante um build
Quando um arquivo de entrada é modificado durante uma compilação, o Bazel pode fazer upload de resultados inválidos para o cache remoto. É possível ativar a detecção de alterações 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 o [problema #3360] para conferir as atualizações. Em geral, evite modificar arquivos de origem durante um
build.
Variáveis de ambiente vazando para uma ação
Uma definição de ação contém variáveis de ambiente. Isso pode ser um problema ao compartilhar ocorrências de cache remoto entre máquinas. Por exemplo, ambientes com diferentes variáveis $PATH
não compartilharão ocorrências em cache. Somente variáveis de ambiente explicitamente colocadas 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 ocorrências em cache do que o esperado, verifique se o ambiente não tem um arquivo
/etc/bazel.bazelrc
antigo.
O Bazel não monitora ferramentas fora de um espaço de trabalho
No momento, o Bazel não monitora ferramentas fora de um espaço de trabalho. Isso pode ser um
problema se, por exemplo, uma ação usar um compilador da /usr/bin/
. Assim,
dois usuários com diferentes compiladores instalados compartilharão erroneamente ocorrências em cache
porque as saídas são diferentes, mas têm o mesmo hash de ação. Consulte o
problema 4558 para conferir atualizações.
O estado incremental na memória é perdido ao executar builds dentro de 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 os builds. Ao executar builds dentro de 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
Seu build em um data center:a equipe do Bazel deu uma palestra sobre armazenamento em cache e execução remota no FOSDEM 2018.
Builds mais rápidos do Bazel com armazenamento em cache remoto: uma referência:Nicolò Valigi escreveu uma postagem do blog em que compara o armazenamento em cache remoto no Bazel.
bazel-remote (em inglês)
Hazelcast (link em inglês)
Apache httpd (em inglês)
AWS S3 (em inglês)
Buildbarn (link em inglês)
Buildfarm (em inglês)