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

As compilações do Bazel bem-sucedidas no local 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 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ê solucione problemas do build sem precisar 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 do conjunto de ferramentas. Você pode usar os mesmos contêineres de conjunto de ferramentas para executar sua compilação local e remotamente por meio de um serviço compatível com a execução remota em contêiner.

  • Nenhum dado externo cruza os limites do contêiner. Somente entradas e saídas declaradas explicitamente entram e saem do contêiner e somente após a conclusão bem-sucedida da ação de compilação associada.

  • Cada ação é executada em um novo contêiner. Um contêiner novo e exclusivo é criado para cada ação de compilação 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 à compilação iguais às de execução remota. No entanto, esse método não detectará ferramentas, estados e dados locais com vazamento para o build, o que causará problemas na execução remota.

  • Como solucionar problemas em um contêiner do Docker. Com esse método, o Bazel e as ações de compilação são executados dentro de um contêiner do Docker, o que permite detectar ferramentas, estados e vazamentos de dados da máquina local para a compilação, 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-Datasets, fixado na versão de lançamento mais recente, ao arquivo WORKSPACE do seu build, conforme descrito neste link.
  • 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 flags abaixo são um exemplo de referência. Consulte o arquivo .bazelrc mais recente no repositório bazel-Datasets 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 com um Dockerfile e criando a imagem localmente.

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

Solução de problemas nativa

Esse método executa o Bazel e todas as ações de compilação diretamente na máquina local e é uma maneira confiável de confirmar se a compilação terá êxito 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 ESPAÇO DE TRABALHO de estilo de configuração. Esses vazamentos causam problemas na execução remota. Para detectá-los, resolva os 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 compilação. Exemplo:

    bazel --bazelrc=.bazelrc build --config=docker-sandbox target
    
  2. Execute o build e aguarde a conclusão. A compilação será executada até quatro vezes mais devagar 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 o build novamente com a flag --experimental_docker_verbose. Essa sinalização permite mensagens de erro detalhadas. Esse erro normalmente é causado por uma instalação com falha do Docker ou por 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 Como solucionar 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 dele 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 compilação e apenas uma ação é executada em cada contêiner de conjunto de ferramentas.

Esse método oferece 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 as ferramentas instaladas no mínimo, você pode 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 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. Seu código-fonte está disponível em /src dentro do contêiner.

O comando é iniciado intencionalmente a partir de um contêiner de 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 estes 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 são 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

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

  • Se o build falhar com um erro de "sem 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 problemas detectados.