OpenShift 4 (CodeReady Containers)でのArgo CDの導入・利用

# はじめに

当記事では、OpenShift 4環境(厳密にはMacに導入したCodeReady Containers環境)にGitOpsツールであるArgo CDを導入します。

また、Argo CDで配布されているサンプルアプリケーションをデプロイし、マニフェストファイルを更新してその内容がOpenShiftに反映されることを確認します。


# 参考記事/ページ

[GitOps on OpenShift 入門](https://qiita.com/mamomamo/items/5b130fbe9d4956a9465f)
[Argo CD Getting Started](https://argoproj.github.io/argo-cd/getting_started/)


# 動作環境

以下の環境で試しています。

| 項目       |       内容 |
|:-----------------|:------------------|
| マシン             | MacBook Pro (16インチ, Late 2019) (8コア、64GBメモリ) |
| OS           | macOS Catalina (10.15.3) |
| CodeReady Containers             | 1.6.0+8ef676f (OpenShift version: 4.3.0 (embedded in binary)) |
| ocコマンド               | 4.4.0 |
| Argo CD CLI             | v1.5.1+8a3b36b |
| Argo CD(server)          | v1.5.0+bdda410 |


# OpenShiftへのArgoCDのインストール

OpenShiftに対してArgo CDのインストールを実施します。

oc loginコマンドでログイン後、Argo CD用のnamespaceを作成します。

```
❯ kubectl create namespace argocd
namespace/argocd created
```

作成した「argocd」namespaceにArgo CDをインストールします。


```
❯ kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
customresourcedefinition.apiextensions.k8s.io/applications.argoproj.io unchanged
customresourcedefinition.apiextensions.k8s.io/appprojects.argoproj.io unchanged
serviceaccount/argocd-application-controller created
serviceaccount/argocd-dex-server created
serviceaccount/argocd-server created
role.rbac.authorization.k8s.io/argocd-application-controller created
role.rbac.authorization.k8s.io/argocd-dex-server created
role.rbac.authorization.k8s.io/argocd-server created
clusterrole.rbac.authorization.k8s.io/argocd-application-controller unchanged
clusterrole.rbac.authorization.k8s.io/argocd-server unchanged
rolebinding.rbac.authorization.k8s.io/argocd-application-controller created
rolebinding.rbac.authorization.k8s.io/argocd-dex-server created
rolebinding.rbac.authorization.k8s.io/argocd-server created
clusterrolebinding.rbac.authorization.k8s.io/argocd-application-controller unchanged
clusterrolebinding.rbac.authorization.k8s.io/argocd-server unchanged
configmap/argocd-cm created
configmap/argocd-rbac-cm created
configmap/argocd-ssh-known-hosts-cm created
configmap/argocd-tls-certs-cm created
secret/argocd-secret created
service/argocd-dex-server created
service/argocd-metrics created
service/argocd-redis created
service/argocd-repo-server created
service/argocd-server-metrics created
service/argocd-server created
deployment.apps/argocd-application-controller created
deployment.apps/argocd-dex-server created
deployment.apps/argocd-redis created
deployment.apps/argocd-repo-server created
deployment.apps/argocd-server created
```


Argo CDの各種PodがRunningステータスになっていることを確認します。

```
❯ oc get pods -n argocd
NAME                                            READY   STATUS    RESTARTS   AGE
argocd-application-controller-7c587fb9d-mnp87   1/1     Running   0          100s
argocd-dex-server-595c68bc44-5wmwq              1/1     Running   0          100s
argocd-redis-8c568b5db-7jdtl                    1/1     Running   0          100s
argocd-repo-server-b94d7dcb6-5nptx              1/1     Running   0          100s
argocd-server-764bb4b-728qm                     1/1     Running   0          100s
```

[GitOps on OpenShift 入門](https://qiita.com/mamomamo/items/5b130fbe9d4956a9465f)で記載の通り、WebコンソールにアクセスするためのRouteを作成しますが、CodeReady Containersでは自己証明書を使用しているため、Deploymentにpatchを適用し、その後にRouteを作成します。
（この手順については、[Introduction to GitOps with OpenShift](https://www.openshift.com/blog/introduction-to-gitops-with-openshift)にも記載されています）

```
❯ PATCH='{"spec":{"template":{"spec":{"$setElementOrder/containers":[{"name":"argocd-server"}],"containers":[{"command":["argocd-server","--insecure","--staticassets","/shared/app"],"name":"argocd-server"}]}}}}'

❯ oc -n argocd patch deployment argocd-server -p $PATCH
deployment.extensions/argocd-server patched

❯ oc -n argocd create route edge argocd-server --service=argocd-server --port=http --insecure-policy=Redirect
route.route.openshift.io/argocd-server created
```



# Argo CD Webコンソールへのログイン

Webコンソールにログインするためのホスト名を確認します。以下の「HOST/PORT」の項がホスト名になります。

```
❯ oc get route -n argocd
NAME            HOST/PORT                               PATH   SERVICES        PORT   TERMINATION     WILDCARD
argocd-server   argocd-server-argocd.apps-crc.testing          argocd-server   http   edge/Redirect   None
```

ブラウザで https://<上記HOST/PORT>/ にアクセスすることで、Argo CDのWebコンソールのログイン画面にアクセスできます。

<img width="700" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/ccb985ab-07a1-be1a-c69d-d957f969bbf4.png">


Argo CDの管理者ユーザ名、初期パスワードは以下の通りです。

- 管理者ユーザ名　：　admin
- 初期パスワード　：　argocd-serverのPod名

以下でパスワードとなるPod名を取得できます。

```
❯ kubectl get pods -n argocd -l app.kubernetes.io/name=argocd-server -o name | cut -d'/' -f 2
```

ただし、一度Argo CDを導入し、削除した後に再度導入すると、パスワードにPod名を入力してもログインエラーとなりました。
この場合、以下のページを参考に、Secretに設定されているパスワードを変更するとログインできました。

[Argo CD FAQ : I forgot the admin password, how do I reset it?](https://github.com/argoproj/argo-cd/blob/master/docs/faq.md#i-forgot-the-admin-password-how-do-i-reset-it)


# Argo CD CLIのインストール

HomebrewからArgo CD CLIをインストールします。

```
> brew tap argoproj/tap

> brew install argoproj/tap/argocd
```


# CLIでのログイン

CLIでのアクセスは、「argocd-server」serviceのポートフォワーディングを利用します。

[Argo CD Getting Started](https://argoproj.github.io/argo-cd/getting_started/)では、argocd-server serviceをLoad Balancerに変更するといった手順も紹介されていますが、ポートフォワーディングが最も容易でした。

別のコンソールを起動してkubectl port-forwardを実行します。

```
❯ kubectl port-forward svc/argocd-server -n argocd 8080:443
```

その後、CLIでログインします。

```
❯ argocd login localhost:8080
```

ユーザ名、パスワードが求められますが、Webコンソールのログイン時と同じユーザ(admin)、パスワードを入力します。
ログインが成功したら、adminのパスワードを変更しておきます。

```
❯ argocd account update-password
```



# サンプルアプリのデプロイ

まず、サンプルアプリをデプロイするためのプロジェクトを作成します。ここではプロジェクト名を「argocd-example-apps」とします。

```
❯ oc new-project argocd-example-apps
Now using project "argocd-example-apps" on server "https://api.crc.testing:6443".

You can add applications to this project with the 'new-app' command. For example, try:

    oc new-app ruby~https://github.com/sclorg/ruby-ex.git

to build a new example application in Python. Or use kubectl to deploy a simple Kubernetes application:

    kubectl create deployment hello-node --image=gcr.io/hello-minikube-zero-install/hello-node
```

Argo CDでCDを試すことのできるサンプルアプリは https://github.com/argoproj/argocd-example-apps にあります。
このGitHubのリポジトリを自分のアカウントにForkします。

Fork後のリポジトリのURLが、https://github.com/&lt;GitHubのアカウント名&gt;/argocd-example-apps になります。


次に、Argo CDのWeb UIでアプリケーションをデプロイしていきます。

ログイン後の画面で、「＋NEW APP」ボタンをクリックします。

<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/0bfbf98f-d523-a95f-eb88-08696b0b2a09.png">


以下の通り入力し、他はデフォルトのままで、「CREATE」ボタンを押します。
「Namespace」には、先ほど作成したOpenShiftプロジェクトのプロジェクト名(argocd-example-apps）を設定します。

<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/ad6010a1-ae5c-347a-6a60-0f235afc868f.png">

<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/af218178-7cc6-062d-bee8-0307c954a8f0.png">

<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/5fd79f8f-3149-ee65-d096-c89c372048e9.png">


ここまで実行すると、guestbookアプリケーションがWeb UIに現れます。
ただ、ステータスが「OutOfSync」となっており、まだguestbookアプリケーションはOpenShiftにデプロイされていません。

<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/f5449955-9212-8d8e-2b07-3d8a7fb56ef5.png">

アプリケーションをデプロイするために、Gitの状態(＝マニフェストファイルで定義した状態）とOpenShiftの状態を同期させます。
これにより、Gitリポジトリに格納されているマニフェストファイルが適用され、guestbookアプリケーションがデプロイされます。

上記の画面の「SYNC」ボタンをクリックします。

次に、以下の画面で「SYNCHRONIZE」ボタンをクリックします。

<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/79c90a28-e252-e8bc-01ca-8913aa6a18bc.png">

その後しばらく待つと、以下の状態になります。
Statusが「Synced」になっており、Gitと同期が取れた状態になっているのが分かります。

<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386149/8ac94c87-1bdf-2b84-0e17-33d44523484a.png">


デプロイされたPodが起動したことを確認します。

```
❯ oc get pods
NAME                            READY   STATUS             RESTARTS   AGE
guestbook-ui-65b878495d-g9shv   0/1     CrashLoopBackOff   6          8m44s

❯ oc logs guestbook-ui-65b878495d-g9shv
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 10.128.0.58. Set the 'ServerName' directive globally to suppress this message
(13)Permission denied: AH00072: make_sock: could not bind to address [::]:80
(13)Permission denied: AH00072: make_sock: could not bind to address 0.0.0.0:80
no listening sockets available, shutting down
AH00015: Unable to open logs

❯ oc get pod guestbook-ui-65b878495d-g9shv -o yaml | grep -i serviceAccountName
  serviceAccountName: default
```

oc get podsしてみると、CrashLoopBackOffになっています。
oc logsでログを確認すると、80ポートでListenできていないようです。

OpenShiftで実行されるコンテナは、OpenShiftがランダムに割り当てたUIDで実行されますが、80ポートを使用するためにはroot権限が必要です。

そのため、PodのserviceAccount 「default」に対してanyuid SCCを付与し、コンテナイメージのDockerfileのUSERインストラクションで設定されているユーザでコンテナを実行できるようにする必要があります。

```
❯ oc adm policy add-scc-to-user anyuid -z default
securitycontextconstraints.security.openshift.io/anyuid added to: ["system:serviceaccount:argocd-example-apps:default"]
```

Podを一度削除し、その後自動復旧したPodがRunningになることを確認します。

```
❯ oc delete pod/guestbook-ui-65b878495d-g9shv
pod "guestbook-ui-65b878495d-g9shv" deleted

❯ oc get pods
NAME                            READY   STATUS    RESTARTS   AGE
guestbook-ui-65b878495d-9l6vq   1/1     Running   0          2m19s
```

oc expose でアプリを公開します。

```
❯ oc expose svc/guestbook-ui
route.route.openshift.io/guestbook-ui exposed

❯ oc get route
NAME           HOST/PORT                                           PATH   SERVICES       PORT   TERMINATION   WILDCARD
guestbook-ui   guestbook-ui-argocd-example-apps.apps-crc.testing          guestbook-ui   80                   None
```

oc get routeで取得したホストにアクセスします。
http://guestbook-ui-argocd-example-apps.apps-crc.testing/



# マニフェストファイルを更新してみる

最後に、マニフェストファイルを更新してGitHubに反映し、Argo CDでSyncすることでOpenShiftに反映されることを確認します。

git cloneしたリポジトリのmasterブランチにおいて、guestbook/guestbook-ui-deployment.yamlの spec.replicas を 1 から 2 に変更します。

```
apiVersion: apps/v1
kind: Deployment
metadata:
  name: guestbook-ui
spec:
  replicas: 2    # 1から2に変更
  revisionHistoryLimit: 3
  selector:
    matchLabels:
      app: guestbook-ui
  template:
    metadata:
      labels:
        app: guestbook-ui
    spec:
      containers:
      - image: gcr.io/heptio-images/ks-guestbook-demo:0.2
        name: guestbook-ui
        ports:
        - containerPort: 80
```

変更したファイルをcommitし、GitHubにpushします。

Argo CDでのSyncの前に、guestbookアプリケーションの状態を確認しておきます。

```
❯ argocd app list
NAME       CLUSTER                         NAMESPACE            PROJECT  STATUS     HEALTH   SYNCPOLICY  CONDITIONS  REPO                                             PATH       TARGET
guestbook  https://kubernetes.default.svc  argocd-example-apps  default  OutOfSync  Healthy  <none>      <none>      https://github.com/imanissy/argocd-example-apps  guestbook  HEAD
```

STATUSがOutOfSuncになっているのは、oc exposeによりrouteを作成したためです。
（本来的には、routeについてもマニフェストファイルに定義する必要がありますがここでは割愛）


argocd app syncで、guestbookアプリケーションをGitHubと同期させます。

```
❯ argocd app sync guestbook
TIMESTAMP                  GROUP                     KIND   NAMESPACE                           NAME    STATUS    HEALTH        HOOK  MESSAGE
2020-04-27T23:40:44+09:00  route.openshift.io       Route  argocd-example-apps          guestbook-ui  OutOfSync                       
2020-04-27T23:40:44+09:00                         Service  argocd-example-apps          guestbook-ui    Synced   Healthy              
2020-04-27T23:40:44+09:00   apps               Deployment  argocd-example-apps          guestbook-ui    Synced   Healthy              

Name:               guestbook
Project:            default
Server:             https://kubernetes.default.svc
Namespace:          argocd-example-apps
URL:                http://localhost:8080/applications/guestbook
Repo:               https://github.com/imanissy/argocd-example-apps
Target:             HEAD
Path:               guestbook
SyncWindow:         Sync Allowed
Sync Policy:        <none>
Sync Status:        OutOfSync from HEAD (5efadca)
Health Status:      Progressing

Operation:          Sync
Sync Revision:      5efadca55276c68f0e01ac24522d37ab8700fe25
Phase:              Succeeded
Start:              2020-04-27 22:02:10 +0900 JST
Finished:           2020-04-27 22:02:12 +0900 JST
Duration:           2s
Message:            successfully synced (all tasks run)

GROUP               KIND        NAMESPACE            NAME          STATUS     HEALTH       HOOK  MESSAGE
route.openshift.io  Route       argocd-example-apps  guestbook-ui  OutOfSync                     ignored (requires pruning)
                    Service     argocd-example-apps  guestbook-ui  Synced     Healthy            service/guestbook-ui unchanged
apps                Deployment  argocd-example-apps  guestbook-ui  Synced     Progressing        deployment.apps/guestbook-ui configured
FATA[0003] 1 resources require pruning                           
```

Deploymentについて、「deployment.apps/guestbook-ui configured」というメッセージが出ており、deployment.yamlの変更が反映されたようです。

Podの状態も確認しておきます。

```
❯ oc get pods
NAME                            READY   STATUS    RESTARTS   AGE
guestbook-ui-65b878495d-4xs5t   1/1     Running   0          80s
guestbook-ui-65b878495d-9l6vq   1/1     Running   0          27m
```

Deploymentに設定したレプリカ数=2の変更が実際に反映されていました。

argocd app historyで反映の履歴を確認できます。

```
❯ argocd app history guestbook
ID  DATE                           REVISION
0   2020-04-27 21:06:53 +0900 JST  HEAD (11113c9)
1   2020-04-27 21:59:23 +0900 JST  HEAD (11113c9)
2   2020-04-27 22:02:12 +0900 JST  HEAD (5efadca)
```

このように、Argo CDを利用することで、Gitリポジトリの状態をベースとしたアプリケーションの反映（状態の同期）を実現することができます。