容器环境

Beam SDK 运行时环境可以使用 容器化Docker 隔离它与其他运行时系统。要了解有关容器环境的更多信息,请阅读 Beam SDK 运行容器契约

预构建的 SDK 容器镜像在 Beam 版本发布时根据支持的语言发布并推送到 Docker Hub

自定义容器

您可能出于多种原因想要自定义容器镜像,包括

本指南介绍如何为 Beam SDK 创建和使用自定义容器。

先决条件

注意:2020 年 11 月 20 日,Docker Hub 对匿名和免费经过身份验证的使用实施了 速率限制,这可能会影响多次拉取容器的大型管道。

为了获得最佳的用户体验,我们还建议您使用 Beam 的最新发布版本。

构建和推送自定义容器

Beam SDK 容器镜像 是从签入 Github 存储库的 Dockerfile 构建的,并在每次发布时发布到 Docker Hub。您可以通过以下三种方式之一构建自定义容器

  1. 编写新的 基于已发布容器镜像的 Dockerfile。这足以满足对镜像的简单添加,例如添加工件或环境变量。
  2. 修改 Beam 中的源 Dockerfile。此方法需要从 Beam 源代码构建,但允许更大程度地自定义容器(包括替换工件或基本操作系统/语言版本)。
  3. 修改 现有容器镜像以使其与 Apache Beam 运行器兼容。当用户从现有镜像开始并配置镜像以使其与 Apache Beam 运行器兼容时,使用此方法。

编写基于现有发布的容器镜像的新 Dockerfile

  1. 创建一个新的 Dockerfile,使用 FROM 指令 指定基本镜像。
FROM apache/beam_python3.7_sdk:2.25.0

ENV FOO=bar
COPY /src/path/to/file /dest/path/to/file/

Dockerfile 使用预构建的 Python 3.7 SDK 容器镜像 beam_python3.7_sdk,标签为(SDK 版本)2.25.0,并将其他环境变量和文件添加到镜像中。

  1. 使用 Docker 构建推送 镜像。
export BASE_IMAGE="apache/beam_python3.7_sdk:2.25.0"
export IMAGE_NAME="myremoterepo/mybeamsdk"
# Avoid using `latest` with custom containers to make reproducing failures easier.
export TAG="mybeamsdk-versioned-tag"

# Optional - pull the base image into your local Docker daemon to ensure
# you have the most up-to-date version of the base image locally.
docker pull "${BASE_IMAGE}"

docker build -f Dockerfile -t "${IMAGE_NAME}:${TAG}" .
  1. 如果您的运行器在远程运行,请重新标记并将镜像 推送到 适当的存储库。
docker push "${IMAGE_NAME}:${TAG}"
  1. 推送容器镜像后,验证远程镜像 ID 和摘要是否与本地镜像 ID 和摘要匹配,本地镜像 ID 和摘要是在 docker builddocker images 的输出中。

修改 Beam 中的源 Dockerfile

此方法需要从 Beam 源代码构建镜像工件。有关设置开发环境的更多说明,请参阅 贡献指南

注意:建议您从与要运行管道的 SDK 版本相同的稳定发布分支 (release-X.XX.X) 开始。SDK 版本的不同可能会导致意外错误。

  1. 克隆 beam 存储库。
export BEAM_SDK_VERSION="2.26.0"
git clone https://github.com/apache/beam.git
cd beam

# Save current directory as working directory
export BEAM_WORKDIR=$PWD

git checkout origin/release-$BEAM_SDK_VERSION
  1. 自定义特定语言的 Dockerfile,通常是 sdks/<language>/container/Dockerfile 目录(例如,Python 的 Dockerfile)。

  2. 返回到根 Beam 目录并运行 Gradle docker 目标以构建您的镜像。

cd $BEAM_WORKDIR

# The default repository of each SDK
./gradlew :sdks:java:container:java11:docker
./gradlew :sdks:java:container:java17:docker
./gradlew :sdks:java:container:java21:docker
./gradlew :sdks:go:container:docker
./gradlew :sdks:python:container:py39:docker
./gradlew :sdks:python:container:py310:docker
./gradlew :sdks:python:container:py311:docker
./gradlew :sdks:python:container:py312:docker

# Shortcut for building all Python SDKs
./gradlew :sdks:python:container:buildAll
  1. 通过运行 docker images 验证您构建的镜像是否已创建。
$> docker images --digests
REPOSITORY                         TAG                  DIGEST                   IMAGE ID         CREATED           SIZE
apache/beam_java8_sdk              latest               sha256:...               ...              1 min ago         ...
apache/beam_java11_sdk             latest               sha256:...               ...              1 min ago         ...
apache/beam_java17_sdk             latest               sha256:...               ...              1 min ago         ...
apache/beam_python3.6_sdk          latest               sha256:...               ...              1 min ago         ...
apache/beam_python3.7_sdk          latest               sha256:...               ...              1 min ago         ...
apache/beam_python3.8_sdk          latest               sha256:...               ...              1 min ago         ...
apache/beam_python3.9_sdk          latest               sha256:...               ...              1 min ago         ...
apache/beam_python3.10_sdk          latest               sha256:...               ...              1 min ago         ...
apache/beam_go_sdk                 latest               sha256:...               ...              1 min ago         ...
  1. 如果您的运行器在远程运行,请重新标记镜像并将镜像 推送到 您的存储库。如果您提供了自定义存储库/标签作为 其他参数,您可以跳过此步骤。
export BEAM_SDK_VERSION="2.26.0"
export IMAGE_NAME="gcr.io/my-gcp-project/beam_python3.7_sdk"
export TAG="${BEAM_SDK_VERSION}-custom"

docker tag apache/beam_python3.7_sdk "${IMAGE_NAME}:${TAG}"
docker push "${IMAGE_NAME}:${TAG}"
  1. 推送容器镜像后,验证远程镜像 ID 和摘要是否与 docker_images --digests 输出的本地镜像 ID 和摘要匹配。

其他构建参数

docker Gradle 任务定义了默认镜像存储库,并且 标签 是在 gradle.properties 中定义的 SDK 版本。默认存储库是 Docker Hub 的 apache 命名空间,默认标签是在 gradle.properties 中定义的 SDK 版本

您可以通过向构建任务提供参数来指定构建镜像的不同存储库或标签。例如

./gradlew :sdks:python:container:py36:docker -Pdocker-repository-root="example-repo" -Pdocker-tag="2.26.0-custom"

构建 Python 3.6 容器并将其标记为 example-repo/beam_python3.6_sdk:2.26.0-custom

从 Beam 2.21.0 开始,引入了 docker-pull-licenses 标志,用于将第三方依赖项的许可证/通知添加到 docker 镜像中。例如

./gradlew :sdks:java:container:java11:docker -Pdocker-pull-licenses

/opt/apache/beam/third_party_licenses/ 中创建具有适当许可证的 Java 11 SDK 镜像。

默认情况下,不会将许可证/通知添加到 docker 镜像中。

修改现有容器镜像以使其与 Apache Beam 运行器兼容

Beam 提供了一种使用自定义容器镜像的方法。构建与 Apache Beam 运行器兼容的自定义镜像最简单的方法是使用多阶段构建过程。这将从默认的 Apache Beam 基础镜像中复制必要的工件,以构建您的自定义容器镜像。

  1. 从 Apache Beam 基础镜像复制必要的工件到您的镜像。
# This can be any container image,
FROM python:3.8-bookworm

# Install SDK. (needed for Python SDK)
RUN pip install --no-cache-dir apache-beam[gcp]==2.52.0

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

# Perform any additional customizations if desired

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

注意:此示例假设在现有基础镜像上已经安装了必要的依赖项(在本例中为 Python 3.8 和 pip)。将 Apache Beam SDK 安装到镜像中将确保镜像具有必要的 SDK 依赖项,并减少工作程序启动时间。在RUN指令中指定的版本必须与启动管道使用的版本匹配。
确保基础镜像中指定的 Python 或 Java 运行时版本与运行管道使用的版本相同。

注意:任何额外的 Python 依赖项都应该安装在自定义镜像中的全局 Python 环境中。

  1. 使用 Docker 构建推送 镜像。
  export BASE_IMAGE="apache/beam_python3.8_sdk:2.52.0"
  export IMAGE_NAME="myremoterepo/mybeamsdk"
  export TAG="latest"

  # Optional - pull the base image into your local Docker daemon to ensure
  # you have the most up-to-date version of the base image locally.
  docker pull "${BASE_IMAGE}"

  docker build -f Dockerfile -t "${IMAGE_NAME}:${TAG}" .
  1. 如果您的运行器在远程运行,请重新标记镜像并推送镜像到您的仓库。
docker push "${IMAGE_NAME}:${TAG}"

从头开始构建兼容的容器镜像(Go)

从 2.55.0 版本开始,Beam Go SDK 已切换到使用distroless 镜像作为基础。这些镜像通过不包含常见工具和实用程序来减少安全攻击面。这可能会导致使用上述方法之一自定义镜像时出现困难。作为一种回退方法,可以通过构建匹配的引导加载程序并将其设置为容器的入口点,从头开始构建自定义镜像。

例如,如果更倾向于使用 alpine 作为容器操作系统,您的多阶段 docker 文件可能如下所示

FROM golang:latest-alpine AS build_base

# Set the Current Working Directory inside the container
WORKDIR /tmp/beam

# Build the Beam Go bootloader, to the local directory, matching your Beam version.
# Similar go targets exist for other SDK languages.
RUN GOBIN=`pwd` go install github.com/apache/beam/sdks/v2/go/container@v2.53.0

# Set the real base image.
FROM alpine:3.9
RUN apk add ca-certificates

# The following are required for the container to operate correctly.
# Copy the boot loader `container` to the image.
COPY --from=build_base /tmp/beam/container /opt/apache/beam/boot

# Set the container to use the newly built boot loader.
ENTRYPOINT ["/opt/apache/beam/boot"]

如上修改现有基础镜像时一样,构建并推送新镜像。

注意:Java 和 Python 需要额外的依赖项,例如它们的运行时和 SDK 包,才能形成有效的容器镜像。引导加载程序不足以创建这些 SDK 的自定义容器。

使用自定义容器镜像运行管道

提供容器镜像的常见方法需要使用 PortableRunner 标志--environment_config,如 PortableRunner 或支持 PortableRunner 标志的运行器所支持。其他运行器,例如 Dataflow,支持使用不同的标志指定容器。

export IMAGE="my-repo/beam_python_sdk_custom"
export TAG="X.Y.Z"
export IMAGE_URL="${IMAGE}:${TAG}"

python -m apache_beam.examples.wordcount \
--input=/path/to/inputfile \
--output /path/to/write/counts \
--runner=PortableRunner \
--job_endpoint=embed \
--environment_type="DOCKER" \
--environment_config="${IMAGE_URL}"
export IMAGE="my-repo/beam_python_sdk_custom"
export TAG="X.Y.Z"
export IMAGE_URL = "${IMAGE}:${TAG}"

# Run a pipeline using the SparkRunner which starts the Spark job server
python -m apache_beam.examples.wordcount \
--input=/path/to/inputfile \
--output=path/to/write/counts \
--runner=SparkRunner \
# When running batch jobs locally, we need to reuse the container.
--environment_cache_millis=10000 \
--environment_type="DOCKER" \
--environment_config="${IMAGE_URL}"
export GCS_PATH="gs://my-gcs-bucket"
export GCP_PROJECT="my-gcp-project"
export REGION="us-central1"

# By default, the Dataflow runner has access to the GCR images
# under the same project.
export IMAGE="my-repo/beam_python_sdk_custom"
export TAG="X.Y.Z"
export IMAGE_URL = "${IMAGE}:${TAG}"

# Run a pipeline on Dataflow.
# This is a Python batch pipeline, so to run on Dataflow Runner V2
# you must specify the experiment "use_runner_v2"

python -m apache_beam.examples.wordcount \
  --input gs://dataflow-samples/shakespeare/kinglear.txt \
  --output "${GCS_PATH}/counts" \
  --runner DataflowRunner \
  --project $GCP_PROJECT \
  --region $REGION \
  --temp_location "${GCS_PATH}/tmp/" \
  --experiment=use_runner_v2 \
  --sdk_container_image=$IMAGE_URL

避免在自定义镜像中使用标签:latest。使用日期或唯一标识符标记您的构建。如果出现问题,使用这种类型的标签可能会使您能够将管道执行回滚到以前已知的工作配置,并允许您检查更改。

故障排除

以下部分描述了一些在使用自定义容器运行 Beam 管道时遇到意外错误时需要考虑的常见问题。