0. 前提
- Docker, Kubernetes, Gitに関する基礎的な知識があるとなお良いです。
1. GitOps概要
GitOpsとは一言でいうと、**「Gitを軸として、サービスを管理するためのCI/CDの手法のこと」**です。
GitOpsの導入には、主に下記の二つのメリットがあります。
- 「Single Source of Truth」(信頼可能な唯一の情報源)として、サービスの状態をGitリポジトリで一元管理できる
- CIとCDの責任範囲を明確に分離できる
これだけ見るとGitOpsは非常に便利なCI/CD手法であることが分かると思います。しかし、ここまでの内容だけでは
- GitOpsが具体的にどのような仕組み・構成で実現されるのか?
- なぜGitOpsを導入すると上記のような利点を享受できるのか?
が分からないと思います。そのため、以下順に説明します。
2. GitOpsの全体像
言葉で説明する前に、具体的なGitOpsの流れを図示します。以下のような流れになります。(様々な実装方法があると思いますが、リポジトリとしてGitHub, CI/CDツールとしてGitHub ActionsとArgo CDとの利用を想定して図を記載しました)
言葉で説明すると下記のような流れになります。
- 開発者がGitリポジトリ(GitHub)にコードをPushして、リポジトリの内容を更新します。
- リポジトリの更新を契機にイメージのCIツールが起動します。コンテナイメージのテスト・ビルドを行います。(.github/workflows/main.yamlが起動するイメージです。)
- ビルドが正常に終了した後に、生成されたイメージをコンテナレジストリへPushします。
- クラスタ側(注意 GitHub Actions側ではない←ここが意外と重要だったりします。)からgithubの変更を検知し、ビルドされたイメージを基に、Kubernetesクラスタへアプリケーションがデプロイされます。
このように、CI(テスト・ビルド)とCD(クラスタへのデプロイ)をGitですべて実行することが出来ます。
3. どうして、「サービスの状態を一元管理」できるのか?
(↓ 以下の説明は宣言的設定の説明になります。内容ご存じの方は飛ばしてもらって構いません。)
結論から言うと、Kubernetesが「命令的設定」でなく、**「宣言的設定」**を採用しているからです。
たとえるなら、「命令的」とはマニュアル運転で、「宣言的」とは自動運転のようなものです。
例えば、自分がコンビニに行きたいという目的があるとします。コンビニは、「交差点を曲がって」、「直進」すれば到達するとします。この時、
- 「命令的」設定の場合は、**具体的な手段を指示する必要があります。**そのため、①「交差点を曲がる」②「その後直進する」、という指示をコードとして記載する必要があります。コンビニに行くという例ではあまり他の代替手段は無いかもしれませんが、目的を実現するための指示が複数パターン存在する場合、人によってコードの内容が異なってしまい、コードの品質が低下する可能性があります。
- 「宣言的」設定は、最終的な状態だけを提示します。そのため、「コンビニに到達している」、という目標の状態だけをコードとして記載します。手順は指示しないので、交差点を曲がってその後直進するかもしれませんし、裏道を通ってコンビニに到達するかもしれません。あくまでゴールだけを設定する形になります。
「命令的」設定の場合は、具体的な手順を指示するので、同じ操作を繰り返すと最終的な目標にたどり着けないことがあるという課題があります。(冪等性が無いということ)。より実務に近い内容で例えますと、コンテナを1個起動したいという目的があるとします。このとき、「命令的」設定では、コンテナを1個起動するという設定になります。この設定が一度だけ実行されたとすると、コンテナは目的の状態と等しくなりますが、もう一度実行するとコンテナは2個起動した状態になり、さらに実行するとコンテナが3個起動してしまい、コンテナを1個起動したいという本来の目的と違う状態になってしまいます。
これとは違い「宣言的」設定の場合は、目的の状態のみを定義するので、何度設定を投入しても結果は同じです。コンテナが一つも起動していなければ、コンテナを新たに1個起動しますし、コンテナが4個起動していれば3個減らして1個にしてくれます。
このような「宣言的」設定を利用することで、リポジトリの状態とサービスの状態を常に一致させることが可能になります。これにより、サービスの状態をGitで一元管理できるというわけです。
4. なぜCIとCDの責任範囲を明確に分離できるのか?
4.1 CIの責任範囲はどこまでか?
先ほどの図に関して、CIの範囲を強調した図が下記になります。
このようにCI(ビルド・テスト)の部分は開発者のPushが起点となって、流れが進んでいることが分かります。図のようにCIでデプロイを実行しておらず、明確にCDと責任範囲が分かれています。(直線の矢印で記載してある部分がCIの責任範囲です。)
4.2 CDの責任範囲はどこまでか?
これとは違い、CDの責任範囲を強調した図が下記になります。
このように、CD(デプロイ)の部分はkubernetesクラスタが起点となり、クラスタがGitHubへ情報を取得しに行って、クラスタ自身がクラスタにアプリをデプロイしています。図のようにCDでは、ビルドやテストを実行しておらず、明確にCIと責任範囲が分かれています。(曲線の矢印で説明している部分がCDの責任範囲です。)
4.3 (参考)CIOpsとの違い
ここで、CIOpsとの違いを説明するために、CIOpsで実装した場合にどうなるかを図にしました。
赤字の部分で記載しているようにCIOpsの場合、CIが起点となってCIの流れの中でKubernetesへのデプロイが実行されています(つまりgithub actionsの中でkubectl apply -f を実行しています。)。この場合だと、CIを担当するチームがCDも担当してしまっており、CIとCDの責任範囲が明確に分離されていないことが分かります。つまり、GitOpsになっていないです。(矢印も全て直線にし、CIの範囲で全て実行されていることを分かりやすくしました。)
4.4 CIとCDの責任範囲に関するまとめ
このようにCIに関してはPushが起点でCIツール(図ではGitHub Actions)のみ担当していることが分かります。これとは違いCDについては、CDツール(図ではArgo CD)が起点となりArgo CDがメインとなってCDを実行しています。このように、CIとCDで明確に責任範囲を分離するのがGitOpsの特徴です。
5.GitOps まとめ
本記事ではGitOpsに関して調べた内容を、基礎編として記載しました。4章で述べさせていただいたように、GitOpsでは基本的に「クラスタ外部からクラスタへkubectlを叩く」Push型でなく、クラスタから外部のレジストリに情報を取得するPull型がメインとなっています。記事の内容は以上ですが、ご指摘等ありましたら遠慮なくコメントいただけると助かります。最後になりますが、ここまで読んでいただきありがとうございました。
6.次回
掲載時期未定ですが、下記のテーマで記事を投稿する予定です
- K8sのGitOpsツールに関する記事
- K8sでのGitOpsツール比較
- Ansibleを用いたGitOpsの実装
- Terraformを用いたGitOps(チックなIaaC)の実装