CIOpsからGitOpsへ。Flux2でマイクロサービスのデプロイを爆速にした話

CIOpsからGitOpsへ。Flux2でマイクロサービスのデプロイを爆速にした話

はじめに

こんにちは。SRE部の巣立(@ksudate)です。

ZOZOTOWNのマイクロサービス基盤では、GitHub Actionsを利用したCDパイプラインを構築しています。しかし、管理するマイクロサービスが増えるにつれて運用負荷が高まりつつありました。

本記事では、ZOZOTOWNのマイクロサービス基盤のCDパイプラインが抱える課題と、それらをFlux2でどのように解決したのかを紹介します。また、Flux2の導入にあたり工夫したポイントを紹介します。

目次

Flux2の導入背景

マイクロサービス基盤のCI/CDパイプラインが抱える課題とこれまでの対策

マイクロサービス基盤では、アプリケーションのソースコードとKubernetesマニフェストを別々のリポジトリで管理しています。また、マニフェストを管理するリポジトリは複数のアプリケーションのマニフェストを含んでいます。

それぞれのリポジトリ上で稼働するCI/CDパイプラインは以下のような構成になっていました。

Flux導入前のCI/CDパイプライン

アプリケーションリポジトリではECRへImageがPushされます。PushされたImageをクラスタへ反映するにはインフラリポジトリのマニフェストを更新します。そのため、KubernetesクラスタへのデプロイはインフラリポジトリのGitHub Actionsから実行されます。GitHub ActionsによるCI/CDパイプラインで実行するkubectl diffkubectl applyは全てのマイクロサービスに対して実行します。そのため、マイクロサービスが増えるとCI/CDが完了するまでの時間も増えていく構成となります。

CI/CDパイプラインではKubernetesクラスタへデプロイするJobだけでなくCloudFormationやTerraformを利用したデプロイを行うJobも同じワークフローで管理しています。そのため、CloudFormation・Terraformに対する変更の場合でも毎回kubectl diffkubectl applyが実行されます。

そこでpaths-filterを利用することにしました。paths-filterを使うことでPull Requestに含まれる変更内容によって実行するJobを制御できるようになります。例えば、Kubernetesマニフェストに変更があった場合のみkubectl diffkubectl applyを実行するといったことが可能になります。

paths-filter導入後のWorkflow

paths-filterによりCI/CDの高速化に成功したのですが、依然としてkubectl diffkubectl applyの長期化は解決しない状況でした。そのため、paths-filterを利用してマニフェストに変更があったマイクロサービスに対してkubectl diffkubectl 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と呼ばれるいくつかのコンポーネントにより動作します。

Flux2のアーキテクチャ

Source Controllerでは、Git、S3などからアーティファクトという形で外部ソースを取得します。アーティファクトはGitコミットのハッシュ値つまり、リビジョン番号を持ち、アーティファクトが更新されるとリビジョン番号も更新されます。

Kustomize Controllerでは、Source Controllerが取得したアーティファクトを元にクラスタへマニフェストを適用します。クラスタへ適用する間隔はCustom Resourceの設定により可能ですが、リビジョン番号が更新された場合は設定に関係なくクラスタへ適用されます。また、アーティファクトからマニフェストを生成する際には、kustomize buildを使用します。

その他Flux2が提供する機能について詳しくは公式ドキュメントをご覧ください。

fluxcd.io

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.suspendtrueにします。

---
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導入後のCI/CDパイプライン

Flux2の導入で工夫したポイント

ここでは、マイクロサービス基盤にFlux2を導入した際に工夫したポイントをいくつか紹介します。

Flux2の管理

Flux2もKubernetes上で動作するアプリケーションの1つです。そのため、Flux2本体(Source ControllerやKustomize Controllerなど)をFlux2自身で管理可能です。

GitRepositoryとKustomizationの管理

初回導入時のみ手動でインストールを行い、それ以降はFlux2自身で管理するようにしています。

GitRepositoryとKustomizationの管理

マイクロサービス基盤では、マイクロサービスごとにGitRepositoryとKustomizationを作成しています。この2つもFlux2により同期を行なっています。このマイクロサービスごとに作成された同期設定を管理するGitRepositoryとKustomizationは初回導入時のみ手動でインストールを行い、それ以降はFlux2で管理するようにしています。

GitRepositoryとKustomizationの管理

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をご覧ください。

fluxcd.io

今後の展望

アプリケーションの自動更新

Flux2では、ECRなどのImageリポジトリからImageを取得し、Gitリポジトリへコミットする機能があります。この機能を利用し、さらなるデプロイ時間の短縮に取り組んでいきます。

権限の最小化

Flux2のようにクラスタ上でソースとの差分を適用するというGitOps方式により、GitHub ActionsのようなCDパイプライン側に秘匿情報を渡す必要がなくなりました。しかし、現状のCIパイプラインでは秘匿情報が必要です。今後はCDパイプラインのみならずCIパイプラインの改善も取り組んでいきます。

さいごに

Flux2を導入することで既存のCDパイプラインが抱えていた課題を解決できました。Flux2を活用することで改善できる課題はまだまだあります。

引き続き、快適な開発・運用を実現すべく改善していきます。

ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。

hrmos.co

カテゴリー