LambdaからEKSを操作する

## はじめに
KubernetesではJobやCronJobが用意されていますが、ジョブ内容によってはLambdaに寄せてもよい場合があります。
今回はEKSクラスタに対するkubectl操作をLambdaから実施するための手順を説明します。

## 設定手順

### Lambda Layerのデプロイ

AWSコンソールのLambdaのページから作成していきます。
![スクリーンショット 2020-12-13 23.01.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307853/762d17ce-808d-85cd-fcbd-ce69e0be5d6f.png)


Serverless Application Repositoryから[lambda-layer-kubectl](https://github.com/aws-samples/aws-lambda-layer-kubectl)を選択します。
![スクリーンショット 2020-12-13 23.06.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307853/456ff877-f544-688a-f3bc-e6caccf75fc8.png)


LayerNameに任意の名前を入力しデプロイします。
![スクリーンショット 2020-12-13 23.11.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307853/f96c4ef9-3a18-071a-3d25-9658e38dea30.png)

### IAM Roleの作成
Lambda関数で使用するIAM Roleを`lambda-kubectl`という名前で作成します。

EKSの権限を持つ以下のようなIAM Policyをアタッチします。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "ec2:*",
                "eks:*"
            ],
            "Resource": "*",
            "Effect": "Allow"
        }
    ]
}
```

また、信頼関係には以下のようにLambdaサービスを入れておきます。

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
```

### aws-authの更新

以下のようにaws-authを編集しapplyします。

```diff:aws-auth.yaml
apiVersion: v1
data:
  mapRoles: |
+    - rolearn: 'arn:aws:iam::XXXXXXXXXXXX:role/lambda-kubectl'
+      username: 'lambda-kubectl'
+      groups:
+        - 'system:masters'
kind: ConfigMap
metadata:
  name: aws-auth
  namespace: kube-system
```

```console
$ kubectl apply -f aws-auth.yaml
```

### Lambda関数の作成

Lambda Layer同様、AWSコンソールのLambdaのページから作成していきます。
![スクリーンショット 2020-12-13 23.01.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307853/762d17ce-808d-85cd-fcbd-ce69e0be5d6f.png)

カスタムランタイムを使用し、先述のIAM Roleを付与します。

![スクリーンショット 2020-12-14 0.43.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307853/0cb811fd-c4ac-67f0-3baa-c4e1355a2fc0.png)

まずはLambda Layerを追加します。
![スクリーンショット 2020-12-14 0.46.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307853/4346bdfa-5a87-0ca7-b6e1-a0a25a375304.png)

ARNを指定して追加します。
![スクリーンショット 2020-12-14 0.49.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307853/12097499-6474-45d2-7958-92f28363b972.png)

`bootstrap`にkubectlの処理を追記します。

```diff:bootstrap
#!/bin/sh
set -euo pipefail

# Handler format: <script_name>.<bash_function_name>
#
# The script file <script_name>.sh  must be located at the root of your
# function's deployment package, alongside this bootstrap executable.
source $(dirname "$0")/"$(echo $_HANDLER | cut -d. -f1).sh"

while true
do
+   export PATH=$PATH:/opt/awscli:/opt/kubectl
+   aws eks update-kubeconfig --name test-cluster --kubeconfig /tmp/kubeconfig
+   kubectl get node --kubeconfig /tmp/kubeconfig
    # Request the next event from the Lambda runtime
    HEADERS="$(mktemp)"
    EVENT_DATA=$(curl -v -sS -LD "$HEADERS" -X GET "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next")
    INVOCATION_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2)

    # Execute the handler function from the script
    RESPONSE=$($(echo "$_HANDLER" | cut -d. -f2) "$EVENT_DATA")

    # Send the response to Lambda runtime
    curl -v -sS -X POST "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$INVOCATION_ID/response" -d "$RESPONSE"
done
```

## 確認

Lambdaのテストイベントでジョブを実行してみます。
関数で定義したようにEKSに対して操作できていることが確認できます。

![スクリーンショット 2020-12-14 1.08.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307853/01e2e555-17f5-f9a1-aec0-ff18017a44de.png)


## まとめ

LambdaからEKSを操作する方法を説明しました。
使用したLambda Layerの[lambda-layer-kubectl](https://github.com/aws-samples/aws-lambda-layer-kubectl)にはAWS CLIやkubectlの他、helmやjqも同梱されているので汎用性が高いです。

EKSがprivateである場合はLambdaを同一VPC内で実行するようにしましょう。