Fluxは、kubernets クラスターで GitOps を実現するツールです。
類似のツールとしては Argo CD などがありますが、Argo CD のように Web UI のような機能はありませんが、そのため比較的シンプルなので小規模なクラスターに向いていると思います。
注意: Flux version 2 は、現在開発中の段階で GA になっていません。本番環境などで使うのは、まだ待ったほうがよさそうです。今回検証したコンテナイメージを自動更新する機能についても、ワークアラウンドな対応が必要な箇所があったり、マルチテナントで使う場合に考慮されていない箇所があったりします。
Flux について
Git でバージョン管理されたものを正しい情報源としてシステムやアプリケーションを管理する仕組みです。
Flux では図のように定期的に対象の Git レポジトリの変更を監視し、変更があった場合に自身のKubernetesクラスターに変更を適用します。
コンテナイメージの自動更新機能について
Flux では更に、コンテナイメージを Docker Hub や ECR などのコンテナレポジトリをスキャンして、更新があった場合に Git の内容を書き換えてコンテナイメージに変更があった場合に自動で更新することができます。
この仕組みを使うことでアプリケーションのデプロイをコンテナイメージをプッシュするだけで実現することができるようになります。
コンテナイメージの自動更新は、Flux の追加コンポーネントである image automation components で実現しています。
動作としては上記のように、コンテナレポジトリをスキャンして変更があった場合にコードを書き換える Image automation の処理と Git レポジトリの変更に対して Kubernetes に適用するコンポーネントは分離されて動作しています。
Flux のレポジトリ構成
Flux では kubernetes のマニフェストファイルを管理する Git のレポジトリ構成をクラスターやアプリケーションの管理を1つのレポジトリで管理する方法と、クラスターの設定とアプリケーションの設定を別々のレポジトリで管理する方法を選ぶことができます。
1つの Git レポジトリを採用する場合、1つのクラスターで1つアプリケーションを動かす、開発と運用が同じチームなど、比較的小規模なクラスター構成で採用すると良いと思います。
逆に、1つのKubernetesクラスターに複数の開発チームがそれぞれのアプリケーションを動かすような場合(マルチテナント)や、開発チームと運用チームの役割が明確に分かれている場合はレポジトリを分けて管理した方が権限の分離もできるのでこちらを採用した方が良いと思います。
この場合、プラットフォーム側の Git レポジトリは運用チームが管理することになり、クラスター全体で使用する設定や、各テナントの namespace や service account の設定、テナントの Git レポジトリの Flux 設定などの設定を行うことになります。
テナント側の Git レポジトリでは、プラットフォーム側から設定された namespace 内で動かすアプリケーションのみの設定に注力できるようになります。(そのため、基本的には設定された namespace 外を操作することは無いです)
Flux でマルチテナントで管理する場合は以下のレポジトリが参考になると思います。
GitHub - fluxcd/flux2-multi-tenancy: Manage multi-tenant clusters with Flux
Flux のコンテナイメージの自動更新機能のセットアップ
残念ながら、コンテナイメージの自動更新機能については基本的にflux-system
の namespace 内で設定しないと現状は動かないようです。
後記するイメージをスキャンするコンテナイメージレポジトリの設定や、イメージタグの命名規則から最新版を定義するポリシーの設定など、アプリケーションのnamespaecで定義した方が良さそうなもの flux-system
の namespace で定義することになるのですが、マルチテナントの場合、テナントのGitレポジトリで管理する場合、アプリケーションが動作する namespace 以外のものを管理することになるし、プラットフォームのGitレポジトリで管理する場合、アプリケーション側の設定が混じることになります。
この辺りは Issue でも上がっているので恐らくGAまでには解消されるんじゃないかなと期待しています。
では、ここでは Flux の基本的なセットアップは完了済みで、コンテナイメージの自動更新機能のセットアップのみを扱います。
Flux のセットアップについては以下から行ってください。
Installation - Flux | GitOps Toolkit
image automation components を有効化する
デフォルトではコンテナイメージの自動更新に必要なコンポーネントが有効になっていないため、flux bootstrap
コマンドで有効化します。弊社の環境では Generic Git Server の設定方法でインストールしたので以下のようになりました。
Flux マニュフェストを再生性
$ flux install \
--components-extra=image-reflector-controller,image-automation-controller \
--export > gotk-components.yaml
コミットしてプッシュ
$ git add .
$ git commit -m "Add flux image automation components"
$ git push
変更を適用させます
$ flux reconcile kustomization flux-system --with-source
適用されたことを確認
$ kubectl get deployments -n flux-system | grep image
image-automation-controller 1/1 1 1 2m
image-reflector-controller 1/1 1 1 2m
イメージスキャンの設定
スキャンするコンテナイメージのコンテナイメージレポジトリを設定します。
もし、AWS ECR などのクラウドプロバイダーのイメージレポジトリを使っている場合、認証の設定を行う必要があるのですが、この辺りはまだちゃんとした実装がされていないのでワークアラウンドな対応が必要になります。GAになる頃には解消されていると思います。
弊社では EKS + ECR を使っているため、まず以下のようなIAMロールを作成します。
- ウェブIDプロバイダーに EKS の OpenID Connect プロバイダーを指定
- アタッチするポリシーは
AmazonEC2ContainerRegistryReadOnly
のみ - 最大セッション時間を
12時間
に設定
あとはマニュアルにあるマニフェストで変更箇所を編集して適用します。
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: ecr-credentials-sync
namespace: flux-system
rules:
- apiGroups: [""]
resources:
- secrets
verbs:
- delete
- create
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: ecr-credentials-sync
namespace: flux-system
subjects:
- kind: ServiceAccount
name: ecr-credentials-sync
roleRef:
kind: Role
name: ecr-credentials-sync
apiGroup: ""
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: ecr-credentials-sync
namespace: flux-system
# Uncomment and edit if using IRSA
# annotations:
# eks.amazonaws.com/role-arn: <role arn>
---
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: ecr-credentials-sync
namespace: flux-system
spec:
suspend: false
schedule: 0 */6 * * *
failedJobsHistoryLimit: 1
successfulJobsHistoryLimit: 1
jobTemplate:
spec:
template:
spec:
serviceAccountName: ecr-credentials-sync
restartPolicy: Never
volumes:
- name: token
emptyDir:
medium: Memory
initContainers:
- image: amazon/aws-cli
name: get-token
imagePullPolicy: IfNotPresent
# You will need to set the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables if not using
# IRSA. It is recommended to store the values in a Secret and load them in the container using envFrom.
# envFrom:
# - secretRef:
# name: aws-credentials
env:
- name: REGION
value: us-east-1 # change this if ECR repo is in a different region
volumeMounts:
- mountPath: /token
name: token
command:
- /bin/sh
- -ce
- aws ecr get-login-password --region ${REGION} > /token/ecr-token
containers:
- image: bitnami/kubectl
name: create-secret
imagePullPolicy: IfNotPresent
env:
- name: SECRET_NAME
value: ecr-credentials
- name: ECR_REGISTRY
value: <account id>.dkr.ecr.<region>.amazonaws.com # fill in the account id and region
volumeMounts:
- mountPath: /token
name: token
command:
- /bin/bash
- -ce
- |-
kubectl delete secret --ignore-not-found $SECRET_NAME
kubectl create secret docker-registry $SECRET_NAME \
--docker-server="$ECR_REGISTRY" \
--docker-username=AWS \
--docker-password="$(</token/ecr-token)"
CronJob として定義しているので初回は手動で実行します。
$ kubectl create job --from=cronjob/ecr-credentials-sync -n flux-system ecr-credentials-sync-init
完了すると認証情報を持ったシークレットが生成されます
$ kubectl get secrets -n flux-system | grep ecr-credentials
ecr-credentials kubernetes.io/dockerconfigjson 1 1m
AWS Elastic Container Registry
次に、ImageRepository
を定義します。ECRなので認証情報として spec.secretRef.name
に ecr-credentails
を指定します。
apiVersion: image.toolkit.fluxcd.io/v1alpha1
kind: ImageRepository
metadata:
name: sample-app
namespace: flux-system
spec:
secretRef:
name: ecr-credentials
image: <AWS_ACCOUNT_ID>.dkr.ecr.<REGION>.amazonaws.com/<IMAGE>
interval: 1m0s
そして イメージのタグから命名規則を定義する ImagePolicy
を定義します。
タグの命名規則は ${GIT_BRANCH}-${GIT_SHORT_SHA}-$(date +%s)
となっていて、main
ブランチでタイムスタンプが最も大きい(新しい)ものを最新とします。
---
apiVersion: image.toolkit.fluxcd.io/v1alpha1
kind: ImagePolicy
metadata:
name: sample-app
namespace: flux-system
spec:
imageRepositoryRef:
name: sample-app
filterTags:
pattern: '^main-[a-fA-F0-9]+-(?P<ts>.*)'
extract: '$ts'
policy:
numerical:
order: asc
マニフェストを適用後、コマンドで確認します。
$ flux get image repository sample-app
NAME READY MESSAGE LAST SCAN SUSPENDED
sample-app True successful scan, found 2 tags 2021-04-02T11:43:15+09:00 False
$ flux get image policy sample-app
NAME READY MESSAGE LATEST IMAGE
sample-app True Latest image tag for '<イメージURL>' resolved to: main-e2dc7e21-1617154068 <イメージURL>:main-e2dc7e21-1617154068
ただし今の状態では最新のイメージは検知できても、Deployment などのコンテナイメージを書き換えることはできないので、書き換えたいイメージの箇所にマーカーを記載します。
Kustomization を使っている場合は以下のようになります。
apiVersion: kustomize.toolkit.fluxcd.io/v1beta1
kind: Kustomization
metadata:
name: sample-app
namespace: sample-app
spec:
images:
- name: <イメージURL>
newName: <イメージURL>
newTag: latest # {"$imagepolicy": "flux-system:sample-app"}
Git レポジトリに書き込みを行う設定を ImageUpdateAutomation
で定義します
apiVersion: image.toolkit.fluxcd.io/v1alpha1
kind: ImageUpdateAutomation
metadata:
name: flux-system
namespace: flux-system
spec:
checkout:
branch: main
gitRepositoryRef:
name: sample-app
commit:
authorEmail: fluxcd@example.com
authorName: fluxcd
messageTemplate: '{{range .Updated.Images}}{{println .}}{{end}}'
interval: 1m0s
push:
branch: main
update:
path: ./clusters/my-cluster
strategy: Setters
これらを適用の後、新しくコンテナイメージをビルドしてイメージレポジトリにプッシュするとプッシュした最新のコンテナイメージにマニフェストが書き換えられて、最新のコンテナイメージがKubernetsにデプロイされます。
Git のログにコミットされている
$ git log -1
commit xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Author: flux <flux@gitlab.rux.jp>
Date: Wed Mar 31 09:24:46 2021 +0000
<AWS_ACCOUNT_ID>.dkr.ecr.ap-northeast-1.amazonaws.com/sample-app:main-e2dc7e21-1617178407
git pull
してファイルを確認しても newTag
も最初に設定した latest
から main-e2dc7e21-1617178407
に書き換わっているのがわかります。
apiVersion: kustomize.toolkit.fluxcd.io/v1beta1
kind: Kustomization
metadata:
name: sample-app
namespace: sample-app
spec:
images:
- name: <イメージURL>
newName: <イメージURL>
newTag: main-e2dc7e21-1617178407 # {"$imagepolicy": "flux-system:sample-app"}
まとめ
残念ながら現状ではもう少しな部分もありますが、GAになる頃にはこの辺りが解消されることを期待しています。
A/B テストや Blue/Green デプロイメインとについては Flux project の中で、Flagger という別のツールがあるのでこちらと組み合わせるといいのかなと思います。