Fluxを使ってGitOpsハンズオンしてみた

# 概要

- GitOpsを概念としては知っているけど、実際に触れたことは無かったのでハンズオンしてみる。
    - GitOpsを知らない人は、まずこちらを参照
        - [weaveworks GitOps](https://www.weave.works/technologies/gitops/)
        - weaveworks社が提唱しはじめた、Kubernetesの運用ベストプラクティス
- GitOpsを実践できるツールに、Agro CD、Flux、Jenkins X、の3種類が有るけど、GitOps提唱元のFluxをまずは使ってみる。

# Fluxハンズオン

- 以下を読みながら進めていく
    - https://github.com/weaveworks/flux#get-started-with-flux

## 事前準備

### fluxctlのインストール
- Windows環境での利用は想定していない？
    - https://github.com/weaveworks/flux/issues/1214 
- 気にせずインストール
- CLIでインストールしたいので、chocolateyをインストール
    - https://chocolatey.org/install#installing-chocolatey
    - scoop対応していないみたい。
- インストール

```
$choco install -y fluxctl
$ fluxctl version
1.12.3
```

### Kubernetesクラスタの構築

- 以下の記事の通りに進めて、GKEクラスタを構築
    - [なるべくCLIでGKEクラスタを作成してみる](https://qiita.com/ttr_tkmkb/items/26328fbbf5f17b920046)

```bash
$ gcloud container clusters create flux-handson
$ gcloud container clusters list
NAME          LOCATION           MASTER_VERSION  MASTER_IP       MACHINE_TYPE  NODE_VERSION   NUM_NODES  STATUS
flux-handson  asia-northeast1-a  1.12.7-gke.10   35.243.120.194  n1-standard-1 1.12.7-gke.10  3          RUNNING
```

### Fluxの同期先のリポジトリを用意

- GitHubで適当な空のリポジトリを作る
    - https://github.com/ssbtn/flux-handson/tree/master
- 今回は使用しないが、Fluxが用意しているサンプルリポジトリもあるみたい。
    - https://qiita.com/sotoiwa/items/ef6913653b5d4958e89c#%E6%BA%96%E5%82%99

## Fluxの導入

- 以下を参考に進める。
    - https://github.com/weaveworks/flux/blob/master/site/get-started.md

### FluxのリポジトリをClone

```bash
$ git clone  https://github.com/weaveworks/flux
$ cd flux
```

### 同期先のリポジトリを変更
- `--git-url`のURLを事前に準備したリポジトリに変更

```diff
#書き換え
$ vim ./deploy/flux-deployment
$ git add .
$ git commit -m "edit --git-url"
#差分
$ git diff HEAD^
diff --git a/deploy/flux-deployment.yaml b/deploy/flux-deployment.yaml
index a75d636e..bf46be90 100644
--- a/deploy/flux-deployment.yaml
+++ b/deploy/flux-deployment.yaml
@@ -118,7 +118,7 @@ spec:
         - --ssh-keygen-dir=/var/fluxd/keygen

         # Replace or remove the following URL.
-        - --git-url=git@github.com:weaveworks/flux-get-started
+        - --git-url=git@github.com:ssbtn/flux-handson.git
         - --git-branch=master
         # include this if you want to restrict the manifests considered by flux
         # to those under the following relative paths in the git repository

```

### Fluxをクラスタにデプロイ
deployフォルダ配下で定義されているオブジェクトをデプロイ

```
$ kubectl apply -f deploy
serviceaccount/flux created
clusterrole.rbac.authorization.k8s.io/flux created
clusterrolebinding.rbac.authorization.k8s.io/flux created
deployment.apps/flux created
secret/flux-git-deploy created
deployment.apps/memcached created
service/memcached created
```
Podが起動していることを確認

```
$ kubectl get all
NAME                             READY   STATUS    RESTARTS   AGE
pod/flux-6c99dbcd46-g47m5        1/1     Running   0          5m4s
pod/memcached-5d46855d78-ww5hd   1/1     Running   0          5m3s

NAME                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)     AGE
service/kubernetes   ClusterIP   10.23.240.1    <none>        443/TCP     138m
service/memcached    ClusterIP   10.23.245.54   <none>        11211/TCP   5m2s

NAME                        DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/flux        1         1         1            1           5m4s
deployment.apps/memcached   1         1         1            1           5m3s

NAME                                   DESIRED   CURRENT   READY   AGE
replicaset.apps/flux-6c99dbcd46        1         1         1       5m4s
replicaset.apps/memcached-5d46855d78   1         1         1       5m3s

```

### Fluxの公開鍵をリポジトリに登録

Flux起動時に作られた公開鍵の取得

```bash
$ fluxctl identity
#fluxctlが無くても、以下のコマンドでも可能
$ kubectl logs deployment/flux | grep identity.pub
```

GitHubにデプロイキーとして登録。
作成したリポジトリで
`Setting`→`Deploy Keys`→`Add deploy key`→`鍵入力`→`Add key`

| Key | Value | 
|:-----------|------------:|
| Title | Flux-key | 
| Key | 取得した公開鍵 | 
| Allow write access | 許可 | 


## 動作確認
Namespaceを作成するmanifestをpushしてみる

作成したmanifest

```yaml
apiVersion: v1
kind: Namespace
metadata:
  labels:
    name: flux-handson
  name: flux-handson
```

リモートリポジトリにpush

```bash
$ git clone git@github.com:ssbtn/flux-handson.git
$ cd flux-handson
$ mkdir namespaces
#上記のmenifestを配置
$ vim namespaces/namespace-flux-handson.yml
$ git add .
$ git commit -m "Create namespace of flux-handson"
$ git push

```

2分ぐらい待機してnamespaceを取得。
ちゃんと反映されてる！

```bash
$ kubectl get namespaces
NAME           STATUS   AGE
default        Active   5h21m
flux-handson   Active   2m27s
kube-public    Active   5h21m
kube-system    Active   5h21m

$ kubectl get pod
NAME                         READY   STATUS    RESTARTS   AGE
flux-6c99dbcd46-g47m5        1/1     Running   0          3h7m
memcached-5d46855d78-ww5hd   1/1     Running   0          3h7m

$ kubectl logs flux-6c99dbcd46-g47m5
～～～～～～～中略～～～～～～～～～
ts=2019-06-01T09:52:58.54924271Z caller=sync.go:542 component=cluster method=Sync cmd="kubectl apply -f -" took=623.753268ms err=null output="namespace/flux-handson created"
ts=2019-06-01T09:52:58.557341121Z caller=daemon.go:624 component=daemon event="Sync: c1a8c33, <cluster>:namespace/flux-handson" logupstream=false
ts=2019-06-01T09:53:02.404396193Z caller=loop.go:441 component=sync-loop tag=flux-sync old=76fa76fb551c93ed72413ad8a603cac6c60b4ccc new=c1a8c3341d7ce8b47251e7d002a0aeb0f5bf91ce
ts=2019-06-01T09:53:05.788483492Z caller=loop.go:103 component=sync-loop event=refreshed url=git@github.com:ssbtn/flux-handson.git branch=master HEAD=c1a8c3341d7ce8b47251e7d002a0aeb0f5bf91ce
ts=2019-06-01T09:53:56.006830168Z caller=images.go:18 component=sync-loop msg="polling images"
ts=2019-06-01T09:53:56.126944631Z caller=images.go:28 component=sync-loop msg="no automated workload

```

Gitの状態を真としてくれることを期待して、namespaceを削除してみる。
リアルタイム(5分間隔?)に差分検出して、同期してくれた。

```bash
$ kubectl delete namespace/flux-handson
namespace "flux-handson" deleted
#上記コマンドの直後
$ kubectl get namespaces
NAME          STATUS   AGE
default       Active   5h25m
kube-public   Active   5h25m
kube-system   Active   5h25m
#上記コマンドから5分後
$ kubectl get namespaces
NAME           STATUS   AGE
default        Active   5h29m
flux-handson   Active   16s
kube-public    Active   5h29m
kube-system    Active   5h29m

```

Deploymentとか、Serviceとかも同じ要領でデプロイしてみる。

- manifestの詳細は長くなるのでこちら
    - Namespaceは指定せずに書いてみた
    - https://github.com/ssbtn/flux-handson/commit/e736a41af622652144e390817d7367c613ae8c69

Namespaceを指定しないとdefaultにデプロイされた。当然だが。
Serviceも公開されている。

```bash
$ kubectl get pod --namespace default
NAME                         READY   STATUS    RESTARTS   AGE
flux-6c99dbcd46-g47m5        1/1     Running   0          3h27m
memcached-5d46855d78-ww5hd   1/1     Running   0          3h27m
nginx-5947c4dd86-24qm7       1/1     Running   0          102s
nginx-5947c4dd86-7s767       1/1     Running   0          102s
nginx-5947c4dd86-lp5nz       1/1     Running   0          102s
nginx-5947c4dd86-lzd9c       1/1     Running   0          102s
nginx-5947c4dd86-ptwz5       1/1     Running   0          102s

$ curl 104.198.119.148:30080
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   612  100   612    0     0  38250      0 --:--:-- --:--:-- --:--:-- 38250<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
```

正しい手順か分からないが、ロールバックを試してみる。

```
#リモートリポジトリの状態を1つ前のコミットを取り消す
$ git push -f origin HEAD~:master

#10分後
$ kubectl get pod
NAME                         READY   STATUS    RESTARTS   AGE
flux-6c99dbcd46-g47m5        1/1     Running   0          3h48m
memcached-5d46855d78-ww5hd   1/1     Running   0          3h48m
nginx-5947c4dd86-24qm7       1/1     Running   0          22m
nginx-5947c4dd86-7s767       1/1     Running   0          22m
nginx-5947c4dd86-lp5nz       1/1     Running   0          22m
nginx-5947c4dd86-lzd9c       1/1     Running   0          22m
nginx-5947c4dd86-ptwz5       1/1     Running   0          22m
```

Git上からmanifestは無くなったが、クラスタ上に各objectが残ってしまった。
コミットを取り消すとファイルが消えてしまう＝管理対象から離れてしまったため、ロールバックできなかったのだと思われる。

探したけど見つからなかったので、公式なロールバック手順があればコメントください。

## 所感
予想していたよりも簡単に導入することができた。
次回はhelmのデプロイや、ロールバックの手順、manifestとpull requestの自動作成、GitOps製品の比較をやっていきたい。

# 参考

- 今回は使用しなかったけど、FluxをHelmでセットアップする方法も用意されている。
    - https://github.com/weaveworks/flux/blob/master/site/helm-get-started.md
    - Helmが使える環境ならば、こちらの手順が良さそう。
- スタンドアローンな環境へのインストールは以下を参照
    - https://github.com/weaveworks/flux/blob/master/site/standalone-setup.md#using-a-private-git-host
- FluxについてのFAQ
    - https://github.com/weaveworks/flux/blob/master/site/faq.md#does-it-work-only-with-one-git-repository
        - Fluxで制御できるNamespaceはFluxのサービスアカウントのRBACを設定することで制限できるみたい。
        - 同期先のリポジトリ1つしか選べない。マイクロサービスで使うには運用方法を考える必要が有りそう。
            - 公式見解では、複数のFluxを起動してそれぞれのサービスアカウントに適切なRBACを与えなさいってことみたい。
- fluxctlのインストール
    - https://github.com/weaveworks/flux/blob/master/site/fluxctl.md
- MinikubeへのFluxセットアップ
    - https://qiita.com/sotoiwa/items/ef6913653b5d4958e89c#%E6%BA%96%E5%82%99