のべラボ.blog

謙虚に、臆せず、さぼらずにブログを書く

AWS Step Functions と Amazon EKS の連携でハマったこと

AWS Step Functions のステートマシンから Amazon EKS クラスターで Job を実行しようとしたときにハマったことをメモしておきます。

最初は、次の AWS Blog の記事がキッカケでした。

AWS Step FunctionsとAmazon EKSの統合のご紹介

この記事では、AWS Step Functions と Amazon EKS の統合について記載されてます。

これを、自分なりに試してみようと思ったわけです。

ただシンプルに試したかったので、EKS クラスターで Job の実行を行うだけのステートマシンを作成することにしました。

まず、自分が既に用意している EKS クラスターのエンドポイントや CertificateAuthority の情報を用意しておきます。

これは AWS マネジメントコンソール で EKSのクラスターの情報を表示すれば参照できますが、今回は AWS CLI で取得しています。

次のコマンドは dev-cluster というクラスターのエンドポイントを取得する例です。

aws eks describe-cluster --name dev-cluster | jq '.cluster.endpoint'

結果の例

"https://xxxxxxxxxxxxxxx.yz5.ap-northeast-1.eks.amazonaws.com"

次は CertificateAuthority を取得する例です。

aws eks describe-cluster --name dev-cluster | jq '.cluster.certificateAuthority.data'

結果の例 (実際は長い文字列ですが、省略しています。)

"LS0t6UFBwS29jU ...  0tCg=="

次に、AWS Step Functions で 標準タイプのステートマシンを作成していきます。

WorkFlow Builderで、次の図のように作成します。③ の APIパラメータに、EKS クラスター名やエンドポイント、CertificateAuthority を設定します。

③ の APIパラメータで、実行する Job のマニフェストを指定できます。デフォルトのままでもいいのですが、Job の実行時間を短縮したかったので、print bpi(2000) の個所を print bpi(20) に変更しました。

次の例は、APIパラメータで、Job のマニフェスト部分を抜粋したものです。

        "Job": {
          "apiVersion": "batch/v1",
          "kind": "Job",
          "metadata": {
            "name": "my-example-job20"
          },
          "spec": {
            "template": {
              "metadata": {
                "name": "my-example-job"
              },
              "spec": {
                "containers": [
                  {
                    "name": "my-function-name",
                    "image": "perl",
                    "command": [
                      "perl"
                    ],
                    "args": [
                      "-Mbignum=bpi",
                      "-wle",
                      "print bpi(20)"
                    ]
                  }
                ],
                "restartPolicy": "Never"
              }
            }
          }
        }
      },

次に、[ステートマシン設定を指定] のページの [アクセス許可] のセクションで、ステートマシンに設定するIAM ロールを指定します。 今回は、新しい IAM ロールを作成するように指定します。

そしてステートマシンの作成を完了させると、自動作成された IAM ロールの ARN が表示されるので、それをコピーします。

この IAM ロールは、 EKS クラスターで Job を実行する許可を与える必要があるため、EKS クラスターに接続できる環境から eksctl コマンドを使って次を実行します。

なお、AWSアカウント ID や IAM ロール名、EKS クラスター名は環境に応じて変更する必要があります。

実はこれがハマるポイントなのですが、説明は後にして、ひとまずこのまま実行します。

eksctl create iamidentitymapping --cluster dev-cluster --arn arn:aws:iam::000000000000:role/service-role/StepFunctions-EKS-RunJob-StateMachine-role-4b53263d --group system:masters --username my-statemachine

このコマンドは、あくまで例の一つですが、ステートマシンの IAM ロールと Kubernetes の system:masters グループをマッピングすることで、ステートマシンが EKS クラスターで Job を実行することを許可しています。

これで、ステートマシンから EKS クラスターに対して Job を実行する準備が整いました。

しかし、ステートマシンを実行すると、次のようなエラーが出てしまいます。

エラー

EKS.401
原因

{
  "ResponseBody": {
    "kind": "Status",
    "apiVersion": "v1",
    "metadata": {},
    "status": "Failure",
    "message": "Unauthorized",
    "reason": "Unauthorized",
    "code": 401
  },
  "StatusCode": 401,
  "StatusText": "Unauthorized"
}

さきほどの ekstctl コマンドで、IAM ロールを system:masters グループにマッピングしたはずなのに、なぜ Unauthorized になるのか理解できず、しばらく悩みました。

しかし、次の Step Functions のドキュメントをみると、この原因がわかりました。

Permissions | Call Amazon EKS with Step Functions

このドキュメントには、Note として次のような記載があります。

Note You may see the ARN for an IAM role displayed in a format that includes the path /service-role/, such as arn:aws:iam::123456789012:role/service-role/my-role. This service-role path token should not be included when listing the role in aws-auth.

つまり、ekstctl コマンドで、IAM ロールを system:masters グループにマッピングするときに、IAM ロールのARNに /service-role/ というパスを含めてはいけない! ということなんですね。

では、修正していきます。

まず、さきほどのマッピングを削除します。

eksctl delete iamidentitymapping --cluster dev-cluster --arn arn:aws:iam::000000000000:role/service-role/StepFunctions-EKS-RunJob-StateMachine-role-4b53263d  

次に、/service-role/ を含まない ARN を指定して、マッピングを作成します。

eksctl create iamidentitymapping --cluster dev-cluster --arn arn:aws:iam::000000000000:role/StepFunctions-EKS-RunJob-StateMachine-role-4b53263d --group system:masters --username my-statemachine

このあと、ステートマシンを実行すると問題なく Job を起動することができました。

また、EKS クラスター側でも Job が完了したことを確認できました。

$ kubectl get jobs
NAME               COMPLETIONS   DURATION   AGE
my-example-job     1/1           4s         4m21s

$ kubectl describe  job my-example-job
Name:           my-example-job
Namespace:      default
Selector:       controller-uid=cc291e67-8d12-4f64-b6c3-01e330957bed
Labels:         controller-uid=cc291e67-8d12-4f64-b6c3-01e330957bed
                job-name=my-example-job
Annotations:    <none>
Parallelism:    1
Completions:    1
Start Time:     Sun, 26 Jun 2022 00:24:58 +0000
Completed At:   Sun, 26 Jun 2022 00:25:02 +0000
Duration:       4s
Pods Statuses:  0 Running / 1 Succeeded / 0 Failed
Pod Template:
  Labels:  controller-uid=cc291e67-8d12-4f64-b6c3-01e330957bed
           job-name=my-example-job
  Containers:
   my-function-name:
    Image:      perl
    Port:       <none>
    Host Port:  <none>
    Command:
      perl
    Args:
      -Mbignum=bpi
      -wle
      print bpi(20)
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Events:
  Type    Reason            Age    From            Message
  ----    ------            ----   ----            -------
  Normal  SuccessfulCreate  4m33s  job-controller  Created pod: my-example-job-8k5xr
  Normal  Completed         4m29s  job-controller  Job completed

ハマったといっても実はドキュメントには Note として記載してあったので、うっかりのレベルではあります。

ただ、ステートマシン作成時に IAM ロールを作成して、そのままコンソールに表示されている ARN をコピーしてしまうということは、ありがちではないかと思うので注意したいと思います。