

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

# ポッドテンプレートの使用
<a name="pod-templates"></a>

Amazon EMR バージョン 5.33.0 または 6.3.0 以降、Amazon EMR on EKS では Spark のポッドテンプレート機能がサポートされます。ポッドは、共有ストレージとネットワークリソース、およびコンテナの実行方法についての仕様を持つ 1 つ以上のコンテナのグループです。ポッドテンプレートは、各ポッドの実行方法を決定する仕様です。ポッドテンプレートファイルを使用して、Spark 構成でサポートされていないドライバーまたはエグゼキューターポッドの設定を定義できます。Spark のポッドテンプレート機能の詳細については、[ポッドテンプレート](https://spark.apache.org/docs/latest/running-on-kubernetes.html#pod-template)を参照してください。

**注記**  
ポッドテンプレート機能は、ドライバーポッドとエグゼキューターポッドでのみ機能します。ポッドテンプレートを使用してジョブ送信者ポッドを設定することはできません。

## 一般的なシナリオ
<a name="pod-template-use-cases"></a>

Amazon EMR on EKS でポッドテンプレートを使用して、共有 EKS クラスター上で Spark ジョブを実行する方法を定義し、コストを削減し、リソース使用率とパフォーマンスを向上させることができます。
+ コストを削減するために、Spark エグゼキュータータスクを Amazon EC2 スポットインスタンスで実行するようにスケジューリングするのと同時に、Spark ドライバータスクを Amazon EC2 オンデマンドインスタンスで実行するようにスケジュールできます。
+ リソースの使用率を高めるために、複数のチームが同じ 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>
```

ポッドテンプレートは以下のタスクを実行します。
+ 新しい [init コンテナ](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/)を追加します。これは Spark メインコンテナが起動する前に実行されます。init コンテナは、`source-data-volume` という [EmptyDir ボリューム](https://kubernetes.io/docs/concepts/storage/volumes/#emptydir)を Spark メインコンテナと共有します。init コンテナで、依存関係のダウンロードや入力データの生成などの初期化ステップを実行できます。次に、Spark メインコンテナがデータを消費します。
+ Spark メインコンテナとともに実行される別の[サイドカーコンテナ](https://kubernetes.io/docs/concepts/workloads/pods/#how-pods-manage-multiple-containers)を追加します。2 つのコンテナは `metrics-files-volume` という別の `EmptyDir` ボリュームを共有しています。Spark ジョブは Prometheus メトリクスなどのメトリクスを生成できます。その後、Spark ジョブはメトリクスをファイルに入れ、サイドカーコンテナで自分の BI システムにファイルをアップロードして将来の分析を行うことができます。
+ Spark メインコンテナに新しい環境変数を追加します。ジョブに環境変数を使用させることができます。
+ ポッドが `emr-containers-nodegroup` ノードグループでのみスケジュールされるように、[ノードセレクタ](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/)を定義します。これは、ジョブとチーム間でコンピューティングリソースを分離するのに役立ちます。

## Amazon EMR on EKS でポッドテンプレートを有効にする
<a name="use-pod-templates"></a>

Amazon EMR on EKS でポッドテンプレート機能を有効にするには、Amazon S3 のポッドテンプレートファイルを指すように Spark プロパティ `spark.kubernetes.driver.podTemplateFile` および `spark.kubernetes.executor.podTemplateFile` を設定します。その後、Spark は、ポッドテンプレートファイルをダウンロードし、それを使用してドライバーポッドとエグゼキューターポッドを作成します。

**注記**  
Spark はジョブ実行ロールを使用してポッドテンプレートをロードするため、ジョブ実行ロールには Amazon S3 にアクセスしてポッドテンプレートをロードするためのアクセス許可が必要です。詳細については、「[ジョブ実行ロールを作成する](creating-job-execution-role.md)」を参照してください。

次のジョブ実行 JSON ファイルが示すように、`SparkSubmitParameters` を使用して、ポッドテンプレートへの Amazon S3 パスを指定できます。

```
{
  "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"
    }
  }
}
```

別の方法として、次のジョブ実行 JSON ファイルが示すように、`configurationOverrides` を使用して、ポッドテンプレートへの Amazon S3 パスを指定できます。

```
{
  "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"
         }
      }
    ]
  }
}
```

**注記**  
信頼できないアプリケーションコードの分離など、Amazon EMR on EKS でポッドテンプレート機能を使用する場合は、セキュリティガイドラインに従う必要があります。詳細については、「[Amazon EMR on EKS でのセキュリティのベストプラクティス](security-best-practices.md)」を参照してください。
Spark メインコンテナ名は `spark-kubernetes-driver` および `spark-kubernetes-executors` としてハードコードされているため、`spark.kubernetes.driver.podTemplateContainerName` および `spark.kubernetes.executor.podTemplateContainerName` を使用して変更することはできません。Spark メインコンテナをカスタマイズする場合は、これらのハードコードされた名前を使用してポッドテンプレートでコンテナを指定する必要があります。

## ポッドテンプレートフィールド
<a name="pod-templates-fields"></a>

Amazon EMR on EKS でポッドテンプレートを設定する場合は、次のフィールド制限を考慮してください。
+ Amazon EMR on EKS では、ポッドテンプレート内の以下のフィールドのみで適切なジョブスケジューリングの有効化が許可されます。

  許可されるポッドレベルのフィールドは次のとおりです。
  + `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`

  ポッドテンプレートで許可されていないフィールドを使用すると、Spark は例外をスローし、ジョブは失敗します。次の例では、許可されていないフィールドが原因で Spark コントローラーログにエラーメッセージが表示されています。

  ```
  Executor pod template validation failed.
  Field container.command in Spark main container not allowed but specified.
  ```
+  Amazon EMR on EKS は、ポッドテンプレートで次のパラメータを事前定義しています。ポッドテンプレートで指定するフィールドは、これらのフィールドと重複してはいけません。

  事前定義済みのボリューム名は次のとおりです。
  + `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>]
  ```

## サイドカーコンテナの考慮事項
<a name="pod-template-sidecar"></a>

Amazon EMR は、Amazon EMR on EKS によってプロビジョニングされるポッドのライフサイクルを制御します。サイドカーコンテナは Spark メインコンテナと同じライフサイクルに従う必要があります。追加のサイドカーコンテナをポッドに挿入する場合は、Spark メインコンテナが終了したときにサイドカーコンテナが自動的に停止できるように、Amazon EMR が定義するポッドライフサイクル管理と統合することをお勧めします。

コストを削減するため、サイドカーコンテナを備えたドライバーポッドがジョブの完了後に実行し続けないようにするプロセスを実装することをお勧めします。Spark ドライバーは、エグゼキューターの完了時にエグゼキューターポッドを削除します。ただし、ドライバープログラムが完了すると、追加のサイドカーコンテナは引き続き実行されます。ポッドは、Amazon EMR on EKS がドライバーポッドをクリーンアップするまで課金されます。通常は、ドライバー Spark メインコンテナが完了してから 1 分未満です。コストを削減するために、次のセクションで説明するように、追加のサイドカーコンテナをライフサイクル管理メカニズムと統合できます。このメカニズムは Amazon EMR on EKS がドライバーポッドとエグゼキューターポッドの両方に対して定義するものです。

ドライバーポッドとエクゼキューターポッドの Spark メインコンテナは、2 秒ごとに `heartbeat` をファイル `/var/log/fluentd/main-container-terminated` に送信します。Amazon EMR の事前定義済み `emr-container-communicate` ボリュームマウントをサイドカーコンテナに追加すると、サイドカーコンテナのサブプロセスを定義して、このファイルの最終更新時刻を定期的に追跡できます。その後、サブプロセスにより Spark メインコンテナがより長い期間 `heartbeat` を停止していることが検出されると、サブプロセス自体が停止します。

次の例は、ハートビートファイルを追跡し、それ自体を停止するサブプロセスを示しています。*your\$1volume\$1mount* を、事前定義されたボリュームをマウントするパスに置き換えます。スクリプトは、サイドカーコンテナで使用されるイメージ内にバンドルされています。ポッドテンプレートファイルでは、次のコマンドを使用してサイドカーコンテナを指定できます。`sub_process_script.sh` および `main_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
```