Lambdaのコンテナイメージからkubectlを実行してEKSを操作してみます。AWS Lambda runtime API
辺りが少し面倒ですが、総じてとても便利です。
なじみの人が多いだろうシェルスクリプトで実装していきます。
はじめに
kubectlをLambdaで実行すると便利な場面もあると思っていて、実際に以下のような情報で今までもできたと思います。せっかくコンテナイメージが使えるようになったので、Dockerfileからビルドして利用してみようと思います。
最初に読んでおくべきもの
まずは日本語の資料で全体像つかむのが良いと思います。
上記にもありますが、Lambdaのサービスと関数をイベント連携させるため、AWS Lambda runtime API
を実装していく必要があります。
AWSからもいくつかの言語用にLambda Runtime API実装済みイメージが提供されているもありますが、今回は自分で実装してみます。その際には以下のチュートリアルが参考になると思います。(これ自体はコンテナイメージの説明ではないですが実装はほぼ同じです)
実装
Lambda実行要のIAMロールの作成
Lambda実行用に以下のような名前のロールを作成します。
arn:aws:iam::123456789012:role/kubectl-lambda
以下のような権限を付けました。
- プライベートのEKSクラスタ扱うので、VPCでのLambda実行権限。
- ECRからのコンテナイメージ読み込み権限
- EKSクラスタの認証トークン取得用の権限
LambdaかあAssumeするための設定もお忘れ無く。
EKSクラスタへのKubenetesロールのバインディング
上記で作成したIAMロールにEKSのロールをBindingします。
この記事ではkube-systemのPod一覧取ろうと思うので、kube-system名前空間のView権限付けてみます。
$ ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text) $ cat <<EOF > lambda-kubectl-role-binding.yml --- apiVersion: v1 kind: ConfigMap metadata: name: aws-auth namespace: kube-system data: mapRoles: | - rolearn: arn:aws:iam::${ACCOUNT_ID}:role/kubectl-lambda groups: - lambda-kubectl --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: lambda-kubectl-rolebinding namespace: kube-system roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: view subjects: - kind: Group namespace: kube-system name: lambda-kubectl EOF $ kubectl apply -f lambda-kubectl-role-binding.yml
設定を確認します。
$ kubectl get cm -n kube-system aws-auth -oyaml
イメージの作成
ディレクトリ構成
Dockerfileと二つのShellで実装しています。
$ tree . ├── bootstrap.sh ├── Dockerfile ├── handler.sh
handler.sh
やりたいことは handler()
関数の中だけで、他は AWS Lambda runtime API
への対応となります。
Lambdaは /tmp
に書き込み可能なのでkubectlのconfig等は /tmp
配下に設定しています。
#!/bin/sh set -euo pipefail handler () { EVENT_DATA=$1 echo "$EVENT_DATA" export KUBECONFIG=/tmp/kubeconfig echo "update kubeconfig" /usr/local/bin/aws eks update-kubeconfig --name ${CLUSTER_NAME} --kubeconfig ${KUBECONFIG} echo "exec kubectl" 1>&2 kubectl --cache-dir /tmp/kube-cache --log-dir /tmp/kube-log get all -n ${K8S_NAMESPACE} echo "completed" } while true do HEADERS="$(mktemp)" # Get an event. The HTTP request will block until one is received EVENT_DATA=$(curl -sS -LD "$HEADERS" -X GET "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next") # Extract request ID by scraping response headers received above REQUEST_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2) # Run the handler handler ${EVENT_DATA} # Send the response curl -s -X POST "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/response" -d "OK" done
bootstrap.sh
上記の handler.sh
を呼び出しています。
aws-lambda-rieというのはLambdaのエミュレータでローカルテスト時に利用します。
#!/bin/sh export _HANDLER=/aws/handler.sh if [ -z "${AWS_LAMBDA_RUNTIME_API}" ]; then exec /usr/local/bin/aws-lambda-rie $_HANDLER else exec $_HANDLER fi
Dockerfile
AWS CLI使うために amazon/aws-cli
からビルドしてみましたが、AWS CLIを別途インストールすれば、ベースイメージは別のでも大丈夫です。
FROM amazon/aws-cli # AWS Lambda Runtime Interface Emulator インストール RUN curl -L -o /usr/local/bin/aws-lambda-rie https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie RUN chmod 755 /usr/local/bin/aws-lambda-rie # kubectlインストール RUN curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.20.0/bin/linux/amd64/kubectl && \ chmod +x ./kubectl && \ mv ./kubectl /usr/local/bin/kubectl # スクリプトの配置 ADD ./bootstrap.sh /aws/bootstrap ADD ./handler.sh /aws/handler.sh RUN chmod +x /aws/handler.sh RUN chmod +x /aws/bootstrap ENTRYPOINT ["/aws/bootstrap"]
ローカルテスト
ローカルテストの方法も簡単に書いておきます。
以下のように、イメージをビルドして8080に繋げた形でコンテナ起動します。起動するとすぐに待機状態になります。
$ docker build -t aws-kubectl . $ docker run -p 9000:8080 aws-kubectl
別のコンソールから以下のようにアクセスすると処理が動きます。必要に応じてイベントを渡したりも可能です。
$ curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"EVENT":"DUMMY"}'
Lambdaを設定して実行
細かい手順は省きますが、Lambdaに設定していきます。
IAMロールは先程作成したものを設定します。
DockerのイメージはECRにアップしておいて指定します。
環境変数を指定します。
今回のスクリプトでは CLUSTER_NAME
と K8S_NAMESPACE
のみ必須です。
閉域ネットワークでPROXY設定が必要な場合は追加で設定を行います。NO_PROXYの設定辺りが参考になりそうだったので記載しておきます。
あとは実行するだけです。以下のようにkubectlなじみの出力が取得できました。 (空のクラスタなのでcoredns起動してないですが・・)
所感
気になった点としては、 Lambdaのイメージ変更が1分程度かかります、また、 起動が少し遅いですが、ほとんどの管理タスク程度なら全く問題無い程度。もしかしたら、リアルタイムでの同期処理とかは辛い場面もありそうです。
AWS Lambda runtime API辺りが少し面倒です。
ただ、Cronみたいに使うとすると、ログがCloudWatchに残るのも便利だったり、依存関係で複雑なことすることなくなるので便利なポイント沢山のサービスに感じてます。