SPECIALIST

多様な専門性を持つNRIデジタル社員のコラム、インタビューやインサイトをご紹介します。

BACK

GitOps化を通じた「AWS Controllers for Kubernetes」の実用性検証

こんにちは、NRIデジタルの島です。

コンテナベースのアプリケーションを構築、運用していく場合、多くの場合コンテナを継続的に運用・管理していくコンテナオーケストレーションツールが必要になってきます。AWSにおけるコンテナオーケストレーションサービスには「Elastic Container Service(ECS)」や「Elastic Kubernetes Service(以下EKS)」などがありますが、AWS上でKubernetes(以下k8s)の恩恵を受けられることから、今後EKSを選択する利用者は増えていくのではないかと思っております。

k8sの恩恵は多々ありますが、その一つに「CustomResourceDefinitions(以下CRD)」 があります。CRDを使用すると、k8s環境への柔軟なリソース追加や機能拡張が可能になります。

CRD
Extend the Kubernetes API with CustomResourceDefinitions

EKSを使用してアプリケーションを構築する場合、アプリケーションの要件がそのコンテナサービス内のみで満たせることは少なく、各AWSサービスと連携するユースケースがほとんどかと思います。その際、「アプリケーション構築は開発チーム、連携するAWSサービスの構築はインフラチーム」というようにチームを跨って管理が分断されてしまうことや、「アプリケーションの設定はk8sのマニフェスト、連携するAWSサービスは手動」というようにリソース管理手段が分断されてしまうことも多いと思います。これらはアプリケーション開発のアジリティを損なう要因になりかねません。

本記事で紹介する「AWS Controllers for Kubernetes」はk8sのCRDを利用して、使用するAWSサービスをコンテナ内リソースと同等に扱うことが出来るツールであり、上記の分断管理の課題を解決できるソリューションとなっています。

AWS Controllers for Kubernetes

概要

AWS Controllers for Kubernetes(以下ACK)は、k8sのCRD機能における「カスタムリソース」や「カスタムコントローラ」の仕組みを利用して、k8sから利用する各種AWSサービスの作成やライフサイクル管理を実行できるようにAWSが開発したツール(AWSサービス用のカスタムリソース/コントローラ)となります。

※カスタムリソース/コントローラについては以下をご参照ください
カスタムリソース

ACKを利用すると、それらのAWSリソースを個別に管理することなく、k8sクラスタ内で一元管理できるようになります。
ACKの実態はk8sクラスタのデータプレーン上で稼働するPodとなります。Podとして稼働している各AWSサービスのコントローラ(下図例では「ack-s3-controller」)がk8sのコントロールプレーン上のAPI Server(下図「kube-apiserver」)と常時会話をし、実際のAWSサービスの状態を、applyされているリソース定義情報に整えます(※1)。

ACK公式ページより

※1
このようにリソースをあるべき状態に整えるk8sの機能を「Reconciliation Loop」といい、各コントローラによって実現されています。
コントローラー

ACKのメリット

ACKもCDKやTerraform等と同様にリソースをコード化してデプロイするいわゆる「Infrastructure as Code(IaC)」の一種と捉えることができますが、筆者が考えるACKのメリットは以下となります。

  1. マニフェストファイルを定義さえすれば、AWSサービスを作成・変更可能
  2. アプリケーションで利用するAWSサービスの構築をインフラチームに依頼するような開発スキームの場合、構築までのリードタイムが開発のボトルネックになる可能性があります。
    ACKを利用すると、開発者自身が、利用するAWSサービスまで構築できるようになる為、開発業務がスムーズになります。

  3. マニフェストを作成するので、インフラ設計のマスタにすることができる
  4. SSOT(Single Source of Truth 唯一の信頼できる情報)を実現できます。

  5. APIを直接コールする為、デプロイが高速
  6. CDKやTerraformはCloudFormationをラップしているツールの為デプロイが遅いですが、ACKはCloudFormationを介さず、コントローラから各AWSサービスのAPIを直接コールする為、数秒で反映できます。

  7. k8sの「Reconciliation Loop」機能により、適切な状態を維持できる
  8. 各AWSサービスを手動で設定変更(誤削除や手動設定ミス)してしまった場合もマニフェストを正として自動リカバリが可能となります。

  9. k8sと連携可能なツールを利用でき、ACKと組み合わせて利用できる
  10. k8sのエコシステム上の様々な製品と連携できる為、Argo CDやFlux等を利用したGitOpsなどが実現可能となります。

  11. k8sで利用しているツールをそのまま利用可能(e.g. kubectl apply・・・)
  12. CDKやTerraformのように、それを実装、実行する為の独自のツール等のインストールが不要で、kubectlなどk8s標準ツールがあれば利用できます。

※上記メリットのいくつかはCDKやTerraformでも同様ですが、yamlマニフェストレベルでより容易に管理できる(複雑なコード記述は不要)ことがポイントになると思っています

ACK検証(サマリ)

このACKついて、先日いくつかのサービスが一般利用可能になったとアナウンスがありました。
ACKコントローラーが一般提供を開始

早速、実際にEKS上にACKとArgo CDをインストールし、各種リソースのデプロイを試してみました。次項よりその検証内容を共有させていただきますが、検証を通しての筆者の感想、気になった事項を先に共有したいと思います。

まず全体的な使用感ですが、アプリケーションが利用する各AWSサービスを、アプリケーションと同レベル(手順等含め)で扱えることが体感できました。これは、開発者自身がTry and ErrorでAWSサービスも含めて変更、テストを迅速に繰り返し実施できるということになり、これは開発のアジリティ向上における大きなメリットになると感じました。また、前述した通り、ACKはCloudFormationを介さず、APIを直コールします。検証を通して、作成時も変更時も削除時も、適用後ほんの数秒で反映されておりました。これはCDKやTerraformにはない大きなメリットかと思います。

一方、使用するにあたり気になるポイントについてもお伝えしておきます。使用是非のご判断のご参考になれば幸いです。

  1. 対応済みサービスが少ない
  2. 一般利用可能となったとはいえ、執筆時点ではGA済みのサービスはまだまだ少ない状態です。
    ※公式ドキュメントにおいて各サービスの対応状況が確認できます
    Services

    また、仮にGAしているサービスでも、一部の機能だけに留まっているケース(例えばAPI Gatewayが対応しているのは「HTTP API及びWebsocket API」であり、「REST API」に対応しておりません)もある為、サービスだけでなく、サービス内の利用予定機能が対応しているかどうかも確認が必要です。

  3. API仕様が理解しにくい
  4. マニフェスト作成する為にはその定義方法を知らなければなりません。基本的には公式ページのAPIリファレンスを参照することになります。
    API Reference

    しかしながら、各項目の説明もデータ型のみだったり、サンプルが少なかったりと、執筆時点ではまだ十分に充実した内容にはなっていない印象です。

    e.g. API Gateway Integration

    ACKは「CloudFormation」の仕様をベースに作成されているようで、基本的には定義方法もほぼ同様となっています。上記APIリファレンスを見てもわからない場合は、CloudFormationのドキュメントを参照すると良いと思います。もしそれでもうまくいかない場合は、ACKのGitHubページのソースコードやサンプルをご確認いただければと思います。
    aws-controllers-k8s

  5. 「Reconciliation Loop」の間隔が長い
  6. 前述のメリット説明で、ACKを使用することで「k8sのReconciliation Loopの恩恵を享受できる」と述べました。しかしながら実際に試してみたところ、そのループ間隔が非常に長いようで、迅速な状態チェックはされてないようでした。確認したところ、以下Issueにもある通り、Reconciliation 自体は実装に含まれているものの、頻繁なリソースの確認は CPU 負荷が高い為、執筆時点では 8 時間の間隔のようです。

    [Reconcillation issue in s3 controller #1288] Reconcillation issue in s3 controller · Issue #1288 · aws-controllers-k8s/community ''' We have common logic in all of the ACK controllers that when the resource reaches its desired state, it will pause reconciliation for a while. It would be CPU intensive to continuously check for drift at a relatively short interval, so we leave that to the K8s API server default requeue time, which I think is 8 hours.

    ここは今後の改善に期待したいところですが、執筆時点ではこのような仕様になっていることをご認識いただければと思います。

では、次項より検証内容を共有させていただきますので、興味ある方は是非ご参照ください。

ACK検証

システム構成

本検証で構築するシステム構成は以下の通りです。
※EKSはデータプレーンにEC2を使用しています

本検証では「外部からのデータを受け付け、非同期に処理を行う」ようなユースケースを想定しました。
簡単な処理の流れは以下の通りです。

  1. クライアントからAPI Gateway経由でJSONを受信し、Application Load Ballancer(以下ALB)経由でPod(REST API)へ転送する ※API Gateway→ALB→Podの通信はPrivate
  2. JSONを受信したPod(REST API)はそのJSONをS3バケットへ書き込む(S3 Put Object)
  3. S3のPutイベントによりLambda関数が起動される(S3 Put Event)
  4. Lambda関数より該当のJSONを取得する(S3 Get Object)
  5. 取得したJSONをDynamoDBへ書き込む(DynamoDB Put Item)

 

※非同期処理であれば「Amazon Simple Queue Service(SQS)」利用にしたいところでしたが、執筆時点ではACK未対応だった為、ACKに対応している「S3」及び「S3 Event」で代用しております

また、前述した通りEKSはk8sに準拠している為、k8sエコシステムの製品を容易に導入可能です。今回はArgo CDを導入し、ACKのカスタムリソースや各種マニフェストをデプロイしていきました。
Argo CD

前提条件

以降の流れを実施するには以下の事前準備が必要です。

ACKコントローラ

まず、ACKコントローラをインストール、設定していきます。

  1. ACKコントローラのインストール
  2. インストールは以下公式ページの手順で簡単にインストール可能です。(Helmによるインストール)
    Install an ACK Controller

    インストール後、正常にPodが起動しました。

    # helm list
    >helm list -n ack-system
    NAME                            NAMESPACE       REVISION        UPDATED                                 STATUS          CHART                           APP VERSION
    ack-apigatewayv2-controller     ack-system      1               2022-06-07 08:06:54.536616213 +0000 UTC deployed        apigatewayv2-chart-v0.1.0       v0.1.0
    ack-dynamodb-controller         ack-system      1               2022-06-07 08:08:18.47455996 +0000 UTC  deployed        dynamodb-chart-v0.1.0           v0.1.0
    ack-ecr-controller              ack-system      1               2022-06-07 08:05:08.285228847 +0000 UTC deployed        ecr-chart-v0.1.0                v0.1.0
    ack-iam-controller              ack-system      1               2022-06-07 08:05:36.612184657 +0000 UTC deployed        iam-chart-v0.0.15               v0.0.15
    ack-lambda-controller           ack-system      1               2022-06-07 08:05:58.013244891 +0000 UTC deployed        lambda-chart-v0.0.16            v0.0.16
    ack-s3-controller               ack-system      1               2022-06-07 08:03:17.493889296 +0000 UTC deployed        s3-chart-v0.0.20                v0.0.20
    
    # get pod
    >kubectl get po -n ack-system
    NAME                                                              READY   STATUS    RESTARTS   AGE
    ack-apigatewayv2-controller-apigatewayv2-chart-5d7df474c6-t4brg   1/1     Running   0          99s
    ack-dynamodb-controller-dynamodb-chart-5fd44ffbf6-97s4w           1/1     Running   0          15s
    ack-ecr-controller-ecr-chart-6f4d6f66f4-thstm                     1/1     Running   0          3m25s
    ack-iam-controller-iam-chart-68db864587-mxpjv                     1/1     Running   0          2m57s
    ack-lambda-controller-lambda-chart-579cd85ddb-jwhm9               1/1     Running   0          2m36s
    ack-s3-controller-s3-chart-665f6c5679-l9wf4                       1/1     Running   0          5m17s
    
  3. コントローラのアクセス制御
  4. 各コントローラに対してAWSリソースを操作できる権限を「IAM Roles for Service Accounts (IRSA) 」で与えます。
    ※WorkerNodeのIAMロールへの直接的な権限付与は過剰な権限付与になる可能性がある為、「IAM Roles for Service Accounts (IRSA) 」で Pod レベルのアクセスコントロールを付与することが推奨されています

以下公式ページの手順を全コントローラに対して実施しました。
IAM roles for service accounts – Amazon EKS

以下例のように各コントローラに対して、IAMロールやWeb ID Tokenが設定されていれば正常です。

# DynamoDBコントローラ 例
kubectl describe po ack-dynamodb-controller-dynamodb-chart-55f7d79bf5-xd9gw  -n ack-system | grep "^\s*AWS_"
      AWS_REGION:                      ap-northeast-1
      AWS_ENDPOINT_URL:
      AWS_ROLE_ARN:                    arn:aws:iam::XXXXXXXXXXXX:role/ack-dynamodb-controller-role
      AWS_WEB_IDENTITY_TOKEN_FILE:     /var/run/secrets/eks.amazonaws.com/serviceaccount/token

Argo CD

ACKやその他のマニフェストをベースにGitOpsする為に、Gitリポジトリの準備とArgo CDのインストール、設定をします。
※Argo CDも構成図の通り、Podとして稼働します

  1. Gitリポジトリの準備
  2. 今回はBitBucketにてリポジトリを作成し、マニフェストを格納するディレクトリを準備します。

     

  3. Argo CDのインストール
  4. ACKやその他のマニフェストをベースにGitOpsする為にArgo CDをインストールします。
    インストールは以下公式ページの手順でインストール、設定しました。
    Getting Started – Argo CD – Declarative GitOps CD for Kubernetes

    今回はCLIで対象クラスタを追加し、その後はGUI画面から対象クラスタ、①のリポジトリ、マニフェスト対象ディレクトリを指定してアプリケーションを作成しました。

なお、Argo CDのGUI画面へのアクセスについてhttpでアクセスしたい場合は、以下のようにinstall.yamlを修正する必要があります。(「argocd-server」のContainerに「− − insecure」を追加)

# install.yaml
---略---
 containers:
      - command:
        - argocd-server
        - --insecure  ←※追加
        env:
        - name: ARGOCD_SERVER_INSECURE
---略---

アプリケーション

アプリケーション(Podとして稼働させるREST APIとLambda関数(実行IAMロールを含む))をデプロイしていきます。今回両アプリケーションはDockerコンテナとして稼働させる為、Dockerイメージを格納する「ECRリポジトリ」とAPI GatewayがPod(REST API)へ連携する為の「ALB」もあわせて作成します。

  1. ECRリポジトリの作成
  2. ACKを使用して、アプリケーションのイメージを格納するECRリポジトリを作成します。
    ※API用とLambda用2つのリポジトリを作成

    以下のマニフェストを作成し、BitBucketのリポジトリへPushします。

    #################################
    # Repository
    #################################
    apiVersion: ecr.services.k8s.aws/v1alpha1
    kind: Repository
    metadata:
      name: ecrr-coe-val-stg0-api
      namespace: coe-system
    spec:
      imageScanningConfiguration:
        scanOnPush: true
      name: coe-api-repo
    ---
    apiVersion: ecr.services.k8s.aws/v1alpha1
    kind: Repository
    metadata:
      name: ecrr-coe-val-stg0-function
      namespace: coe-system
    spec:
      imageScanningConfiguration:
        scanOnPush: true
      name: coe-function-repo
    

    Argo CDのGUIでマニフェストが認識されます。

    同期します。

    EKS上にECR Repositoryのリソースがデプロイされ、ACK ECRコントローラにより実際のリポジトリが作成されます。

    >kubectl get repository -n coe-system
    NAME                         AGE
    ecrr-coe-val-stg0-api        17s
    ecrr-coe-val-stg0-function   17s
    

  3. イメージのPush
  4. 作成したアプリケーションのイメージを①で作成したリポジトリにPushします。

    # ECRへログイン
    >aws ecr get-login-password --region ap-northeast-1 | sudo docker login --username AWS --password-stdin https://XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com
    Login Succeeded
    
    # APIアプリケーションのイメージ
    >sudo docker push XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/coe-api-repo:0.0.1
    The push refers to repository [XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/coe-api-repo]
    727be6559cfd: Pushed
    b6bbb8e20db6: Pushed
    0f1b6278031e: Pushed
    d14c97a375c7: Pushed
    d51348c0e837: Pushed
    b4886229869b: Pushed
    426e0bc3b80d: Pushed
    e6b57a4b7e93: Pushed
    c4c461dc990d: Pushed
    0.0.1: digest: sha256:cd8532d86473fba75ef5c59840c5a07117bdcce39b6e23e3a0c664da5f08eaf6 size: 2208
    
    # Lambda関数のイメージ
    >sudo docker push XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/coe-function-repo:0.0.1
    The push refers to repository [XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/coe-function-repo]
    87899ff4e869: Pushed
    20716ea1d5b7: Pushed
    b5a0b85cd06a: Pushed
    a17ea8ccee27: Pushed
    e44da8829878: Pushed
    929f92908dc5: Pushed
    365acf1eba74: Pushed
    79672c89d4a0: Pushed
    0.0.1: digest: sha256:b1c41deb3c90a5b75e557041b04cf96d0e201c18273b52a9a920bbbbcc2bb1f7 size: 2004
    
  5. PodとALBのデプロイ
  6. API用PodとALBをデプロイします。
    以下のマニフェストを作成し、BitBucketのリポジトリへPush後、Argo CDよりデプロイします。

    ※ALBはIngressリソースとして作成します。
    また、後続で作成するAPIGatewayとPrivate統合としたい為「Internal」で作成します。

    #################################
    # Deployment
    #################################
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: coe-rest-api
      namespace: coe-system
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: coe-rest-api
      progressDeadlineSeconds: 600
      strategy:
        type: RollingUpdate
        rollingUpdate:
          maxSurge: 50%
          maxUnavailable: 50%
      template:
        metadata:
          name: coe-rest-api
          labels:
            app: coe-rest-api
        spec:
          containers:
            - name: coe-rest-api
              image: XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/coe-api-repo:0.0.1 ←ECRリポジトリへPushしたイメージ
              imagePullPolicy: IfNotPresent
              ports:
                - containerPort: 18081
              resources:
                limits:
                  cpu: "250m"
                  memory: "512Mi"
              readinessProbe:
                httpGet:
                  path: /actuator/health/readiness
                  port: 18081
                initialDelaySeconds: 10
                periodSeconds: 5
                timeoutSeconds: 1
                successThreshold: 1
                failureThreshold: 5
              livenessProbe:
                httpGet:
                  path: /actuator/health/liveness
                  port: 18081
                    - ./
                initialDelaySeconds: 10
                periodSeconds: 5
                timeoutSeconds: 1
                successThreshold: 1
                failureThreshold: 5
          affinity:
            podAntiAffinity:
              preferredDuringSchedulingIgnoredDuringExecution:
                - weight: 100
                  podAffinityTerm:
                    labelSelector:
                      matchExpressions:
                        - key: app
                          operator: In
                          values: ["coe-rest-api"]
                    topologyKey: failure-domain.beta.kubernetes.io/zone
                - weight: 1
                  podAffinityTerm:
                    labelSelector:
                      matchExpressions:
                        - key: app
                          operator: In
                          values: ["coe-rest-api"]
                    topologyKey: kubernetes.io/hostname
    ---
    #################################
    # Service
    #################################
    apiVersion: v1
    kind: Service
    metadata:
      name: coe-rest-api-svc
      namespace: coe-system
      labels:
        app: coe-rest-api-svc
    spec:
      ports:
        - name: http
          port: 18081
          targetPort: 18081
          protocol: TCP
      selector:
        app: coe-rest-api
    ---
    ################################
    # Ingress
    #################################
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      namespace: coe-system
      name: coe-rest-api-ing
      annotations:
        alb.ingress.kubernetes.io/scheme: internal
        alb.ingress.kubernetes.io/target-type: ip
        alb.ingress.kubernetes.io/subnets: 'subnet-XXXXXXXXXXXXXXXXX,subnet-XXXXXXXXXXXXXXXXX'
        alb.ingress.kubernetes.io/security-groups: 'sg-XXXXXXXXXXXXXXXXX,sg-XXXXXXXXXXXXXXXXX'
        alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}]'
        alb.ingress.kubernetes.io/healthcheck-path: '/actuator/health/readiness'
        alb.ingress.kubernetes.io/success-codes: '200-399'
    spec:
      ingressClassName: alb
      rules:
        - http:
            paths:
              - path: /
                pathType: Prefix
                backend:
                  service:
                    name: coe-rest-api-svc
                    port:
                      number: 18081
    

    Argo CDで同期。

    正常に各リソースがデプロイされました。

    >kubectl get po,svc,ing -n coe-system
    NAME                                READY   STATUS    RESTARTS   AGE
    pod/coe-rest-api-86886fcc5c-pnrk5   1/1     Running   0          74s
    pod/coe-rest-api-86886fcc5c-vm48l   1/1     Running   0          74s
    
    NAME                       TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)     AGE
    service/coe-rest-api-svc   ClusterIP   172.20.229.49   <none>        18081/TCP   74s
    
    NAME                                         CLASS   HOSTS   ADDRESS                                                                                PORTS   AGE
    ingress.networking.k8s.io/coe-rest-api-ing   alb     *       internal-k8s-coesyste-coeresta-7aca3ea431-721558692.ap-northeast-1.elb.amazonaws.com   80      74s
    
  7. Lambda関数のデプロイ
  8. ACKを使用して、DynamoDBにデータを書き込むLambdaをデプロイします。
    以下のマニフェストを作成し、BitBucketのリポジトリへPush後、Argo CDよりデプロイします。

    ※Lambdaの実行ロールもACKで作成します

#################################
# Role
#################################
apiVersion: iam.services.k8s.aws/v1alpha1
kind: Role
metadata:
  name: irl-coe-val-stg0-lambda
  namespace: coe-system
spec:
  assumeRolePolicyDocument: >
    {
      "Version": "2012-10-17",
      "Statement": [ {
        "Effect": "Allow",
        "Principal": {
          "Service": [
            "lambda.amazonaws.com"
          ]
        },
        "Action": [ "sts:AssumeRole" ]
      } ]
    }
  description: "role for coe lambda function.."
  name: coe-function-role
  policies:
    - arn:aws:iam::aws:policy/AWSLambda_FullAccess
    - arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess
    - arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
    - arn:aws:iam::aws:policy/CloudWatchLogsFullAccess
---
#################################
# Function
#################################
apiVersion: lambda.services.k8s.aws/v1alpha1
kind: Function
metadata:
  name: lmdf-coe-val-stg0-dkr
  namespace: coe-system
spec:
  name: coe-function
  packageType: Image
  code:
    imageURI: XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/coe-function-repo:0.0.1 ←ECRリポジトリへPushしたイメージ
  role: arn:aws:iam::XXXXXXXXXXXX:role/coe-function-role ←上記Role名
  memorySize: 512
  timeout: 15

Argo CDで同期。

以下の通り、正常にLambda関数がデプロイされました。

Lambda実行ロール

Lambda関数

※注意
今回のユースケースでは、S3イベントによりLambda関数をキックできるようにLambda設定にリソースベースポリシーの適用が必要ですが、ACKのLambdaはまだ「Preview」の為、「Lambda::Permission」リソースが使用できません。その為、今回は以下の通り手動で設定しました。

その他AWSサービス

前項までに未デプロイのAWSサービス(S3/DynamoDB/API Gateway)をACKを使用してデプロイしていきます。

手順は前項のECR等と同様の為、適用したマニフェストと作成された実リソースのみ掲載します。

S3

マニフェスト

※S3イベントでLambdaをキックするユースケースの為「notification」の設定が必要

# s3-bucket.yaml
#################################
# Bucket
#################################
apiVersion: s3.services.k8s.aws/v1alpha1
kind: Bucket
metadata:
  name: s3-coe-val-stg0-bucket
  namespace: coe-system
spec:
  name: coe-items-bucket
  notification:
    lambdaFunctionConfigurations:
      - events:
          - s3:ObjectCreated:Put
        lambdaFunctionARN: arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:coe-function ←前項で作成したLambda関数名

S3バケット

DynamoDB

マニフェスト
※ハッシュキーのみの単純なテーブルです

#################################
# Table
#################################
apiVersion: dynamodb.services.k8s.aws/v1alpha1
kind: Table
metadata:
  name: ddbt-coe-val-stg0
  namespace: coe-system
spec:
  attributeDefinitions:
    - attributeName: id
      attributeType: S
  billingMode: PAY_PER_REQUEST
  keySchema:
    - attributeName: id
      keyType: HASH
  tableName: coe-ddbt-table

DynamoDB テーブル

API Gateway

API GatewayはALBを経由してPod(REST API)とPrivate統合で連携します。
Understanding VPC links in Amazon API Gateway private integrations

※上記ブログより引用

マニフェスト
※上記の通りPrivate統合としたい為「VPC Link」も作成しています

#################################
# API
#################################
apiVersion: apigatewayv2.services.k8s.aws/v1alpha1
kind: API
metadata:
  name: agw-coe-val-stg0-api
  namespace: coe-system
spec:
  name: agw-coe-api
  protocolType: HTTP
---
#################################
# Integration
#################################
apiVersion: apigatewayv2.services.k8s.aws/v1alpha1
kind: Integration
metadata:
  name: itg-coe-val-stg0-api
  namespace: coe-system
spec:
  apiRef:
    from:
      name: agw-coe-val-stg0-api
  connectionType: VPC_LINK
  connectionRef:
    from:
      name: vpcl-coe-val-stg0-api
  integrationType: HTTP_PROXY
  integrationURI: arn:aws:elasticloadbalancing:ap-northeast-1:XXXXXXXXXXXX:listener/app/k8s-coesyste-coeresta-7aca3ea431/f436e085bb226b51/755ccd3f2fceb1ce ←リスナーARN(※1)
  integrationMethod: POST
  payloadFormatVersion: "1.0"
---
#################################
# VPCLink
#################################
apiVersion: apigatewayv2.services.k8s.aws/v1alpha1
kind: VPCLink
metadata:
  name: vpcl-coe-val-stg0-api
  namespace: coe-system
spec:
  name: vpcl-coe-api
  securityGroupIDs:
    - sg-XXXXXXXXXXXXXXXXX
  subnetIDs:
    - subnet-XXXXXXXXXXXXXXXXX
    - subnet-XXXXXXXXXXXXXXXXX
---
#################################
# Route
#################################
apiVersion: apigatewayv2.services.k8s.aws/v1alpha1
kind: Route
metadata:
  name: rte-coe-val-stg0-api
  namespace: coe-system
spec:
  apiRef:
    from:
      name: agw-coe-val-stg0-api
  routeKey: POST /api/v1/items
  targetRef:
    from:
      name: itg-coe-val-stg0-api
---
#################################
# Stage
#################################
apiVersion: apigatewayv2.services.k8s.aws/v1alpha1
kind: Stage
metadata:
  name: stg-coe-val-stg0-api
  namespace: coe-system
spec:
  apiRef:
    from:
      name: agw-coe-val-stg0-api
  stageName: $default
  autoDeploy: true
  description: "auto deployed stage for agw-coe-val-stg0-api"

※1
VPC Link経由の通信となる為、上記マニフェストのIntegrationリソース「integrationURI」は前項で作成したALBのリスナーARNを指定します。

※注意
執筆時点では、ACKは「REST_APIタイプ」をサポートしておりません。作成しようとすると以下の通りのエラーとなりますのでご注意ください。
ERROR controller.api Reconciler error {"reconciler group": "apigatewayv2.services.k8s.aws", "reconciler kind": "API", "name": "agw-coe-val-stg0-api", "namespace": "coe-system", "error": "BadRequestException: Invalid protocol specified. Must be one of ["HTTP, WEBSOCKET]"}
今回は「HTTP_API」タイプを使用しています。

 

API

VPC Link

ルート/統合

ステージ

以上で構築完了です。
全てデプロイ完了すると、Argo CD上では全てのリソースが「Synced」となります。

アプリケーションの実行

構築したアプリケーションが正常に動作するかを確認します。
以下の通りの特定の端末からCurlでリクエストしてみます。

curl -v -X POST -H "Content-Type: application/json" -d "{\"item1\":\"001\",\"item2\":\"002\",\"item3\":\"003\",\"item4\":\"004\",\"item5\":\"005\"}" https://XXXXXXXXXX.execute-api.ap-northeast-1.amazonaws.com/api/v1/items

作成したS3バケットにリクエストしたJSONが想定通り保存されました。

また、作成したDynamoDBのテーブルにJSONデータが正常に登録されました。

以上の通り、AWSリソースを個別で作成することなく、アプリケーションの実行に必要なリソースをマニフェストのみで作成することができました。Argo CD等のGitOpsツールを使用することで状態の可視化も簡単に行えました。

さいごに

昨今、日々刻々と変化するビジネス要求に対応する為に、アプリケーションに対する変更要求は短期間で発生し続けます。その要求に迅速に対応していく為には、アプリケーションレイヤのみでなく、インフラレイヤも同時に迅速かつ臨機応変に対応していかなければなりません。
EKSを採用しているシステムにおいて、ACKはCDKやTerraformにはない迅速性、リコンサイルによる整合性担保などを備えており開発アジリティ向上に向けた優れたツールだと思います。
まだまだ開発途上で課題も多いですが、今後のアップデートを期待しながら、採用検討を進めていきたいと思います。

以上