Flux CD用の AWS CloudFormation コントローラーがリリース(プレビュー)されました

2023.05.11

「Flux でCloudFormationをデプロイ! CloudFormationでもPull型のGitOpsが始まる!?」

Fluxを使用して、CloudFormationをデプロイできるようになりました。

以下のアップデートの紹介ブログです。

Introducing the AWS CloudFormation Template Sync Controller for Flux

Fluxとは

Gitリポジトリで宣言された状態とKubernetesクラスタの状態を同期するツールです。

類似のツールとしては、Argo CDなどがあげられます。

CDに使われるツールでGitリポジトリ上のコードが変更されたら、自動的にクラスターにデプロイすることができます。

「GitHub ActionsやAWSのCodeシリーズなどのCI/CDツールと何が違うの?」と思う方もいるかもしれません。

FluxやArgo CDなどはGitOpsツール(Pull型)と言われます。(Github Actions等はCIOps(Push型))

リポジトリへのPushをトリガーにするわけではなく、FluxがKubernetesクラスター上でGitリポジトリの変更をポーリング(Pull)して変更があったら反映(デプロイ)するためです。

Flux

CIOpsとGitOpsについては、以下の記事の説明が分かりやすいです。

CIOpsとGitOpsの話 - inductor's blog

何が嬉しい?

CloudFormationをFluxで管理できることで、以下のようなメリットがあります。

  • 正しい設定にリソースを保ちやすい
  • KubernetesとCloudFormationのCDを1つのツールで行える
  • CD用の権限をAWS外部に渡す必要がない

正しい設定にリソースを保ちやすい

Push型のCIOpsでは、CloudFormationが実行されるのはトリガー時(特定ブランチへのPush等)だけです。

もし手動でAWSリソースが変更された場合、次のPushまではAWSリソースとコードの状態が一致しません。

しかし、Pull型のGitOpsは定期的にGitリポジトリをPollingします。

Gitリポジトリの状態を正として、リソースに差分があればGitリポジトリで定義されている状態に変更します。

リソースを正しい設定状態に保ちやすいのがメリットだと思います。  

KubernetesとCloudFormationのCDを1つのツールで行える

既にFluxを使用していてかつ、CloudFormationも使っている場合、CloudFormation用にCDの仕組みを作らなくても、FluxにCDを統一できるメリットもあります。

CD用の権限をAWS外部に渡す必要がない

Pull型のGitOpsのメリットとして、認証情報を外部に渡す必要がないというのも挙げられます。

Github ActionsやCircle CI等でCloudFormationのデプロイを行う場合は、AWS外部に認証情報を渡す必要がありました。

EKSに権限を渡せばいいので、AWS外部に認証情報を渡す必要がありません。 (Codeシリーズだったら、AWS外部に渡さないでCloudFormationデプロイは可能ですが。)

やってみる

前提条件

以下をローカルPC上で使用できるようにしておく必要があります。

構成図

FluxのCloudFormation Controllerを使用して、CloudFormation Stackを作成してみます。

CloudFormationでは、Parameter Storeを作成します。

フォルダ構成

最終的なフォルダ構成を紹介します。

CodeCommitを2つ使用します。(手順中のサンプルリソース作成用CloudFormationでデプロイします)

リポジトリ名 概要
my-flux-configuration Fluxマニフェストファイル保存用
my-cloudformation-templates CloudFormationテンプレートファイル保存用
# my-flux-configuration リポジトリ
$ tree my-flux-configuration/
my-flux-configuration
├── cfn-controller-source.yaml # cfn controllerのGitリポジトリ設定
├── cfn-controller.yaml # cfn controllerデプロイ
├── cfn-stack.yaml # cfn stackデプロイ
├── cfn-templates-repo.yaml # my-cloudformation-templatesのGitリポジトリ設定
└── flux-system
    ├── gotk-components.yaml
    ├── gotk-sync.yaml
    └── kustomization.yaml
# my-cloudformation-templates リポジトリ
$ tree my-cloudformation-templates/
my-cloudformation-templates
└── template.yaml # デプロイするCloudFormationテンプレート

eksctlでクラスター作成

$ eksctl create cluster \
--name flux-cfn-sample \
--region ap-northeast-1 \
--node-type t3.small \
--nodes 2

FluxのCloudFormationコントローラーがリソースを操作できるように権限を付与します。

本来なら必要な権限に絞るべきですが、検証のためPowerUserAccessを与えます。

$ eksctl utils associate-iam-oidc-provider --cluster flux-cfn-sample --approve
$ eksctl create iamserviceaccount \
    --cluster flux-cfn-sample \
    --namespace flux-system \
    --name cfn-controller \
    --role-only \
    --role-name "AWSCloudFormationControllerFluxIRSARole" \
    --attach-policy-arn arn:aws:iam::aws:policy/PowerUserAccess \
    --approve

Fluxの設定ファイル保存用のCodeCommit等の準備

Fluxの設定ファイルやCloudFormationテンプレート等を置くCodeCommitやS3バケットを用意します。

サンプルのCloudFormationを使用して、必要なリソースを作成します。

Fluxのインストール

作成したクラスターでFluxを実行可能か確認します。

問題なければ以下のような出力があります。

$ flux check --pre

► checking prerequisites
✔ Kubernetes 1.25.3 >=1.20.6-0
✔ prerequisites checks passed

リポジトリはサンプルCloudFormationで作成されるCodeCommitを使用します。

flux-gitというIAMユーザーが作成されているため、CodeCommit認証情報を生成してflux bootstrapを行います。

flux bootstrap git \
  --url=https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/my-flux-configuration \
  --branch=main \
  --username=<my-username> \
  --password=<my-password> \
  --token-auth=true

bootstrapを実施したことで、EKSにはFluxのインストールおよび、CodeCommitにFluxを使用するために必要なファイルがpushされました。

CloudFormationコントローラリポジトリをFluxに登録する

CloudFormationコントローラーをFluxで使用できるようにするために、リポジトリをFluxに登録します。

任意のディレクトリにFluxのCodeCommitリポジトリをCloneします。

git clone codecommit::ap-northeast-1://my-flux-configuration

以下のファイルを作成して、CodeCommitにPushします。

cfn-controller-source.yaml

apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: GitRepository
metadata:
  name: aws-cloudformation-controller-for-flux
  namespace: flux-system
spec:
  interval: 1h
  timeout: 60s
  ref:
    branch: main
  url: https://github.com/awslabs/aws-cloudformation-controller-for-flux
git add .
git commit -m "add: cfn controller repo"
git push origin HEAD

Pushまでできたら、以下のコマンドを実行してFluxがリポジトリに正常に接続できることを確認します。

$ flux reconcile source git flux-system
$ flux reconcile source git aws-cloudformation-controller-for-flux

Fluxを使用してCloudFormationコントローラーをデプロイする

CloudFormationコントローラーをデプロイします。

以下のファイルを用意して、CodeCommitにPushします。

cfn-controller.yaml

apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
  name: aws-cloudformation-controller-for-flux
  namespace: flux-system
spec:
  interval: 5m
  path: ./config/default
  prune: true
  wait: true
  timeout: 5m
  sourceRef:
    kind: GitRepository
    name: aws-cloudformation-controller-for-flux
  patches:
    - patch: |
        apiVersion: v1
        kind: ServiceAccount
        metadata:
          name: cfn-controller
          annotations:
            eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/AWSCloudFormationControllerFluxIRSARole # AWSアカウントIDを書き換える
      target:
        kind: ServiceAccount
        name: cfn-controller
    - patch: |
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: cfn-controller
        spec:
          template:
            spec:
              containers:
              - name: manager
                env:
                  - name: AWS_REGION
                    value: "ap-northeast-1"
                  - name: TEMPLATE_BUCKET
                    value: "<バケット名>" # サンプルCFnで作成したバケット名
      target:
        kind: Deployment
        name: cfn-controller
git add .
git commit -m "add: cfn controller"
git push origin HEAD

以下のコマンドでデプロイを確認します。

$ flux reconcile source git flux-system
$ flux reconcile kustomization aws-cloudformation-controller-for-flux
$ kubectl rollout status deployment/cfn-controller -n flux-system
$ kubectl logs deployment/cfn-controller -n flux-system

CloudFormationコントローラーのFlux通知の有効化

CloudFormationコントローラーはサードパーティーコントローラーのため、以下の設定を追加して通知を有効化します。

flux-system/kustomization.yaml

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - gotk-components.yaml
  - gotk-sync.yaml
patches:
  - patch: |
      - op: add
        path: /spec/versions/0/schema/openAPIV3Schema/properties/spec/properties/eventSources/items/properties/kind/enum/-
        value: CloudFormationStack
      - op: add
        path: /spec/versions/1/schema/openAPIV3Schema/properties/spec/properties/eventSources/items/properties/kind/enum/-
        value: CloudFormationStack
    target:
      kind: CustomResourceDefinition
      name:  alerts.notification.toolkit.fluxcd.io
  - patch: |
      - op: add
        path: /spec/versions/0/schema/openAPIV3Schema/properties/spec/properties/resources/items/properties/kind/enum/-
        value: CloudFormationStack
      - op: add
        path: /spec/versions/1/schema/openAPIV3Schema/properties/spec/properties/resources/items/properties/kind/enum/-
        value: CloudFormationStack
    target:
      kind: CustomResourceDefinition
      name:  receivers.notification.toolkit.fluxcd.io
  - patch: |
      - op: add
        path: /rules/-
        value:
          apiGroups: [ 'cloudformation.contrib.fluxcd.io' ]
          resources: [ '*' ]
          verbs: [ '*' ]
    target:
      kind: ClusterRole
      name:  crd-controller-flux-system

CloudFormationテンプレートリポジトリにテンプレートファイルをPush

CloudFormationテンプレートファイル用のリポジトリにテンプレートファイルをPushします。

このテンプレートファイルがFluxによって、デプロイされます。

$ git clone codecommit::ap-northeast-1://my-cloudformation-templates
$ cd my-cloudformation-templates
$ touch template.yaml

template.yaml

Resources:
  SampleResource:
    Type: AWS::SSM::Parameter
    Properties:
      Type: String
      Value: "Hello World"
$ git add .
$ git commit -m "add: temaplte file"
$ git push origin HEAD

CloudFormation用のCodeCommitリポジトリと接続

FluxがCloudFormation用のCodeCommitリポジトリの内容を見れるように設定を行います。

bootstrap時に使用したCodeCommitの認証情報をSecretに登録します。

$ CODECOMMIT_USERNAME="<CodeCommitユーザー名>"
$ CODECOMMIT_PASSWORD="<CodeCommitパスワード>"
$ flux create secret git cfn-template-repo-auth \
    --url=https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/my-cloudformation-templates \
    --username=$CODECOMMIT_USERNAME \
    --password=$CODECOMMIT_PASSWORD

fluxの設定リポジトリに移動して、以下のファイルを作成します。

$ cd my-flux-configuration
$ touch cfn-templates-repo.yaml cfn-stack.yaml

cfn-templates-repo.yamlはCloudFormation用のCodeCommitリポジトリを登録します。

cfn-templates-repo.yaml

apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: GitRepository
metadata:
  name: my-cfn-templates-repo
  namespace: flux-system
spec:
  url: https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/my-cloudformation-templates
  interval: 5m
  ref:
    branch: main
  secretRef:
    name: cfn-template-repo-auth

cfn-stack.yamlでは、デプロイするCloudFormation Stackの設定を行います。

このファイルで、同期の間隔やStack名などを定義します。

cfn-stack.yaml

apiVersion: cloudformation.contrib.fluxcd.io/v1alpha1
kind: CloudFormationStack
metadata:
  name: my-cfn-stack
  namespace: flux-system
spec:
  stackName: my-cfn-stack-deployed-by-flux
  templatePath: ./template.yaml
  sourceRef:
    kind: GitRepository
    name: my-cfn-templates-repo
  interval: 1h
  retryInterval: 5m
  destroyStackOnDeletion: true

ファイルの用意ができたら、リポジトリにPushします。

$ git add .
$ git commit -m "add: cfn stack file"
$ git push origin HEAD

動作確認: CloudFormation Stackデプロイ確認

先ほどのファイルのPushができたら、自動的にCloudFormation Stackが作成されます。

まずは、kubectlコマンドで確認してみます。

デプロイが成功していれば、以下のような出力を確認できます。

$ kubectl describe cfnstack my-cfn-stack --namespace flux-system
Name:         my-cfn-stack
Namespace:    flux-system
Labels:       kustomize.toolkit.fluxcd.io/name=flux-system
              kustomize.toolkit.fluxcd.io/namespace=flux-system
Annotations:  <none>
API Version:  cloudformation.contrib.fluxcd.io/v1alpha1
Kind:         CloudFormationStack
Metadata:
  Creation Timestamp:  2023-05-10T08:43:54Z
  Finalizers:
    finalizers.cloudformation.contrib.fluxcd.io
  Generation:  1
  Managed Fields:
    API Version:  cloudformation.contrib.fluxcd.io/v1alpha1
    Fields Type:  FieldsV1
# 省略

マネジメントコンソールからも確認してみましょう。

Flux CDで定義したスタックmy-cfn-stack-deployed-by-fluxが確認できます。

CloudFormationを変更して、CodeCommitにPushで自動的にデプロイされるか確認します。

Parameter StoreのValueをHello WorldからHello Flux CloudFormation Controllerに変更しました。

commitして、CodeCommitにPushしました。

$ git diff
diff --git a/template.yaml b/template.yaml
index 51b5e79..752a177 100644
--- a/template.yaml
+++ b/template.yaml
@@ -3,4 +3,4 @@ Resources:
     Type: AWS::SSM::Parameter
     Properties:
       Type: String
-      Value: "Hello World"
+      Value: "Hello Flux Cloudformation Controller"

少し待つと変更した値にパラメータストアの値が変わっていることを確認できました。

クリーンアップ

EKSクラスターを削除します。

$ eksctl delete cluster --name=flux-cfn-sample

まとめ

Terraform用のControllerはあったみたいですが、CloudFormationもきましたね。

How to GitOps Your Terraform | Flux

EKSの運用が必要になるので少し敷居は高いと思いますが、CloudFormationでもGitOpsが可能になるのは激アツだと思いました。

以上、AWS事業本部の佐藤(@chari7311)でした。

参考