使用 Pod 模板 - Amazon EMR

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

使用 Pod 模板

從亞馬遜 EMR 5.33.0 或 6.3.0 開始,EKS 上的亞馬遜 EMR 支持 Spark 的容器模板功能。容器是一組或多個容器,其中包含共享存儲和網絡資源,以及如何運行容器的規範。容器模板是確定如何運行每個容器的規範。您可以使用容器模板文件來定義 Spark 配置不支持的驅動程序或執行程序容器容器的配置。如需 Spark 的容器模板功能的詳細資訊,請參Pod 模板

注意

容器模板功能僅適用於驅動程序和執行程序窗格。您無法使用容器模板配置作業控制器窗格。

常用案例

您可以定義如何在共享 EKS 上使用具有 Amazon EMR 的容器模板在共享 EKS 羣集上運行 Spark 作業,從而節省成本並提高資源利用率和性能。

  • 為了降低成本,您可以安排 Spark 驅動程序任務在 Amazon EC2 上按需運行I安排在 Amazon EC2 競價上運行的 Spark 執行器任務時的情況I立場。

  • 為了提高資源利用率,您可以支持多個團隊在同一個 EKS 羣集上運行其工作負載。每個團隊都將獲得一個指定的 Amazon EC2 節點組來運行其工作負載。您可以使用容器模板將相應的容差應用於其工作負載。

  • 要改進監控,您可以運行單獨的日誌記錄容器,將日誌轉發到現有的監視應用程序。

例如,以下容器模板文件演示了一種常見的使用方案。

apiVersion: v1 kind: Pod spec: volumes: - name: source-data-volume emptyDir: {} - name: metrics-files-volume emptyDir: {} nodeSelector: eks.amazonaws.com/nodegroup: emr-containers-nodegroup containers: - name: spark-kubernetes-driver # This will be interpreted as driver Spark main container env: - name: RANDOM value: "random" volumeMounts: - name: shared-volume mountPath: /var/data - name: metrics-files-volume mountPath: /var/metrics/data - name: custom-side-car-container # Sidecar container image: <side_car_container_image> env: - name: RANDOM_SIDECAR value: random volumeMounts: - name: metrics-files-volume mountPath: /var/metrics/data command: - /bin/sh - '-c' - <command-to-upload-metrics-files> initContainers: - name: spark-init-container-driver # Init container image: <spark-pre-step-image> volumeMounts: - name: source-data-volume # Use EMR predefined volumes mountPath: /var/data command: - /bin/sh - '-c' - <command-to-download-dependency-jars>

容器模板完成下列任務:

  • 新增一個新的初始化容器,這是在 Spark 主容器啟動之前執行的。init 容器共享EmptyDir體積呼叫source-data-volume與火花主容器。您可以讓 init 容器運行初始化步驟,例如下載依賴關係或生成輸入數據。然後 Spark 主容器會消耗數據。

  • 新增另一個附屬容器,它與 Spark 主容器一起執行。這兩個容器正在共享另一個EmptyDir卷調用metrics-files-volume。您的 Spark 作業可以生成指標,例如 Prometheus 指標。然後,Spark 作業可以將指標放入文件中,並讓 side car 容器將文件上傳到您自己的 BI 系統,以便將來進行分析。

  • 將新的環境變量添加到 Spark 主容器。您可以讓您的作業使用環境變量。

  • 定義節點選擇器,以便該容器僅安排在emr-containers-nodegroup節點組。這有助於跨工作和團隊隔離計算資源。

在 EKS 上使用 Amazon EMR 啟用容器模板

要使用 EKS 上的 Amazon EMR 啟用容器模板功能,請配置 Spark 屬性spark.kubernetes.driver.podTemplateFilespark.kubernetes.executor.podTemplateFile以指向 Amazon S3 中的容器模板檔案。然後,Spark 下載容器模板文件並使用它來構建驅動程序和執行器窗格。

注意

Spark 使用作業執行角色加載容器模板,因此作業執行角色必須具有訪問 Amazon S3 才能加載容器模板的權限。如需詳細資訊,請參閱 建立任務執行角色

您可以使用SparkSubmitParameters以指定容器模板的 Amazon S3 路徑,如以下作業運行 JSON 文件所示。

{ "name": "myjob", "virtualClusterId": "123456", "executionRoleArn": "iam_role_name_for_job_execution", "releaseLabel": "release_label", "jobDriver": { "sparkSubmitJobDriver": { "entryPoint": "entryPoint_location", "entryPointArguments": ["argument1", "argument2", ...], "sparkSubmitParameters": "--class <main_class> \ --conf spark.kubernetes.driver.podTemplateFile=s3://path_to_driver_pod_template \ --conf spark.kubernetes.executor.podTemplateFile=s3://path_to_executor_pod_template \ --conf spark.executor.instances=2 \ --conf spark.executor.memory=2G \ --conf spark.executor.cores=2 \ --conf spark.driver.cores=1" } } }

或者,您也可以使用configurationOverrides以指定容器模板的 Amazon S3 路徑,如以下作業運行 JSON 文件所示。

{ "name": "myjob", "virtualClusterId": "123456", "executionRoleArn": "iam_role_name_for_job_execution", "releaseLabel": "release_label", "jobDriver": { "sparkSubmitJobDriver": { "entryPoint": "entryPoint_location", "entryPointArguments": ["argument1", "argument2", ...], "sparkSubmitParameters": "--class <main_class> \ --conf spark.executor.instances=2 \ --conf spark.executor.memory=2G \ --conf spark.executor.cores=2 \ --conf spark.driver.cores=1" } }, "configurationOverrides": { "applicationConfiguration": [ { "classification": "spark-defaults", "properties": { "spark.driver.memory":"2G", "spark.kubernetes.driver.podTemplateFile":"s3://path_to_driver_pod_template", "spark.kubernetes.executor.podTemplateFile":"s3://path_to_executor_pod_template" } } ] } }
注意
  1. 將容器模板功能與 EKS 上的 Amazon EMR 配合使用時,您需要遵循安全指南,例如隔離不受信任的應用程序代碼。如需詳細資訊,請參閱 安全最佳實務

  2. 您不能更改 Spark 主容器名稱,使用spark.kubernetes.driver.podTemplateContainerNamespark.kubernetes.executor.podTemplateContainerName,因為這些名稱被硬編碼為spark-kubernetes-driverspark-kubernetes-executors。如果要自定義 Spark 主容器,則必須使用這些硬編碼名稱在容器模板中指定容器。

Pod 模板字段

在 EKS 上使用 Amazon EMR 配置容器模板時,請考慮以下字段限制。

  • EKS 上的 Amazon EMR 僅允許在容器模板中使用以下字段,以實現正確的作業安排。

    以下是允許的容器級別字段:

    • apiVersion

    • kind

    • metadata

    • spec.activeDeadlineSeconds

    • spec.affinity

    • spec.containers

    • spec.enableServiceLinks

    • spec.ephemeralContainers

    • spec.hostAliases

    • spec.hostname

    • spec.imagePullSecrets

    • spec.initContainers

    • spec.nodeName

    • spec.nodeSelector

    • spec.overhead

    • spec.preemptionPolicy

    • spec.priority

    • spec.priorityClassName

    • spec.readinessGates

    • spec.restartPolicy

    • spec.runtimeClassName

    • spec.schedulerName

    • spec.subdomain

    • spec.terminationGracePeriodSeconds

    • spec.tolerations

    • spec.topologySpreadConstraints

    • spec.volumes

    以下是允許的 Spark 主容器級別字段:

    • env

    • envFrom

    • name

    • lifecycle

    • livenessProbe

    • readinessProbe

    • resources

    • startupProbe

    • stdin

    • stdinOnce

    • terminationMessagePath

    • terminationMessagePolicy

    • tty

    • volumeDevices

    • volumeMounts

    • workingDir

    當您在容器模板中使用任何不允許的字段時,Spark 會拋出異常,並且作業失敗。以下示例顯示了由於不允許的字段而在 Spark 控制器日誌中出現的錯誤消息。

    Executor pod template validation failed. Field container.command in Spark main container not allowed but specified.
  • EKS 上的 Amazon EMR 在容器模板中預定義了以下參數。在容器模板中指定的字段不得與這些字段重疊。

    以下是預定義的卷名稱:

    • emr-container-communicate

    • config-volume

    • emr-container-application-log-dir

    • emr-container-event-log-dir

    • temp-data-dir

    • mnt-dir

    • home-dir

    • emr-container-s3

    以下是僅適用於 Spark 主容器的預定義卷裝載:

    • 名稱: emr-container-communicate;MountPath:/var/log/fluentd

    • 名稱: emr-container-application-log-dir;MountPath:/var/log/spark/user

    • 名稱: emr-container-event-log-dir;MountPath:/var/log/spark/apps

    • 名稱: mnt-dir;MountPath:/mnt

    • 名稱: temp-data-dir;MountPath:/tmp

    • 名稱: home-dir;MountPath:/home/hadoop

    以下是僅適用於 Spark 主容器的預定義環境變量:

    • SPARK_CONTAINER_ID

    • K8S_SPARK_LOG_URL_STDERR

    • K8S_SPARK_LOG_URL_STDOUT

    • SIDECAR_SIGNAL_FILE

    注意

    您仍然可以使用這些預定義的卷並將其安裝到附加的側車容器中。例如,您可以使用emr-container-application-log-dir並將其安裝到容器模板中定義的自己的側車容器中。

    如果您指定的字段與容器模板中的任何預定義字段發生衝突,Spark 將拋出異常,並且作業將失敗。以下示例顯示了由於與預定義字段衝突而在 Spark 應用程序日誌中出現的錯誤消息。

    Defined volume mount path on main container must not overlap with reserved mount paths: [<reserved-paths>]

附屬注意事項

亞馬遜 EMR 控制由亞馬遜 EMR 在 EKS 上配置的容器的生命週期。側車容器應遵循 Spark 主容器的相同生命週期。如果您將其他側車容器注入到容器中,我們建議您與 Amazon EMR 定義的容器生命週期管理集成,以便在 Spark 主容器退出時,邊車容器可以自行停止。

為了降低成本,我們建議您實施一個過程,以防止帶有側車容器的驅動程序窗格在作業完成後繼續運行。執行程序完成後,Spark 驅動程序會刪除執行程序窗格。但是,當驅動程序完成後,額外的側車容器將繼續運行。在 EKS 上的 Amazon EMR 清理驅動程序容器之前,通常在驅動程序 Spark 主容器完成後不到一分鐘後,該容器將計費。為了降低成本,您可以將附加的側車容器與 Amazon EMR on EKS 為驅動程序和執行程序容器定義的生命週期管理機制集成,如以下部分所述。

驅動程序中的火花主容器和執行器窗格發送heartbeat到一個文件/var/log/fluentd/main-container-terminated每兩秒鐘一次 通過添加預定義的 Amazon EMRemr-container-communicate卷掛載到您的 sidecar 容器中,您可以定義一個 Sidecar 容器的子進程,以定期跟蹤此文件的上次修改時間。如果子進程發現 Spark 主容器停止heartbeat持續時間更長。

以下示例演示了跟蹤檢測信號文件並停止自身的子進程。Replace您的卷安裝與安裝預定義卷的路徑一起使用。該腳本捆綁在旁邊集裝箱使用的圖像中。在容器模板文件中,您可以使用以下命令指定側車容器sub_process_script.shmain_command

MOUNT_PATH="your_volume_mount" FILE_TO_WATCH="$MOUNT_PATH/main-container-terminated" INITIAL_HEARTBEAT_TIMEOUT_THRESHOLD=60 HEARTBEAT_TIMEOUT_THRESHOLD=15 SLEEP_DURATION=10 function terminate_main_process() { # Stop main process } # Waiting for the first heartbeat sent by Spark main container echo "Waiting for file $FILE_TO_WATCH to appear..." start_wait=$(date +%s) while ! [[ -f "$FILE_TO_WATCH" ]]; do elapsed_wait=$(expr $(date +%s) - $start_wait) if [ "$elapsed_wait" -gt "$INITIAL_HEARTBEAT_TIMEOUT_THRESHOLD" ]; then echo "File $FILE_TO_WATCH not found after $INITIAL_HEARTBEAT_TIMEOUT_THRESHOLD seconds; aborting" terminate_main_process exit 1 fi sleep $SLEEP_DURATION; done; echo "Found file $FILE_TO_WATCH; watching for heartbeats..." while [[ -f "$FILE_TO_WATCH" ]]; do LAST_HEARTBEAT=$(stat -c %Y $FILE_TO_WATCH) ELAPSED_TIME_SINCE_AFTER_HEARTBEAT=$(expr $(date +%s) - $LAST_HEARTBEAT) if [ "$ELAPSED_TIME_SINCE_AFTER_HEARTBEAT" -gt "$HEARTBEAT_TIMEOUT_THRESHOLD" ]; then echo "Last heartbeat to file $FILE_TO_WATCH was more than $HEARTBEAT_TIMEOUT_THRESHOLD seconds ago at $LAST_HEARTBEAT; terminating" terminate_main_process exit 0 fi sleep $SLEEP_DURATION; done; echo "Outside of loop, main-container-terminated file no longer exists" # The file will be deleted once the fluentd container is terminated echo "The file $FILE_TO_WATCH doesn't exist any more;" terminate_main_process exit 0