Dataproc on GKE 自定义容器映像

您可以指定要与 Dataproc on GKE 搭配使用的自定义容器映像。您的自定义容器映像必须使用 Dataproc on GKE 的某个 Spark 基础映像

使用自定义容器映像

要使用 Dataproc on GKE 自定义容器映像,请将 spark.kubernetes.container.image property,当您 创建 Dataproc on GKE 虚拟集群 或者向集群提交 Spark 作业

  • 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 用户身份通过 1099 UID 和 1099 GID 来运行 Spark 容器。使用 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:Dataproc on GKE 基本 Spark 映像/opt/conda 安装了 Miniconda3。CONDA_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 进程的额外类路径。建议:将罐子放入 /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 中。

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

    • 入口点Dataproc on GKE 会忽略所有 对 ENTRYPOINTCMD 基元所做的更改, 容器映像。

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

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

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

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

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

基础 Spark 映像

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

  • Spark 2.4:${REGION}-docker.pkg.dev/cloud-gclid/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 连接器。

    gcloud storage 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}"