使用 Pod 範本 - Amazon EMR

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

使用 Pod 範本

從 Amazon EMR 5.33.0 或 6.3.0 版開始,Amazon EMR on EKS 支援 Spark 的 Pod 範本功能。Pod 是一個或多個容器群組,其中包含共用儲存和網路資源,以及如何執行容器的規範。Pod 範本是決定如何執行每個 Pod 的規範。可以使用 Pod 範本檔案來定義 Spark 組態不支援的驅動程式或執行程式 Pod 組態。如需有關 Spark 的 Pod 範本功能的詳細資訊,請參閱 Pod 範本

注意

Pod 範本功能僅適用於驅動程式和執行程式 Pod。無法使用 Pod 範本來設定作業控制器 Pod。

常用案例

透過搭配使用 Pod 範本與 Amazon EMR on EKS,可定義如何在共用的 EKS 叢集上執行 Spark 作業,並節省成本及改善資源使用率和效能。

  • 為了降低成本,可以排程 Spark 驅動程式任務在 Amazon EC2 隨需執行個體上執行,同時排程 Spark 執行程式任務在 Amazon EC2 Spot 執行個體上執行。

  • 若要提高資源使用率,您可以支援多個團隊在相同 EKS 叢集上執行其工作負載。每個團隊都會獲得一個指定的 Amazon EC2 節點群組,以便在其中執行工作負載。可以使用 Pod 範本,將對應的容差套用至其工作負載。

  • 若要改善監控,可以執行單獨的日誌容器,將日誌轉寄至現有的監控應用程式。

例如,下列 Pod 範本檔案示範常見的使用案例。

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>

Pod 範本會完成下列任務:

  • 新增一個在 Spark 主容器啟動之前執行的初始化容器。初始化容器與 Spark 主容器共用稱為 source-data-volumeEmptyDir 磁碟區。可以讓初始化容器執行初始化步驟,例如下載相依性或產生輸入資料。然後,Spark 主容器會消耗資料。

  • 新增與 Spark 主容器一起執行的另一個附屬容器。這兩個容器正在共用另一個稱為的 metrics-files-volumeEmptyDir 磁碟區。您的 Spark 作業可以產生指標,例如 Prometheus 指標。然後,Spark 作業可以將指標放入檔案中,並讓附屬容器將檔案上傳到您自己的 BI 系統,以供將來分析。

  • 將新環境變數新增至 Spark 主容器。可以讓作業消耗環境變數。

  • 定義節點選取器,以便僅在 emr-containers-nodegroup 節點群組上排程 Pod。這有助於隔離作業和團隊的運算資源。

使用 Amazon EMR on EKS 啟用 Pod 範本

若要使用 Amazon EMR on EKS 啟用 Pod 範本功能,請設定 Spark 屬性 spark.kubernetes.driver.podTemplateFilespark.kubernetes.executor.podTemplateFile,以指向 Amazon S3 中的 Pod 範本檔案。然後,Spark 會下載 Pod 範本檔案,並使用它來建構驅動程式和執行程式 Pod。

注意

Spark 使用作業執行角色來載入 Pod 範本,因此作業執行角色必須有權存取 Amazon S3 以載入 Pod 範本。如需更多詳細資訊,請參閱 建立作業執行角色

您可以使用 SparkSubmitParameters 來指定 Pod 範本的 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 來指定 Pod 範本的 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. 搭配使用 Pod 範本功能與 Amazon EMR on EKS 時,需要遵循安全準則,例如隔離不受信任的應用程式碼。如需更多詳細資訊,請參閱 Amazon EMR on EKS 安全最佳實務

  2. 無法使用 spark.kubernetes.driver.podTemplateContainerNamespark.kubernetes.executor.podTemplateContainerName 來變更 Spark 主容器名稱 ,因為這些名稱硬編碼為 spark-kubernetes-driverspark-kubernetes-executors。如果想要自訂 Spark 主容器,則必須使用這些硬編碼名稱在 Pod 範本中指定容器。

Pod 範本欄位

使用 Amazon EMR on EKS 設定 Pod 範本時,請考慮下列欄位限制。

  • Amazon EMR on EKS 僅允許 Pod 範本中的下列欄位啟用適當的作業排程。

    以下是允許的 Pod 層級欄位:

    • 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.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

    當您在 Pod 範本中使用任何不允許的欄位時,Spark 會擲出例外狀況,且作業失敗。下列範例會顯示 Spark 控制器日誌中的錯誤訊息,因為存在不允許的欄位。

    Executor pod template validation failed. Field container.command in Spark main container not allowed but specified.
  • Amazon EMR on EKS 會在 Pod 範本中預先定義下列參數。在 Pod 範本中指定的欄位不得與這些欄位重疊。

    以下是預先定義的磁碟區名稱:

    • 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;掛載路徑:/var/log/fluentd

    • 名稱:emr-container-application-log-dir;掛載路徑:/var/log/spark/user

    • 名稱:emr-container-event-log-dir;掛載路徑:/var/log/spark/apps

    • 名稱:mnt-dir;掛載路徑:/mnt

    • 名稱:temp-data-dir;掛載路徑:/tmp

    • 名稱:home-dir;掛載路徑:/home/hadoop

    這些是僅適用於 Spark 主容器的預定義環境變數:

    • SPARK_CONTAINER_ID

    • K8S_SPARK_LOG_URL_STDERR

    • K8S_SPARK_LOG_URL_STDOUT

    • SIDECAR_SIGNAL_FILE

    注意

    您仍然可以使用這些預先定義的磁碟區,並將其掛載至額外的附屬容器中。例如,您可以使用 emr-container-application-log-dir 並將其掛接到 Pod 範本中定義的您自己的附屬容器。

    如果您指定的欄位與 Pod 範本中的任何預定義欄位衝突,Spark 會擲出例外狀況,而作業會失敗。下列範例會顯示 Spark 應用程式日誌中的錯誤訊息,因為與預定義的欄位衝突。

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

附屬容器的考量

Amazon EMR 控制由 Amazon EMR on EKS 佈建的 Pod 的生命週期。附屬容器應遵循 Spark 主容器的相同生命週期。如果將額外的附屬容器插入至 Pod,建議與 Amazon EMR 定義的 Pod 生命週期管理整合,以便 Spark 主容器結束時,附屬容器可以自行停止。

為了降低成本,建議您實施一個程序,以防止具有附屬容器的驅動程式 Pod 在作業完成後繼續執行。當執行程式完成時,Spark 驅動程式會刪除執行程式 Pod。但是,當驅動程式完成時,其他附屬容器會繼續執行。Pod 會計費,直到 Amazon EMR on EKS 清理驅動程式 Pod 為止,通常在驅動程式 Spark 主容器完成後不到一分鐘。為了降低成本,可以將其他附屬容器與 Amazon EMR on EKS 為驅動程式和執行程式 Pod 定義的生命週期管理機制整合,如下節所述。

驅動程式和執行程式 Pod 中的 Spark 主容器每兩秒向檔案 /var/log/fluentd/main-container-terminated 傳送一次 heartbeat。透過將 Amazon EMR 預先定義的 emr-container-communicate 磁碟區掛載新增至附屬容器,可以定義附屬容器的子流程,以定期追蹤此檔案的上次修改時間。然後,如果子流程發現 Spark 主容器長時間停止 heartbeat,則子流程會自行停止。

下列範例示範追蹤活動訊號檔案並自行停止的子流程。將 your_volume_mount 取代為掛載預定義磁碟區的路徑。指令碼綁定在附屬容器使用的映像內。在 Pod 範本檔案中,可以使用下列命令 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