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

Informar um problema Ver código-fonte Nightly · 7.4 . 7.3 · 7.2 · 7.1 · 7.0 · 6.5

As versões do Bazel que são bem-sucedidas localmente podem falhar quando executadas remotamente devido a restrições e requisitos que não afetam as versões locais. As causas mais comuns dessas falhas são descritas em Como adaptar as 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 à compilação iguais às da execução remota. Isso permite que você resolva problemas do build sem precisar de um serviço de execução remota.

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

  • As ações de build são executadas em contêineres de conjunto de ferramentas. É possível usar os mesmos contêineres de conjunto de ferramentas para executar o build local e remotamente usando um serviço que ofereça suporte à execução remota em contêineres.

  • Nenhum dado externo cruza a fronteira do contêiner. Somente entradas e saídas declaradas explicitamente entram e saem do contêiner, e somente depois que a ação de build associada for concluída.

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

É possível resolver esses problemas usando um dos seguintes métodos:

  • Solução de problemas nativa. Com esse método, o Bazel e as ações de compilação dele são executados nativamente na máquina local. O recurso de sandbox do Docker impõe restrições ao build iguais às da execução remota. No entanto, esse método não detecta ferramentas, estados e vazamentos de dados locais no build, o que causa problemas na execução remota.

  • Solução de problemas em um contêiner do Docker. Com esse método, o Bazel e as ações de build são executados em um contêiner do Docker, o que permite detectar ferramentas, estados e dados que vazam da máquina local para o build, além de impor restrições iguais às da execução remota. Esse método fornece informações sobre sua versão, mesmo que partes dela apresentem falha. Esse método é experimental e não tem suporte oficial.

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 de sandbox do Docker.
  • Adicione o repositório bazel-toolchains, fixado na versão mais recente, ao arquivo WORKSPACE do build, conforme descrito aqui.
  • Adicione flags ao arquivo .bazelrc para ativar o recurso. Crie o arquivo no diretório raiz do projeto do Bazel, se ele não existir. As flags abaixo são um exemplo de referência. Consulte o arquivo .bazelrc mais recente no repositório bazel-toolchains e copie os valores das flags definidas 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 ferramentas adicionais, faça o seguinte:

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

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

Solução de problemas nativa

Esse método executa o Bazel e todas as ações de build diretamente na máquina local e é uma maneira confiável de confirmar se o build vai ser bem-sucedido quando executado remotamente.

No entanto, com esse método, ferramentas, binários e dados instalados localmente podem vazar para o build, especialmente se ele usar regras WORKSPACE de estilo de configuração. Esses vazamentos causam problemas na execução remota. Para detectá-los, resolva problemas em um contêiner do Docker e também de forma nativa.

Etapa 1: executar o build

  1. Adicione a flag --config=docker-sandbox ao comando do Bazel que executa o build. Exemplo:

    bazel --bazelrc=.bazelrc build --config=docker-sandbox target
  2. Execute o build e aguarde a conclusão. O build vai ser executado até quatro vezes mais lento do que o normal devido ao recurso de 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 flag --experimental_docker_verbose. Essa flag ativa mensagens de erro detalhadas. Esse erro geralmente é causado por uma instalação incorreta 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 o problema persistir, pule para 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 do Docker host, e as ações de build do Bazel são executadas em contêineres de toolchain individuais gerados pelo recurso de sandbox do Docker. O sandbox gera um contêiner de conjunto de ferramentas novo 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 de build e manter o mínimo de ferramentas instaladas, é possível verificar se o build tem dependências 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 montando 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. O 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 vão causar erros de build.

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 corresponder a um diretório que pode ser acessado pelo mesmo caminho absoluto de dentro do contêiner do 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 compilação do Bazel estão em execução 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

É possível resolver falhas de build da seguinte maneira:

  • Se o build falhar com um erro de "sem espaço em disco", você poderá aumentar esse limite iniciando o contêiner host com a flag --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 as regras do Bazel para execução remota para conhecer as possíveis causas e soluções alternativas.

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