ArgoCDをArgoCDで管理 + Kustomize


# はじめに
巷で噂のGitOpsツールとしてArgoCDをテストしています。
調査にあたりなかなか情報が見つからなかった次の２点をまとめます。
* Kustomize + ArgoCD
* Manage Argo CD Using Argo CD

KustomizeやArgoCDの基本的なことは多くの記事があるので、別記事をご参考いただければと思います。

# Kustomize + ArgoCD
ArgoCDではKustomizeを使うことにしました。

[ドキュメント](https://argoproj.github.io/argo-cd/user-guide/kustomize/)を見るとサポートしているっぽいのですが、どのように設定すればいいのか読み取れませんでした。。
結論は、pathに `./overlay/prod` のように `kustomization.yaml` があるdirectoryを指定することです。
`kustomize build ./overlay/prod` と同じ感覚です。

GUIならPATHの項目に指定することと同じ意味です。

```yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: # application name (e.g. app1)
spec:
  project: # project name (e.g. pj1)
  source:
    repoURL: # repository name H(e.g. https://github.com/sample/test.git)
    targetRevision: # target branch (e.g. master)
    path: # path (e.g. ./overlays/prod/)　<--- こ
  destination:
    server: # target cluster (e.g. https://kubernetes.default.svc)
    namespace: # target namespace (e.g. app1-stg)
```

## Tips: Kustomize ≠ kubectl kustomize?
同僚から `$ kubectl kustomize` でrepositoryのmanifestを使おうとしたら、エラーが出ると連絡がありました。

原因は `$ kubectl kustomize` と `$ kustomize` はversionが異なるため、Parseができていなかったようです。先ほど見たところの `Kustomize` の最新は[v3.8.1](https://github.com/kubernetes-sigs/kustomize/tree/kustomize/v3.8.1)ですが、 `$ kubectl kustomize` の
README.mdに記載されている[integration list](https://github.com/kubernetes-sigs/kustomize)を抜粋すると `v2.0.3` と差異がありました。

| kubectl version | kustomize version |
| --- | --- |
| v1.16.0 | v2.0.3 |
| v1.15.x | v2.0.3 |
| v1.14.x | v2.0.3 |

ちなみにArgoCDは[v1.6.0](https://github.com/argoproj/argo-cd/releases)でkustomizeのv3.6.1に対応しているようです。

## Tips: kustomize build? kubectl kustomize?
上のことからArgoCDでは、`$ kustomize build`を使っていそうだったので気になって調べてみました。
全部は見切れていませんが、src codeを抜粋すると確かにしていそうです。

[util/kustomize/kustomize.go](https://github.com/argoproj/argo-cd/blob/master/util/kustomize/kustomize.go#L114-L120)

```go
func (k *kustomize) Build(opts *v1alpha1.ApplicationSourceKustomize, kustomizeOptions *v1alpha1.KustomizeOptions) ([]*unstructured.Unstructured, []Image, error) {
    ---snip---
    var cmd *exec.Cmd
    if kustomizeOptions != nil && kustomizeOptions.BuildOptions != "" {
        params := parseKustomizeBuildOptions(k.path, kustomizeOptions.BuildOptions)
        cmd = exec.Command(k.getBinaryPath(), params...)
    } else {
        cmd = exec.Command(k.getBinaryPath(), "build", k.path)
    }
    ---snip---
}

func (k *kustomize) getBinaryPath() string {
	if k.binaryPath != "" {
		return k.binaryPath
	}
	return "kustomize"
}
```



## Tips: kustomization.yaml, Kustomize, kustomization.yml

[Tool Detection](https://argoproj.github.io/argo-cd/user-guide/tool_detection/)には `Kustomize` と同じように、`kustomization.yaml`, `kustomization.yml`, `Kustomization`のいずれでも検出するようですね。

[util/kustomize/kustomize.go](https://github.com/argoproj/argo-cd/blob/master/util/kustomize/kustomize.go#L168-L179)

```go
var KustomizationNames = []string{"kustomization.yaml", "kustomization.yml", "Kustomization"}

// kustomization is a file that describes a configuration consumable by kustomize.
func (k *kustomize) findKustomization() (string, error) {
	for _, file := range KustomizationNames {
		kustomization := filepath.Join(k.path, file)
		if _, err := os.Stat(kustomization); err == nil {
			return kustomization, nil
		}
	}
	return "", errors.New("did not find kustomization in " + k.path)
}
```

# Manage Argo CD Using Argo CD
[コチラ](https://argoproj.github.io/argo-cd/operator-manual/declarative-setup/)を読むと興味深いのですが、ArgoCD自体もArgoCDで管理(宣言的セットアップ)します。

簡単な流れは次の通りです。
1. ArgoCDのマニフェスト作成
2. ClusterにArgoCDを`手動`でDeploy
3. 以降はArgoCDでSync

[公式サイト](https://argoproj.github.io/argo-cd/operator-manual/declarative-setup/)を見ると次のマニフェストが利用できるとあります。

| File Name | Resource Name | Kind | Description |
| --- | --- | --- | --- |
| argocd-cm.yaml | argocd-cm | ConfigMap | General Argo CD configuration |
| argocd-secret.yaml | argocd-secret | Secret | Password, Certificates, Signing Key |
| argocd-rbac-cm.yaml | argocd-rbac-cm | ConfigMap | RBAC Configuration |
| argocd-tls-certs-cm.yaml | argocd-tls-certs-cm | ConfigMap | Custom TLS certificates for connecting Git repositories via HTTPS (v1.2 and later) |
| argocd-ssh-known-hosts-cm.yaml | argocd-ssh-known-hosts-cm | ConfigMap | SSH known hosts data for connecting Git repositories via SSH (v1.2 and later) |
| application.yaml | - | Application | Example application spec |
| project.yaml | - | AppProject | Example project spec |

## 1. ArgoCDのマニフェスト作成
早速やっていきます。用意するのは次の二つです。
* kustomization.yaml　(ArgoCD本体のマニフェスト)
* その他.yaml　(ArgoCDをArgoCDで管理するための設定ファイル)

### kustomization.yaml　(ArgoCD本体のマニフェスト)
上の表には本体のマニフェストがありませんが、末尾に利用できるサンプルがあるので流用します。

```yaml
bases:
- github.com/argoproj/argo-cd//manifests/cluster-install?ref=v1.0.1

# additional resources like ingress rules, cluster and repository secrets.
resources:
- clusters-secrets.yaml
- repos-secrets.yaml

# changes to config maps
patchesStrategicMerge:
- overlays/argo-cd-cm.yaml
```

まずは `bases:` に着目します。

実はこのサンプルを利用すると次のようなエラーが出て`失敗`します。ハマりました。。

```sh
$ kustomize build .
Error: accumulating resources: accumulateFile "accumulating resources from 'github.com/argoproj/argo-cd//manifests/cluster-install?ref=v1.0.1': ...
```

そもそも `github.com/argoproj/argo-cd//manifests/cluster-install?ref=v1.0.1` というURLに違和感があったので調べました。どうやら、Kustomizeの `base:` に記載したURLは[hashicorp/go-getter](https://github.com/hashicorp/go-getter#url-format)の表記方法を[習う](https://github.com/kubernetes-sigs/kustomize/blob/177297c0efb92ec2d7eea8f20c65c1d11c6ae7ef/docs/kustomization.yaml#L130-L133)ようです。

関連する部分は次です。
* `//(double-slash)` で、利用するsub-directoryを指定
* `ref=` で[git tag](https://github.com/argoproj/argo-cd/tags)を指定

go-getterのrepositoryを見るとsub-directoryのdouble-slashはURLの中ではなく、末尾に記載する実装に変わった？のがエラーの理由です。ここら辺が参考になりました。
* [#3814](https://github.com/argoproj/argo-cd/issues/3814)
* [#2538](https://github.com/kubernetes-sigs/kustomize/issues/2538)
* [#923](https://github.com/kubernetes-sigs/kustomize/issues/923#issuecomment-477291170)


結局どうするのっていう話ですが、basesの表記をdouble-slashを使わず今のversionを指定すれば良いようです。

```yaml
bases:
- github.com/argoproj/argo-cd/manifests/cluster-install?ref=v1.6.1
```

続いて`resources:`と`patchesStrategicMerge:`に注目します。

これは`bases:`でArgoCDのマニフェスト読み込んでいるのですが、それに対して重複する・しないで使い分けます。
* resources: 複数存在することが想定されるファイル　(application.yaml, project.yamlなど)
* patchesStrategicMerge: 単一のファイル　(argocd-secret.yaml, argo-cd.yamlなど)

~~まあ `kustomize build .` をすればエラーが出るのでそれで区別もできます。~~

### argocd-secret.yaml
いくつか[設定](https://argoproj.github.io/argo-cd/operator-manual/argocd-secret.yaml)可能ですが、ここではadmin passwordを設定します。デフォルトではpod名が使われますが、調べるのはめんどくさいので固定で設定します。

設定にあたってはbcryptしたデータを記載するのですが、[FAQ](https://argoproj.github.io/argo-cd/faq/)が参考になります。
> I forgot the admin password, how do I reset it?
> By default the password is set to the name of the server pod, as per the getting started guide.
> To change the password, edit the argocd-secret secret and update the admin.password field with a new bcrypt hash. You can use a site like https://www.browserling.com/tools/bcrypt to generate a new hash. For example:

上記のサイトで出力したbrcypt hashを `admin.password` に設定すると、base64 encodeされた値でsecretにapplyされます。

```yaml
apiVersion: v1
kind: Secret
metadata:
  name: argocd-secret
  labels:
    app.kubernetes.io/name: argocd-secret
    app.kubernetes.io/part-of: argocd
type: Opaque
data:
  admin.password: # bcrypt data
```

### argocd-cm.yaml( + repository-secret.yaml)
manifestを参照するrepositoryを登録します。基本的にPrivate Repositoryかと思いますが、その場合はGitにアクセスするための `passwordSecret`, `usernameSecret` が必要です。これらは後述のsecretファイルを別途作成して参照させます。

```yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cm
  labels:
    app.kubernetes.io/name: argocd-cm
    app.kubernetes.io/part-of: argocd
data:
  repositories: |
    - name: // repository name
      type: git
      url: // your git url
      passwordSecret:
        name: // secret resource
        key: password
      usernameSecret:
        name: // secret resource
        key: username
```

続いてArgoCDが該当repositoryに接続するためのsecretを用意します。[ココ](https://argoproj.github.io/argo-cd/user-guide/private-repositories/)を参考にRead権限のAccess Tokenを作成して登録しました。  
*GitHub: Settings > Developer Settings > Personal access tokens > Generate Token*

base64 encodeした情報をつけるため、セキュリティ的にはもう少し考えた方がいいかもしれません。

```yaml
kind: Secret
apiVersion: v1
metadata:
  name: repository-manifests-addresscode
data:
  password: # github token password
  username: # github token id
type: Opaque
```

### application.yaml
ArgoCDで管理するrepositoryを設定します。ArgoCDのmanifest repositoryを指定しておくことで、ArgoCDでArgoCDを管理できます。

```yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: # application name (e.g. argocd)
spec:
  project: # project name (e.g. default)
  source:
    repoURL: # repository name H(e.g. https://github.com/sample/argocd.git)
    targetRevision: # target branch (e.g. master)
    path: # path (e.g. .)
  destination:
    server: # target cluster (e.g. https://kubernetes.default.svc)
    namespace: # target namespace (e.g. argocd)
```

### option: project.yaml
projectにおける制限をかけられます。デフォルトの`default`でも動作はしますが、色々オプションがあるので、[公式](https://argoproj.github.io/argo-cd/operator-manual/project.yaml)を参考に必要に応じて作成します。

## 2. Clusterに手動でDeploy
マニフェストが作成できたらArgoCDはまだ存在しないので、手動でDeployします.

`$ kustomize build | kubectl apply -f -`

## 3. 以降はArgoCDでSync
程なくアクセスできるようになっているはずなので、 `ID: admin` と `PW: マニフェストに記載したbcrypt前のpw` でログインします。

`Application` 画面を開くと、設定に問題なければArgoCDのrepositoryが見えるはずで、あとはsyncボタンを力強く押せば程なくして完了します。

ArgoCDで管理したいrepositoryが増えたら、`application.yaml` や `project.yaml` など必要な設定をArgoCDのGit Repositoryにpushし、ArgoCDからsyncすれば宣言的な管理が可能となります。

## Directoryについて
まだ悩ましいのですが次のように一旦区切っています。
* project: directory単位にproject.yaml, application.yaml, secret-repos.yamlを追加
* common: ArgoCDの共通設定関連のマニフェストを管理
* cluster: ArgoCDをdeployするために必要なマニフェストを管理(commonと一緒でもいいかも。。)

```sh
.
├── README.md
├── kustomization.yaml // `base:`を含んだrootになるkustomization.yaml
├── project1
│   ├── app1.yaml
│   ├── app2.yaml
│   ├── kustomization.yaml
│   ├── project.yaml
│   └── secret-repos.yaml
├── project2
│   ├── app1.yaml
│   ├── app2.yaml
│   ├── kustomization.yaml
│   ├── project.yaml
│   └── secret-repos.yaml
├── common
│   ├── application-argocd.yaml
│   ├── configmap-argocd.yaml *
│   ├── kustomization.yaml // `application-argocd.yaml`のみ`resources:`で指定
│   └── secret-auth.yaml *
└── cluster
    ├── deployment-argocd-server.yaml * // ArgoCD起動時にオプションを与えたい場合
    ├── kustomization.yaml
    ├── namespace.yaml
    └── ingress.yaml

* rootの kustomization.yamlからpatchesStrategicMergeで指定。あとはdirのkustomization.yamlからresourcesで指定。
```

# まとめ
全てマニフェストでDeployできる状態にしたので、clusterの初期構築状態から簡単にアプリケーションを起動できる状態になりました。
* ArgoCDを手動でDeploy `$ kustomize build . | kubectl apply -f -`
* ArgoCDにログインしてGUIからアプリケーション単位にsyncボタンをぽちぽち

マニフェストが宣言的に管理できるようになったことで透明性も上がりましたし、環境構築も大変楽になったので開発にあたってはなるべく使っていきたいなあと思っています。
ただ、本番に向けてはArgoCDによるリリース作業のテストはなどは十分にできていないので、ここら辺は今後の課題です。