Criar imagens de contêiner personalizadas para o Dataflow

Neste documento, descrevemos como criar uma imagem de contêiner personalizada para jobs do Dataflow.

Requisitos

Uma imagem de contêiner personalizada para o Dataflow precisa atender aos seguintes requisitos:

  • O SDK do Apache Beam e as dependências necessárias instaladas Recomendamos começar com uma imagem padrão do SDK do Apache Beam. Para mais informações, consulte Selecionar uma imagem de base neste documento.
  • O script /opt/apache/beam/boot precisa ser executado como a última etapa durante a inicialização do contêiner. O script inicializa o ambiente do worker e inicia o processo de worker do SDK. Esse script é o ENTRYPOINT padrão nas imagens do SDK do Apache Beam. No entanto, se você usar uma imagem de base diferente ou modificar o ENTRYPOINT padrão, será necessário executar o script explicitamente. Para mais informações, consulte Modificar o ponto de entrada do contêiner neste documento.
  • A imagem do contêiner precisa ser compatível com a arquitetura das VMs de worker para seu job do Dataflow. Se você planeja usar o contêiner personalizado em VMs ARM, recomendamos criar uma imagem de multiarquitetura. Para mais informações, consulte Criar uma imagem de contêiner de várias arquiteturas.

Antes de começar

  1. Verifique se a versão do SDK do Apache Beam instalada é compatível com o Runner v2 e sua versão de linguagem. Para mais informações, consulte Instalar o SDK do Apache Beam.

  2. Para testar sua imagem de contêiner localmente, é necessário ter o Docker instalado. Para mais informações, consulte Obter Docker.

  3. Crie um repositório do Artifact Registry. Especifique o formato de imagem do Docker. É preciso ter pelo menos o acesso "Gravador do Artifact Registry" no repositório.

    Para criar um novo repositório, execute o comando gcloud artifacts repositories create:

    gcloud artifacts repositories create REPOSITORY \
      --repository-format=docker \
      --location=REGION \
      --async
    

    Substitua:

    • REPOSITORY: um nome para o repositório. Os nomes dos repositórios precisam ser exclusivos para cada local em um projeto.
    • REGION: a região onde o job do Dataflow será implantado. Selecione uma região do Dataflow próxima de onde você executa os comandos. O valor precisa ser um nome de região válido. Para mais informações sobre regiões e locais, consulte Locais do Dataflow.

    Neste exemplo, usamos a flag --async. O comando retorna imediatamente, sem aguardar a conclusão da operação.

  4. Para configurar o Docker para autenticar solicitações do Artifact Registry, execute o comando gcloud auth configure-docker:

    gcloud auth configure-docker REGION-docker.pkg.dev
    

    O comando atualiza a configuração do Docker. Agora é possível se conectar ao Artifact Registry no seu projeto do Google Cloud para enviar imagens.

Selecionar uma imagem de base

Recomendamos começar com uma imagem do SDK do Apache Beam como a imagem do contêiner de base. Essas imagens padrão são lançadas como parte das versões do Apache Beam para DockerHub.

Usar uma imagem de base do Apache Beam

Para usar uma imagem do SDK do Apache Beam como a imagem de base, especifique a imagem do contêiner na instrução FROM e adicione suas próprias personalizações.

Java

Este exemplo usa o Java 8 com a versão 2.59.0 do SDK do Apache Beam.

FROM apache/beam_java8_sdk:2.59.0

# Make your customizations here, for example:
ENV FOO=/bar
COPY path/to/myfile ./

A versão do tempo de execução do contêiner personalizado precisa corresponder ao tempo de execução que você usará para iniciar o pipeline. Por exemplo, se você iniciar o pipeline a partir de um ambiente Java 11 local, a linha FROM precisará especificar um ambiente Java 11: apache/beam_java11_sdk:....

Python

Este exemplo usa o Python 3.10 com a versão 2.59.0 do SDK do Apache Beam.

FROM apache/beam_python3.10_sdk:2.59.0

# Make your customizations here, for example:
ENV FOO=/bar
COPY path/to/myfile ./

A versão do tempo de execução do contêiner personalizado precisa corresponder ao tempo de execução que você usará para iniciar o pipeline. Por exemplo, se você iniciar o pipeline em um ambiente Python 3.10 local, a linha FROM precisará especificar um ambiente Python 3.10: apache/beam_python3.10_sdk:....

Go

Este exemplo usa o Go com o SDK do Apache Beam versão 2.59.0.

FROM apache/beam_go_sdk:2.59.0

# Make your customizations here, for example:
ENV FOO=/bar
COPY path/to/myfile ./

Usar uma imagem de base personalizada

Se você quiser usar uma imagem de base diferente ou precisar modificar algum aspecto das imagens padrão do Apache Beam (como versão ou patches do SO), use um processo de build de vários estágios. Copie os artefatos necessários de uma imagem base padrão do Apache Beam.

Defina o ENTRYPOINT para executar o script /opt/apache/beam/boot, que inicializa o ambiente do worker e inicia o processo de worker do SDK. Se você não definir esse ponto de entrada, os workers do Dataflow não serão iniciados corretamente.

Veja no exemplo a seguir um Dockerfile que copia arquivos do SDK do Apache Beam:

Java

FROM openjdk:8

# Copy files from official SDK image, including script/dependencies.
COPY --from=apache/beam_java8_sdk:2.59.0 /opt/apache/beam /opt/apache/beam

# Set the entrypoint to Apache Beam SDK launcher.
ENTRYPOINT ["/opt/apache/beam/boot"]

Python

FROM python:3.10-slim

# Install SDK.
RUN pip install --no-cache-dir apache-beam[gcp]==2.59.0

# Verify that the image does not have conflicting dependencies.
RUN pip check

# Copy files from official SDK image, including script/dependencies.
COPY --from=apache/beam_python3.10_sdk:2.59.0 /opt/apache/beam /opt/apache/beam

# Set the entrypoint to Apache Beam SDK launcher.
ENTRYPOINT ["/opt/apache/beam/boot"]

Neste exemplo, presume-se que as dependências necessárias (neste caso, Python 3.10 e pip) foram instaladas na imagem base atual. A instalação do SDK do Apache Beam na imagem garante que ela tenha as dependências necessárias do SDK e reduz o tempo de inicialização do worker.

Importante: a versão do SDK especificada nas instruções RUN e COPY precisa corresponder à versão usada para iniciar o pipeline.

Go

FROM golang:latest

# Copy files from official SDK image, including script/dependencies.
COPY --from=apache/beam_go_sdk:2.59.0 /opt/apache/beam /opt/apache/beam

# Set the entrypoint to Apache Beam SDK launcher.
ENTRYPOINT ["/opt/apache/beam/boot"]

Modificar o ponto de entrada do contêiner

Se o contêiner executar um script personalizado durante a inicialização, ele precisará terminar com a execução de /opt/apache/beam/boot. Os argumentos transmitidos pelo Dataflow durante a inicialização do contêiner precisam ser passados para o script de inicialização padrão. O exemplo a seguir mostra um script de inicialização personalizado que chama o script de inicialização padrão:

#!/bin/bash

echo "This is my custom script"
# ...

# Pass command arguments to the default boot script.
/opt/apache/beam/boot "$@"

No Dockerfile, defina ENTRYPOINT para chamar o script:

Java

FROM apache/beam_java8_sdk:2.59.0

COPY script.sh path/to/my/script.sh
ENTRYPOINT [ "path/to/my/script.sh" ]

Python

FROM apache/beam_python3.10_sdk:2.59.0

COPY script.sh path/to/my/script.sh
ENTRYPOINT [ "path/to/my/script.sh" ]

Go

FROM apache/beam_go_sdk:2.59.0

COPY script.sh path/to/my/script.sh
ENTRYPOINT [ "path/to/my/script.sh" ]

Criar e enviar a imagem

Use o Cloud Build ou o Docker para criar a imagem do contêiner e enviá-la para um repositório do Artifact Registry.

Cloud Build

Para criar o arquivo e enviá-lo ao repositório do Artifact Registry, execute o comando gcloud builds submit:

  gcloud builds submit --tag REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/dataflow/FILE_NAME:TAG .

Docker

docker build . --tag REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/dataflow/FILE_NAME:TAG
docker push REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/dataflow/FILE_NAME:TAG

Substitua:

  • REGION: a região em que o job do Dataflow será implantado. O valor da variável REGION precisa ser um nome de região válido.
  • PROJECT_ID: o nome do projeto ou do usuário.
  • REPOSITORY: o nome do repositório de imagens.
  • FILE_NAME: o nome do Dockerfile.
  • TAG: a tag de imagem. Sempre especifique uma tag ou um SHA de contêiner com controle de versões. Não use a tag :latest ou uma tag mutável.

Pré-instalar dependências do Python

Esta seção se aplica aos pipelines do Python.

Ao iniciar um job em Python do Dataflow, é possível especificar outras dependências usando as opções --requirements_file ou --extra_packages no ambiente de execução. Para mais informações, leia Como gerenciar dependências de pipeline do Python. Outras dependências são instaladas em cada contêiner de worker do Dataflow. Quando o job é iniciado e durante o escalonamento automático, a instalação de dependência geralmente leva ao alto uso da CPU e a um longo período de aquecimento em todos os workers recém-iniciados do Dataflow.

Para evitar instalações repetitivas, é possível pré-criar a imagem de um contêiner personalizada do SDK do Python com as dependências pré-instaladas. É possível executar essa etapa no tempo de build usando um Dockerfile ou no ambiente de execução, ao enviar o job.

Os workers criam um novo ambiente virtual do Python quando iniciam o contêiner. Por esse motivo, instale dependências no ambiente Python padrão (global) em vez de criar um ambiente virtual. Se você ativar um ambiente virtual na imagem do contêiner, talvez esse ambiente não esteja ativado quando o job for iniciado. Para saber mais, consulte Problemas comuns.

Pré-instalar usando um Dockerfile

Para adicionar mais dependências diretamente ao contêiner personalizado do Python, use os seguintes comandos:

FROM apache/beam_python3.10_sdk:2.59.0

COPY requirements.txt .

# Pre-install Python dependencies. For reproducibile builds,
# supply all of the dependencies and their versions in a requirements.txt file.
RUN pip install -r requirements.txt

# You can also install individual dependencies.
RUN pip install lxml
# Pre-install other dependencies.
RUN apt-get update \
  && apt-get dist-upgrade \
  && apt-get install -y --no-install-recommends ffmpeg

Envie o job com as opções de pipeline --sdk_container_image e --sdk_location. A opção --sdk_location vai impedir o download do SDK quando o job for iniciado. O SDK é recuperado diretamente da imagem do contêiner.

O exemplo a seguir executa o pipeline de exemplo wordcount:

python -m apache_beam.examples.wordcount \
  --input=INPUT_FILE \
  --output=OUTPUT_FILE \
  --project=PROJECT_ID \
  --region=REGION \
  --temp_location=TEMP_LOCATION \
  --runner=DataflowRunner \
  --experiments=use_runner_v2 \
  --sdk_container_image=IMAGE_URI
  --sdk_location=container

Substitua:

  • INPUT_FILE: um arquivo de entrada para o pipeline.
  • OUTPUT_FILE: um caminho de arquivo para gravar a saída.
  • PROJECT_ID: o ID do projeto do Google Cloud
  • REGION: a região em que o job do Dataflow será implantado.
  • TEMP_LOCATION: o caminho do Cloud Storage para o Dataflow em que os arquivos de job temporários são preparados
  • IMAGE_URI: o URI da imagem do contêiner personalizado

Crie previamente uma imagem de contêiner ao enviar o job.

A pré-criação de uma imagem de contêiner permite a pré-instalação das dependências do pipeline antes da inicialização do job. Você não precisa criar uma imagem de contêiner personalizada.

Para criar previamente um contêiner com outras dependências do Python ao enviar um job, use as seguintes opções de pipeline:

  • --prebuild_sdk_container_engine=[cloud_build | local_docker]. Quando essa flag é definida, o Apache Beam gera um contêiner personalizado e instala todas as dependências especificadas pelas opções --requirements_file e --extra_packages. Essa flag é compatível com os seguintes valores:

    • cloud_build: Use o Cloud Build para criar o contêiner. A API Cloud Build precisa estar ativada no projeto.
    • local_docker. Use a instalação local do Docker para criar o contêiner.
  • --docker_registry_push_url=IMAGE_PATH. Substitua IMAGE_PATH por uma pasta do Artifact Registry.

  • --sdk_location=container. Essa opção impede que os workers façam o download do SDK quando o job é iniciado. Em vez disso, o SDK é recuperado diretamente da imagem do contêiner.

O exemplo a seguir usa o Cloud Build para pré-criar a imagem:

python -m apache_beam.examples.wordcount \
  --input=INPUT_FILE \
  --output=OUTPUT_FILE \
  --project=PROJECT_ID \
  --region=REGION \
  --temp_location=TEMP_LOCATION \
  --runner=DataflowRunner \
  --disk_size_gb=DISK_SIZE_GB \
  --experiments=use_runner_v2 \
  --requirements_file=./requirements.txt \
  --prebuild_sdk_container_engine=cloud_build \
  --docker_registry_push_url=IMAGE_PATH \
  --sdk_location=container

O recurso de pré-build requer a versão 2.25.0 ou mais recente do SDK do Apache Beam para Python.

O fluxo de trabalho de pré-criação da imagem do contêiner do SDK usa a imagem transmitida com a opção de pipeline --sdk_container_image como a imagem base. Se a opção não estiver definida, uma imagem do Apache Beam é usada como a imagem base por padrão.

É possível reutilizar uma imagem de contêiner do SDK do Python pré-criada em outro job com as mesmas dependências e a mesma versão do SDK. Para reutilizar a imagem, transmita o URL de imagem de contêiner pré-criado para o outro job usando a opção de pipeline --sdk_container_image. Remova as opções de dependência --requirements_file, --extra_packages e --setup_file.

Se você não for reutilizar a imagem, pode excluí-la depois de concluir o job. É possível excluir a imagem com a CLI gcloud ou nas páginas do Artifact Registry no console do Google Cloud.

Se a imagem estiver armazenada no Artifact Registry, use o comando artifacts docker images delete:

   gcloud artifacts docker images delete IMAGE --delete-tags

Problemas comuns

  • Se o job tiver outras dependências do Python de um espelho PyPi particular e não puder ser extraído por um job remoto do Cloud Build, tente usar a opção local do Docker ou criar o contêiner usando um Dockerfile.

  • Se o job do Cloud Build falhar com docker exit code 137, quer dizer que o job de compilação ficou sem memória, possivelmente devido ao tamanho das dependências que estão sendo instaladas. Use um tipo de máquina de worker do Cloud Build maior transmitindo --cloud_build_machine_type=machine_type, em que machine_type é uma das seguintes opções:

    • n1-highcpu-8
    • n1-highcpu-32
    • e2-highcpu-8
    • e2-highcpu-32

    Por padrão, o Cloud Build usa o tipo de máquina e2-medium.

  • No Apache Beam 2.44.0 e versões posteriores, os workers criam um ambiente virtual ao iniciar um contêiner personalizado. Se o contêiner criar o próprio ambiente virtual para instalar dependências, elas serão descartadas. Esse comportamento pode causar erros como os mostrados a seguir:

    ModuleNotFoundError: No module named '<dependency name>'

    Para evitar esse problema, instale dependências no ambiente Python padrão (global). Como solução alternativa, desative esse comportamento no Beam 2.48.0 e versões mais recentes, definindo a seguinte variável de ambiente na imagem do contêiner:

    ENV RUN_PYTHON_SDK_IN_DEFAULT_ENVIRONMENT=1

A seguir