【ArgoCD/GKE】GitOpsを実現させる

# GitOps
Kubernetesがコンテナを管理するツールとして、デファクトスタンダードになりつつある中、そのデプロイ方法についても日々更新されていっています。
その中でも、`GitOps`という方法が最近勢いがあり、触る機会があったので、備忘録として残します。

## GitOpsとは
Githubを正とし、またGithubを中心に`CI/CD`を回していきましょうという考え方で理解をしています。
[GitOps - Operations by Pull Request](https://www.weave.works/blog/gitops-operations-by-pull-request)

## GitOpsの特徴
- KubernetesのソースコードもGithubで管理する
- アプリケーションのリポジトリとKubernetesのリポジトリを分ける
- `kubectl`コマンドを使用しない

### Githubで管理する
アプリケーションの場合は、これが普通だとは思いますが、KubernetesをGithubで管理できているところは少ないでしょう。
Githubで管理することで、どんな設定が現在反映されているのか**ソースベース**で確認することができます。
また、**Githubのソースコードを正とします。**

### アプリケーションとKubernetesのリポジトリを分ける
なぜこうするかというと、同じにしてしまうと、無駄なビルドが走ってしまう時が多くあると考えられます。
例えば、Kuerbenetsの変更だけの場合は、アプリケーションのビルドは必要ありません。
このような場合、リポジトリを分けることが有効です。

**アプリケーション用リポジトリ**
アプリケーションのリポジトリです。Goだったり、Rubyだったりが入ってます。

**マニフェスト用リポジトリ**
Kubernetesのマニフェストを格納するリポジトリです。

### `kubectl`コマンドを使用しない
こうすることで、人為的ミスを最大限に減らすことができます。
実際の運用で、 `kuberctl apply`コマンドだったり、`kubectl set image`コマンドを使用している人は多いと思います。
間違って異なる `kubectl`コマンドを使ってしまったなどは往々にしてあります。



## 全体像
下記のようなフローがGitOpsに当たります。
この流れは、どのツールを使っても共通だと思います。

- 開発者がgithubにコミット
- CIツールが回って、imageをbuild&push
- Kubernetesのめなファイルをアップデート
- クラスタ情報を更新

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148392/d2f118b8-db2a-b353-1969-e6e8374ca543.png)

GitOpsの全体像を理解するのは下記が役立つので、一読することをお勧めします。
[Argo CDによってGKEでGitOpsをする](https://www.1915keke.com/entry/2018/12/15/034843)


## GitOpsを実現する方法
GitOpsを実現する方法は下記の通り、いくつか存在します。

[Jenkins X](https://jenkins-x.io/)
[ArgoCD](https://argoproj.github.io/argo-cd/getting_started/)
[Flux](https://docs.fluxcd.io/en/stable/introduction.html)

今回は上記のうちArgoCDを使ってGitOpsを実現していきたいと思います。
ちなみにArgoCDを選択した理由としては、下記の通りです。

- 学習コストが低い
- GUIで操作が可能
- 最近ArgoCDを取り入れている企業が多い

## ArgoCDとは
Kubernetesで、GitOpsを実現する方法の一つです。
公式では下記のように説明されています。
>Argo CD is a declarative, GitOps continuous delivery tool for Kubernetes.

[What Is Argo CD?](https://argoproj.github.io/argo-cd/)


## ArgoCDを使ったデプロイまでの流れ

1. 開発者がmasterにmerge
2. CircleCIでテスト、GCRへimageをpush、マニフェストリポジトリにpush
3. ArgoCDがGithub上のマニフェストファイルの更新を検知
4. ArgoCDがクラスタを最新の状態にする


## 構築

### アプリケーションのデプロイ

#### アプリケーションの公開
今回は`GKE`を使ってGitOpsを構築していきます。
下記のGCPのドキュメントを参考に、「ステップ 6: アプリケーションをインターネットに公開する」まで実施してください。
[コンテナ化されたウェブ アプリケーションのデプロイ](https://cloud.google.com/kubernetes-engine/docs/tutorials/hello-app?hl=ja)

この際注意しなければいけないのは、Kubernetesのマニフェストを実装して、別のリポジトリで管理することです。
リポジトリを分けて管理するのは、下記の記事が参考になります。
[Cloud Build を使用した GitOps スタイルの継続的デリバリー](https://cloud.google.com/kubernetes-engine/docs/tutorials/gitops-cloud-build?hl=ja)

#### アプリケーションの確認
GKEの管理画面の「ServiceとIngress」という箇所をクリックし、作成したアプリケーションの「エンドポイント」をクリックします。そして作成したアプリケーションが表示されているか確認します。

#### Githubに最新の状態をpush
アプリケーション用、マニフェスト用のリポジトリに最新の物をpushしておきます。

### ArgoCDのマニフェスト作成
下記ドキュメントを使ってArgoCDの設定を行なっていきます。
「4. Login Using The CLI」まではスラスラいくと思います。
[Getting Started](https://argoproj.github.io/argo-cd/getting_started/)

#### Register A Cluster To Deploy Apps To(自分が少し詰まったところ)
連携したいクラスタを登録します。

現在自分が扱っている全てのクラスタ情報を出力してくれます。
`argocd cluster add`

接続するクラスタを選択します。
`argocd cluster add [Cluster NAME]`

ここでは、後ほどArgoCDで連携させるクラスタを選択する必要があるので、それを事前に登録しているイメージです。


#### ログイン
設定が完了したら、ArgoCDログインの画面が下記のように出ていると思います。
ここで先ほど設定した`Username`と`Password`を入力し、ログインします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148392/7f0b0785-e080-ff84-e32b-6134fba62b28.png)


### ArgoCD 設定
ここでは、クラスタと連携するための設定を行なっていきます。

#### Githubのリポジトリを連携する
左メニューの歯車マークをクリックし、「Repositoriy」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148392/1dec9588-e06c-5d89-8b02-c443326d8403.png)

Repository URLには、HTTPSのURLを入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148392/eb7567cf-40f6-b8f9-2306-44b260ccd18a.png)


#### アプリケーションの作成
右側のプラスマークをクリックし、「New Application」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148392/c13f22d4-7557-2a67-43ad-54416da5d8fe.png)

リポジトリ選択の画面が出てくるので、ここで先ほど登録したリポジトリを選択します(自動で候補として出てきます)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148392/78bac28b-c259-03d5-2935-943ba46bb64f.png)

次の画面に遷移したら、「Create App From Directory」を選択します。

下記が入力項目になります。
**Revision**
`HEAD`という書き方かブランチ名にすることができます。
[Tracking and Deployment Strategies](https://argoproj.github.io/argo-cd/user-guide/tracking_strategies/)

**Path**
Kubernetesのマニフェストファイルがどこに存在するかを記載します。
注意すべきところとしては、マニフェストに関してはディレクトリで管理していないと、アプリケーションの作成ができないところです。

使用できるマニフェストファイルは以下の通りです。
`kustomize`
`helm`
`プレーンなYAMLファイル`

私の場合は、`プレーンなYAMLファイル`を使用しました。

**Project**
TODO: 内容調べる
成功したアプリケーションだと、defaultを使用していた。

**Application Name**
ここは任意の名前で問題ありません。

**Cluster URL**
ここで事前に登録したクラスタを選択します。
ここもクリックすると自動的に選択肢が表示されます。

**Namespace**
ここは基本defaultで問題ないと思います。
異なるNamespaceを作成している場合は、それを指定してください。
[Namespace(名前空間)](https://kubernetes.io/ja/docs/concepts/overview/working-with-objects/namespaces/#%e8%a4%87%e6%95%b0%e3%81%aenamespace%e3%82%92%e4%bd%bf%e3%81%86%e6%99%82)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148392/43ece270-192d-3581-40b2-bc86831f7753.png)

### ArgoCDとクラスタを連携できるようにする
アプリケーションが作成し終わったタイミングで、ほとんど連携はできています。
あとは、「Enable Auto Sync」をクリックして、自動で連携するようにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148392/259f2cfc-3c6e-d5e6-9509-9999d1198f16.png)

### CircleCI連携
CIツールは、CircleCIを使用します。
Githubとの連携は、下記を非常にわかりやすかったです。
[GitHub+CircleCI入門](https://qiita.com/tatane616/items/8624e61473a9957d9a81)

### CircleCIでGKEにアクセスできるようにする
GKEにアクセスできるようにするには、**サービスアカウント**を作成する必要があるので作成していきます。
GCPにログインし、「IAMと管理」から「サービスアカウント」をクリックします。
権限としては2つ付与しておきます。
**「ストレージ管理者」**と**「Kubernetes Engine 管理者」**を選択し、Keyを作成します。
ストレージ管理者は、GCRへのpushができるようにすることと、
Kubernetes Engine 管理者は、GKEにアクセスするために付与しました。

### CircleCIでマニフェスト用のリポジトリをclone,commit,pushできるようにする
CircleCI上で、プライベートリポジトリを扱うためには、`SSH Key`を設定する必要があります。
Keyには、2つの種類があり、`deploy key`, `user key`があります。

設定項目の「Checkout SSH Keys」をクリックし、そこでKeyを追加することで、設定が可能です。
[GitHub および Bitbucket のインテグレーション](https://circleci.com/docs/ja/2.0/gh-bb-integration/)

これを追加すると、`fingerprint`が発行されるので、それをCircleCIに設定します。

```yaml
- add_ssh_keys:
    fingerprints:
      - "発行されたfingerprint"
```

またgithubに公開鍵を追加するような手順で、「SSH Permissions」も設定することができます。
[SSH Permissions](https://circleci.com/docs/ja/2.0/add-ssh-key/)
※この登録をする際は、CircleCIはpemの形式でしか登録できないので、気をつけてください！
[Circle CI で Github に write access 可能な Deploy key を設定する](https://qiita.com/boushi-bird@github/items/6b6eb1d1ed6f6d3341e4)

### imageの修正
デプロイするためには、`Deployment`ファイルの`image`を更新する必要があります。
自分の場合は、原始的ですが、`sed`コマンドで対応しました。
[sedでこういう時はどう書く?](https://qiita.com/hirohiro77/items/7fe2f68781c41777e507)
[sedコマンドの備忘録](https://qiita.com/takech9203/items/b96eff5773ce9d9cc9b3)
[sedコマンドで覚えておきたい使い方12個(+3個)](https://orebibou.com/2015/07/sed%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%A7%E8%A6%9A%E3%81%88%E3%81%A6%E3%81%8A%E3%81%8D%E3%81%9F%E3%81%84%E4%BD%BF%E3%81%84%E6%96%B912%E5%80%8B/#i-14)


## 使用してみての感想
ArgoCDは気軽にGitOpsを始めるのであれば、最適なツールかもしれないです。
ただイメージのアップデートの方法は、`sed`コマンド使いましたが、別の方法があるとは思うので、引き続き調査したいと思います。

改めて、ArgoCDのメリットとしては、 `学習コストが低い`。
ただ、sedコマンド以外にデプロイメントを更新する方法が調査中ということと、
すでに動いていて、マニフェスト管理をしていない`GKE`や`EKS`に対して、途中でArgoCDを導入する方法が、ピンときませんでした。
クラスタ構築からすることになるのかな。

## 参考にした記事
#### デプロイについて
[ビルドとデプロイって結局なんやねん！？！？！？？？みたいな記事](https://qiita.com/isoyam/items/3d1fc5cf7403cdf4818d)

#### ArgoCDについて
[Architectural Overview](https://argoproj.github.io/argo-cd/operator-manual/architecture/)
[ArgoCDで出てくる言葉の意味 Core Concepts](https://argoproj.github.io/argo-cd/core_concepts/)

#### Webhookについて[Optional]
ArgoCDは、3分おきにgithubの最新の状態を見ているので、時折更新が遅くなることがあります。
そこで、WebHookを設定しておき、Githubに更新があるたびにArgoCDがそれを検知してくれます。
詳しくは下記を参考にしてみてください。

[Webhookとは？](https://qiita.com/soarflat/items/ed970f6dc59b2ab76169)

#### その他
[Webinar Series: GitOps Tool Sets on Kubernetes with CircleCI and Argo CD](https://www.digitalocean.com/community/tutorials/webinar-series-gitops-tool-sets-on-kubernetes-with-circleci-and-argo-cd)
[Kubernetes Meetup Tokyo 21 で 『Argo CD 実践ガイド』というタイトルで LT をしました #k8sjp](https://dev.classmethod.jp/etc/k8smeetup-argocd/)
[Gitブランチ、Dockerリポジトリ、環境毎のk8sクラスタ 俺のデプロイフローを聞いてほしい](https://dev.classmethod.jp/cloud/aws/gitbrunch-docker-env-k8s-deploy/)
[FAQ](https://argoproj.github.io/argo-cd/faq/)