Deploying Spark jobs on Amazon EKSをやってみた

# はじめに
EKS上でSparkを動作させる記事を見つけたので、記載されている内容に沿って試してみました。
参照記事：https://aws.amazon.com/jp/blogs/opensource/deploying-spark-jobs-on-amazon-eks/

## やったこと
- S3バケット作成
- IAMポリシー作成
- EKSクラスター作成
- ECRレポジトリ作成
- DockerイメージBuild/Push
- Spark Job実行

## S3バケットの作成
```sh
aws s3 mb s3://y-smp-bucket01
```

## IAMポリシーの作成
ポリシーをpolicyファイルとして定義

```sh:policy
cat << EOF > policy
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "s3:*",
            "Resource": "*"
        }
    ]
}
EOF
```

ポリシー作成

```sh
aws iam create-policy --policy-name eks2s3 --policy-document file://policy
```

## EKSクラスター作成
EKSクライアントインストール

```sh
curl --silent --location "https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp
sudo mv /tmp/eksctl /usr/local/bin
```

`eksctl.yaml`に作成したポリシーのarnを設定

```diff:amazon-eks-apache-spark-etl-sample/example/eksctl.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: spark-cluster
  region: us-east-1
  version: "1.14"

availabilityZones: ["us-east-1a", "us-east-1b","us-east-1c"]

nodeGroups:
  - name: spark-nodes
    instanceType: m5.xlarge
    volumeSize: 30
    desiredCapacity: 4
    privateNetworking: true
    maxPodsPerNode: 10
    iam:
      attachPolicyARNs:
        - arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy
        - arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy
-       - <YOUR_AWS_IAM_POLICY_ARN>
+       - arn:aws:iam::xxxxxxxxxxxx:policy/eks2s3
```

EKSクラスタ作成

```sh
eksctl create cluster -f example/eksctl.yaml
```

事前にインストールするのを忘れたので、後からkubectlインストール

```sh
curl -LO "https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl"
curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.14.0/bin/linux/amd64/kubectl
chmod +x ./kubectl
sudo mv ./kubectl /usr/local/bin/kubectl
```

kubecltバージョンが`1.14`であることを確認

```sh
kubectl version --client
```

認証設定

```sh
aws eks --region us-east-1 update-kubeconfig --name spark-cluster
```

## ECR作成
レポジトリ作成

```sh
aws ecr create-repository --repository-name eks/spark
```

## Spark上で動作するアプリケーションのイメージをBuild

Dependency Resolutionに失敗するので`build.sbt`に記載されているTypesafeレポジトリのプロトコルを`http`から`https`へ変更

```diff:amazon-eks-apache-spark-etl-sample/build.sbt
- resolvers += "Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/"
+ resolvers += "Typesafe Repository" at "https://repo.typesafe.com/typesafe/releases/"
```

DockerイメージをBuild

```sh
sudo docker build -t xxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/eks/spark:v1.0 .
```

ECRレジストリ認証設定

```sh
aws ecr get-login-password --region us-east-1 | sudo docker login --username AWS --password-stdin xxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com
```

イメージPush

```sh
sudo docker push xxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/eks/spark:v1.0
```

## RBAC変更

SparkがJobをサブミットできるようにRBACを変更

```sh
kubectl apply -f example/kubernetes/spark-rbac.yaml
```

## Spark BaseイメージBuild

`spark-submit`を実行するイメージをBuild&Push

```sh
sudo docker build --target=spark -t xxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/eks/spark:v2.4.4 .
sudo docker push xxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/eks/spark:v2.4.4
```

## spark-job.yaml実行

`spark-job.yaml`編集

```diff:amazon-eks-apache-spark-etl-sample/example/kubernetes/spark-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: spark-on-eks
spec:
  template:
    spec:
      containers:
        - name: spark
-          image: <REPO_NAME>/spark:v2.4.4
+          image: xxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/eks/spark:v2.4.4
          command: [
            "/bin/sh",
            "-c",
            "/opt/spark/bin/spark-submit \
            --master k8s://https://10.100.0.1:443 \
            --deploy-mode cluster \
            --name spark-on-eks \
            --class ValueZones \
            --conf spark.executor.instances=4 \
            --conf spark.executor.memory=10G \
            --conf spark.executor.cores=2 \
            --conf spark.sql.shuffle.partitions=60 \
-           --conf spark.kubernetes.container.image=<REPO_NAME>/<IMAGE_NAME>:<TAG> \
+           --conf spark.kubernetes.container.image=xxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/eks/spark:v1.0 \
            --conf spark.hadoop.fs.s3a.impl=org.apache.hadoop.fs.s3a.S3AFileSystem \
            --conf spark.kubernetes.container.image.pullPolicy=Always \
            --conf spark.kubernetes.authenticate.driver.serviceAccountName=spark \
            local:///opt/spark/jars/spark-on-eks-assembly-v1.0.jar \
            \"s3a://nyc-tlc/trip data/yellow_tripdata_2018*.csv\" \
            \"s3a://nyc-tlc/trip data/green_tripdata_2018*.csv\" \
            \"s3a://nyc-tlc/misc/taxi _zone_lookup.csv\" \
-           \"s3a://<YOUR_S3_BUCKET>\""
+           \"s3a://y-smp-bucket01\""
          ]
      serviceAccountName: spark
      restartPolicy: Never
  backoffLimit: 4

```

Job実行前のPodを確認

```sh
ubuntu@ip-10-0-1-96:~/amazon-eks-apache-spark-etl-sample$ kubectl get pods
No resources found.
```

Job実行

```sh
kubectl apply -f example/kubernetes/spark-job.yaml
```

Job実行前のPodを確認
`spark-submit`にて指定したパラメーターに沿ってPodが作成されていることが確認できる

```sh
ubuntu@ip-10-0-1-96:~/amazon-eks-apache-spark-etl-sample$ kubectl get pods
NAME                                READY   STATUS    RESTARTS   AGE
spark-on-eks-1605426627391-driver   1/1     Running   0          45s
spark-on-eks-1605426627391-exec-1   1/1     Running   0          30s
spark-on-eks-1605426627391-exec-2   1/1     Running   0          30s
spark-on-eks-1605426627391-exec-3   1/1     Running   0          30s
spark-on-eks-1605426627391-exec-4   1/1     Running   0          30s
spark-on-eks-qck66                  1/1     Running   0          61s
```

Jobの終了に伴いPodが終了していることを確認

```sh
ubuntu@ip-10-0-1-96:~/amazon-eks-apache-spark-etl-sample$ kubectl get pods
NAME                                READY   STATUS      RESTARTS   AGE
spark-on-eks-1605431350121-driver   0/1     Completed   0          8m35s
spark-on-eks-cstdh                  0/1     Completed   0          8m52s
```

## まとめ
Spark Driverからの要求に応じてKubernetesがリソーススケジューリングを行なっていることが確認できました。