Usa contenedores personalizados en Dataflow

En esta página, se describe cómo personalizar el entorno de ejecución del código de usuario en las canalizaciones de Dataflow mediante una imagen de contenedor personalizada. Los contenedores personalizados son compatibles con las canalizaciones que usan Runner v2 de Dataflow.

Cuando Dataflow inicia las VM de trabajador, usa imágenes de contenedor de Docker para iniciar procesos del SDK en contenedores en los trabajadores. Puedes especificar una imagen de contenedor personalizada en lugar de usar una de las imágenes predeterminadas de Apache Beam. Cuando especificas una imagen de contenedor personalizada, Dataflow inicia los trabajadores que extraen la imagen especificada. Estas son algunas razones por las que puedes usar un contenedor personalizado:

  • Preinstalación de las dependencias de canalizaciones para reducir la hora de inicio de los trabajadores.
  • Preinstalación de las dependencias de canalizaciones que no están disponibles en los repositorios públicos.
  • Realización de una etapa de pruebas de archivos grandes para reducir la hora de inicio de los trabajadores.
  • Lanzamiento del software de terceros en segundo plano.
  • Personaliza el entorno de ejecución

Para obtener una descripción más detallada de los contenedores personalizados, consulta la guía de contenedores personalizados de Apache Beam.

Antes de comenzar

Verifica que hayas instalado una versión del SDK de Apache Beam que sea compatible con Runner v2 y tu versión de lenguaje.

Luego, puedes usar las siguientes opciones de canalización para habilitar contenedores personalizados:

Java

Usa --experiments=use_runner_v2 para habilitar Runner v2.

Usa --sdkContainerImage a fin de especificar una imagen de contenedor de cliente para tu entorno de ejecución de Java.

Python

Si usas la versión del SDK 2.30.0 o posterior, usa la opción de canalización --sdk_container_image.

Para versiones anteriores del SDK, usa la opción de canalización --worker_harness_container_image.

Para obtener más información, consulta la guía para instalar el SDK de Apache Beam.

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

Imágenes de contenedor del SDK predeterminadas

Recomendamos comenzar con una imagen predeterminada del SDK de Apache Beam como una imagen base de contenedor. Las imágenes predeterminadas se lanzan como parte de las actualizaciones de Apache Beam en DockerHub.

Crea y compila la imagen de contenedor

En esta sección, se proporcionan ejemplos de diferentes formas de crear una imagen de contenedor del SDK personalizada que cumpla con el contrato de contenedor del SDK de Apache Beam.

Una imagen de contenedor del SDK personalizada debe tener lo siguiente:

  • Se instalan el SDK de Apache Beam y las dependencias necesarias.
  • La secuencia de comandos ENTRYPOINT predeterminada (/opt/apache/beam/boot en los contenedores predeterminados) se ejecuta como el último paso durante el inicio del contenedor. Consulta Personaliza el punto de entrada para obtener más información.

Usa la imagen base de Apache Beam

En este ejemplo, usamos Python 3.8 con la versión 2.33.0 del SDK de Apache Beam. Para crear una imagen de contenedor personalizada, especifica la imagen de Apache Beam como la imagen superior y agrega tus propias personalizaciones.

  1. Crea un objeto Dockerfile nuevo, especifica el apache/beam_python3.8_sdk:2.33.0 como superior y agrega las personalizaciones. Si deseas obtener más información sobre cómo escribir Dockerfiles, consulta Prácticas recomendadas para escribir Dockerfiles.

    FROM apache/beam_python3.8_sdk:2.33.0
    
    # Make your customizations here, for example:
    ENV FOO=/bar
    COPY path/to/myfile ./
    
  2. Compila la imagen secundaria y envía esta imagen a un registro de contenedores.

    Cloud Build

    export PROJECT=PROJECT
    export REPO=REPO
    export TAG=TAG
    export IMAGE_URI=gcr.io/$PROJECT/$REPO:$TAG
    
    gcloud builds submit . --tag $IMAGE_URI
    

    Docker

    export PROJECT=PROJECT
    export REPO=REPO
    export TAG=TAG
    export IMAGE_URI=gcr.io/$PROJECT/$REPO:$TAG
    
    docker build . --tag $IMAGE_URI
    docker push $IMAGE_URI
    

    Reemplaza lo siguiente:

    • PROJECT es el nombre del proyecto o el nombre de usuario
    • REPO es el nombre del repositorio de imágenes
    • TAG: es la etiqueta de imagen, que suele ser latest.

Usa una imagen base personalizada o compilaciones de varias etapas

Si tienes una imagen base existente o necesitas modificar algún aspecto de las imágenes predeterminadas de Apache Beam (versión de SO, parches, etc.), usa un proceso de compilación de varias etapas para copiar los artefactos necesarios de una imagen base predeterminada de Apache Beam y proporcionar tu imagen de contenedor personalizada.

Este es un Dockerfile de ejemplo que copia los archivos del SDK de Apache Beam para Python:

FROM python:3.8-slim

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

# Copy files from official SDK image, including script/dependencies
COPY --from=apache/beam_python3.8_sdk:2.33.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.8 y pip) en la imagen base existente.

Modifica el punto de entrada del contenedor

Los contenedores personalizados deben ejecutar la secuencia de comandos predeterminada ENTRYPOINT /opt/apache/beam/boot, que inicializa el entorno del trabajador y, luego, inicia el proceso de trabajador del SDK. Si no configuras este punto de entrada, el trabajador se detendrá y nunca se iniciará de forma correcta.

Si necesitas ejecutar tu propia secuencia de comandos cuando se inicia el contenedor, debes asegurarte de que tu imagen ENTRYPOINT aún inicie de forma correcta este proceso del SDK de trabajador, incluida la transmisión de argumentos de Dataflow a esta secuencia de comandos.

Esto significa que tu ENTRYPOINT personalizado debe terminar con la ejecución de /opt/apache/beam/boot y que todos los argumentos necesarios que pase Dataflow durante el inicio del contenedor se pasan de forma correcta a la secuencia de comandos de inicio predeterminada. Esto se puede hacer mediante la creación de una secuencia de comandos personalizada que ejecute /opt/apache/beam/boot:

#!/bin/bash

echo "This is my custom script"

# ...

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

Luego, anula el ENTRYPOINT predeterminado. Un ejemplo Dockerfile:

FROM apache/beam_python3.8_sdk:2.33.0

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

Ejecuta un trabajo con contenedores personalizados

En esta sección, se analiza la ejecución de canalizaciones con contenedores personalizados mediante ejecutores locales para realizar pruebas y en Dataflow. Ve a Inicia el trabajo de Dataflow si ya verificaste la imagen de contenedor y la canalización.

Antes de comenzar

Cuando ejecutes tu canalización, asegúrate de iniciarla mediante el SDK de Apache Beam con la misma versión (p. ej., 2.XX.0) y la versión del idioma (p. ej., Python 3.X) que el SDK en tu imagen de contenedor personalizada. Esto evita errores inesperados de dependencias o SDK incompatibles.

Pruebas locales

Si deseas obtener más información sobre el uso específico de Apache Beam, consulta la guía de Apache Beam para ejecutar canalizaciones con imágenes de contenedor personalizadas.

Pruebas básicas con PortableRunner

Prueba que las imágenes de contenedor remotas se puedan extraer y que puedan ejecutar al menos una canalización simple con PortableRunner de Apache Beam.

A continuación, se ejecuta un ejemplo simple de wordcount de Apache Beam:

python path/to/my/pipeline.py \
  --runner=PortableRunner \
  --job_endpoint=embed \
  --environment_type=DOCKER \
  --environment_config=<var>$IMAGE_URI</var> \
  --input=<var>INPUT_FILE</var> \
  --output=<var>OUTPUT_FILE</var>

Reemplaza lo siguiente:

  • IMAGE_URI: El URI de la imagen del contenedor personalizado. Puedes usar la variable de shell $IMAGE_URI construida en el paso anterior si la variable todavía está dentro del alcance.
  • INPUT_FILE: es un archivo de entrada que se puede leer como un archivo de texto. La imagen del contenedor
    del aprovechamiento del SDK debe poder acceder a este archivo, ya sea precargado en la imagen de contenedor o en un archivo remoto.
  • OUTPUT_FILE: es una ruta de acceso del archivo a la que se escribirá el resultado. Esta ruta es una ruta de acceso remota o local en el contenedor.

Una vez que la canalización se haya completado de forma correcta, consulta los registros de la consola para verificar que la canalización se haya completado correctamente y que se haya usado la imagen remota especificada por $IMAGE_URI.

Ten en cuenta que después de ejecutarse, los archivos guardados en el contenedor no estarán en tu sistema de archivos local y el contenedor se detendrá. Los archivos se pueden copiar desde el sistema de archivos del contenedor detenido docker cp.

Como alternativa: * proporciona resultados en sistemas de archivos remotos, como Cloud Storage. Ten en cuenta que esto puede requerir la configuración manual del acceso con fines de prueba, incluidos los archivos de credenciales o las credenciales predeterminadas de la aplicación. * Agrega registro temporal para una depuración rápida.

Usa el ejecutor directo

Para realizar pruebas locales más detalladas de la imagen de contenedor y tu canalización, usa DirectRunner de Apache Beam.

Puedes verificar la canalización por separado del contenedor mediante la prueba en un entorno local que coincida con la imagen de contenedor o si inicias la canalización en un contenedor en ejecución.

docker run -it --entrypoint "/bin/bash" $IMAGE_URI
...
# On docker container:
root@4f041a451ef3:/#  python path/to/my/pipeline.py ...

En este ejemplo, se supone que todos los archivos de canalización (incluida la canalización path/to/my/pipeline.py) se encuentra en el contenedor personalizado, se activaron desde un sistema de archivos local, o son remotos y accesibles mediante Apache Beam y el contenedor. Consulta la documentación de Docker sobre Storage y docker run para obtener más información.

Ten en cuenta que el objetivo de las pruebas de DirectRunner es probar la canalización en el entorno del contenedor personalizado, no probar ejecutar el contenedor con el ENTRYPOINT predeterminado. Modifica ENTRYPOINT (p.ej., docker run --entrypoint ...) para ejecutar directamente tu canalización o permitir que se ejecuten comandos en el contenedor de forma manual.

Si dependes de la configuración específica que se ejecuta en Compute Engine, puedes ejecutar este contenedor directamente en una VM de Compute Engine. Consulta Contenedores en Compute Engine para obtener más información.

Inicia el trabajo de Dataflow

Especifica la ruta a la imagen de contenedor cuando inicies la canalización de Apache Beam en Dataflow mediante la opción de canalización --sdk_container_image.

Los contenedores personalizados solo son compatibles con Dataflow Runner v2. Si inicias una canalización de Python por lotes, configura la marca --experiment=use_runner_v2. Si vas a iniciar una canalización de Python de transmisión, no es necesario especificar el experimento, ya que las canalizaciones de transmisión de Python usan Runner v2 de forma predeterminada.

En el siguiente ejemplo, se muestra cómo iniciar el ejemplo de wordcount por lotes con un contenedor personalizado mediante el SDK de Apache Beam para la versión 2.30.0 de Python o una posterior:

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 \
  --experiment=use_runner_v2 \
  --sdk_container_image=$IMAGE_URI

Reemplaza lo siguiente:

  • INPUT_FILE: la ruta de acceso del archivo de entrada de Cloud Storage que Dataflow lee cuando ejecuta el ejemplo.
  • OUTPUT_FILE: es la ruta de acceso del archivo de salida de Cloud Storage en la que se escribe la canalización de ejemplo. Esto contendrá los conteos de palabras.
  • PROJECT_ID es el ID de tu proyecto de Google Cloud.
  • REGION es el extremo regional para implementar tu trabajo de Dataflow.
  • TEMP_LOCATION es una ruta de acceso de Cloud Storage para que Dataflow almacene en etapa intermedia los archivos de trabajo temporales creados durante la ejecución de la canalización
  • $IMAGE_URI: El URI de la imagen del contenedor personalizado. Puedes usar la variable de shell $IMAGE_URI construida en el paso anterior si la variable todavía está dentro del alcance.
  • $DISK_SIZE_GB: Si tu contenedor es grande, considera aumentar el tamaño de disco de arranque predeterminado para evitar que se quede sin espacio en el disco (opcional).

Soluciona problemas

En esta sección, se proporcionan instrucciones para solucionar problemas relacionados con el uso de contenedores personalizados en Dataflow. Se centra en los problemas con contenedores o trabajadores que no se inician. Si tus trabajadores pueden iniciar y el trabajo avanza, sigue las instrucciones generales para solucionar problemas de tu canalización.

Esta guía se centra en los problemas de los trabajadores que no se inician, ya que la mayoría de los problemas con contenedores personalizados hará que el contenedor no se inicie y bloquee a los trabajadores para que inicien su trabajo.

Si el trabajo avanza en tu canalización, sigue las instrucciones generales para solucionar problemas de tu canalización.

Antes de comunicarte con el equipo de asistencia, asegúrate de haber descartado los problemas relacionados con la imagen de contenedor:

  • Sigue los pasos para probar la imagen de contenedor de forma local.
  • Busca errores en los Registros de trabajos o en los Registros de trabajadores y compara cualquier error encontrado con lo especificado en la guía Errores comunes.
  • Asegúrate de que la versión del SDK de Apache Beam y la versión del idioma que usas para iniciar la canalización coincidan con el SDK en tu imagen de contenedor personalizada.
  • Si usas Python, asegúrate de que la versión principal y secundaria de Python que usas para iniciar la canalización coincida con la versión instalada en la imagen de contenedor y que la imagen no tenga dependencias en conflicto. Puedes ejecutar pip check{.external} para confirmar.

Busca registros de trabajador relacionados con contenedores personalizados

Los registros de trabajador de Dataflow para los mensajes de error relacionados con contenedores se pueden encontrar mediante el Explorador de registros:

  1. Selecciona nombres de registro. Los errores de inicio de los contenedores personalizados tienen más probabilidades de ocurrir en alguno de los siguientes supuestos:

    • dataflow.googleapis.com/docker
    • dataflow.googleapis.com/kubelet
    • dataflow.googleapis.com/worker-startup
  2. Selecciona el recurso Dataflow Step y especifica job_id.

En particular, si ves mensajes de registro Error Syncing pod..., debes seguir la guía de errores comunes. Puedes consultar estos mensajes de registro en los registros de trabajador de Dataflow mediante el Explorador de registros con la siguiente consulta:

resource.type="dataflow_step" AND jsonPayload.message:("$IMAGE_URI") AND severity="ERROR"

Problemas comunes

El trabajo tiene errores o falló porque no se puede extraer la imagen de contenedor

Los trabajadores de Dataflow deben poder acceder a las imágenes de contenedor personalizadas. Si el trabajador no puede extraer la imagen debido a URL no válidas, credenciales mal configuradas o falta de acceso a la red, el trabajador no se iniciará.

En los trabajos por lotes en los que no se inició ningún trabajo y no se pueden iniciar varios trabajadores de forma secuencial, Dataflow no puede realizar el trabajo. De lo contrario, Dataflow registra los errores, pero no realiza ninguna acción para evitar destruir el estado del trabajo de larga duración.

Sigue la guía de errores comunes para determinar por qué y cómo solucionar este problema.

Los trabajadores no se inician o el trabajo no progresa

En algunos casos, si el contenedor del SDK no se inicia debido a algún error, Dataflow no puede determinar si el error es permanente o grave, y trata de reiniciar el trabajador de forma continua a medida que falla.

Encuentra errores específicos en {:#worker-logs} y consulta la guía de errores comunes.

Si no hay errores evidentes, pero ves registros de nivel INFO de [topologymanager] RemoveContainer en dataflow.googleapis.com/kubelet, estos registros indican que la imagen de contenedor personalizada sale antes y no inició el proceso del SDK de trabajador de larga duración. Esto puede suceder si una ENTRYPOINT personalizada no inicia la secuencia de comandos de inicio predeterminada /opt/apache/beam/boot o no pasó argumentos de forma adecuada a esta secuencia de comandos. Consulta Modifica la ENTRYPOINT personalizada.