Compila imágenes de contenedor personalizadas para Dataflow

En este documento, se describe cómo crear una imagen de contenedor personalizada para los trabajos de Dataflow.

Requisitos

Una imagen de contenedor personalizada para Dataflow debe cumplir con los siguientes requisitos:

  • Tener instalado el SDK de Apache Beam y las dependencias necesarias. Recomendamos comenzar con una imagen predeterminada del SDK de Apache Beam. Para obtener más información, consulta Selecciona una imagen base en este documento.
  • La secuencia de comandos /opt/apache/beam/boot debe ejecutarse como el último paso durante el inicio del contenedor. Esta secuencia de comandos inicializa el entorno de trabajador y, luego, inicia el proceso de trabajador del SDK. Esta secuencia de comandos es el ENTRYPOINT predeterminado en las imágenes del SDK de Apache Beam. Sin embargo, si usas una imagen base diferente o si anulas el ENTRYPOINT predeterminado, debes ejecutar la secuencia de comandos de forma explícita. Para obtener más información, consulta Modifica el punto de entrada del contenedor en este documento.
  • La imagen de contenedor debe ser compatible con la arquitectura de las VMs de trabajador para el trabajo de Dataflow. Si planeas usar el contenedor personalizado en VMs de ARM, te recomendamos que compiles una imagen de varias arquitecturas. Para obtener más información, consulta Compila una imagen de contenedor de varias arquitecturas.

Antes de comenzar

  1. Verifica que la versión del SDK de Apache Beam instalada sea compatible con Runner v2 y tu versión de lenguaje. Para obtener más información, consulta Instala el SDK de Apache Beam.

  2. Para probar la imagen de contenedor de forma local, debes tener Docker instalado. Para obtener más información, consulta Obtén Docker.

  3. Crea un repositorio de Artifact Registry. Especifica el formato de imagen de Docker. Debes tener al menos el acceso de escritor de Artifact Registry al repositorio.

    Para crear un repositorio nuevo, ejecuta el comando gcloud artifacts repositories create:

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

    Reemplaza lo siguiente:

    • REPOSITORY: un nombre para tu repositorio. Los nombres de los repositorios deben ser únicos para cada ubicación de un proyecto.
    • REGION: la región en la que se implementará tu trabajo de Dataflow. Selecciona una región de Dataflow cercana a donde ejecutas los comandos. El valor debe ser un nombre de región válido. Para obtener más información acerca de las regiones y ubicaciones, consulta Ubicaciones de Dataflow.

    En este ejemplo, se usa la marca --async. El comando se muestra de inmediato, sin necesidad de esperar a que se complete la operación.

  4. Para configurar Docker para autenticar solicitudes de Artifact Registry, ejecuta el comando gcloud auth configure-docker:

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

    El comando actualiza tu configuración de Docker. Ahora puedes conectarte con Artifact Registry en tu proyecto de Google Cloud para enviar imágenes.

Selecciona una imagen base

Recomendamos comenzar con una imagen del SDK de Apache Beam como imagen base del contenedor. Estas imágenes se lanzan como parte de las actualizaciones de Apache Beam en Docker Hub.

Usa una imagen base de Apache Beam

Para usar una imagen del SDK de Apache Beam como imagen base, especifica la imagen del contenedor en la instrucción FROM y, luego, agrega tus propias personalizaciones.

Java

En este ejemplo, se usa Java 8 con la versión 2.56.0 del SDK de Apache Beam.

FROM apache/beam_java8_sdk:2.56.0

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

La versión del entorno de ejecución del contenedor personalizado debe coincidir con el entorno de ejecución que usarás para iniciar la canalización. Por ejemplo, si iniciarás la canalización desde un entorno local de Java 11, la línea FROM debe especificar un entorno de Java 11: apache/beam_java11_sdk:....

Python

En este ejemplo, se usa Python 3.10 con la versión 2.56.0 del SDK de Apache Beam.

FROM apache/beam_python3.10_sdk:2.56.0

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

La versión del entorno de ejecución del contenedor personalizado debe coincidir con el entorno de ejecución que usarás para iniciar la canalización. Por ejemplo, si iniciarás la canalización desde un entorno local de Python 3.10, la línea FROM debe especificar un entorno de Python 3.10: apache/beam_python3.10_sdk:....

Go

En este ejemplo, se usa Go con la versión 2.56.0 del SDK de Apache Beam.

FROM apache/beam_go_sdk:2.56.0

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

Usa una imagen base personalizada

Si deseas usar una imagen base diferente o necesitas modificar algún aspecto de las imágenes predeterminadas de Apache Beam (como la versión o los parches del SO), usa un comando de compilación de varias etapas. Copia los artefactos necesarios de una imagen base predeterminada de Apache Beam.

Configura ENTRYPOINT para ejecutar la secuencia de comandos /opt/apache/beam/boot, que inicializa el entorno de trabajador y, luego, inicia el proceso de trabajador del SDK. Si no estableces este punto de entrada, los trabajadores de Dataflow no se inician de forma correcta.

En el siguiente ejemplo, se muestra un Dockerfile que copia los archivos del SDK de Apache Beam:

Java

FROM openjdk:8

# Copy files from official SDK image, including script/dependencies.
COPY --from=apache/beam_java8_sdk:2.56.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.56.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.56.0 /opt/apache/beam /opt/apache/beam

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

En este ejemplo, se supone que se instalaron las dependencias necesarias (en este caso, Python 3.10 y pip) en la imagen base existente. La instalación del SDK de Apache Beam en la imagen garantiza que esta tenga las dependencias del SDK necesarias y reduce el tiempo de inicio del trabajador.

Importante: La versión del SDK especificada en las instrucciones RUN y COPY debe coincidir con la versión que se usa para iniciar la canalización.

Go

FROM golang:latest

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

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

Modifica el punto de entrada del contenedor

Si el contenedor ejecuta una secuencia de comandos personalizada durante el inicio del contenedor, la secuencia de comandos debe terminar con la ejecución de /opt/apache/beam/boot. Los argumentos que pasa Dataflow durante el inicio del contenedor deben pasarse a la secuencia de comandos de inicio predeterminada. En el siguiente ejemplo, se muestra una secuencia de comandos de inicio personalizada que llama a la secuencia de comandos de inicio predeterminada:

#!/bin/bash

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

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

En tu Dockerfile, configura ENTRYPOINT para que llame a tu secuencia de comandos:

Java

FROM apache/beam_java8_sdk:2.56.0

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

Python

FROM apache/beam_python3.10_sdk:2.56.0

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

Go

FROM apache/beam_go_sdk:2.56.0

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

Compila y envía la imagen

Puedes usar Cloud Build o Docker para compilar tu imagen de contenedor y enviarla a un repositorio de Artifact Registry.

Cloud Build

Para compilar el archivo y enviarlo a tu repositorio de Artifact Registry, ejecuta el 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

Reemplaza lo siguiente:

  • REGION: la región en la que se implementará tu trabajo de Dataflow. El valor de la variable REGION debe ser un nombre de región válido.
  • PROJECT_ID: el nombre del proyecto o el nombre de usuario.
  • REPOSITORY es el nombre del repositorio de imágenes
  • FILE_NAME: es el nombre de tu Dockerfile.
  • TAG es la etiqueta de imagen Especifica siempre una etiqueta o un SHA de contenedor con control de versiones. No uses la etiqueta :latest ni una etiqueta mutable.

Instala previamente las dependencias de Python

Esta sección se aplica a las canalizaciones de Python.

Cuando inicias un trabajo de Dataflow de Python, puedes especificar dependencias adicionales con --requirements_file o la opción --extra_packages en el entorno de ejecución. Para obtener más información, consulta Administra las dependencias de canalización de Python. Se instalan dependencias adicionales en cada contenedor de trabajadores de Dataflow. Cuando el trabajo se inicia por primera vez y durante el ajuste de escala automático, la instalación de la dependencia a menudo genera un alto uso de CPU y un largo período de preparación en todos los trabajadores recién iniciados de Dataflow.

Para evitar las instalaciones de dependencias repetitivas, puedes compilar previamente la imagen de contenedor personalizada del SDK de Python con las dependencias preinstaladas. Puedes realizar este paso en el tiempo de compilación con un Dockerfile o en el tiempo de ejecución cuando envías el trabajo.

Los trabajadores crean un entorno de Python virtual nuevo cuando inician el contenedor. Por este motivo, instala las dependencias en el entorno predeterminado de Python (global) en lugar de crear un entorno virtual. Si activas un entorno virtual en la imagen de contenedor, es posible que este entorno no se active cuando se inicie el trabajo. Para obtener más información, consulta los problemas comunes.

Instala previamente con un Dockerfile

Para agregar dependencias adicionales directamente a tu contenedor personalizado de Python, usa los siguientes comandos:

FROM apache/beam_python3.10_sdk:2.56.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

Envía el trabajo con las opciones de canalización --sdk_container_image y --sdk_location. La opción --sdk_location evita que el SDK se descargue cuando se inicia tu trabajo. El SDK se recupera directamente de la imagen de contenedor.

En el siguiente ejemplo, se ejecuta la canalización de ejemplo 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

Reemplaza lo siguiente:

  • INPUT_FILE: un archivo de entrada para la canalización
  • OUTPUT_FILE: una ruta de acceso en la que se escribirá el resultado
  • PROJECT_ID: El ID del proyecto de Google Cloud
  • REGION: la región en la que se implementará tu trabajo de Dataflow
  • TEMP_LOCATION: la ruta de acceso de Cloud Storage para que Dataflow almacene en etapa intermedia los archivos de trabajo temporales
  • IMAGE_URI: el URI de la imagen de contenedor personalizada

Compila previamente una imagen de contenedor cuando envíes el trabajo

La compilación previa de una imagen de contenedor te permite instalar previamente las dependencias de la canalización antes del inicio del trabajo. No necesitas compilar una imagen de contenedor personalizada.

Para compilar previamente un contenedor con dependencias adicionales de Python cuando envías un trabajo, usa las siguientes opciones de canalización:

  • --prebuild_sdk_container_engine=[cloud_build | local_docker]. Cuando se establece esta marca, Apache Beam genera un contenedor personalizado y, además, instala todas las dependencias especificadas por --requirements_file y las opciones --extra_packages. Esta marca admite los siguientes valores:

    • cloud_build. Usa Cloud Build para compilar el contenedor. La API de Cloud Build debe estar habilitada en tu proyecto.
    • local_docker. Usa tu instalación local de Docker para compilar el contenedor.
  • --docker_registry_push_url=IMAGE_PATH. Reemplaza IMAGE_PATH por una carpeta de Artifact Registry.

  • --sdk_location=container. Esta opción evita que los trabajadores descarguen el SDK cuando se inicia tu trabajo. En cambio, el SDK se recupera directamente de la imagen de contenedor.

En el siguiente ejemplo, se usa Cloud Build para compilar previamente la imagen:

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

La función de compilación previa requiere el SDK de Apache Beam para la versión 2.25.0 o posterior de Python.

El flujo de trabajo de compilación previa de imagen de contenedor del SDK usa la imagen que se pasa con la opción de canalización --sdk_container_image como imagen base. Si la opción no está configurada, se usa de forma predeterminada una imagen de Apache Beam como imagen base.

Puedes reutilizar una imagen de contenedor del SDK de Python compilada previamente en otro trabajo con las mismas dependencias y la misma versión del SDK. Para volver a usar la imagen, pasa la URL de la imagen de contenedor compilada previamente al otro trabajo mediante la opción de canalización --sdk_container_image. Quita las opciones de dependencia --requirements_file, --extra_packages y --setup_file.

Si no planeas volver a utilizar la imagen, bórrala una vez que se complete el trabajo. Puedes borrar la imagen con gcloud CLI o en las páginas de Artifact Registry de la consola de Google Cloud.

Si la imagen se almacena en Artifact Registry, usa el comando artifacts docker images delete:

   gcloud artifacts docker images delete IMAGE --delete-tags

Problemas comunes

  • Si tu trabajo tiene dependencias adicionales de Python de una duplicación de PyPi privada, y un trabajo remoto de Cloud Build no puede extraerlo, intenta con la opción de Docker local o compila tu contenedor con un Dockerfile.

  • Si el trabajo de Cloud Build falla con docker exit code 137, este se quedó sin memoria, lo que podría deberse al tamaño de las dependencias que se están instalando. Para usar un tipo de máquina de trabajador de Cloud Build más grande, pasa --cloud_build_machine_type=machine_type, en el que machine_type es una de las siguientes opciones:

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

    De forma predeterminada, Cloud Build usa el tipo de máquina e2-medium.

  • En Apache Beam 2.44.0 y versiones posteriores, los trabajadores crea un entorno virtual cuando se inicia un contenedor personalizado. Si el contenedor crea su propio entorno virtual para instalar dependencias, esas dependencias se descartan. Este comportamiento puede causar errores como los siguientes:

    ModuleNotFoundError: No module named '<dependency name>'

    Para evitar este problema, instala las dependencias en el entorno predeterminado de Python (global). Como solución alternativa, inhabilita este comportamiento en Beam 2.48.0 y versiones posteriores a través de la configuración de la siguiente variable de entorno en tu imagen de contenedor:

    ENV RUN_PYTHON_SDK_IN_DEFAULT_ENVIRONMENT=1

Próximos pasos