はじめに
こんにちは。SRE部の巣立(@ksudate)です。
ZOZOTOWNのマイクロサービス基盤では、GitHub Actionsを利用したCDパイプラインを構築しています。しかし、管理するマイクロサービスが増えるにつれて運用負荷が高まりつつありました。
本記事では、ZOZOTOWNのマイクロサービス基盤のCDパイプラインが抱える課題と、それらをFlux2でどのように解決したのかを紹介します。また、Flux2の導入にあたり工夫したポイントを紹介します。
目次
Flux2の導入背景
マイクロサービス基盤のCI/CDパイプラインが抱える課題とこれまでの対策
マイクロサービス基盤では、アプリケーションのソースコードとKubernetesマニフェストを別々のリポジトリで管理しています。また、マニフェストを管理するリポジトリは複数のアプリケーションのマニフェストを含んでいます。
それぞれのリポジトリ上で稼働するCI/CDパイプラインは以下のような構成になっていました。
アプリケーションリポジトリではECRへImageがPushされます。PushされたImageをクラスタへ反映するにはインフラリポジトリのマニフェストを更新します。そのため、KubernetesクラスタへのデプロイはインフラリポジトリのGitHub Actionsから実行されます。GitHub ActionsによるCI/CDパイプラインで実行するkubectl diff
とkubectl apply
は全てのマイクロサービスに対して実行します。そのため、マイクロサービスが増えるとCI/CDが完了するまでの時間も増えていく構成となります。
CI/CDパイプラインではKubernetesクラスタへデプロイするJobだけでなくCloudFormationやTerraformを利用したデプロイを行うJobも同じワークフローで管理しています。そのため、CloudFormation・Terraformに対する変更の場合でも毎回kubectl diff
とkubectl apply
が実行されます。
そこでpaths-filterを利用することにしました。paths-filterを使うことでPull Requestに含まれる変更内容によって実行するJobを制御できるようになります。例えば、Kubernetesマニフェストに変更があった場合のみkubectl diff
とkubectl apply
を実行するといったことが可能になります。
paths-filterによりCI/CDの高速化に成功したのですが、依然としてkubectl diff
とkubectl apply
の長期化は解決しない状況でした。そのため、paths-filterを利用してマニフェストに変更があったマイクロサービスに対してkubectl diff
とkubectl apply
を実行する構成を考えました。しかし、この方法では条件分岐が増えることでワークフローが複雑になったり、マイクロサービスが増えるたびにJobが肥大化したりといくつか問題がありました。
そこで候補として上がったのがFlux2です。
Flux2とは?
Flux2はGitリポジトリで宣言された状態とクラスタの状態を同期するGitOpsツールの1つです。類似のOSSとしてArgoCDが挙げられます。Flux2ではKustomizeで組み立てられたマニフェストをクラスタへ同期できます。そのため、Kustomizeを利用してマイクロサービスごとにマニフェストを生成し同期することが可能になります。
今回、ArgoCDではなくFlux2を採用したのは既にマイクロサービス基盤で利用しているFlaggerがFlux2と同じFlux Projectに所属しているため親和性が高いことを期待しました。Flaggerについては近日テックブログで公開する予定です。
Flux2によるGitOpsの実現
ここでは、Flux2のアーキテクチャと仕組みについて簡単に解説します。
Flux2はGitOps Toolkitと呼ばれるいくつかのコンポーネントにより動作します。
Source Controllerでは、Git、S3などからアーティファクトという形で外部ソースを取得します。アーティファクトはGitコミットのハッシュ値つまり、リビジョン番号を持ち、アーティファクトが更新されるとリビジョン番号も更新されます。
Kustomize Controllerでは、Source Controllerが取得したアーティファクトを元にクラスタへマニフェストを適用します。クラスタへ適用する間隔はCustom Resourceの設定により可能ですが、リビジョン番号が更新された場合は設定に関係なくクラスタへ適用されます。また、アーティファクトからマニフェストを生成する際には、kustomize build
を使用します。
その他Flux2が提供する機能について詳しくは公式ドキュメントをご覧ください。
Flux2の同期の設定は、Custom Resourceにより設定できます。ここでは、GitRepositoryとKustomizationの2つのCustom Resourceについて簡単に紹介します。
GitRepositoryでは、Source ControllerがGitリポジトリからソースを取得する際の設定を書きます。これを各マイクロサービス毎に作成しています。同じNamespaceに複数のGitRepositoryを配置できるため、複数のマイクロサービスが配置されている場合でも問題なく動作します。
--- apiVersion: source.toolkit.fluxcd.io/v1beta2 kind: GitRepository metadata: name: flux-system namespace: flux-system spec: interval: 1m0s ref: branch: master secretRef: name: zozo-flux-system-secrets-202206141800 url: https://github.com/stefanprodan/podinfo
KustomizationではKustomize Controllerがマニフェストをクラスタへ適用する際の設定を書きます。KustomizationもGitRepository同様にマイクロサービス毎に作成しています。なお、検証作業等のためにクラスタへの適用を一時的に停止したい場合は、spec.suspend
をtrue
にします。
--- apiVersion: kustomize.toolkit.fluxcd.io/v1beta2 kind: Kustomization metadata: name: flux-system namespace: flux-system spec: interval: 1m0s path: ./k8s/dev/gitops-toolkit prune: false sourceRef: kind: GitRepository name: flux-system suspend: false
ここで紹介したKustomizationはKustomizeで利用するKustomizationとは異なることに注意して下さい。
apiVersion: kustomize.config.k8s.io/v1beta1 apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
これらのCustom Resourceによりspec.interval
で指定した時間毎にクラスタへの同期が行われます。また、Flux CLIを使って即時の同期も可能です。
flux reconcile kustomization flux-system --with-source
実際にFlux2導入後のマイクロサービス基盤のCI/CDパイプラインは以下のような構成になります。これまでのgit push
を契機としたデプロイ手法であるCIOpsから、ソースをpullして差分を適用するGitOpsへとデプロイ手法が変わりました。変更後のCI/CDパイプラインでは、Pull Requestが作成されるとGitHub Actionsによりkubectl diff
が実行されます。Pull Requestがmergeされた後、GitRepositoryの設定によりFlux2がリポジトリの更新を検知するとクラスタへ反映します。
これまでのCI/CDパイプラインではマニフェストの適用が完了するまでに10分から15分ほどかかっていました。しかし、マイクロサービス毎のデプロイが可能となりマニフェスト適用までの時間を1分以内に短縮できました。
Flux2の導入で工夫したポイント
ここでは、マイクロサービス基盤にFlux2を導入した際に工夫したポイントをいくつか紹介します。
Flux2の管理
Flux2もKubernetes上で動作するアプリケーションの1つです。そのため、Flux2本体(Source ControllerやKustomize Controllerなど)をFlux2自身で管理可能です。
初回導入時のみ手動でインストールを行い、それ以降はFlux2自身で管理するようにしています。
GitRepositoryとKustomizationの管理
マイクロサービス基盤では、マイクロサービスごとにGitRepositoryとKustomizationを作成しています。この2つもFlux2により同期を行なっています。このマイクロサービスごとに作成された同期設定を管理するGitRepositoryとKustomizationは初回導入時のみ手動でインストールを行い、それ以降はFlux2で管理するようにしています。
Flux2によるkustomize build
マイクロサービス基盤ではCIパイプラインでkubectl diff
を実行します。そこで利用するマニフェストはkustomize build
を実行し生成しています。生成されたマニフェストがFlux2のKustomize Controllerが適用するマニフェストと異なっていては困ります。そのため、いくつかのオプションを付与しFlux2が使用するKustomizeと同じ動作を実現しています。
kustomize build --load-restrictor=LoadRestrictionsNone --reorder=legacy . \ | kubectl diff --server-side --force-conflicts -f -
--load-restrictor=LoadRestrictionsNone
では、kustomization.yaml
が配置されたディレクトリ外からファイルを読み込むことを許可します。
--reorder=legacy
では、生成したマニフェストを出力する順番に関するオプションです。legacy
では、NamespaceとClusterRole/RoleBindingが最初に出力され、CRの前にCRD、最後にWebhookが出力されます。
詳しくはFlux2のFAQをご覧ください。
今後の展望
アプリケーションの自動更新
Flux2では、ECRなどのImageリポジトリからImageを取得し、Gitリポジトリへコミットする機能があります。この機能を利用し、さらなるデプロイ時間の短縮に取り組んでいきます。
権限の最小化
Flux2のようにクラスタ上でソースとの差分を適用するというGitOps方式により、GitHub ActionsのようなCDパイプライン側に秘匿情報を渡す必要がなくなりました。しかし、現状のCIパイプラインでは秘匿情報が必要です。今後はCDパイプラインのみならずCIパイプラインの改善も取り組んでいきます。
さいごに
Flux2を導入することで既存のCDパイプラインが抱えていた課題を解決できました。Flux2を活用することで改善できる課題はまだまだあります。
引き続き、快適な開発・運用を実現すべく改善していきます。
ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。