GitOps とデプロイ

昨日はトランクベース開発とデプロイについて書いたので

bufferings.hatenablog.com

この勢いで GitOps とデプロイも書いてしまうー。先に言っておくと、自分は GitOps の経験はない。でも、よさそうだなぁと思う手法なので、機会があれば挑戦してみたい気持ち

GitOps?

GitOps は2017年に Weaveworks の Alexis によって提唱された手法で、Kubernetes を対象としている

Git のリポジトリーに入れてある設定ファイルを Single Source of Truth として、Kubernetesクラスター管理とアプリケーションデリバリーを行う。上記の記事には次の4つの原則が書かれている

  1. システム全体が宣言的に記述されていること
  2. 正規の望ましいシステムの状態が Git でバージョン管理されていること
  3. 変更が承認されたら自動的にシステムに反映されること
  4. ソフトウェアエージェントによって正しさが確認され、相違がある場合には警告されること

つまり、Kubernetesクラスターやアプリケーションの設定を Git で管理しておいて、変更されたら自動的にその設定通りになるようにしておく。ということ

また、(4) にあるように、Git 上で設定が変更された場合だけでなくクラスターやアプリケーション側で何か変更が発生した場合も「Git 上の設定と異なる状態になっている」ことが検知されて常に Git 上の設定とシステムやアプリケーションの状態が一致するようにしておく

今回はこの GitOps の考え方と、アプリケーションのデプロイに関して見ていく

アプリケーション用の2つのリポジトリ

f:id:bufferings:20220410100907p:plain:w600

https://www.weave.works/technologies/gitops/ より)

GitOps の場合は、ソースコードリポジトリー(図の左側にある Git)とは別に、設定用のリポジトリー(図の右下の Config Repo)がある

ソースコードリポジトリーからはコンテナレジストリーへのイメージの push までをしておいて、デプロイは設定用のリポジトリーの更新をトリガーにして実行される

設定用リポジトリーの中身

設定用のリポジトリーには、アプリケーションのデプロイ設定を入れる。この記事に紹介されてるのが分かりやすい↓

全ての環境の設定を入れてこんな感じのディレクトリになる。本番環境用の設定は会社のルール上、別で管理しないといけない場合もあるかもしれないね

f:id:bufferings:20220410162252p:plain:w200

生の YAML だとちょっとめんどくさいので Kustomize や Helm を使って管理すると良いと思う

この設定ファイルを更新することでデプロイを行う。例えば、本番環境用のイメージタグを更新することで本番環境に新しいイメージをデプロイすることができる

push 型と pull 型

設定用のリポジトリーからのデプロイは、ソースコードのビルドがないのでシンプル。単純に設定リポジトリーの main ブランチに設定の更新がマージされたら Kubernetes に反映されるようにしておいたら良い

その、反映の手法には push 型と pull 型がある。GitOps では pull 型の方がおすすめされているように感じる

push 型

  • 設定リポジトリーの更新をトリガーにして CI サービスから Kubernetes に対して変更を apply する

pull 型

  • Kubernetes の Operator が設定リポジトリーをポーリングしておいて、現在 Kubernetes 上で動いているリソースとの差分を検知したら、設定に合わせて Kubernetes に変更を適用する

push 型は簡単で分かりやすいし実装もしやすいけど、次のような課題がある

  • Kubernetes への書き込み権限を CI サービスに渡さないといけないこと
  • クラスター内を手で書き換えたりした場合に、設定と実際のクラスターの状態に差分が生じてしまうこと(つまり、原則の4が満たされない)

その点 pull 型だと

  • Kubernetes 上で動く自分たちの Operator から操作できるので、CI サービスに権限を渡さなくても良い
  • クラスター内の状態と設定ファイルとの差分を検知して、設定に合うように更新することができる

CircleCI のブログに、CircleCI と ArgoCD を使った pull 型の GitOps の記事があるのでよかったらチェックしてください!

GitOps と CI/CD

設定リポジトリを更新したら push 型または pull 型でデプロイされるとして、じゃあそこまでどう繋げば良いんだろう?というのを考えてみる

コンテナイメージ

まず、コンテナイメージ。The Twelve-Factor App にもあるように、外部から設定を与えることで同じイメージがどの環境でも動くように作っておきたい。てか、これは GitOps に関係なくやっておきたいことだな

デプロイ用のリポジトリーが別になっていて僕がいいなぁと思うのは、プロモーションが簡単に実現できるところ。ステージング環境にデプロイしてテストしたものと全く同じコンテナを本番環境にデプロイする、というプロモーションが設定ファイルのイメージタグを更新することで簡単に実現できる

全く同じイメージを使うのは、前回までに説明したソースコードリポジトリーを使ったデプロイだとなかなか難しい。デプロイするときに別のコミットが作られる場合が多いので、コードは同じだとしてもイメージは異なるものになってしまう

CI

次に CI。Git-flow でもトランクベース開発でも構わない。CI の仕事は「デプロイ可能なイメージをコンテナレジストリーに push する」ところまでになる

なので、Git-flow で release ブランチや main ブランチをデプロイしたいなら、それらのブランチが更新されたときにイメージを push しておけばいいし、トランクベース開発なら常に main のコンテナイメージを push しておけばいい

CD

そして CI からの CD。自動化できる場合は CI が終わったらビルドされたイメージのタグを使って設定ファイルを自動で更新したり、ちょっとレビューを入れておきたい場合は変更のプルリクエストを作成するようにしておけば良さそう

こんな感じになるのかなぁ?

トランクベース開発で、main を常時デプロイしたい場合は

  • main の CI の最後に、自動で設定ファイルを変更してデプロイ(↑で紹介した CircleCI の記事はこのパターン)

トランクベース開発で、デプロイのタイミングを調整したい場合は

  • デプロイしたいときに設定ファイルをそのイメージタグに更新してデプロイ

GitLab Flow みたいにやりたい場合は、例えば

  • main が更新されたら自動でステージング環境の設定ファイルを変更してデプロイ
  • プロモーション用のジョブでも作っておいて、ワンクリックでステージングの設定からプリプロダクション環境へ反映したり、プリプロダクションから本番環境へ設定を書き換えたりする

Git-flow で特定のブランチなどからデプロイしたい場合は

  • んー。ワンクリックデプロイのジョブのパラメーターでブランチ名を渡して、そのブランチの最新のコミットのコンテナイメージのタグで設定ファイルを更新することでデプロイ。かなぁ

(追記)ちなみに、Weave Cloud だとコンテナレジストリーの変更を検知して、設定リポジトリーの更新をしてくれるようにできるみたい。そんで、その設定変更を検知して、pull 型で変更を反映するっぽい。さすが便利ね

GitOps とデプロイ

デプロイ用のリポジトリーを別にしてしまうことで、ソースコードのブランチ戦略を考えるときのデプロイ周りの難しさが切り離されるのが面白い。さらに、その設定リポジトリーがデプロイの履歴になるのも良い

複数環境をひとつのリポジトリーに入れると、設定の共通部分などをまとめて管理できて便利だけど、例えば本番環境だけを急遽ロールバックしたい場合には、単純に昔の状態にもどしただけだと他の環境も元に戻ってしまうなぁとは思う。まぁ、だいたいの場合は、本番のイメージタグだけ昔に戻せばいいと思うので、ひとつでいいのかな

そんな風に便利そうだなって思うと同時に、管理するリポジトリーが増えることになるのと、CI と CD のつなぎこみも必要になるのとで、本当に必要かなぁ?ってのは考えたい。今のところ自分のチームは、トランクベース開発で常時本番環境にデプロイされている状態なので、それで満足している

というところで、Git とデプロイのお話はおしまい!あとは、頭の整理を兼ねてまとめを書きたい気持ちが少しあるくらい。ではー