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

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.

Esta página descreve como identificar e resolver os problemas mais comuns que surgem com a execução remota usando o recurso de sandbox do Docker, que impõe restrições à build iguais às da execução remota. Isso permite resolver problemas da versão sem a necessidade de um serviço de execução remota.

O recurso de 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. É possível usar os mesmos contêineres de conjunto de ferramentas para executar a build local e remotamente usando um serviço que oferece suporte à execução remota conteinerizada.

  • Nenhum dado estranho cruza o limite do contêiner. Somente as entradas e saídas declaradas explicitamente entram e saem do contêiner, e somente após a conclusão da ação de build associada.

  • 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 solucionar esses problemas usando um dos seguintes métodos:

  • Solução de problemas nativa. Com esse método, o Bazel e as ações de build são executados nativamente na sua máquina local. O recurso de sandbox do Docker impõe restrições à versão iguais às da execução remota. No entanto, esse método não detecta ferramentas, estados e vazamento de dados locais na sua build, o que causa problemas com a 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 vazamento de dados da máquina local para a versão, além de impor restrições iguais às da execução remota. Esse método fornece insights sobre a versão, mesmo que partes dela estejam falhando. Esse método é experimental e não tem suporte oficial.

Pré-requisitos

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

  • 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 oferecem suporte ao recurso de sandbox do Docker.
  • Adicione o bazel-toolchains repo, fixado na versão de lançamento mais recente, ao arquivo WORKSPACE da versão 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 sua imagem do 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 a versão será bem-sucedida quando executada remotamente.

No entanto, com esse método, ferramentas, binários e dados instalados localmente podem vazar para a versão, especialmente se ela usar regras de WORKSPACE no estilo de configuração. Esses vazamentos causam problemas com a execução remota. Para detectá-los, solucione problemas em um contêiner do Docker além de solucionar problemas nativamente.

Etapa 1: executar a versão

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

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

Você pode encontrar o seguinte erro:

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

Se isso acontecer, execute a versão novamente com a flag --experimental_docker_verbose. Essa flag ativa mensagens de erro detalhadas. Esse erro geralmente é causado por uma instalação com falha 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, avance para Solução de problemas em um contêiner do Docker.

Etapa 2: resolver problemas detectados

A seguir, estão 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 do host, e as ações de build do Bazel são executadas em contêineres de conjunto 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 da versão da execução das ações de build e manter as ferramentas instaladas no mínimo, é possível verificar se a versão 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 do 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. O código-fonte está disponível em /src no contêiner.

O comando começa intencionalmente com 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 comandos a seguir de dentro do contêiner do Docker para testá-lo:

docker ps
bazel version

Etapa 4: executar a versão

Execute a versão 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 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 build do Bazel estão em execução e da máquina local em que os contêineres de host e ação são executados.

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

Etapa 5: resolver problemas detectados

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

  • Se a versão falhar com um erro "sem espaço em disco", é possível aumentar este limite iniciando o contêiner do host com a flag --memory=XX em que XX é o espaço em disco alocado em gigabytes. Esse recurso é experimental e pode resultar em um comportamento imprevisível.

  • Se a versão 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 possíveis causas e soluções alternativas.

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