この記事から得られる知識
この記事を読むと、以下を "完全に理解" できます✌️
- ArgoCDのアーキテクチャを構成するコンポーネントの種類について
- ArgoCDがマニフェストを自動デプロイする仕組みについて
- この記事から得られる知識
- 01. はじめに
- 02. 概要
- 03. repo-server
- 04. application-controller、redis-server
- 05. dex-server
- 06. argocd-server (argocd-apiserver)
- argocd-serverとは
- 仕組み
- (1) application-controllerによるヘルスチェック
- (2) application-controllerによるマニフェスト差分検出
- (3) application-controllerによる処理結果保管
- (4) application-controllerによる処理結果取得
- (5) プロダクト用Cluster管理者のログイン
- (6) Ingressコントローラーによるルーティング
- (7) IDプロバイダーへの認証フェーズ委譲
- (8) IDプロバイダーによる認証フェーズ実施
- (9) argocd-serverによる認可フェーズ実施
- (10) application-controllerによるマニフェストデプロイ
- 07. アーキテクチャのまとめ
- 08. おわりに
- 謝辞
- 記事関連のおすすめ書籍
01. はじめに
ロケットに乗るタコのツラが腹立つわー。
さて最近の業務で、全プロダクトの技術基盤開発チームに携わっており、全プロダクト共有のArgoCD🐙とAWS EKSをリプレイスしました。
今回は、採用した設計プラクティスの紹介も兼ねて、ArgoCDのマイクロサービスアーキテクチャと自動デプロイの仕組みを記事で解説しました。
ArgoCDは、kubectl
コマンドによるマニフェストのデプロイを自動化するツールです。
ArgoCDのアーキテクチャには変遷があり、解説するのは執筆時点 (2023/05/02) で最新の 2.6
系のArgoCDです。
アーキテクチャや仕組みはもちろん、個々のマニフェストの実装にもちょっとだけ言及します。
それでは、もりもり布教していきます😗
飛ばしていただいても大丈夫ですが、読んでもらえるとより理解が深まるはずです👍
02. 概要
アーキテクチャ
レイヤー
まずは、ArgoCDのアーキテクチャのレイヤーがどのようになっているかを見ていきましょう。
ArgoCD公式から、コンポーネント図が公開されています。
図から、次のようなことがわかります👇
- 下位レイヤー向きにしか依存方向がなく、例えばコアドメインとインフラのレイヤー間で依存性は逆転させていない。
- レイヤーの種類 (UI、アプリケーション、コアドメイン、インフラ) とそれらの依存方向から、レイヤードアーキテクチャのようなレイヤーに分けている。
- 特にコアドメインレイヤーが独立したコンポーネントに分割されており、マイクロサービスアーキテクチャを採用している。
ArgoCDのマイクロサービスアーキテクチャは、機能単位の分割方法を採用していると推測しています。
本記事では詳しく言及しませんが、マイクロサービスアーキテクチャの分割方法には大小いくつかの種類があり、境界付けられたコンテキストで分割することがベタープラクティスと言われています😎
(境界付けられたコンテキストについても、ちゃんと記事を投稿したい...)
機能単位による分割は、境界付けられたコンテキストのそれよりも粒度が小さくなります。
ArgoCDでは、マイクロサービスアーキテクチャの設計図にコンポーネント図を使用しています。
コンポーネント図では、依存方向 (そのコンポーネントがいずれのコンポーネントを使用するのか) に着目できます。
そのため、これはマイクロサービス間の依存方向を視覚化するために有効なUML図です🙆🏻
コンポーネント
次に、コンポーネントの種類を紹介します。
ArgoCDの各コンポーネントが組み合わさり、マニフェストの自動的なデプロイを実現します。
ArgoCD (2.6
系) のコンポーネントはいくつかあり、主要なコンポーネントの種類とレイヤーは以下の通りです👇
コンポーネント | レイヤー | 機能 |
---|---|---|
argocd-server (argocd-apiserver) |
UI・アプリケーション | みんながよく知るArgoCDのダッシュボードです。 また、ArgoCDのAPIとしても機能します。 現在、複数のレイヤーの責務を持っており、将来的にUIとアプリケーションは異なるコンポーネントに分割されるかもしれません。 |
application-controller | コアドメイン | Clusterにマニフェストをデプロイします。 また、ArgoCD系カスタムリソースのカスタムコントローラーとしても機能します。 |
repo-server | コアドメイン | マニフェスト/チャートリポジトリからクローンを取得します。 また、クローンからマニフェストを作成します。 |
redis-server | インフラ | application-controllerの処理結果のキャッシュを保管します。 |
dex-server | インフラ | SSOを採用する場合、argocd-serverの代わりに認可リクエストを作成し、またIDプロバイダーに送信します。 これにより、argocd-server上の認証フェーズをIDプロバイダーに委譲できます。 |
以降の図の凡例です。
ArgoCDの各コンポーネント (application-controller、argocd-server、dex-server、repo-server) と各リソース (Application、AppProject) を区別しています。
仕組み
それでは、ArgoCDは、どのようにコンポーネントを組み合わせて、マニフェストをデプロイするのでしょうか。
ここではプロダクト用Cluster管理者 (デプロイ先となるClusterを管理するエンジニア) は、ArgoCDのダッシュボードを介してマニフェストをデプロイするとしましょう。
まずは、概要を説明していきます。
(1) repo-serverによるクローン取得
ArgoCDのCluster上で、repo-serverがマニフェスト/チャートリポジトリのクローンを取得します。
(2) application-controllerによるマニフェスト取得
application-controllerは、repo-serverからマニフェストを取得します。
(3) application-controllerによるCluster確認
application-controllerは、プロダクト用Clusterの現状を確認します。
(4) application-controllerによる処理結果保管
application-controllerは、処理結果をredis-serverに保管します。
(5) argocd-serverによるキャッシュ取得
argocd-serverは、redis-serverからキャッシュを取得します。
(6) 管理者のログイン
プロダクト用Cluster管理者は、argocd-serverにログインしようとします。
(7) IDプロバイダーへの認証フェーズ委譲
argocd-serverは、ログイン時にIDプロバイダーに認証フェーズを委譲するために、dex-serverをコールします。
プロダクト用Cluster管理者のログインには、利便性と安全性を兼ね備えたSSOの採用がオススメです。
今回の記事では、SSOを採用した場合の仕組みを紹介しています🙇🏻
(8) dex-serverによる認証リクエスト送信
dex-serverは、IDプロバイダーに認可リクエストを作成し、これをIDプロバイダーに送信します。
(9) argocd-serverによる認可フェーズ実行
argocd-serverで認可フェーズを実施します。
ログインが完了し、プロダクト用Cluster管理者は認可スコープに応じてダッシュボードを操作できます。
プロダクト用Clusterの障害の影響範囲を受けないように、ArgoCDは、プロダクト用Clusterとは独立したClusterで作成した方がよいです。
今回の記事では、ArgoCD用Clusterを採用した場合の仕組みを紹介しています🙇🏻
(10) application-controllerによるマニフェストデプロイ
application-controllerは、Clusterにマニフェストをデプロイします。
マニフェストのデプロイの仕組みをざっくり紹介しました。
ただこれだと全く面白くないため、各コンポーネントの具体的な処理と、各々がどのように通信しているのかを説明します✌️
03. repo-server
repo-serverとは
まずは、コアドメインレイヤーにあるrepo-serverです。
マニフェスト/チャートリポジトリ (例:GiHub、GitHub Pages、Artifact Hub、AWS ECR、Artifact Registryなど) からクローンを取得します。
repo-serverを持つPodには、他に軽量コンテナイメージからなるInitContainerとサイドカー (cmp-server) がおり、それぞれ機能が切り分けられています👍
仕組み
(1) InitContainerによるお好きなツールインストール & argocd-cliバイナリコピー
repo-serverの起動時に、InitContainerでお好きなマニフェスト管理ツール (Helm、Kustomizeなど) やプラグイン (helm-secrets、KSOPS、SOPS、argocd-vault-pluginなど) をインストールします。
また、サイドカーのcmp-serverでは起動時に/var/run/argocd/argocd-cmp-server
コマンドを実行する必要があり、InitContainer (ここではcopyutil
コンテナ) を使用して、ArgoCDのコンテナイメージからargocd-cli
のバイナリファイルをコピーします。
repo-serverのざっくりした実装例は以下の通りです👇
ここでは、ArgoCDで使いたいツール (Helm、SOPS、helm-secrets) をInitContainerでインストールしています。
apiVersion: v1 kind: Pod metadata: name: argocd-repo-server namespace: argocd spec: containers: - name: repo-server image: quay.io/argoproj/argocd:latest initContainers: # HelmをインストールするInitContainer - name: helm-installer image: alpine:latest command: - /bin/sh - -c args: - | # インストール処理 volumeMounts: - mountPath: /custom-tools name: custom-tools # SOPSをインストールするInitContainer - name: sops-installer image: alpine:latest command: - /bin/sh - -c args: - | # インストール処理 volumeMounts: - mountPath: /custom-tools name: custom-tools # helm-secretsをインストールするInitContainer - name: helm-secrets-installer image: alpine:latest command: - /bin/sh - -c args: - | # インストール処理 volumeMounts: - mountPath: /helm-working-dir/plugins name: helm-working-dir ... # cmp-serverにargocd-cliのバイナリをコピーするInitContainer - name: copyutil image: quay.io/argoproj/argocd:latest command: - cp - -n - /usr/local/bin/argocd - /var/run/argocd/argocd-cmp-server volumeMounts: - name: var-files mountPath: /var/run/argocd # Podの共有ボリューム volumes: - name: custom-tools emptyDir: {} - name: var-files emptyDir: {}
ArgoCDのコンテナイメージ (
quay.io/argoproj/argocd
) には、いくつかのツール (例:Helm、Kustomize、Ks、Jsonnetなど) の推奨バージョンがあらかじめインストールされています。
そのため、これらのツールのプラグイン (例:helm-secrets) を使用する場合、上記のコンテナイメージからなるrepo-server内のツールをcmp-serverにコピーすればよいのでは、と思った方がいるかもしれません。
この方法は全く問題なく、cmp-serverの
/usr/local/bin
ディレクトリ配下にツールをコピーするように、InitContainerを定義してもよいです。
apiVersion: v1 kind: Pod metadata: name: argocd-repo-server namespace: foo spec: containers: - name: repo-server image: quay.io/argoproj/argocd:latest volumeMounts: - mountPath: /usr/local/bin/helm # Podの共有ボリュームを介して、repo-serverでHelmを使用する。 name: custom-tools initContainers: - name: copy-helm image: quay.io/argoproj/argocd:latest # InitContainer上のHelmをVolumeにコピーする command: - /bin/cp - -n - /usr/local/bin/helm - /custom-tools/helm volumeMounts: - mountPath: /custom-tools name: custom-tools # 共有ボリューム volumes: - name: custom-tools emptyDir: {}
反対に、これらツールをInitContainerでインストールし直す場合は、ArgoCD上での推奨バージョンをちゃんとインストールするようにしましょう👍
2.6
系では、ArgoCDのリポジトリ内のtool-versions.sh
ファイルに、Helmのバージョンが定義されています。
spec: ... initContainers: - name: helm-installer image: alpine:latest command: - /bin/sh - -c # ArgoCDのリポジトリ上のtool-versions.shファイルから、Helmのバージョンを取得する args: - | apk --update add curl wget ARGOCD_VERSION=$(curl -s https://raw.githubusercontent.com/argoproj/argo-helm/argo-cd-<ArgoCDのバージョン>/charts/argo-cd/Chart.yaml | grep appVersion | sed -e 's/^[^: ]*: //') HELM_RECOMMENDED_VERSION=$(curl -s https://raw.githubusercontent.com/argoproj/argo-cd/"${ARGOCD_VERSION}"/hack/tool-versions.sh | grep helm3_version | sed -e 's/^[^=]*=//') wget -q https://get.helm.sh/helm-v"${HELM_RECOMMENDED_VERSION}"-linux-amd64.tar.gz tar -xvf helm-v"${HELM_RECOMMENDED_VERSION}"-linux-amd64.tar.gz cp ./linux-amd64/helm /custom-tools/ chmod +x /custom-tools/helm volumeMounts: - mountPath: /custom-tools name: custom-tools ...
(2) repo-serverによる認証情報取得
repo-serverは、Secret (argocd-repo-creds) からリポジトリの認証情報を取得します。
argocd-repo-credsではリポジトリの認証情報のテンプレートを管理しています。
指定した文字列から始まる (前方一致) URLを持つリポジトリに接続する場合、それらの接続で認証情報を一括して適用できます。
argocd-repo-credsのざっくりした実装例は以下の通りです👇
ここでは、リポジトリのSSH公開鍵認証を採用し、argocd-repo-credsに共通の秘密鍵を設定しています。
apiVersion: v1 kind: Secret metadata: name: argocd-repo-creds-github namespace: argocd labels: argocd.argoproj.io/secret-type: repo-creds type: Opaque data: type: git url: https://github.com/hiroki-hasegawa # 秘密鍵 sshPrivateKey: | MIIC2 ...
あとは、各リポジトリのSecret (argocd-repo) にURLを設定しておきます。
すると、先ほどのargocd-repo-credsのURLに前方一致するURLを持つSecretには、一括して秘密鍵が適用されます。
# foo-repositoryをポーリングするためのargocd-repo apiVersion: v1 kind: Secret metadata: namespace: argocd name: foo-argocd-repo labels: argocd.argoproj.io/secret-type: repository type: Opaque data: # 認証情報は設定しない。 # チャートリポジトリ名 name: bar-repository # https://github.com/hiroki-hasegawa に前方一致する。 url: https://github.com/hiroki-hasegawa/bar-chart.git --- # baz-repositoryをポーリングするためのargocd-repo apiVersion: v1 kind: Secret metadata: namespace: foo name: baz-argocd-repo labels: argocd.argoproj.io/secret-type: repository type: Opaque data: # 認証情報は設定しない。 # チャートリポジトリ名 name: baz-repository # https://github.com/hiroki-hasegawa に前方一致する。 url: https://github.com/hiroki-hasegawa/baz-chart.git
(3) repo-serverのよるクローン取得とポーリング
repo-serverは、認証情報を使用して、リポジトリにgit clone
コマンドを実行します。
取得したクローンを、/tmp/_argocd-repo
ディレクトリ配下にUUIDの名前で保管します。
また、リポジトリの変更をポーリングし、変更を検知した場合はgit fetch
コマンドを実行します。
# クローンが保管されていることを確認できる $ kubectl -it exec argocd-repo-server \ -c repo-server \ -n foo \ -- bash -c "ls /tmp/_argocd-repo/<URLに基づくUUID>" # リポジトリ内のファイル Chart.yaml README.md templates values.yaml
2.3
以前では、repo-serverは/tmp
ディレクトリ配下にURLに基づく名前でクローンを保管します。
$ kubectl -it exec argocd-repo-server \ -c repo-server \ -n foo \ -- bash -c "ls /tmp/https___github.com_hiroki-hasegawa_foo-repository" # リポジトリ内のファイル Chart.yaml README.md templates values.yaml
(4) repo-serverによるサイドカーコール
repo-serverは、自身にマウントされたいくつかのマニフェスト管理ツール (例:Helm、Kustomize) を実行する機能を持っています。
しかし、実行できないツールではサイドカー (cmp-server) をコールします。
この時、Applicationの.spec.source.plugin
キーでプラグイン名を指定すると、そのApplicationではサイドカーをコールします。
逆を言えば、プラグイン名を指定していないApplicationは、サイドカーをコールしない です。
apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: foo-application namespace: foo spec: source: plugin: name: helm-secrets # このプラグイン名は、ConfigManagementPluginのmetadata.nameキーに設定したもの ...
このコールは、Volume上のUnixドメインソケットを経由します。
Unixドメインソケットのエンドポイントの実体は.sock
ファイルです。
$ kubectl exec -it argocd-repo-server -c foo-plugin-cmp-server\ -- bash -c "ls /home/argocd/cmp-server/plugins/" foo-plugin.sock
Unixソケットドメインは、同じOS上のファイルシステムを介して、データを直接的に送受信する仕組みです。
Unixソケットドメインを使用すると、同じVolumeがマウントされたコンテナのプロセス間で、データを送受信できます👍
(5) repo-serverによる暗号化キーと暗号化変数の取得
cmp-serverは、暗号化キー (例:AWS KMS、Google CKMなど) を使用してSecretストア (例:AWS SecretManager、Google SecretManager、SOPS、Vaultなど) の暗号化変数を復号化します。
暗号化キーがクラウドプロバイダーにある場合、クラウドプロバイダーがHTTPSプロトコルの使用を求める場合があります。
cmp-serverに軽量なコンテナイメージを使用していると、
/etc/ssl
ディレクトリ (ディレクトリはOSによって異なる) に証明書が無く、cmp-serverがHTTPSプロトコルを使用できない可能性があります。
その場合は、お好きな方法で証明書をインストールし、コンテナにマウントするようにしてください👍
apiVersion: v1 kind: Pod metadata: name: argocd-repo-server namespace: foo spec: containers: - name: repo-server image: quay.io/argoproj/argocd:latest ... # サイドカーのcmp-server - name: helm-secrets-cmp-server image: ubuntu:latest ... volumeMounts: # サイドカーがAWS KMSを使用する時にHTTPSリクエストを送信する必要があるため、証明書をマウントする - name: certificate mountPath: /etc/ssl ... initContainers: - name: certificate-installer image: ubuntu:latest command: - /bin/sh - -c args: - | apt-get update -y # ルート証明書をインストールする apt-get install -y ca-certificates # 証明書を更新する update-ca-certificates volumeMounts: - mountPath: /etc/ssl name: certificate volumes: - name: certificate emptyDir: {}
(6) サイドカーによるプラグイン処理の取得
cmp-serverは、マニフェスト管理ツールのプラグイン (helm-secrets、argocd-vault-pluginなど) を実行します。
この時マニフェストの作成時のプラグインとして、ConfigMap配下のConfigManagementPluginでプラグインの処理を定義します。
ざっくりした実装例は以下の通りです👇
ここでは、プラグインとしてhelm-secretsを採用し、helm secrets template
コマンドの実行を定義します。
apiVersion: v1 kind: ConfigMap metadata: name: argocd-cmp-cm namespace: foo data: helm-secrets-plugin.yaml: | apiVersion: argoproj.io/v1alpha1 kind: ConfigManagementPlugin metadata: namespace: foo name: helm-secrets # このプラグイン名は、Applicationのspec.source.pluginキーで指定したもの spec: generate: command: - /bin/bash - -c args: - | set -o pipefail helm secrets template -f $ARGOCD_ENV_SECRETS -f $ARGOCD_ENV_VALUES -n $ARGOCD_APP_NAMESPACE $ARGOCD_APP_NAME . foo-plugin.yaml: | ...
プラグインごとにConfigManagementPluginのマニフェストを定義できるように、各ConfigManagementPluginを異なるファイル名とし、ConfigMapで管理するとよいです👍
(7) サイドカーによるプラグイン処理の実行
cmp-serverはプラグインを実行し、Secretを含むマニフェストを作成します。
ConfigMap配下のファイルをplugin.yaml
の名前でサイドカーにマウントする必要があります。
また、先ほどのUnixドメインソケットの.sock
ファイルや、 cmp-serverがプラグインを実行するための各バイナリファイルもマウントが必要です。
ざっくりした実装例は以下の通りです👇
ここでは、helm-secrets
プラグインを実行するサイドカー (helm-secrets-cmp-server) を作成します。
apiVersion: v1 kind: Pod metadata: name: argocd-repo-server spec: containers: # repo-server - name: repo-server image: quay.io/argoproj/argocd:latest ... # helm-secretsのcmp-server - name: helm-secrets-cmp-server # コンテナイメージは軽量にする image: ubuntu:latest command: - /var/run/argocd/argocd-cmp-server env: # helmプラグインの場所を設定する - name: HELM_PLUGINS value: /helm-working-dir/plugins securityContext: runAsNonRoot: true runAsUser: 999 volumeMounts: # リポジトリのクローンをコンテナにマウントする - name: tmp mountPath: /tmp # ConfigManagementPluginのマニフェスト (helm-secrets.yaml) を "plugin.yaml" の名前でコンテナにマウントする - name: argocd-cmp-cm mountPath: /home/argocd/cmp-server/config/plugin.yaml subPath: helm-secrets.yaml # コンテナ間で通信するためのUnixドメインソケットファイルをコンテナにマウントする - name: plugins mountPath: /home/argocd/cmp-server/plugins # 任意のツールのバイナリファイルをコンテナにマウントする - name: custom-tools mountPath: /usr/local/bin # helmプラグインのバイナリをコンテナにマウントする - name: helm-working-dir mountPath: /helm-working-dir/plugins ... # Podの共有ボリューム volumes: # リポジトリのクローンを含む - name: tmp emptyDir: {} # Helmなどの任意のツールを含む - name: custom-tools emptyDir: {} # helmプラグインを含む - name: helm-working-dir emptyDir: {}
ArgoCDの
v2.6
では、ConfigManagementPluginのマニフェストを/home/argocd/cmp-server/config
ディレクトリに、plugin.yaml
の名前でマウントしないといけません。
これは、cmp-serverの起動コマンド (
/var/run/argocd/argocd-cmp-server
) がplugin.yaml
の名前しか扱えないためです。
ArgoCD公式の見解で、サイドカーでは単一のプラグインしか実行できないように設計しているとのコメントがありました。
今後のアップグレードで改善される可能性がありますが、
v2.6
では、ConfigManagementPluginの数だけcmp-serverが必要になってしまいます🙇🏻
Kustomizeのプラグイン (例:KSOPS) によるマニフェスト作成は、サイドカーではなくrepo-serverで実行した方がよいかもしれません (Helmプラグインはサイドカーで問題ないです)。
執筆時点 (2023/05/02) では、ArgoCDとKustomizeが密に結合しています。
例えば、ArgoCD上のKustomize系オプションはrepo-serverでマニフェストを作成することを想定して設計されています。
無理やりサイドカーでKustomizeのプラグインを実行しようとすると、ArgoCDの既存のオプションを無視した実装になってしまうため、Kustomizeのプラグインだけはrepo-serverで実行することをお勧めします😢
今回は詳しく言及しませんが、クラウドプロバイダーのSecretストア (例:AWS SecretManager、Google SecretManagerなど) の変数を使用する場合は、Secretのデータ注入ツールのプラグイン (特にargocd-vault-plugin) を採用しなくてもよいです。
この場合、代わりにSecretsストアCSIドライバーやExternalSecretsOperatorを使用できます。
これらは、クラウドプロバイダーから変数を取得し、これをSecretにデータとして注入してくれます🙇🏻
04. application-controller、redis-server
application-controllerとは
コアドメインレイヤーにあるapplication-controllerです。
Clusterにマニフェストをデプロイします。
また、ArgoCD系カスタムリソースのカスタムコントローラーとしても機能します。
redis-serverとは
インフラレイヤーにあるredis-serverです。
application-controllerの処理結果のキャッシュを保管します。
仕組み
(1) ArgoCD用Cluster管理者のkubectl apply
コマンド
ArgoCD用Clusterの管理者は、ClusterにArgoCD系のカスタムリソース (例:Application、AppProjectなど) をデプロイします。
『卵が先か、ニワトリが先か』みたいな話ですが、ArgoCD自体はArgoCD以外でデプロイする必要があります。
この時、argo-helmを使用すると簡単にArgoCDのマニフェストを作成できます。
️
ただしHelmの重要な仕様として、チャートの更新時に使用する
helm upgrade
コマンドは、CRDを作成できる一方でこれを変更できません。
HelmでCRDを作成するとHelmの管理ラベルが挿入されてしまうため、作成の時点からCRDがHelmの管理外となるように、
kubectl
コマンドでCRDを作成した方がよいです👍
$ kubectl diff -k "https://github.com/argoproj/argo-cd/manifests/crds?ref=<バージョンタグ>" $ kubectl apply -k "https://github.com/argoproj/argo-cd/manifests/crds?ref=<バージョンタグ>"ArgoCD上でHelmを使用してデプロイする場合はこの仕様を気にしなくてよいのかな、と思った方がいるかもしれないです。
ですが本記事で解説した通り、ArgoCDはcmp-serverの
helm template
コマンド (この時、--include-crds
オプションが有効になっている) や、application-controllerのkubectl apply
コマンドを組み合わせてマニフェストをデプロイしているため、CRDもちゃんと更新してくれます👍🏻
️
(2) application-controllerによるArgoCD系カスタムリソースのReconciliation
kube-controller-managerは、application-controllerを操作し、Reconciliationを実施します。
application-controllerは、Etcd上に永続化されたマニフェストと同じ状態のArgoCD系カスタムリソースを作成/変更します。
先ほど記載したと通り、application-controllerはカスタムコントローラーとしても機能します。
本記事では詳しく言及しませんが、カスタムコントローラーの仕組みやCRDとの関係については、以下の記事が非常に参考になりました🙇🏻
️
(3) application-controllerによるマニフェスト取得
application-controllerは、repo-serverからリポジトリのマニフェストを取得します。
取得したマニフェストは、repo-serverのサイドカーであるcmp-serverが作成したものです。
(4) application-controllerによるヘルスチェック
application-controllerは、プロダクト用Clusterをヘルスチェックします。
application-controllerには、gitops-engineパッケージが内蔵されており、これはヘルスチェックからデプロイまでの基本的な処理を実行します。
gitops-engineは、ArgoCDのデプロイに必要な処理を揃えたパッケージです。
執筆時点 (2023/05/02) の最新バージョン
v0.7.0
では以下のディレクトリからなります👇
🐱 gitops-engine/ ├── 📂 pkg │ ├── cache │ ├── diff # リポジトリとClusterの間のマニフェストの差分を検出する。ArgoCDのDiff機能に相当する。 │ ├── engine # 他のパッケージを使い、GitOpsの一連の処理を実行する。 │ ├── health # Clusterのステータスをチェックする。ArgoCDのヘルスチェック機能に相当する。 │ ├── sync # Clusterにマニフェストをデプロイする。ArgoCDのSync機能に相当する。 │ └── utils # 他のパッケージに汎用的な関数を提供する。 │ ...
(5) application-controllerによるマニフェスト差分検出
application-controllerは、プロダクト用Clusterのマニフェストと、repo-serverから取得したマニフェストの差分を検出します。
ここで、kubectl diff
コマンドの実行が自動化されています。
(6) application-controllerによる処理結果保管
application-controllerは、処理結果をredis-serverに保管します。
redis-serverは、Applicationやリポジトリのコミットの単位で、application-controllerの処理結果を保管しています。
$ kubectl exec -it argocd-redis-server \ -n foo \ -- sh -c "redis-cli --raw" 127.0.0.1:6379> keys * ... app|resources-tree|<Application名>|<キャッシュバージョン> cluster|info|<プロダクト用ClusterのURL>|<キャッシュバージョン> git-refs|<マニフェスト/チャートリポジトリのURL>|<キャッシュバージョン> mfst|app.kubernetes.io/instance|<Application名>|<最新のコミットハッシュ値>|<デプロイ先Namespace>|*****|<キャッシュバージョン> ...
(7) application-controllerによるマニフェストデプロイ
application-controllerは、Applicationの操作に応じて、Clusterにマニフェストをデプロイします。
ここで、kubectl apply
コマンドの実行が自動化されています。
Kubernetesリソースのマニフェストには、
metadata.managedFields
キーがあり、何がそのマニフェストを作成/変更したのかを確認できます。
実際にマニフェストを確認してみると、確かにapplication-controllerがマニフェストを作成/変更してくれたことを確認できます。
apiVersion: apps/v1 kind: Deployment metadata: managedFields: # ArgoCDのapplication-controllerによる管理 - manager: argocd-application-controller apiVersion: apps/v1 # kube-apiserverに対するリクエスト内容 operation: Update time: "2022-01-01T16:00:00.000Z" # ArgoCDのapplication-controllerが管理するマニフェストのキー部分 fields: ...
️
05. dex-server
dex-serverとは
インフラレイヤーにあるdex-serverです。
SSO (例:OAuth 2.0
、SAML、OIDC) を採用する場合、argocd-serverの代わりに認可リクエストを作成し、またIDプロバイダー (例:GitHub、Keycloak、AWS Cognito、Google Authなど) に送信します。
これにより、argocd-server上の認証フェーズをIDプロバイダーに委譲できます。
dex-serverを使わずに、argocd-serverからIDプロバイダーに認可リクエストを直接的に送信することもできます。
執筆時点 (2023/05/02) で、argocd-serverは特にOIDCの認可リクエストを作成できるため、ログイン要件がOIDCの場合は、dex-serverを必ずしも採用してなくもよいです。
言い換えれば、その他のSSO (例:OAuth
2.0
、SAML) を使用する場合は、dex-serverを採用する必要があります👍
️
仕組み
(1) プロダクト用Cluster管理者のログイン
プロダクト用Cluster管理者がダッシュボード (argocd-server) にSSOを使用してログインしようとします。
(2) IDプロバイダーへの認証フェーズ委譲
argocd-serverは、認証フェーズをIDプロバイダーに委譲するために、dex-serverをコールします。
argocd-serverの認証認可処理は、AuthN (認証) と AuthZ (認可) から構成されています。
今回は認証フェーズを委譲した場合で仕組みを解説していますが、反対に委譲しなかった場合、このAuthNでArgoCD上で定義したユーザーやグループを認証することになります👍
️
(3) dex-serverによる認可リクエスト作成
dex-serverは、認可リクエストを作成します。
認可リクエストに必要な情報は、ConfigMap (argocd-cm) で設定しておく必要があります。
argocd-cmのざっくりした実装例は以下の通りです👇
ここでは、IDプロバイダーをGitHubとし、認可リクエストに必要なクライアントIDとクライアントシークレットを設定しています。
apiVersion: v1 kind: ConfigMap metadata: namespace: foo name: argocd-cm data: dex.config: | connectors: - type: github id: github name: GitHub SSO config: clientID: ***** clientSecret: ***** # dex-serverが認可レスポンスによるリダイレクトを受信するURLを設定する redirectURI: https://example.com/api/dex/callback
(4) dex-serverによる認可リクエスト送信
dex-serverは、前の手順で作成した認可リクエストをIDプロバイダーに送信します。
(5) IDプロバイダーによる認証フェーズ実施
IDプロバイダー側でSSOの認証フェーズを実施します。
IDプロバイダーは、コールバックURL (<ArgoCDのドメイン名>/api/dex/callback
) を指定して、認可レスポンスを送信します。
認可レスポンスはリダイレクトを発生させ、argocd-serverを介して、再びdex-serverに届きます。
この後、dex-serverはIDプロバイダーのトークンエンドポイントにリクエストを送信し、またIDプロバイダーからトークン (アクセストークン、IDトークンなど) やユーザー情報を取得します。
ただ、SSOの種類によって仕組みが異なるため、詳細は省略します。
IDプロバイダー側のコールバックURLの設定で、dex-serverのエンドポイントを指定する必要があります。
例えばGitHubをIDプロバイダーとする場合、 Developer settingsタブ でSSOを設定する必要があり、この時に
Authorization callback URL
という設定箇所があるはずです👍🏻
(6) argocd-serverによる認可フェーズ実施
argocd-serverは、AuthZで認可フェーズを実施します。
ConfigMap (argocd-rbac-cm) を参照し、IDプロバイダーから取得したユーザーやグループに、ArgoCD系カスタムリソースに関する認可スコープを付与します。
ざっくりした実装例は以下の通りです👇
ここでは、developerロールにはdev
というAppProjectに属するArgoCD系カスタムリソースにのみ、またmaintainerロールには全てのAppProjectの操作を許可しています。
またこれらのロールを、IDプロバイダーで認証されたグループに紐づけています。
特定のArgoCD系カスタムリソースのみへのアクセスを許可すれば、結果として特定のClusterへのデプロイのみを許可したことになります👍
apiVersion: v1 kind: ConfigMap metadata: name: argocd-rbac-cm namespace: foo data: # デフォルトのロール policy.default: role:developer policy.csv: | p, role:developer, *, *, dev/*/*, allow p, role:maintainer, *, *, dev/*/*, allow p, role:maintainer, *, *, prd/*/*, allow g, developers, role:developer g, maintainers, role:maintainer scopes: "[groups]"
ConfigMap (argocd-rbac-cm) の認可スコープの定義には、 Casbin の記法を使用します。
今回の実装例で使用した
p
(パーミッション) とg
(グループ) では、以下を記法を使用できます👍
apiVersion: v1 kind: ConfigMap metadata: name: argocd-rbac-cm namespace: argocd data: policy.default: role:readonly policy.csv: | # ロールとArgoCD系カスタムリソースの認可スコープを定義する p, role:<ロール名>, <Kubernetesリソースの種類>, <アクション名>, <AppProject名>/<ApplicationのNamespace名>/<Application名>, <許否> # 認証済みグループにロールを紐付ける g, <グループ名>, role:<ロール名> scopes: "[groups]"
06. argocd-server (argocd-apiserver)
argocd-serverとは
最後に、インフラレイヤーにあるargocd-serverです。
『argocd-apiserver』とも呼ばれます。
みんながよく知るArgoCDのダッシュボードです。
また、ArgoCDのAPIとしても機能し、他のコンポーネントと通信します🦄
仕組み
(1) application-controllerによるヘルスチェック
application-controllerは、プロダクト用Clusterをヘルスチェックします。
(2) application-controllerによるマニフェスト差分検出
application-controllerは、プロダクト用Clusterのマニフェストと、ポーリング対象のリポジトリのマニフェストの差分を検出します。
(3) application-controllerによる処理結果保管
application-controllerは、処理結果をredis-serverに保管します。
(4) application-controllerによる処理結果取得
argocd-serverは、redis-serverから処理結果を取得します。
(5) プロダクト用Cluster管理者のログイン
プロダクト用Cluster管理者がダッシュボード (argocd-server) にSSOを使用してログインしようとします。
(6) Ingressコントローラーによるルーティング
Ingressコントローラーは、Ingressのルーティングルールを参照し、argocd-serverにルーティングします。
(7) IDプロバイダーへの認証フェーズ委譲
argocd-serverは、ログイン時にIDプロバイダーに認証フェーズを委譲するために、dex-serverをコールします。
(8) IDプロバイダーによる認証フェーズ実施
IDプロバイダー上で認証フェーズが完了します。
argocd-serverは、ConfigMap (argocd-rbac-cm) を参照し、プロダクト用Cluster管理者に認可スコープを付与します。
(9) argocd-serverによる認可フェーズ実施
argocd-serverは、認可スコープに応じて、プロダクト用Cluster管理者がApplicationを操作可能にします。
今回の図のように、単一のArgoCD用Clusterで複数プロダクトのApplicationを管理する場合、Namespaceを単位としたテナント分割を設定した方がよいです。
その場合、ArgoCD本体をNamespacedスコープモードに設定する必要があります。
Namespacedスコープモードの場合、以下の設定が不要です。
apiVersion: v1 kind: ConfigMap metadata: name: argocd-cmd-params-cm namespace: foo data: # 設定してはダメ # application.namespaces: "*" # 全てのNamespaceを許可する。
apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: name: dev-foo-project namespace: foo spec: # 設定してはダメ # sourceNamespaces: # - "foo"これらにより、
foo
のNamespaceに属するArgoCDは、他のNamespaceにはアクセスできなくなります👍
(10) application-controllerによるマニフェストデプロイ
プロダクト用Cluster管理者は、ダッシュボード (argocd-server) を使用して、ClusterにマニフェストをSyncします。
この時、Applicationを介してapplication-controllerを操作し、マニフェストをデプロイします。
図では、App Of Appsパターンを採用したと仮定しています👨👩👧👦
ArgoCDにはApp Of Appsパターンというデザインパターンがあります。
これは、Applicationを階層的に作成するパターンであり、最下層のApplication配下のマニフェストをより疎結合に管理できます✌️
例えば以下の画像の通り、最上位のApplication配下に、チーム別の親Applicationを配置します (アプリチームの親Application、インフラチームのそれ) 。
その後、両方のApplication配下にさらにチャート別に最下層の子Applicationを配置し、チャートのデプロイを管理します。
アプリチーム最下層の子Applicationではアプリコンテナのチャート、インフラチームの子Applicationでは監視/ネットワーク/ハードウェアリソース管理系のチャートを管理します👍
07. アーキテクチャのまとめ
今までの全ての情報をざっくり整理して簡略化すると、ArgoCDは以下の仕組みでマニフェストをデプロイすることになります👇
08. おわりに
ArgoCDによるデプロイの仕組みの仕組みをもりもり布教しました。
ArgoCDは、UIが使いやすく、仕組みの詳細を知らずとも比較的簡単に運用できるため、ユーザーフレンドリーなツールだと思っています。
もしArgoCDを使わずにマニフェストをデプロイしている方は、ArgoCDの採用をハイパー・ウルトラ・アルティメットおすすめします👍
謝辞
ArgoCDの設計にあたり、以下の方に有益なプラクティスをご教授いただきました。
この場で感謝申し上げます🙇🏻