Como solucionar problemas de execução remota do Bazel com o sandbox do Docker

Informar um problema Acessar a origem

As versões do Bazel bem-sucedidas localmente podem falhar quando executadas remotamente devido a restrições e requisitos que não afetam builds locais. As causas mais comuns dessas falhas são descritas em Como adaptar regras do Bazel para execução remota.

Nesta página, descrevemos como identificar e resolver os problemas mais comuns que surgem com a execução remota usando o recurso sandbox do Docker, que impõe restrições na compilação iguais às da execução remota. Isso permite que você resolva problemas no seu build sem precisar de um serviço de execução remota.

O recurso sandbox do Docker imita as restrições da execução remota da seguinte maneira:

  • As ações de build são executadas em contêineres de conjunto de ferramentas. Use os mesmos contêineres de conjunto de ferramentas para executar a compilação de maneira local e remota usando um serviço com suporte à execução remota em contêiner.

  • Nenhum dado irrelevante ultrapassa o limite do contêiner. Somente entradas e saídas declaradas explicitamente entram e saem do contêiner e somente após a ação de build associada ser concluída.

  • Cada ação é executada em um novo contêiner. Um contêiner novo e exclusivo é criado para cada ação de compilação gerada.

Para solucionar esses problemas, use um dos seguintes métodos:

  • Solução de problemas nativa. Com esse método, o Bazel e as ações de compilação são executados de forma nativa na máquina local. O recurso sandbox do Docker impõe restrições à compilação iguais àquelas da execução remota. No entanto, esse método não detectará ferramentas, estados e dados locais vazados para seu build, o que causará problemas com a execução remota.

  • Como solucionar problemas em um contêiner do Docker. Com esse método, o Bazel e as ações de build dele são executados em um contêiner do Docker, o que permite detectar ferramentas, estados e vazamentos de dados da máquina local na compilação, além de impor restrições iguais às da execução remota. Esse método fornece insights sobre o build, mesmo que partes dele falhem. Esse método é experimental e não é oficialmente compatível.

Pré-requisitos

Antes de iniciar a solução de problemas, faça o seguinte se ainda não tiver feito isso:

  • Instale o Docker e configure as permissões necessárias para executá-lo.
  • Instale o Bazel 0.14.1 ou mais recente. As versões anteriores não são compatíveis com o recurso sandbox do Docker.
  • Adicione o repositório bazel-toolkits, fixado à versão de lançamento mais recente, ao arquivo WORKSPACE do seu build, conforme descrito aqui.
  • Adicione sinalizações ao arquivo .bazelrc para ativar o recurso. Crie o arquivo no diretório raiz do projeto do Bazel, se ele não existir. As sinalizações abaixo são um exemplo de referência. Consulte o arquivo .bazelrc (em inglês) mais recente no repositório bazel-datasets e copie os valores das sinalizações definidas nele para a configuração docker-sandbox.
# Docker Sandbox Mode
build:docker-sandbox --host_javabase=<...>
build:docker-sandbox --javabase=<...>
build:docker-sandbox --crosstool_top=<...>
build:docker-sandbox --experimental_docker_image=<...>
build:docker-sandbox --spawn_strategy=docker --strategy=Javac=docker --genrule_strategy=docker
build:docker-sandbox --define=EXECUTOR=remote
build:docker-sandbox --experimental_docker_verbose
build:docker-sandbox --experimental_enable_docker_sandbox

Se as regras exigirem outras ferramentas, faça o seguinte:

  1. Crie um contêiner personalizado do Docker instalando ferramentas usando um Dockerfile e criando a imagem localmente.

  2. Substitua o valor da sinalização --experimental_docker_image acima pelo nome da imagem de contêiner personalizada.

Como solucionar problemas de forma nativa

Esse método executa o Bazel e todas as ações de compilação dele diretamente na máquina local e é uma maneira confiável de confirmar se sua compilação será bem-sucedida quando executada remotamente.

No entanto, com esse método, ferramentas, binários e dados instalados localmente podem vazar para seu build, especialmente se ele usar regras de estilo de configuração do ESPAÇO DE TRABALHO. Esses vazamentos causarão problemas na execução remota. Para detectá-los, solucione problemas em um contêiner do Docker, além de solucionar problemas de maneira nativa.

Etapa 1: executar o build

  1. Adicione a sinalização --config=docker-sandbox ao comando do Bazel que executa sua versão. Exemplo:

    bazel --bazelrc=.bazelrc build --config=docker-sandbox target
    
  2. Execute o build e aguarde a conclusão. O build será executado até quatro vezes mais lento do que o normal devido ao recurso sandbox do Docker.

Talvez você encontre o seguinte erro:

ERROR: 'docker' is an invalid value for docker spawn strategy.

Se isso acontecer, execute o build novamente com a sinalização --experimental_docker_verbose. Essa sinalização ativa mensagens de erro detalhadas. Esse erro normalmente é causado por uma instalação com falhas do Docker ou pela falta de permissões para executá-lo na conta de usuário atual. Consulte a documentação do Docker para mais informações. Se os problemas persistirem, pule para a Solução de problemas em um contêiner do Docker.

Etapa 2: resolver os problemas detectados

Confira a seguir os problemas mais comuns e as soluções alternativas.

Solução de problemas em um contêiner do Docker

Com esse método, o Bazel é executado em um contêiner de host do Docker, e as ações de compilação do Bazel são executadas em contêineres de conjuntos de ferramentas individuais gerados pelo recurso de sandbox do Docker. O sandbox gera um novo contêiner de conjunto de ferramentas para cada ação de build, e apenas uma ação é executada em cada contêiner de conjunto de ferramentas.

Esse método oferece um controle mais granular das ferramentas instaladas no ambiente do host. Ao separar a execução do build da execução das ações e manter o mínimo de ferramentas instaladas, você pode verificar se o build tem alguma dependência no ambiente de execução local.

Etapa 1: criar o contêiner

  1. Crie um Dockerfile que crie o contêiner do Docker e instale o Bazel com um conjunto mínimo de ferramentas de build:

    FROM debian:stretch
    
    RUN apt-get update && apt-get install -y apt-transport-https curl software-properties-common git gcc gnupg2 g++ openjdk-8-jdk-headless python-dev zip wget vim
    
    RUN curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add -
    
    RUN add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable"
    
    RUN apt-get update && apt-get install -y docker-ce
    
    RUN wget https://releases.bazel.build/<latest Bazel version>/release/bazel-<latest Bazel version>-installer-linux-x86_64.sh -O ./bazel-installer.sh && chmod 755 ./bazel-installer.sh
    
    RUN ./bazel-installer.sh
    
  2. Crie o contêiner como bazel_container:

    docker build -t bazel_container - < Dockerfile
    

Etapa 2: iniciar o contêiner

Inicie o contêiner do Docker usando o comando mostrado abaixo. No comando, substitua o caminho para o código-fonte no host que você quer criar.

docker run -it \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /tmp:/tmp \
  -v your source code directory:/src \
  -w /src \
  bazel_container \
  /bin/bash

Esse comando executa o contêiner como raiz, mapeando o soquete do Docker e ativando o diretório /tmp. Isso permite que o Bazel gere outros contêineres do Docker e use diretórios em /tmp para compartilhar arquivos com esses contêineres. Seu código-fonte está disponível em /src dentro do contêiner.

O comando começa intencionalmente de um contêiner base debian:stretch que inclui binários incompatíveis com o contêiner rbe-ubuntu16-04 usado como um contêiner de conjunto de ferramentas. Se os binários do ambiente local estiverem vazando para o contêiner do conjunto de ferramentas, eles causarão erros de compilação.

Etapa 3: testar o contêiner

Execute os seguintes comandos no contêiner do Docker para testá-lo:

docker ps
bazel version

Etapa 4: executar o build

Execute o build conforme mostrado abaixo. O usuário de saída é raiz para que corresponda a um diretório acessível com o mesmo caminho absoluto de dentro do contêiner host em que o Bazel é executado, dos contêineres de conjunto de ferramentas gerados pelo recurso de sandbox do Docker em que as ações de build do Bazel estão sendo executadas e da máquina local em que os contêineres de host e de ação são executados.

bazel --output_user_root=/tmp/bazel_docker_root --bazelrc=.bazelrc \ build --config=docker-sandbox target

Etapa 5: resolver os problemas detectados

Você pode resolver falhas de build da seguinte maneira:

  • Se o build falhar com um erro de "falta de espaço em disco", aumente esse limite iniciando o contêiner do host com a sinalização --memory=XX, em que XX é o espaço em disco alocado em gigabytes. Isso é experimental e pode resultar em um comportamento imprevisível.

  • Se o build falhar durante as fases de análise ou carregamento, uma ou mais regras de build declaradas no arquivo WORKSPACE não serão compatíveis com a execução remota. Consulte Como adaptar regras do Bazel para execução remota para possíveis causas e soluções alternativas.

  • Se o build falhar por qualquer outro motivo, consulte as etapas de solução de problemas na Etapa 2: resolver os problemas detectados.