# Amazon EKSとS3のセットアップ

Dynamic Engineおよびその環境をデプロイする前に、共有ストレージのバックエンドとしてAmazon S3を使用するAmazon EKSクラスターをセットアップします。

## 始める前に

* 必要なIAM権限が付与されたAWSアカウントが必要です。前提条件の完全なリストについては、[Amazon EKSをDynamic Engineデプロイ用に設定する](https://help.qlik.com/talend/ja-JP/dynamic-engine-configuration-guide/Cloud/deploying-dynamic-engine-on-aws-eks "Dynamic Engineは、EKSネイティブのストレージシステムを使用してAmazon Elastic Kubernetes Service(EKS)上にデプロイできます。")を参照してください。

## このタスクについて

情報メモ重要: この手順では、ストレージを基盤とする静的プロビジョニングを利用するEKSのデプロイメントパターンについて説明します。Amazon EKS Auto Modeの場合、[マネージドKubernetes 運用モード](https://help.qlik.com/talend/ja-JP/dynamic-engine-configuration-guide/Cloud/prerequisites-to-use-dynamic-engines)におけるmanaged-modeバンドルを適用します。

本手順では、専用の仮想プライベートクラウド(Amazon VPC)内に新規のEKSクラスターを作成し、AWSマネージドEKSアドオンとしてS3 CSI (Container Storage Interface)ドライバーをインストールします。さらに、永続ボリュームのバックエンドとなるS3バケットを作成し、Dynamic Engineおよびその環境向けに静的なPersistentVolumesとPersistentVolumeClaimsをプロビジョニングします。

* Amazon EFSのセットアップとは異なり、S3では静的プロビジョニングが採用されています。そのため、StorageClassリソースは存在しません。Dynamic Engineのデプロイを実行する前に、PersistentVolumeを手動で作成し、名前付きの PersistentVolumeClaimにバインドする必要があります。

## 手順

1. Amazon EKSおよびS3のデプロイメントの環境変数を設定します。
   ```
   export AWS_REGION=<your-aws-region>
   export EKS_CLUSTER_NAME=<your-eks-cluster-name>
   export S3_BUCKET_NAME=<your-s3-bucket-name>
   ```
2. 永続ボリュームのバックエンドとして使用するS3バケットを作成します。要件に合致するバケットタイプを選択します。

   作成するバケットとAmazon VPCは、同一のAWSリージョンに配置する必要があります。
   * 汎用バケット: すべてのAWSリージョンで利用可能な標準のS3バケットを使用します。1つのバケットでパスプレフィックスを用いて、すべてのDynamic Engineボリュームを管理します。

     ```
     cat <<EOF > $S3_BUCKET_NAME-classic-input.yaml
     Bucket: $S3_BUCKET_NAME
     CreateBucketConfiguration:
       LocationConstraint: ${AWS_REGION}
       Tags:
         - Key: owner
           Value: dyn-engine
         - Key: creator
           Value: ${USER}
         - Key: eks/cluster
           Value: ${EKS_CLUSTER_NAME}
     EOF

     S3_BUCKET_ARN=$(aws s3api create-bucket \
       --cli-input-yaml file://$S3_BUCKET_NAME-classic-input.yaml \
       --region $AWS_REGION \
       --output text \
       --query BucketArn)

     echo "S3 bucket created: $S3_BUCKET_ARN"
     ```

     これら3つのブロックは、所有権タグを含む新しいS3バケット用の設定ファイルを生成し、AWS CLIを使用して実際にバケットを作成した後、そのバケットの一意の識別子を含む確認メッセージを出力します。

   * ディレクトリーバケット: 単一のアベイラビリティーゾーン内でのレイテンシーを低減するには、ディレクトリーバケット(S3 Express One Zoneバケットとも呼ばれる)を使用します。ディレクトリーバケットには、bucket-base-name--zone-id--x-s3という特定の命名形式が必要です。ここで zone-idは、アベイラビリティーゾーンまたはローカルゾーンのIDを表します。この種のバケットは、特定のAWSリージョンおよびアベイラビリティーゾーンでのみ利用できます。

     情報メモ注: このスクリプトを実行する前に、環境変数AWS\_AZ\_ZONE\_IDに、対象となるアベイラビリティーゾーンのIDを設定します。例: eu-west-1リージョンのeuw1-az1。

     ```
     AWS_AZ_ZONE_ID=<your-az-zone-id>
     S3_BUCKET_NAME="${EKS_CLUSTER_NAME}--$AWS_AZ_ZONE_ID--x-s3"

     cat <<EOF > $S3_BUCKET_NAME-directory-input.yaml
     Bucket: $S3_BUCKET_NAME
     CreateBucketConfiguration:
       Location:
         Type: AvailabilityZone
         Name: $AWS_AZ_ZONE_ID
       Bucket:
         DataRedundancy: SingleAvailabilityZone
         Type: Directory
       Tags:
         - Key: owner
           Value: dyn-engine
         - Key: creator
           Value: ${USER}
         - Key: eks/cluster
           Value: ${EKS_CLUSTER_NAME}
     EOF

     S3_BUCKET_ARN=$(aws s3api create-bucket \
       --cli-input-yaml file://$S3_BUCKET_NAME-directory-input.yaml \
       --region $AWS_REGION \
       --output text \
       --query BucketArn)

     echo "S3 bucket created: $S3_BUCKET_ARN"
     ```

     これら4つのブロックは、AWSが定める命名規則および構成に従い、特定の可用性ゾーンに固定された S3 Express One Zone(ディレクトリー)バケットをセットアップおよび作成した後、確認メッセージを出力します。
3. EKSクラスター設定ファイルを作成します。

   IAM権限は、汎用バケットとディレクトリーバケットの間で異なります。前のステップで作成したバケットタイプに一致する設定を使用してください。
   * 汎用バケット: バケットおよびそのオブジェクトに対して、s3:ListBucket、s3:GetObject、s3:PutObject、s3:DeleteObjectの権限を付与します。

     ```
     cat <<EOF > eks-config.yaml
     apiVersion: eksctl.io/v1alpha5
     kind: ClusterConfig

     metadata:
       name: ${EKS_CLUSTER_NAME}
       region: ${AWS_REGION}
       version: "1.34"

     addonsConfig:
       autoApplyPodIdentityAssociations: true

     addons:
       - name: eks-pod-identity-agent
       - name: aws-mountpoint-s3-csi-driver
         namespaceConfig:
           namespace: aws-mountpoint-s3
         podIdentityAssociations:
           - namespace: aws-mountpoint-s3
             serviceAccountName: s3-csi-driver-sa
             permissionPolicy:
               Statement:
                 - Effect: Allow
                   Action:
                     - s3:ListBucket
                   Resource: $S3_BUCKET_ARN
                 - Effect: Allow
                   Action:
                     - s3:GetObject
                     - s3:PutObject
                     - s3:DeleteObject
                   Resource: "$S3_BUCKET_ARN/*"

     vpc:
       cidr: 10.10.0.0/16
       autoAllocateIPv6: false
       hostnameType: resource-name
       clusterEndpoints:
         publicAccess: true
         privateAccess: true
       nat:
         gateway: HighlyAvailable

     managedNodeGroups:
       - name: ng-amd64
         amiFamily: AmazonLinux2023 # (default) or Bottlerocket
         minSize: 2
         maxSize: 4
         desiredCapacity: 2
         instanceSelector:
           vCPUs: 4
           memory: 16GiB
           cpuArchitecture: amd64
         privateNetworking: true

     EOF
     ```

   * ディレクトリーバケット: バケットに対してs3express:CreateSessionを付与します(S3 Express One Zoneのセッションベースの認可):

     ```
     cat <<EOF > eks-config.yaml
     apiVersion: eksctl.io/v1alpha5
     kind: ClusterConfig

     metadata:
       name: ${EKS_CLUSTER_NAME}
       region: ${AWS_REGION}
       version: "1.34"

     addonsConfig:
       autoApplyPodIdentityAssociations: true

     addons:
       - name: eks-pod-identity-agent
       - name: aws-mountpoint-s3-csi-driver
         namespaceConfig:
           namespace: aws-mountpoint-s3
         podIdentityAssociations:
           - namespace: aws-mountpoint-s3
             serviceAccountName: s3-csi-driver-sa
             permissionPolicy:
               Statement:
                 - Effect: Allow
                   Action:
                     - s3express:CreateSession
                   Resource: $S3_BUCKET_ARN

     vpc:
       cidr: 10.10.0.0/16
       autoAllocateIPv6: false
       hostnameType: resource-name
       clusterEndpoints:
         publicAccess: true
         privateAccess: true
       nat:
         gateway: HighlyAvailable

     managedNodeGroups:
       - name: ng-amd64
         amiFamily: AmazonLinux2023 # (default) or Bottlerocket
         minSize: 2
         maxSize: 4
         desiredCapacity: 2
         instanceSelector:
           vCPUs: 4
           memory: 16GiB
           cpuArchitecture: amd64
         privateNetworking: true

     EOF
     ```

   情報メモ注: 次の設定に関する注意事項は、汎用バケットおよびディレクトリーバケットのいずれのタイプにも適用されます。両者の唯一の違いは、S3 CSIドライバーに付与されるIAM権限ポリシーです。

   次を設定します。
   * Kubernetesのバージョンを1.34に設定します。これは、本手順で使用されているサンプルバージョンです。使用するターゲットクラスターのバージョンに合わせて更新してください。サポート対象のバージョンについては、[Dynamic Engineを使用するための前提条件](https://help.qlik.com/talend/ja-JP/dynamic-engine-configuration-guide/Cloud/prerequisites-to-use-dynamic-engines "Dynamic Engineをデプロイする前に、アクセス権限、Kubernetesとの互換性、およびデプロイモードの要件(マネージドKubernetesモードを含む)を確認してください。")を参照してください。
   * aws-mountpoint-s3ネームスペースにaws-mountpoint-s3-csi-driverアドオンをインストールします。この際に、Pod Identity Associationsを使用してIAM権限が自動的に設定されます。これにより、S3 CSIドライバーは、ハードコードされた認証情報を使用することなく、S3オブジェクトをボリュームとしてマウントするために必要な権限を取得します。EKSマネージドポリシーを使用するEFSとは異なり、S3 CSIドライバーには、特定のS3バケットのARN ($S3\_BUCKET\_ARN)にスコープされた、明示的なIAMポリシーが必要です。このポリシーは eksctlによって自動的にアドオンへバインドされるため、追加のAWS CLIコマンドを実行する必要はありません。
   * VPCを作成し、すべてのノードをプライベートサブネットに配置します(privateNetworking: true)。ノードは、NATゲートウェイ経由でのみインターネットにアクセスします。クラスターのエンドポイントは、ローカルマシンおよびVPC内の両方から引き続きアクセスできます。
   * Dynamic Engineワークロード向けにサイジングされたプロビジョニングノード各ノードは、4つのvCPUと16 GiBのRAMを搭載しています。ノードグループは、2〜4ノードの間でスケーリングします(minSize: 2、maxSize: 4)。Dynamic EngineコンテナイメージはAMD64専用に構築されているため、AMD64アーキテクチャ(cpuArchitecture: amd64)が必須となります。
4. EKSクラスターを作成し、ローカルのkubectlをクラスターにリンクして、アクセスを確認します。
   ```
   eksctl create cluster -f eks-config.yaml
   aws eks update-kubeconfig --region "$AWS_REGION" --name "$EKS_CLUSTER_NAME"
   kubectl get nodes
   ```

   すべてのノードが[Ready] (準備完了)ステータスになるまで待機してから、次の手順に進んでください。
5. Dynamic Engine環境のネームスペース向けに環境変数を設定し、Dynamic EngineサービスおよびTalend Management Consoleタスクで使用される3つのPersistentVolumeとPersistentVolumeClaimを作成します。
   ```
   ENV_ID=67f7d562ffd7c3525a902542
   DYNAMIC_ENGINE_ENV_NAMESPACE=qlik-processing-env-$ENV_ID
   # Ensure the target namespace exists before creating PVs/PVCs
   kubectl get namespace "$DYNAMIC_ENGINE_ENV_NAMESPACE" >/dev/null 2>&1 || kubectl create namespace "$DYNAMIC_ENGINE_ENV_NAMESPACE"
   declare -a pvcs=("archive" "job-data" "custom-resources")

   for pvc in "${pvcs[@]}"
   do
     echo "Creating PV/PVC for $pvc"
   cat <<EOF | kubectl apply -f -
   ---
   apiVersion: v1
   kind: PersistentVolume
   metadata:
     name: $pvc
   spec:
     capacity:
       storage: 1200Gi # Ignored for S3 volumes but required by Kubernetes API
     accessModes:
       - ReadWriteMany # Supported options: ReadWriteMany / ReadOnlyMany
     storageClassName: "" # Required for static provisioning
     claimRef: # To ensure no other PVCs can claim this PV
       namespace: $DYNAMIC_ENGINE_ENV_NAMESPACE # Namespace is required even though it's in "default" namespace.
       name: $pvc # Name of your PVC
     mountOptions:
       - allow-delete
       - allow-overwrite
       - region $AWS_REGION
       - prefix $pvc/
     csi:
       driver: s3.csi.aws.com # Required
       volumeHandle: $pvc # Must be unique
       volumeAttributes:
         bucketName: $S3_BUCKET_NAME
   ---
   apiVersion: v1
   kind: PersistentVolumeClaim
   metadata:
     name: $pvc
     namespace: $DYNAMIC_ENGINE_ENV_NAMESPACE
   spec:
     accessModes:
       - ReadWriteMany # Supported options: ReadWriteMany / ReadOnlyMany
     storageClassName: "" # Required for static provisioning
     resources:
       requests:
         storage: 1200Gi # Ignored, required
     volumeName: $pvc # Name of your PV
   EOF
   ```
   * storageClassName: "": 静的プロビジョニングは空である必要があります。
   * claimRef: このPersistentVolumeを、指定された名前空間内の指定されたPersistentVolumeClaimに排他的にバインドします。他のワークロードによる意図しない要求を防ぐために必要です。
   * mountOptions: allow-deleteおよびallow-overwrite. これらにより、S3をバックエンドとするボリュームへの書き込み操作が可能になります。これらのフラグがない場合、ポッドはボリュームをマウントできますが、書き込みは失敗します。
   * prefix: $pvc/: 各ボリュームを共有S3バケット内の個別のパスプレフィックスに割り当て、各ボリュームのデータを分離します。
   * volumeHandle: クラスター内のすべてのPersistentVolumeの間で一意である必要があります。
6. 3つのPersistentVolumeClaimすべてをマウントするテストポッドを作成します。
   ```
   cat <<EOF | kubectl apply -f -
   apiVersion: v1
   kind: Pod
   metadata:
     name: test-pod-pvc
     namespace: $DYNAMIC_ENGINE_ENV_NAMESPACE
   spec:
     securityContext:
       fsGroup: 61000
       runAsUser: 61000
       runAsGroup: 61000
       runAsNonRoot: true
     containers:
       - name: app
         image: ghcr.io/talend/kube-base:5.3.0
         command: ["cat"]
         tty: true
         volumeMounts:
           - name: archive
             mountPath: /opt/talend/archive
           - name: job-data
             mountPath: /opt/talend/data
           - name: custom-resources
             mountPath: /opt/talend/custom-resources
     volumes:
       - name: archive
         persistentVolumeClaim:
           claimName: archive
       - name: job-data
         persistentVolumeClaim:
           claimName: job-data
       - name: custom-resources
         persistentVolumeClaim:
           claimName: custom-resources
   EOF
   ```

   テストポッドは、runAsUser: 61000、runAsGroup: 61000、fsGroup: 61000を使用します。これらの値はDynamic EngineランタイムのUID/GIDと一致しており、マウントされたボリューム上で正しいファイルの所有権を確立するために必要です。
7. ポッドがマウントされたボリュームに対して、データの読み書きができることを検証します。
   ```
   kubectl exec -n $DYNAMIC_ENGINE_ENV_NAMESPACE test-pod-pvc -- \
     sh -c "echo 'Hello world' > /opt/talend/archive/test.txt \
     && cat /opt/talend/archive/test.txt \
     && rm -f /opt/talend/archive/test.txt"
   ```

   コマンドがエラーなく完了した場合、S3バックアップボリュームはアクセス可能かつ書き込み可能になります。

## タスクの結果

ユーザーのEKSクラスターはS3 CSIドライバーがインストールされた状態で稼働しており、qlik-processing-env-&lt;env-id&gt;ネームスペースにおいて、3つのPersistentVolume (archive、job-data、custom-resources)が作成され、それぞれのPersistentVolumeClaimにバインドされています。クラスターのDynamic Engineデプロイメントの準備が整いました。

情報メモヒント: Dynamic Engineに組み込まれているdocker-registryサービスはランダム書き込みを行いますが、S3 CSIドライバー(Mountpoint for Amazon S3)はこの機能をサポートしていません。その結果、docker-registryボリュームでは、S3をバックエンドとするPersistentVolumeClaimを使用できません。docker-registryの永続ボリュームをプロビジョニングするには、EFS、あるいはPOSIX (Portable Operating System Interface)をサポートするその他の外部システムを使用してください。

* docker-registryの設定の詳細については、[Talend Management Consoleタスク向けのカスタムDockerレジストリの設定](https://help.qlik.com/talend/ja-JP/dynamic-engine-configuration-guide/Cloud/configure-docker-registry "Dynamic Engine環境内のデータ統合ジョブ、ビッグデータジョブ、データサービス、ルート向けに、外部Dockerレジストリを設定します。")を参照してください。
* 既存の静的永続ボリュームの使用については、[既存のPersistentVolumeClaimを使用したDynamic Engineのデプロイ](https://help.qlik.com/talend/ja-JP/dynamic-engine-configuration-guide/Cloud/deploy-dynamic-engine-with-static-pvc "Dynamic Engine Helmチャートを構成し、動的なStorageClassプロビジョニングを使用する代わりに、既存のPersistentVolumeClaimを名前で参照するように設定します。")を参照してください。
* 動的永続ボリューム(StorageClass)の使用については、[Dynamic Engine環境サービス専用のストレージクラスをプロビジョニング](https://help.qlik.com/talend/ja-JP/dynamic-engine-configuration-guide/Cloud/deploy-dynamic-engine-with-specific-storageclass "デフォルトのKubernetesストレージクラスを使用する代わりに、Dynamic Engine環境サービス専用のストレージクラスを設定します。")を参照してください。

## 次のタスク

この手順で作成したPersistentVolumeClaimを参照するように、Dynamic EngineHelmチャートを設定およびデプロイします。既存のPVCの使用に関する詳細な手順については、[既存のPersistentVolumeClaimを使用したDynamic Engineのデプロイ](https://help.qlik.com/talend/ja-JP/dynamic-engine-configuration-guide/Cloud/deploy-dynamic-engine-with-static-pvc "Dynamic Engine Helmチャートを構成し、動的なStorageClassプロビジョニングを使用する代わりに、既存のPersistentVolumeClaimを名前で参照するように設定します。")を参照してください。

### このセクション内

* [既存のPersistentVolumeClaimを使用したDynamic Engineのデプロイ](https://help.qlik.com/talend/ja-JP/dynamic-engine-configuration-guide/Cloud/deploy-dynamic-engine-with-static-pvc)