TECHSTEP

ITインフラ関連の記事を公開してます。

ArgoCDに入門する

はじめに

本記事ではGitOps CDツールの一つであるArgoCDの概要と利用方法について紹介いたします。

ArgoCDとは

ArgoCDはKubernetesクラスター向けのContinuous Deliveryを実現するツールです。ArgoCDでは、Kubernetesマニフェストファイルが格納されたGitHub / Helmリポジトリを監視し、リポジトリ上で変更が発生したらその差分を検出して、Kubernetesクラスターに反映します。

GitOpsとは

ArgoCDは、いわゆるGitOpsを実現するツールとして注目されています。GitOpsとは、以下のような特徴を備えたContinuous Deliveryの手法です。

  • GitをSingle Source of Truth(信頼できる唯一の情報源)として扱い、バージョン管理や変更履歴、ピアレビュー、ロールバックなどを、kubectlのようなツールを利用せずに実現する

    • システム全体の状態をバージョン管理下に置き、それは単一のGitリポジトリに表現される(管理される)
    • オペレーションによる変更はpull requestで行われる
    • Gitで管理された状態と比較して、何らかの変更が実際の稼働システム上に発生すると、ある種のツールはそれを差分として検出し、必要に応じてそれらを同期する。
    • ロールバック・監査ログもGit経由で提供される
  • クラウドKubernetesを利用するシステムと相性が良い

    • コードで表現することのできるクラウドやコンテナリソースはGitをSingle Source of Truthとして使うのと相性が良く、コード化することでバージョン管理ができる。これによりシステムに対する操作・回復・監視をより簡単に実現できる。

図:Weaveworks Blogより参照

gitops-pipeline

gitops-description

※参考ドキュメント:

ArgoCDの構成

ArgoCDは、以下のように複数のサービスから成り立っています。

argocd-compose

API Server

ArgoCDにアクセスするWeb UI・CLI・CI/CDに利用するAPIを提供するサーバーで、アプリケーションの状態報告や管理・操作、Credential管理やGit Webhookイベントの受け取りなどを行います。

Repository Server

マニフェストファイルを管理するGitリポジトリの情報をローカルキャッシュとして保持するサーバーで、リポジトリURLなどの情報をインプットとして、Kubernetesマニフェストファイルの生成を行います。

Repository Serverは/tmp配下にリポジトリをCloneします。リポジトリの数やファイル数が多い場合はPersistent Volumeをマウントすることでエラー無く実行することが可能になります。

Repository Serverはデフォルトで3分ごとにマニフェストの更新がないかチェックします。ArgoCDでは、リポジトリに変更があったときのみマニフェストが変更されると想定しており、生成されたマニフェストを24時間キャッシュします。

一つのRepository Serverは、一つのリポジトリに対する操作は同時に一つしか実行しません。同じリポジトリに複数のアプリケーションを所有する場合、Repository Serverのレプリカ数を増加します。

Repository Serverでは、リポジトリの設定時にHEADrevisionのような設定をすると、リポジトリの更新を確認するためにgit ls-remoteを頻繁に実行します。

Application Controller

Kubernetes Controllerとして、起動中のアプリケーションを監視し、実際に稼働するアプリケーションの状態とあるべき状態とを比較します。アプリケーションがOutOfSyncの状態になったことを検知すると適切な操作を行ったり、ユーザーの定義した通りにライフサイクルイベントを実行したりします。

Controllerは、アプリケーションの reconcilliationとアプリケーションの同期のために2種類のQueueを利用します。これらは実行時間に差があり(reconcilliation < Sync)、また利用者がコントロールすることができます。

Controllerは内部でkubectlコマンドを実行し、クラスターへのリソース作成や変更を行います。PodのOOM Killを引き起こす場合は--kubectl-parallelism-limitを利用し、同時に実行するkubectl fork/execの数を制限することができます。

※参考ドキュメント:

ArgoCDの機能

ArgoCDでは、リポジトリとの同期・差分検出を行うだけでなく、それに関連した様々な機能を提供します。

手動・自動でのアプリケーション同期

ArgoCDではGitHubリポジトリを登録し、リポジトリに格納されたファイルを同期することで、マニフェストファイルの変更を検知します。リポジトリの状態と実際の起動したアプリケーションとを同期するには、自動・手動のいずれかを利用できます。

自動同期はsyncPolicyを設定することで有効になります。これを利用すると、CI/CDパイプラインがArgoCD API Serverと直接通信することなく、アプリケーションのデプロイを実行することができます。またGitリポジトリ上のリソースが削除されたとき、デフォルトではそれらを削除するがありませんが、自動的にpruneするよう設定することも可能です。

自動同期をするタイミングはSync Windowsという機能で制限することができます。Sync WindowsではCronの形式で、同期を許可・拒否する時間帯を指定します。Sync Windowsを利用するには、CLIから設定を行うか、AppProjectというマニフェストを作成します。

手動同期では、UI上で同期を行うリソースを選択することができます。

また自動検知はデフォルトで3分毎に実行しますが、Git Provider Webhookを設定することで、Webhook イベントを受け取ることができます。これによりポーリングの間隔と実際の変更のタイミングとのずれによる遅延が発生しなくなります。

※参考ドキュメント:

Web UIを備える

ArgoCDにはWeb UIがはじめから備わっており、GitHubリポジトリKubernetesクラスターの登録、アプリケーションの同期やロールバック、起動状態の確認などを行うことができます。

アプリケーションには、GitHubリポジトリに格納されたアプリケーションのバージョンが表示され、指定したバージョンへのロールバックを実行することができます。

アプリケーションに対しては、Kubernetesリソースに対するヘルスチェックを通じてリアルタイムでの起動状態を表示します。またヘルスチェックをカスタムすることも可能(Luaで記述)で、ArgoCDが対応していないCustom Resourceや、アプリケーションに既知の問題が含まれる場合に対応することが可能です。

また、CSSファイルを用意してUIをカスタマイズすることも可能なようです。

※参考ドキュメント:

複数のKubernetesマニフェスト管理ツールに対応

ArgoCDは、Kubernetesの素のマニフェストファイルに加え、以下のようなマニフェスト管理ツールに対応しています。

またConfig Management Pluginを利用することで、より多くのツールを利用することができます。

※参考ドキュメント:

複数のアプリケーション・Kubernetesクラスターに対応

ArgoCDは複数のアプリケーションを同時に管理することが可能です。現在は1000以上のアプリケーションを利用するとUIの速度が大きく低下する様ですが、将来的には2000以上のアプリケーションに対応できるよう検討されているようです。

またArgoCDは複数のKubernetesクラスターを管理対象として登録することができます。現在対応しているクラスター数の上限は不明ですが、将来的には1つのArgoCD Controllerで100クラスター以上を管理できるよう、Controllerの水平スケールやシャーディングの実装が検討されているようです。

※参考ドキュメント:

ユーザー管理

ArgoCDは初期ユーザーとしてadminが登録されており、ユーザーを追加する場合はConfigMapを利用します。

またArgoCDはSSOにも対応しています。ArgoCDをデプロイする際に利用できるinstall.ymlには、ArgoCDのほかにDex Serverが定義されており、OIDC Providerとしてこちらを利用することができます。また、既に別のOIDC Providerを利用している場合はそちらも利用できます。対応するOIDC Providerは以下の通りです。

※参考ドキュメント:

Sync Operationの設定

ArgoCDはGitHubリポジトリとのSync(同期)はResource Hookを利用して行われます。Syncを行う過程では、その前後で複数の操作を行うことが可能です。ArgoCDで定義するアプリケーションに含まれるマニフェストに指定のAnnotationを付与することでこの操作は可能であり、利用できるHookには以下の種類があります。

  • PreSync: アプリケーションのマニフェストファイルを利用する前に実行します。新しいアプリケーションをデプロイする前にDBスキーマの移行を行う場合などに利用されます。
  • Sync: PreSyn Hookがすべて完了・成功した後に実行されます。アプリケーションのマニフェストファイルを利用するのと同じタイミングです。Kubernetesで利用できるRolling Updateよりも複雑なデプロイ方法をとる場合などに利用できます。
  • Skip: アプリケーションのマニフェストファイルをスキップします。
  • PostSync: SyncHookがすべて完了・成功し、アプリケーションの状態がHealthyになった後で実行されます。アプリケーションのデプロイが完了した後にヘルスチェックなどを別途行う場合などに利用されます。
  • SyncFail: Sync操作が失敗したときに実行されます。デプロイに失敗した場合にリソースのクリーンアップを行う場合などに利用できます。

なおBlue-Green DeploymentやCanary Releaseを利用したい場合は、同プロジェクトにあるArgo Rolloutを利用することで実現できます。

※参考ドキュメント:

その他
  • Audit: Kubernetesクラスター上でのアプリケーションのイベント情報を確認することができるので、GitHub上の履歴だけでなく、実際に稼働したアプリケーションの情報も利用することができます。
  • CLI: argocd CLIを利用することで、自動化及びCIとの統合が可能になります。
  • Prometheus metrics: アプリケーション向け・ArgoCD API Server向けのメトリクスが公開されています。

ArgoCDの利用方法

ArgoCDの概要について紹介したので、次は実際にArgoCDを動かしてみます。今回は公式ドキュメントにあるGetting Startedに従って利用します。

構築環境

今回利用するKubernetesの環境は以下の通りです。

  • Kubernetes Cluster: Amazon EKS
  • 構築方法: eksctlを利用
  • 作業環境: WSL (Ubuntu 18.04.4)

※参考ドキュメント:

ArgoCDのデプロイ

クラスターが用意できたので、まずはArgoCDをクラスター上にデプロイします。ここでは事前に用意されたinstall.ymlを使用します。install.ymlには、ArgoCDを動作するうえで必要な3つのサービスに加え、redis-server dex-serverの2つが定義されています。

$ kubectl get ns
NAME              STATUS   AGE
default           Active   15m
kube-node-lease   Active   15m
kube-public       Active   15m
kube-system       Active   15m


# “argocd” Namespaceの作成
$ kubectl create namespace argocd
namespace/argocd created

# ArgoCDのデプロイ
$ kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
customresourcedefinition.apiextensions.k8s.io/applications.argoproj.io created
customresourcedefinition.apiextensions.k8s.io/appprojects.argoproj.io created
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 created
clusterrole.rbac.authorization.k8s.io/argocd-server created
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 created
clusterrolebinding.rbac.authorization.k8s.io/argocd-server created
configmap/argocd-cm created
configmap/argocd-gpg-keys-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


# デプロイ後の確認
$ kubectl get crd
NAME                                         CREATED AT
applications.argoproj.io                     2020-09-15T05:54:39Z★
appprojects.argoproj.io                      2020-09-15T05:54:39Z★
eniconfigs.crd.k8s.amazonaws.com             2020-09-15T05:38:20Z
securitygrouppolicies.vpcresources.k8s.aws   2020-09-15T05:38:22Z

$ kubectl get sa -n argocd
NAME                            SECRETS   AGE
argocd-application-controller   1         6m55s
argocd-dex-server               1         6m55s
argocd-server                   1         6m55s
default                         1         7m10s

$ kubectl get role -n argocd
NAME                            AGE
argocd-application-controller   9m10s
argocd-dex-server               9m10s
argocd-server                   9m10s

$ kubectl get rolebinding -n argocd
NAME                            AGE
argocd-application-controller   9m17s
argocd-dex-server               9m17s
argocd-server                   9m17s

$ kubectl get clusterrole -n argocd
NAME                                                                   AGE
admin                                                                  25m
argocd-application-controller                                          9m24s
argocd-server                                                          9m24s
aws-node                                                               25m

$ kubectl get clusterrolebinding -n argocd
NAME                                                   AGE
argocd-application-controller                          10m
argocd-server                                          10m
aws-node                                               26m

$ kubectl get cm -n argocd
NAME                        DATA   AGE
argocd-cm                   0      5m47s
argocd-gpg-keys-cm          0      5m47s
argocd-rbac-cm              0      5m47s
argocd-ssh-known-hosts-cm   1      5m47s
argocd-tls-certs-cm         0      5m47s

$ kubectl get secret -n argocd
NAME                                        TYPE                                  DATA   AGE
argocd-application-controller-token-c7kh2   kubernetes.io/service-account-token   3      10m
argocd-dex-server-token-2pnlw               kubernetes.io/service-account-token   3      10m
argocd-secret                               Opaque                                5      10m
argocd-server-token-kvdr4                   kubernetes.io/service-account-token   3      10m
default-token-ctgsz                         kubernetes.io/service-account-token   3      11m

$ kubectl get svc -n argocd
NAME                    TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
argocd-dex-server       ClusterIP   10.100.252.138   <none>        5556/TCP,5557/TCP,5558/TCP   11m
argocd-metrics          ClusterIP   10.100.0.152     <none>        8082/TCP                     11m
argocd-redis            ClusterIP   10.100.56.122    <none>        6379/TCP                     11m
argocd-repo-server      ClusterIP   10.100.67.88     <none>        8081/TCP,8084/TCP            11m
argocd-server           ClusterIP   10.100.114.29    <none>        80/TCP,443/TCP               11m
argocd-server-metrics   ClusterIP   10.100.150.172   <none>        8083/TCP                     11m

$ kubectl get deployment -n argocd
NAME                            READY   UP-TO-DATE   AVAILABLE   AGE
argocd-application-controller   1/1     1            1           11m
argocd-dex-server               1/1     1            1           11m
argocd-redis                    1/1     1            1           11m
argocd-repo-server              1/1     1            1           11m
argocd-server                   1/1     1            1           11m

$ kubectl get replicaset -n argocd
NAME                                       DESIRED   CURRENT   READY   AGE
argocd-application-controller-6bf955dd55   1         1         1       11m
argocd-dex-server-7c8f5cc655               1         1         1       11m
argocd-redis-54b6ff7bf6                    1         1         1       11m
argocd-repo-server-56966df94f              1         1         1       11m
argocd-server-cf8dbb7bd                    1         1         1       11m

$ kubectl get pods -n argocd
NAME                                             READY   STATUS    RESTARTS   AGE
argocd-application-controller-6bf955dd55-9wtrg   1/1     Running   0          11m
argocd-dex-server-7c8f5cc655-vkztr               1/1     Running   0          11m
argocd-redis-54b6ff7bf6-98hl7                    1/1     Running   0          11m
argocd-repo-server-56966df94f-rqkg6              1/1     Running   0          11m
argocd-server-cf8dbb7bd-bnl96                    1/1     Running   0          11m

ArgoCD CLIインストール

次にargocd CLIをインストールします。この時点ではArgoCD API Serverとの疎通が取れない状態なので、argocd versionコマンド実行時にエラーが発生します。

# CLIインストール
$ wget https://github.com/argoproj/argo-cd/releases/download/v1.7.4/argocd-linux-amd64
$ sudo cp -p argocd-linux-amd64 /usr/local/bin/argocd
$ sudo chmod +x /usr/local/bin/argocd

# インストール後の確認
$ argocd version
argocd: v1.7.4+f8cbd6b
  BuildDate: 2020-09-05T02:44:27Z
  GitCommit: f8cbd6bf432327cc3b0f70d23b66511bb906a178
  GitTreeState: clean
  GoVersion: go1.14.1
  Compiler: gc
  Platform: linux/amd64
FATA[0000] Argo CD server address unspecified

ArgoCD API Serverへのアクセス

つぎにArgoCD API Serverへのアクセスができるよう、argocd-serverServiceを一部変更します。

# LoadBalancerに変更
$ kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "LoadBalancer"}}'
service/argocd-server patched

# 変更後の確認
$ kubectl get svc -n argocd
NAME                    TYPE           CLUSTER-IP       EXTERNAL-IP
              PORT(S)                      AGE
argocd-dex-server       ClusterIP      10.100.252.138   <none>
              5556/TCP,5557/TCP,5558/TCP   66m
argocd-metrics          ClusterIP      10.100.0.152     <none>
              8082/TCP                     66m
argocd-redis            ClusterIP      10.100.56.122    <none>
              6379/TCP                     66m
argocd-repo-server      ClusterIP      10.100.67.88     <none>
              8081/TCP,8084/TCP            66m
argocd-server           LoadBalancer   10.100.114.29    a36182a4c9e57495c8e0f245b2d2d8a8-995334307.ap-northeast-1.elb.amazonaws.com   80:30629/TCP,443:30865/TCP   66m
argocd-server-metrics   ClusterIP      10.100.150.172   <none>
              8083/TCP                     66m

ArgoCDへのログイン(CLI

つぎにargocd CLIからログインし、ArgoCDでデプロイを行うクラスターを指定します。ここでログインをしておかないと、クラスターの登録などが実行できません。

# ログイン前にクラスターを登録しようとすると失敗する
$ argocd cluster add ubuntu-cli-user@eks-work-cluster.ap-northeast-1.eksctl.io
INFO[0000] ServiceAccount "argocd-manager" created in namespace "kube-system"
INFO[0000] ClusterRole "argocd-manager-role" created
INFO[0000] ClusterRoleBinding "argocd-manager-role-binding" created
FATA[0001] Argo CD server address unspecified

# ログインパスワードの確認
# 初期アカウントは”admin”のみ、ログインパスワードは”argocd-server”Pod名
$ kubectl get pods -n argocd -l app.kubernetes.io/name=argocd-server -o name | cut -d'/' -f 2
argocd-server-cf8dbb7bd-bnl96

# CLI経由でログイン (先ほど作成したLBのEXTERNAL-IPを指定)
$ argocd login --insecure a36182a4c9e57495c8e0f245b2d2d8a8-995334307.ap-northeast-1.elb.amazonaws.com:443
Username: admin
Password:
'admin' logged in successfully
Context 'a36182a4c9e57495c8e0f245b2d2d8a8-995334307.ap-northeast-1.elb.amazonaws.com:443' updated

# パスワードのアップデート
$ argocd account update-password
*** Enter current password:
*** Enter new password:
*** Confirm new password:
Password updated
Context 'a36182a4c9e57495c8e0f245b2d2d8a8-995334307.ap-northeast-1.elb.amazonaws.com:443' updated

# ログイン後の確認
# 先ほどはエラー表示だったArgoCD Serverの情報が確認できる
$ argocd version
argocd: v1.7.4+f8cbd6b
  BuildDate: 2020-09-05T02:44:27Z
  GitCommit: f8cbd6bf432327cc3b0f70d23b66511bb906a178
  GitTreeState: clean
  GoVersion: go1.14.1
  Compiler: gc
  Platform: linux/amd64
argocd-server: v1.7.3+b4c79cc
  BuildDate: 2020-09-01T23:19:02Z
  GitCommit: b4c79ccb88173604c3786dcd34e83a9d7e8919a5
  GitTreeState: clean
  GoVersion: go1.14.1
  Compiler: gc
  Platform: linux/amd64
  Ksonnet Version: v0.13.1
  Kustomize Version: {Version:kustomize/v3.6.1 GitCommit:c97fa946d576eb6ed559f17f2ac43b3b5a8d5dbd BuildDate:2020-05-27T20:47:35Z GoOs:linux GoArch:amd64}
  Helm Version: version.BuildInfo{Version:"v3.2.0", GitCommit:"e11b7ce3b12db2941e90399e874513fbd24bcb71", GitTreeState:"clean", GoVersion:"go1.13.10"}
  Kubectl Version: v1.17.8

# クラスター登録
$ argocd cluster add
ERRO[0000] Choose a context name from:
CURRENT  NAME                                                       CLUSTER                                    SERVER
*        ubuntu-cli-user@eks-work-cluster.ap-northeast-1.eksctl.io  eks-work-cluster.ap-northeast-1.eksctl.io  https://987C1007C84B9FEEA7CC9F1D529C3393.yl4.ap-northeast-1.eks.amazonaws.com

$ argocd cluster add ubuntu-cli-user@eks-work-cluster.ap-northeast-1.eksctl.io
INFO[0000] ServiceAccount "argocd-manager" already exists in namespace "kube-system"
INFO[0000] ClusterRole "argocd-manager-role" updated
INFO[0000] ClusterRoleBinding "argocd-manager-role-binding" updated
Cluster 'https://987C1007C84B9FEEA7CC9F1D529C3393.yl4.ap-northeast-1.eks.amazonaws.com' added

この時点でWeb UIからログインをしている場合は、以下の画像のように新規クラスターが登録されたことが確認できます。

f:id:FY0323:20200922094630p:plain

ArgoCDへのログイン(UI)

次にWeb UIからログインをします。先ほど作成したLBのExternal-IPを指定して、Webブラウザからアクセスをすると、ログイン画面が表示されます。

f:id:FY0323:20200922095617p:plain

先ほど変更したパスワードとadminユーザーを指定してログインをします。この時点ではまだ何のアプリケーションも登録されていません。

f:id:FY0323:20200922095711p:plain

サンプルアプリケーションの作成

次にWeb UIからアプリケーションを作成します。まずはNEW APPを選択します。

f:id:FY0323:20200922095814p:plain

すると、以下のように入力画面が表示されます。

f:id:FY0323:20200922095939p:plain

Application Nameには作成するアプリケーションの名称を指定します。Projectは複数のアプリケーションを論理的にグルーピングしたものを指し、特に複数のチームが共通のArgoCDを利用する場合に役立ちます。ここではdefaultを指定しますが、特に指定しないアプリケーションはdefaultProjectに属することになります。

SYNC POLICYではManual Automaticから選択することができます。ここではManualを選択しますが、Automaticを選択した場合は以下のように追加の選択オプションが表示されます。

f:id:FY0323:20200922100013p:plain

またSYNC OPTIONSは特に変更せず、次に進みます。

※参考ドキュメント:

次に同期元となるGitリポジトリを指定します。

f:id:FY0323:20200922100055p:plain

今回はArgoCDのサンプル用マニフェストファイルの格納されているこちらを利用します。利用するマニフェストファイルはリポジトリguestbookに含まれるため、pathにはこれを指定します。またrevisionHEADを指定します。

次にアプリケーションをデプロイするクラスターを指定します。

f:id:FY0323:20200922100152p:plain

ここでは、ArgoCDが稼働しているのと同じクラスターを表すhttps://kubernetes.default.svcを指定します。なおクラスターを事前に登録することで、この項目の選択肢としてクラスターの情報が表示されます。

必要な情報はすべて設定したので、画面上部のCREATEを選択します。すると以下の画面のように遷移し、アプリケーションが登録されたことがわかります。

f:id:FY0323:20200922100243p:plain

この時点ではOutOfSyncと表示されている通り、まだGitリポジトリとの同期が行われておらず、クラスター上にもアプリケーションは存在しません。UI上で作成したアプリケーションを選択しても、アプリケーションを構成するKubernetesリソースの構成は見えるものの、やはりOutOfSync状態であることが確認できます。

f:id:FY0323:20200922100329p:plain

この時コマンドライン上で確認をすると、以下のように表示されます。

# アプリケーションは登録されている
$ argocd app list
NAME       CLUSTER                         NAMESPACE  PROJECT  STATUS     HEALTH   SYNCPOLICY  CONDITIONS  REPO                                                 PATH       TARGET
guestbook  https://kubernetes.default.svc  default    default  OutOfSync  Missing  <none>      <none>      https://github.com/argoproj/argocd-example-apps.git  guestbook  HEAD

# アプリケーションの詳細
$ argocd app get guestbook
Name:               guestbook
Project:            default
Server:             https://kubernetes.default.svc
Namespace:          default
URL:                https://a36182a4c9e57495c8e0f245b2d2d8a8-995334307.ap-northeast-1.elb.amazonaws.com/applications/guestbook
Repo:               https://github.com/argoproj/argocd-example-apps.git
Target:             HEAD
Path:               guestbook
SyncWindow:         Sync Allowed
Sync Policy:        <none>
Sync Status:        OutOfSync from HEAD (6bed858)
Health Status:      Missing

GROUP  KIND        NAMESPACE  NAME          STATUS     HEALTH   HOOK  MESSAGE
       Service     default    guestbook-ui  OutOfSync  Missing
apps   Deployment  default    guestbook-ui  OutOfSync  Missing

# クラスター上にはアプリケーションリソースが存在しない
$ kubectl get pods
No resources found in default namespace.

 kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.100.0.1   <none>        443/TCP   3h35m



# “Application”というCustom Resouceは作成されている
$ kubectl get app -n argocd
NAME        AGE
guestbook   37m

次にSyncを実行します。今回はargocdCLIからSyncを実行します。

# 同期の実行
$ argocd app sync guestbook
TIMESTAMP                  GROUP        KIND   NAMESPACE                  NAME    STATUS    HEALTH        HOOK  MESSAGE
2020-09-15T18:14:25+09:00            Service     default          guestbook-ui  OutOfSync  Missing
2020-09-15T18:14:25+09:00   apps  Deployment     default          guestbook-ui  OutOfSync  Missing
2020-09-15T18:14:25+09:00            Service     default          guestbook-ui    Synced  Healthy
2020-09-15T18:14:25+09:00            Service     default          guestbook-ui    Synced   Healthy              service/guestbook-ui created
2020-09-15T18:14:25+09:00   apps  Deployment     default          guestbook-ui  OutOfSync  Missing              deployment.apps/guestbook-ui created
2020-09-15T18:14:25+09:00   apps  Deployment     default          guestbook-ui    Synced  Progressing              deployment.apps/guestbook-ui created

Name:               guestbook
Project:            default
Server:             https://kubernetes.default.svc
Namespace:          default
URL:                https://a36182a4c9e57495c8e0f245b2d2d8a8-995334307.ap-northeast-1.elb.amazonaws.com/applications/guestbook
Repo:               https://github.com/argoproj/argocd-example-apps.git
Target:             HEAD
Path:               guestbook
SyncWindow:         Sync Allowed
Sync Policy:        <none>
Sync Status:        Synced to HEAD (6bed858)
Health Status:      Progressing

Operation:          Sync
Sync Revision:      6bed858de32a0e876ec49dad1a2e3c5840d3fb07
Phase:              Succeeded
Start:              2020-09-15 18:14:25 +0900 JST
Finished:           2020-09-15 18:14:26 +0900 JST
Duration:           1s
Message:            successfully synced (all tasks run)

GROUP  KIND        NAMESPACE  NAME          STATUS  HEALTH       HOOK  MESSAGE
       Service     default    guestbook-ui  Synced  Healthy            service/guestbook-ui created
apps   Deployment  default    guestbook-ui  Synced  Progressing        deployment.apps/guestbook-ui created


# 同期後の確認
$ argocd app get guestbook
Name:               guestbook
Project:            default
Server:             https://kubernetes.default.svc
Namespace:          default
URL:                https://a36182a4c9e57495c8e0f245b2d2d8a8-995334307.ap-northeast-1.elb.amazonaws.com/applications/guestbook
Repo:               https://github.com/argoproj/argocd-example-apps.git
Target:             HEAD
Path:               guestbook
SyncWindow:         Sync Allowed
Sync Policy:        <none>
Sync Status:        Synced to HEAD (6bed858)
Health Status:      Healthy

GROUP  KIND        NAMESPACE  NAME          STATUS  HEALTH   HOOK  MESSAGE
       Service     default    guestbook-ui  Synced  Healthy        service/guestbook-ui created
apps   Deployment  default    guestbook-ui  Synced  Healthy        deployment.apps/guestbook-ui created

# 他のリソースも作成される
$ kubectl get all
NAME                                READY   STATUS    RESTARTS   AGE
pod/guestbook-ui-65b878495d-cs87p   1/1     Running   0          2m53s

NAME                   TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
service/guestbook-ui   ClusterIP   10.100.247.168   <none>        80/TCP    2m53s
service/kubernetes     ClusterIP   10.100.0.1       <none>        443/TCP   3h39m

NAME                           READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/guestbook-ui   1/1     1            1           2m53s

NAME                                      DESIRED   CURRENT   READY   AGE
replicaset.apps/guestbook-ui-65b878495d   1         1         1       2m53s

この時Web UIでも同期が行われていることが確認できます。

f:id:FY0323:20200922100441p:plain

しばらく待機すると、以下のようにSynced Healthyという状態が確認できます。

f:id:FY0323:20200922100519p:plain

アプリケーションを選択すると、Kubernetesリソースの構成が表示されます。

f:id:FY0323:20200922100628p:plain

f:id:FY0323:20200922100638p:plain

アプリケーションの変更

次にアプリケーションの構成を一部変更してみます。先ほどのWeb UIからguestbook-ui Deploymentリソースを選択すると、以下のようにYamlファイルが表示されます。

f:id:FY0323:20200922100716p:plain

ここでGitリポジトリと異なる状態を作るために、spec.replicas3に変更し、SAVEを選択します。すると、Web UI上では新たに2つのPodが作成される様子が見られます。またアプリケーションはOutOfSyncの状態となることも確認できます。

f:id:FY0323:20200922100754p:plain

コマンドラインから確認しても、Podが追加されたことが確認できます。

$ kubectl get all
NAME                                READY   STATUS    RESTARTS   AGE
pod/guestbook-ui-65b878495d-cs87p   1/1     Running   0          16m
pod/guestbook-ui-65b878495d-jp559   1/1     Running   0          3m30s★
pod/guestbook-ui-65b878495d-rcqlw   1/1     Running   0          3m30s★

NAME                   TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
service/guestbook-ui   ClusterIP   10.100.247.168   <none>        80/TCP    16m
service/kubernetes     ClusterIP   10.100.0.1       <none>        443/TCP   3h52m

NAME                           READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/guestbook-ui   3/3     3            3           16m

NAME                                      DESIRED   CURRENT   READY   AGE
replicaset.apps/guestbook-ui-65b878495d   3         3         3       16m

このときWeb UIでAPP DIFFを選択すると、Gitリポジトリで定義された状態と実際のアプリケーションの状態の間にある差分が表示されます。

f:id:FY0323:20200922100949p:plain

Gitリポジトリの状態に戻す(=spec.replicas1に戻す)ため、Web UI上のHISTORY AND ROLLBACKを選択します。すると、変更前のアプリケーションの状態を表すGitリポジトリのRevisionが表示されるので、Redeployを選択します。

f:id:FY0323:20200922101031p:plain

再デプロイの実行を確認し、OKを選択します。

f:id:FY0323:20200922101117p:plain

Gitリポジトリの状態にロールバックされ、先ほど作成した2つのPodが削除される様子が確認できます。

f:id:FY0323:20200922101203p:plain

削除

最後にアプリケーション、及びArgoCDの削除を行います。Web UIからDELETEを選択すると、削除の確認を求められるのでOKを選択します。

f:id:FY0323:20200922101251p:plain

Kubernetesリソースの削除が開始され、しばらくするとすべてのリソースが削除されたことが確認できます。

f:id:FY0323:20200922101401p:plain

f:id:FY0323:20200922101411p:plain

コマンドラインからも、アプリケーションの削除が確認できます。

$ kubectl get all
NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.100.0.1   <none>        443/TCP   4h13m

$ kubectl get app -n argocd
No resources found in argocd namespace.

最後にArgoCDを削除します。

$ kubectl delete -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
warning: deleting cluster-scoped resources, not scoped to the provided namespace
customresourcedefinition.apiextensions.k8s.io "applications.argoproj.io" deleted
customresourcedefinition.apiextensions.k8s.io "appprojects.argoproj.io" deleted
serviceaccount "argocd-application-controller" deleted
serviceaccount "argocd-dex-server" deleted
serviceaccount "argocd-server" deleted
role.rbac.authorization.k8s.io "argocd-application-controller" deleted
role.rbac.authorization.k8s.io "argocd-dex-server" deleted
role.rbac.authorization.k8s.io "argocd-server" deleted
clusterrole.rbac.authorization.k8s.io "argocd-application-controller" deleted
clusterrole.rbac.authorization.k8s.io "argocd-server" deleted
rolebinding.rbac.authorization.k8s.io "argocd-application-controller" deleted
rolebinding.rbac.authorization.k8s.io "argocd-dex-server" deleted
rolebinding.rbac.authorization.k8s.io "argocd-server" deleted
clusterrolebinding.rbac.authorization.k8s.io "argocd-application-controller" deleted
clusterrolebinding.rbac.authorization.k8s.io "argocd-server" deleted
configmap "argocd-cm" deleted
configmap "argocd-gpg-keys-cm" deleted
configmap "argocd-rbac-cm" deleted
configmap "argocd-ssh-known-hosts-cm" deleted
configmap "argocd-tls-certs-cm" deleted
secret "argocd-secret" deleted
service "argocd-dex-server" deleted
service "argocd-metrics" deleted
service "argocd-redis" deleted
service "argocd-repo-server" deleted
service "argocd-server-metrics" deleted
service "argocd-server" deleted
deployment.apps "argocd-application-controller" deleted
deployment.apps "argocd-dex-server" deleted
deployment.apps "argocd-redis" deleted
deployment.apps "argocd-repo-server" deleted
deployment.apps "argocd-server" deleted

$ kubectl delete namespace argocd
namespace "argocd" deleted

本番環境での利用に向けて

ArgoCDは本番環境での稼働実績もあり、また利用するうえでのBest Practiceなども公開されています。最後にこのBest Practiceを眺め、ArgoCDを利用するうえでのポイントについて紹介します。

アプリケーション用リポジトリマニフェストリポジトリを用意する

アプリケーションのソースコードマニフェストファイルを1つのリポジトリに格納すると、以下のような問題が発生します。

  • アプリケーションに対する全てのCommitがデプロイにつながる。例えば、マニフェストファイルの一部を変更するだけの作業でパイプラインを起動したくない、という場合に対応できなくなる。
  • 誰でもアプリケーションをリリースできるようになるが、リリースできるメンバーを制限することが難しくなる。
  • Git Logにはアプリケーションの変更とデプロイの履歴が混在することになる。Audit Logをキレイにすることにつながる。
  • 複数のリポジトリからなるマイクロサービスの場合、それぞれのサービスのバージョンスキーマやリリースサイクルが異なるため、そのうちの一つのリポジトリマニフェストファイルを一緒に格納すると、意味をなさなくなってしまう場合がある。
  • CIパイプラインを自動化している場合、マニフェストファイルをpushすることで、ビルドジョブとGit Commit Triggerの無限ループとなる場合がある。

上記問題を解消するため、アプリケーションのソースコードマニフェストファイルはそれぞれ別のリポジトリに格納することが推奨されています。

デプロイ用リポジトリの数を適切にする

マニフェストファイルを格納するようなリポジトリをいくつ用意するかは、状況によってその答えが変わってきます。そのため、以下のようなポイントを考慮して決める必要があります。

  • 単一のリポジトリ:小規模の会社で自動化をほぼ行わず、全てのメンバーを信頼している
  • チームごとにリポジトリを用意する:中規模の会社である程度の自動化を行う
  • サービスごとにリポジトリを用意する:大規模な会社で自動化を推進しており、メンバー毎のアクセスなどをコントロールする必要がある

チーム単位で自分たちのリポジトリを所有することで、誰がリリースするかを決定・制御することができます。これにより、リリースのボトルネックとなる単一の中央集権的チームを所有したり、全チームに書き込み権限を与える場合と比べて、自分たち自身でリポジトリの管理を行うことができます。

Commitする前にテストを行う

多くのエンジニアがマニフェストファイルの変更をCommit/Pushし、GitOpsのエージェントはアプリケーションがデプロイできるかによってその変更をチェックします。一方、Commit前にテストを行うことで防げたかもしれないような問題が、運用前の環境に入り込み、なかなか表出しなくなる、という自体も発生する場合があります。

GitOpsエージェントは一般的にCLIコマンドを実行することでマニフェストの生成を行うので、同じコマンドを使えば、ローカル環境でもテストを行うことができます。

Gitマニフェストは外部の変更によって変更すべきではない

KustomizeやHelmといったツールは、同じCommitでも異なるテンプレートマニフェストとすることを可能にします。もしもGit Commit無しでマニフェストファイルが変更されると、以下のような問題が発生します。

そのため、KustomizeやhElmを利用する場合は、特定のCommitにピンを打つことが推奨されます。

# Kustomizeの場合
bases:
- github.com/argoproj/argo-cd//manifests/cluster-install?ref=v0.11.1

# Helmの場合
dependencies:
- name: argo-cd
 version: 0.6.1
 repository: github.com/argoproj/argo-cd/manifests/cluster-install

またこれと関連し、Gitマニフェスト中にすべてを定義しないことも推奨されています。例えば、Deploymentのspec.replicasの数をHPAによって制御する場合、Gitマニフェストにレプリカ数を記述してしまうと、Commitのたびにレプリカ数が戻ってしまいます。そのため、外部要因によって動的に変更するパラメータについては、Gitマニフェスト内では管理しないようにすることが推奨されます。

Secretの管理方法を検討する

アプリケーションコードやGitマニフェストに秘匿情報を直接書き込むことはセキュリティ上の問題につながるため、多くはKubernetesのSecretリソースなどを利用します。その他の選択肢としては以下のようなものがあるので、GitOpsを開始する前にSecret情報の扱いについて検討する必要があります。

  • Sealed Secret
  • External Secret
  • Vault
  • Helm Secret
  • Kustomize secret generator plugins

※参考ドキュメント: