在 Dataflow 中使用自定义容器

本页面介绍如何通过提供自定义容器映像来自定义 Dataflow 流水线中的用户代码的运行时环境。使用 Dataflow Runner v2 的流水线支持自定义容器。

Dataflow 在启动工作器虚拟机时,会使用 Docker 容器映像在工作器上启动容器化的 SDK 进程。您可以指定自定义容器映像,而不使用默认的 Apache Beam 映像。您指定自定义容器映像后,Dataflow 会启动拉取指定映像的工作器。以下是您可能需要使用自定义容器的一些原因:

  • 预安装流水线依赖项以减少工作器启动时间。
  • 预安装公共代码库中不可用的流水线依赖项。
  • 预先准备大型文件以减少工作器启动时间。
  • 在后台启动第三方软件。
  • 自定义执行环境。

如需深入了解自定义容器,请参阅 Apache Beam 自定义容器指南

准备工作

验证您是否已安装支持 Runner v2 和您的语言版本的 Apache Beam SDK 版本。

之后,您便可以使用以下流水线选项来启用自定义容器:

Java

使用 --experiments=use_runner_v2 启用 Runner v2。

使用 --sdkContainerImage 为您的 Java 运行时指定自定义容器映像。

Python

如果使用的是 2.30.0 或更高版本 SDK,请使用流水线选项 --sdk_container_image

对于旧版 SDK,请使用流水线选项 --worker_harness_container_image

如需了解详情,请参阅安装 Apache Beam SDK 指南。

如需在本地测试容器映像,您必须安装 Docker。如需了解详情,请参阅获取 Docker

默认 SDK 容器映像

我们建议从作为基础容器映像的默认 Apache Beam SDK 映像开始。默认映像作为 Apache Beam 版本的一部分发布到 DockerHub

创建和构建容器映像

本部分提供不同方式创建自定义 SDK 容器映像的示例。

自定义 SDK 容器映像必须满足以下要求:

  • 已安装 Apache Beam SDK 和必要的依赖项。
  • 默认 ENTRYPOINT 脚本(默认容器上的 /opt/apache/beam/boot)作为容器启动期间的最后一步运行。如需了解详情,请参阅修改容器入口点

使用 Apache Beam 基础映像

要创建自定义容器映像,请将 Apache Beam 映像指定为父映像,并添加您自己的自定义项。 如需详细了解如何编写 Dockerfile,请参阅编写 Dockerfile 的最佳做法

  1. 创建一个新的 Dockerfile,并使用 FROM 指令指定基础映像。

    Java

    在此示例中,我们结合使用 Java 8 和 Apache Beam SDK 2.34.0 版。

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

    自定义容器的运行时版本必须与您用于启动流水线的运行时一致。例如,如果您要从本地 Java 11 环境启动流水线,FROM 行必须指定 Java 11 环境:apache/beam_java11_sdk:...

    Python

    在此示例中,我们结合使用 Python 3.8 和 Apache Beam SDK 2.34.0 版。

    FROM apache/beam_python3.8_sdk:2.34.0
    
    # Make your customizations here, for example:
    ENV FOO=/bar
    COPY path/to/myfile ./
    

    自定义容器的运行时版本必须与您用于启动流水线的运行时一致。例如,如果您要从本地 Python 3.8 环境启动流水线,FROM 行必须指定 Python 3.8 环境:apache/beam_python3.8_sdk:...

  2. 构建子级映像并将此映像推送到 Container Registry。

    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
    

    替换以下内容:

    • PROJECT:项目名称或用户名。
    • REPO:映像代码库名称。
    • TAG:映像标记,通常为 latest

使用自定义基础映像或多阶段构建

如果您具有现有的基础映像,或者需要修改默认 Apache Beam 映像的某些基础方面(操作系统版本、补丁程序等),请使用多阶段构建流程从默认的 Apache Beam 基础映像中复制必要的工件,并提供您的自定义容器映像。

下面是一个从 Apache Beam SDK 复制文件的 Dockerfile 示例:

Java

FROM openjdk:8

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

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

Python

FROM python:3.8-slim

# Install SDK.
RUN pip install --no-cache-dir apache-beam[gcp]==2.34.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.8_sdk:2.34.0 /opt/apache/beam /opt/apache/beam

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

此示例假定现有基础映像上安装了必要的依赖项(在本例中为 Python 3.8 和 pip)。 在映像中安装 Apache Beam SDK 将确保映像具有必要的 SDK 依赖项,并缩短工作器启动时间。RUN 指令中指定的版本必须与启动流水线时使用的版本匹配。

修改容器入口点

自定义容器必须运行默认的 ENTRYPOINT 脚本 /opt/apache/beam/boot,该脚本将初始化工作器环境并启动 SDK 工作器进程。如果未设置此入口点,您的工作器将无法正确启动。

如果您需要在容器启动时运行自己的脚本,则必须确保您的映像 ENTRYPOINT 仍可正确启动此工作器 SDK 进程,包括将 Dataflow 参数传递给此脚本。

这意味着您的自定义 ENTRYPOINT 必须以运行 /opt/apache/beam/boot 结束,并且 Dataflow 在容器启动时传递的所有必需参数均已正确传递到默认启动脚本。这可以通过创建运行 /opt/apache/beam/boot 的自定义脚本来实现:

#!/bin/bash

echo "This is my custom script"

# ...

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

然后,替换默认的 ENTRYPOINT。示例 Dockerfile

Java

FROM apache/beam_java8_sdk:2.34.0

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

Python

FROM apache/beam_python3.8_sdk:2.34.0

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

使用自定义容器运行作业

本部分介绍如何使用自定义运行程序和本地运行程序在 Dataflow 上运行流水线以及进行测试。如果您已经验证了容器映像和流水线,请跳到启动 Dataflow 作业

准备工作

运行流水线时,请务必使用与自定义容器映像上的 SDK 相同的 Apache Beam SDK 版本和语言版本来启动流水线。这样可以避免不兼容的依赖项或 SDK 出现意外错误。

在本地测试

如需详细了解 Apache Beam 特定用法,请参阅有关使用自定义容器映像运行流水线的 Apache Beam 指南。

使用 PortableRunner 进行基本测试

测试是否可以拉取远程容器映像,并且至少可以使用 Apache Beam PortableRunner 运行简单的流水线。

下面运行了一个示例流水线:

Java

mvn compile exec:java -Dexec.mainClass=com.example.package.MyClassWithMain \
    -Dexec.args="--runner=PortableRunner \
    --job_endpoint=embed \
    --environment_type=DOCKER \
    --environment_config=IMAGE_URI \
    --inputFile=INPUT_FILE \
    --output=OUTPUT_FILE"

Python

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

替换以下内容:

  • IMAGE_URI:自定义容器映像 URI。如果上一步中构建的 shell 变量 $IMAGE_URI 仍然在适用范围内,您可以使用该变量。
  • INPUT_FILE:可以作为文本文件读取的输入文件。此文件必须可供 SDK 自动化测试框架容器映像访问
    (已在容器映像上已预加载或是远程文件)。
  • OUTPUT_FILE:要写入输出的文件路径。此路径可以是远程路径或容器上的本地路径。

流水线成功完成后,查看控制台日志以验证流水线是否成功完成以及是否使用了由 $IMAGE_URI 指定的远程映像

请注意,运行后,保存到容器的文件将不在本地文件系统中,并且容器将停止。使用 docker cp 可以从已停止的容器文件系统复制文件。

或者,您也可以使用:

  • 向 Cloud Storage 等远程文件系统提供输出。请注意,您可能需要手动配置访问权限以进行测试,包括凭据文件或应用默认凭据。
  • 添加临时日志记录以进行快速调试

使用 Direct Runner

如需对容器映像和流水线进行更深入的本地测试,请使用 Apache Beam DirectRunner

您可以独立于容器验证流水线,方法是在与容器映像匹配的本地环境中进行测试,或者在正在运行的容器上启动流水线。

Java

docker run -it --entrypoint "/bin/bash" $IMAGE_URI
...
# On docker container:
root@4f041a451ef3:/#  mvn compile exec:java -Dexec.mainClass=com.example.package.MyClassWithMain ...

Python

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

该示例假定所有流水线文件(包括流水线本身 )都位于自定义容器本身上、已从本地文件系统装载或者可供 Apache Beam 和容器远程访问。例如,要使用 Maven (mvn) 运行上面的 Java 示例,您必须将 Maven 及其依赖项暂存在容器上。如需了解详情,请参阅有关 Storagedocker run 的 Docker 文档。

请注意,DirectRunner 测试的目标是在自定义容器环境中测试流水线,而不是使用其默认的 ENTRYPOINT 实际运行容器来进行测试。修改 ENTRYPOINT(例如 docker run --entrypoint ...)以直接运行流水线,或者允许在容器上手动运行命令。

如果您依赖在 Compute Engine 上运行的特定配置,则可以直接在 Compute Engine 虚拟机上运行此容器。如需了解详情,请参阅 Compute Engine 上的容器

启动 Dataflow 作业

在 Dataflow 上启动 Apache Beam 流水线时,请指定容器映像的路径,如下所示:

Java

使用 --sdkContainerImage 为您的 Java 运行时指定自定义容器映像。

使用 --experiments=use_runner_v2 启用 Runner v2。

Python

如果使用的是 2.30.0 或更高版本 SDK,请使用流水线选项 --sdk_container_image

对于旧版 SDK,请使用流水线选项 --worker_harness_container_image

只有 Dataflow Runner v2 支持自定义容器。 如果您要启动批处理 Python 流水线,请设置 --experiments=use_runner_v2 标志。如果您要启动流处理 Python 流水线,则无需指定实验,因为流处理 Python 流水线默认使用 Runner v2。

以下示例演示了如何使用自定义容器启动批量 wordcount 示例。

Java

mvn compile exec:java -Dexec.mainClass=org.apache.beam.examples.WordCount \
   -Dexec.args="--runner=DataflowRunner \
                --inputFile=INPUT_FILE \
                --output=OUTPUT_FILE \
                --project=PROJECT_ID \
                --region=REGION \
                --gcpTempLocation=TEMP_LOCATION \
                --diskSizeGb=DISK_SIZE_GB \
                --experiments=use_runner_v2 \
                --sdkContainerImage=$IMAGE_URI"

Python

使用 Python 版 Apache Beam SDK 2.30.0 或更高版本:

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

替换以下内容:

  • INPUT_FILE:运行示例时,Dataflow 读取的 Cloud Storage 输入文件路径。
  • OUTPUT_FILE:示例流水线写入的 Cloud Storage 输出文件路径。其中包含字数统计。
  • PROJECT_ID:您的 Google Cloud 项目的 ID。
  • REGION:用于部署 Dataflow 作业的区域端点。
  • TEMP_LOCATION:供 Dataflow 暂存流水线执行期间创建的临时作业文件的 Cloud Storage 路径。
  • DISK_SIZE_GB(可选):如果您的容器较大,请考虑增加默认启动磁盘大小,避免耗尽磁盘可用空间
  • $IMAGE_URI:自定义容器映像 URI。如果上一步中构建的 shell 变量 $IMAGE_URI 仍然在适用范围内,您可以使用该变量。

问题排查

本部分介绍如何排查在 Dataflow 中使用自定义容器时出现的问题。它侧重于容器或工作器无法启动的问题。如果工作器能够启动且工作正在进行,请按照排查流水线问题中的一般指南进行操作。

在联系支持团队之前,请确保您已排除与容器映像相关的问题:

  • 按照各步骤操作以在本地测试容器映像
  • 作业日志工作器日志中搜索错误,并将找到的所有错误与常见错误指南进行比较。
  • 确保用于启动流水线的 Apache Beam SDK 版本和语言版本与自定义容器映像上的 SDK 匹配。
  • 如果使用 Java,请确保用于启动流水线的 Java 主要版本与容器映像中安装的版本匹配。
  • 如果使用的是 Python,请确保用于启动流水线的 Python 主要版本和次要版本与容器映像中安装的版本匹配,并且映像不存在冲突的依赖项。您可以运行 pip check 进行确认。

查找与自定义容器相关的工作器日志

您可以使用日志浏览器找到与容器相关的错误消息的 Dataflow 工作器日志:

  1. 选择日志名称。自定义容器启动错误最有可能是以下项目之一:

    • dataflow.googleapis.com/docker
    • dataflow.googleapis.com/kubelet
    • dataflow.googleapis.com/worker-startup
  2. 选择 Dataflow Step 资源并指定 job_id

特别是,如果您看到 Error Syncing pod... 日志消息,则应该遵循常见的错误指南。您可以使用日志浏览器 通过以下查询在 Dataflow 工作器日志中查询这些日志消息:

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

常见问题

作业有错误或失败,因为无法拉取容器映像

自定义容器映像必须可供 Dataflow 工作器访问。如果工作器因无效的网址、错误配置的凭据或缺少网络访问权限而无法拉取映像,则工作器将无法启动。

对于未启动任何工作且多个工作器无法按顺序启动的批处理作业,Dataflow 会使作业失败。否则,Dataflow 会记录错误,但不会采取进一步的操作来避免破坏长时间运行的作业状态。

请按照常见错误指南执行操作,来确定此问题的原因和解决方法。

工作器无法启动或工作没有进展

在某些情况下,如果 SDK 容器因某种错误而无法启动,Dataflow 将无法确定错误是永久性的还是严重的,并且它会在失败时不断尝试重启工作器。

工作器日志 中查找特定错误,并查看常见错误指南

如果没有明显错误,但您在 dataflow.googleapis.com/kubelet 中看到 [topologymanager] RemoveContainer INFO 级日志,则这些日志指示自定义容器映像提前退出,并且未启动长时间运行的工作器 SDK 进程。如果自定义 ENTRYPOINT 未启动默认启动脚本 /opt/apache/beam/boot,或者未正确将参数传递给此脚本,则可能会发生这种情况。请参阅修改自定义 ENTRYPOINT