Dataproc on GKE 自定义容器映像

您可以指定要用于 Dataproc on GKE 的自定义容器映像。您的自定义容器映像必须使用其中一个 Dataproc on GKE 基础 Spark 映像

使用自定义容器映像

如需使用 Dataproc on GKE 自定义容器映像,请在创建 Dataproc on GKE 虚拟集群或向集群提交 Spark 作业时设置 spark.kubernetes.container.image property

  • gcloud CLI 集群创建示例:
    gcloud dataproc clusters gke create "${DP_CLUSTER}" \
        --properties=spark:spark.kubernetes.container.image=custom-image \
        ... other args ...
    
  • gcloud CLI 作业提交示例:
    gcloud dataproc jobs submit spark \
        --properties=spark.kubernetes.container.image=custom-image \
        ... other args ...
    

自定义容器映像要求和设置

基础映像

您可以使用 docker 工具,根据已发布的 Dataproc on GKE 基础 Spark 映像构建自定义 Docker。

容器用户

Dataproc on GKE 以 Linux spark 用户身份运行 Spark 容器,具有 1099 UID 和 1099 GID。使用 UID 和 GID 获得文件系统权限。 例如,如果您在映像的 /opt/spark/jars/my-lib.jar 处添加一个 jar 文件作为工作负载依赖项,则必须向 spark 用户授予该文件的读取权限。

组件

  • JavaJAVA_HOME 环境变量指向 Java 的安装位置。当前默认值为 /usr/lib/jvm/adoptopenjdk-8-hotspot-amd64,可能会发生变化(如需了解更新后的信息,请参阅 Dataproc 版本说明)。

    • 如果您自定义 Java 环境,请确保 JAVA_HOME 设置为正确的位置,并且 PATH 包含二进制文件的路径。
  • Python:GKE 上的 Dataproc 基础 Spark 映像已将 Miniconda3 安装在 /opt/condaCONDA_HOME 指向此位置,${CONDA_HOME}/bin 包含在 PATH 中,并且 PYSPARK_PYTHON 设置为 ${CONDA_HOME}/python

    • 如果您自定义 Conda,请确保 CONDA_HOME 指向 Conda 主目录,${CONDA_HOME}/bin 包含在 PATH 中,并且将 PYSPARK_PYTHON 设置为 ${CONDA_HOME}/python.

    • 您可以在默认基本环境中安装、移除和更新软件包,也可以创建新环境,但强烈建议该环境包含在基础容器映像的基础环境中安装的所有软件包。

    • 如果将 Python 模块(例如带有实用函数的 Python 脚本)添加到容器映像,请将模块目录包含在 PYTHONPATH 中。

  • Spark:Spark 安装在 /usr/lib/spark 中,并且 SPARK_HOME 指向此位置。Spark 无法自定义。如果更改,则容器映像将被拒绝或无法正常运行。

    • 作业:您可以自定义 Spark 作业依赖项。SPARK_EXTRA_CLASSPATH 定义了 Spark JVM 进程的额外类路径。建议:将 jar 放在 /opt/spark/jars 下,并将 SPARK_EXTRA_CLASSPATH 设置为 /opt/spark/jars/*

      如果您在映像中嵌入作业 jar,则推荐的目录为 /opt/spark/job。提交作业时,您可以使用本地路径(例如 file:///opt/spark/job/my-spark-job.jar)引用该作业。

    • Cloud Storage 连接器:Cloud Storage 连接器安装在 /usr/lib/spark/jars 中。

    • 实用程序:运行 Spark 需要 procpstini 实用程序软件包。这些实用程序包含在基础 Spark 映像中,因此自定义映像无需重新安装它们。

    • 入口点GKE 上的 Dataproc 会忽略对容器映像中的 ENTRYPOINTCMD 原语所做的任何更改。

    • 初始化脚本:您可以在 /opt/init-script.sh 中添加可选的初始化脚本。初始化脚本可以从 Cloud Storage 下载文件、在容器内启动代理、调用其他脚本,以及执行其他启动任务。

      在启动 Spark 驱动程序、Spark 执行器和其他进程之前,入口点脚本会使用所有命令行参数 ($@) 调用初始化脚本。初始化脚本可以根据第一个参数 ($1) 选择 Spark 进程的类型:可能的值包括用于驱动程序容器的 spark-submit 和执行程序容器的 executor

  • 配置:Spark 配置位于 /etc/spark/conf 下。SPARK_CONF_DIR 环境变量指向此位置。

    请勿在容器映像中自定义 Spark 配置。应通过 Dataproc on GKE API 提交任何属性,原因如下:

    • 某些属性(例如执行程序内存大小)是在运行时(而不是容器映像构建时)确定的;它们必须由 Dataproc on GKE 注入。
    • Dataproc on GKE 对用户提供的属性施加了限制。Dataproc on GKE 会将配置从 configMap 装载到容器中的 /etc/spark/conf 中,从而替换映像中嵌入的设置。

基础 Spark 映像

Dataproc 支持以下 Spark 基础容器映像:

  • Spark 2.4: ${REGION}-docker.pkg.dev/cloud-dataproc/spark/dataproc_1.5
  • Spark 3.1: ${REGION}-docker.pkg.dev/cloud-dataproc/spark/dataproc_2.0

自定义容器映像构建示例

示例 Dockerfile

FROM us-central1-docker.pkg.dev/cloud-dataproc/spark/dataproc_2.0:latest

# Change to root temporarily so that it has permissions to create dirs and copy
# files.
USER root

# Add a BigQuery connector jar.
ENV SPARK_EXTRA_JARS_DIR=/opt/spark/jars/
ENV SPARK_EXTRA_CLASSPATH='/opt/spark/jars/*'
RUN mkdir -p "${SPARK_EXTRA_JARS_DIR}" \
    && chown spark:spark "${SPARK_EXTRA_JARS_DIR}"
COPY --chown=spark:spark \
    spark-bigquery-with-dependencies_2.12-0.22.2.jar "${SPARK_EXTRA_JARS_DIR}"

# Install Cloud Storage client Conda package.
RUN "${CONDA_HOME}/bin/conda" install google-cloud-storage

# Add a custom Python file.
ENV PYTHONPATH=/opt/python/packages
RUN mkdir -p "${PYTHONPATH}"
COPY test_util.py "${PYTHONPATH}"

# Add an init script.
COPY --chown=spark:spark init-script.sh /opt/init-script.sh

# (Optional) Set user back to `spark`.
USER spark

构建容器映像

在 Dockerfile 目录中运行以下命令

  1. 设置映像(示例:us-central1-docker.pkg.dev/my-project/spark/spark-test-image:latest)并更改为 build 目录。
    IMAGE=custom container image \
        BUILD_DIR=$(mktemp -d) \
        cd "${BUILD_DIR}"
    
  2. 下载 BigQuery 连接器。

    gsutil cp \
        gs://spark-lib/bigquery/spark-bigquery-with-dependencies_2.12-0.22.2.jar .
    

  3. 创建一个 Python 示例文件。

    cat >test_util.py <<'EOF'
    def hello(name):
      print("hello {}".format(name))
    def read_lines(path):   with open(path) as f:     return f.readlines() EOF

  4. 创建一个示例 init 脚本。

    cat >init-script.sh <<EOF
    echo "hello world" >/tmp/init-script.out
    EOF
    

  5. 构建并推送映像。

    docker build -t "${IMAGE}" . && docker push "${IMAGE}"