Argo CD での GKE のマルチクラスタ管理
クラウドエース北野です。
以下の様な Google Kubernetes Engine のフリート機能を使って複数の Google Kubernetes Engine クラスタを Argo CD で管理する方法を紹介します。
概要
以下の方法で Argo CD で複数の Google Kubernetes Engine (以降 GKE と呼びます)クラスタを管理します。
- フリートによる GKE クラスタの管理
- Argo CD プラグインジェネレータ
fleet-argocd-plugin
によるフリート情報の連携
fleet-argocd-plugin
のインストールは、以下のマニフェストで作成します。
apiVersion: v1
kind: ServiceAccount
metadata:
name: argocd-fleet-sync
namespace: argocd
annotations:
iam.gke.io/gcp-service-account: <GOOGLE_CLOUD_SERVICEACCOUNT>
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: argocd
name: argocd-fleet-sync-secrets-role
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
namespace: argocd
name: argocd-fleet-sync-secrets-rolebinding
subjects:
- kind: ServiceAccount
name: argocd-fleet-sync
namespace: argocd
roleRef:
kind: Role
name: argocd-fleet-sync-secrets-role
apiGroup: rbac.authorization.k8s.io
---
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-fleet-sync
namespace: argocd
data:
token: '$argocd-fleet-sync:token'
baseUrl: "http://argocd-fleet-sync.argocd.svc.cluster.local:8888"
FLEET_PROJECT_NUMBER: "<FLEET_HOST_PROJECT>"
PORT: ":4356"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: argocd-fleet-sync
namespace: argocd
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: argocd-fleet-sync
template:
metadata:
labels:
app.kubernetes.io/name: argocd-fleet-sync
spec:
serviceAccountName: argocd-fleet-sync
nodeSelector:
iam.gke.io/gke-metadata-server-enabled: "true"
containers:
- name: argocd-fleet-sync
image: asia-northeast1-docker.pkg.dev/<PROJECT_ID>/argocd-fleet-sync/plugin:v1.0
imagePullPolicy: Always
envFrom:
- configMapRef:
name: argocd-fleet-sync
ports:
- containerPort: 4356
name: http
resources:
requests:
memory: "1Gi"
cpu: "500m"
ephemeral-storage: "1Gi"
limits:
memory: "1Gi"
cpu: "500m"
ephemeral-storage: "1Gi"
volumes:
- name: token
secret:
secretName: argocd-fleet-sync
---
apiVersion: v1
kind: Service
metadata:
name: argocd-fleet-sync
namespace: argocd
spec:
selector:
app.kubernetes.io/name: argocd-fleet-sync
ports:
- name: http
port: 8888
targetPort: 4356
---
apiVersion: v1
kind: Secret
metadata:
name: argocd-fleet-sync
namespace: argocd
labels:
app.kubernetes.io/part-of: argocd
stringData:
token: 'supersecret'
はじめに
Google Cloud のプラクティスに Kubernetes を単一クラスタで構築するとあります。しかし、AWS などの他のクラウドで Kubernetes クラスタを運用していたり、開発、検証、本番環境で別々のクラスタを構築しているなどの様々な理由により、1つのクラスタのみでシステムを構築できないことがあるかと思います。
このマルチクラスタのシステムの運用管理に Argo CD で管理するとき、Argo CD にクラスタを追加し、他のクラスタを管理対象に含める必要があります。Argo CD は他のクラスタとの接続に CLI による接続とマニフェストを使った宣言的方法による接続の2つ方法を提供しています。
これらの方法は管理対象のクラスタごとへの設定が必要で、煩雑となっています。そこで、GKE の Fleet とその機能である Connect Gateway の機能を使って、複数クラスタの管理を簡便におこなう方法を紹介します。
GKE での 複数クラスタの管理方法
GKE で複数クラスタを Argo CD で管理するために Google Cloud は ArgoCD Fleet Plugin を開発しています。このプラグインは、Fleet に登録された GKE クラスタを Argo CD が管理するクラスタに同期します。このプラグインを使うと、Fleet に GKE クラスタを登録するだけで、Argo CD で追加したクラスタを管理できるようになります。
このプラグインは、Fleet の Connect Gateway の機能を使い、マルチクラスタの連携を実現します。Connect Gateway は、Fleet に登録された Kubernetes クラスタへの安全で一貫性のあるクラスタへの接続を提供します。そのため、Connect Gateway を使い CI/CD 環境を構築すると、複数のクラスタ上のシステムをシームレスに Kubernetes リソースをデプロイできます。
構築
実際に以下の様なシステムを構築して、Argo CD で複数のクラスタを管理します。
このシステムは1つの Google Project に2つの GKE クラスタを構築します。この2つの GKE クラスタをそれぞれプライマリクラスタとセカンダリクラスタと呼ぶこととします。プライマリクラスタに Argo CD をデプロイし、セカンダリクラスタに Pod を Argo CD からデプロイさせます。今回は、例として Nginx のコンテナをデプロイします。
また、Fleet も GKE と同じプロジェクトに作成するものとします。
Google Cloud の環境構築に Terraform を使い、Kubernetes の環境構築に manifest と kustomize を使います。紹介するコード内の <>
で囲まれた値は、構築する環境に合わせて変更ください。
Google Cloud の環境構築
Google Cloud の環境構築では、以下をおこないます。
- API の有効化
-
argocd-fleet-sync
イメージの作成 - ネットワークの作成
- サービスアカウントの作成
- GKE クラスタの作成
- Argo CD をインターネットからアクセスさせるための設定
API の有効化とサービスエージェントへの権限付与
以下の API を有効にします。
- container.googleapis.com
- gkehub.googleapis.com
- cloudresourcemanager.googleapis.com
- connectgateway.googleapis.com
- anthos.googleapis.com
locals {
enabled_services = [
"container.googleapis.com",
"gkehub.googleapis.com",
"cloudresourcemanager.googleapis.com",
"connectgateway.googleapis.com",
"anthos.googleapis.com",
]
}
resource "google_project_service" "apis" {
for_each = toset(local.enabled_services)
project = var.project
service = each.value
disable_dependent_services = false
disable_on_destroy = false
}
gkehub.googleapis.com
のサービスエージェントには roles/gkehub.serviceAgent
の権限が必要なため、以下の様に IAM ロールを付与します。
resource "google_project_service_identity" "main" {
provider = google-beta
project = var.project
service = "gkehub.googleapis.com"
}
resource "google_project_iam_member" "gkehub_agent_roles_container_developer" {
member = google_project_service_identity.main.member
project = var.project
role = "roles/gkehub.serviceAgent"
}
argocd-fleet-sync
イメージの登録
ArgoCD Fleet Plugin のプラグインの Docker イメージは公開されていないため、Artifact Registry を作成し、Dockerfile からビルドしてイメージを登録します。
ここでは、元リポジトリをフォークして、Cloud Build トリガーを作り、イメージをビルドする方法を紹介します。
以下のコマンドで、gke-fleet-management リポジトリをフォークします。
gh repo fork GoogleCloudPlatform/gke-fleet-management --org <GITHUB ORGANIZATION>
フォークしたリポジトリを Cloud Build のリポジトリに連携するために必要な GitHub の Personal Access Token (以降 PAT と呼びます) を管理する Secret Manager を作成します。
resource "google_secret_manager_secret" "main" {
secret_id = <SECRET MANAGER>
project = var.project
replication {
user_managed {
replicas {
location = "asia-northeast1"
}
}
}
}
data "google_secret_manager_secret_version" "main" {
secret = <SECRET MANAGER>
project = var.project
}
続いて、フォークしたリポジトリをプロジェクトの Cloud Build リポジトリに接続して、イメージをビルドする Cloud Build トリガーを作成します。
resource "google_cloudbuildv2_connection" "main" {
name = "oss-repositories"
location = local.region
project = var.project
github_config {
app_installation_id = var.cloudbuild_id
authorizer_credential {
oauth_token_secret_version = data.google_secret_manager_secret_version.main.id
}
}
}
resource "google_cloudbuildv2_repository" "main" {
name = "gke-fleet-management"
project = var.project
location = local.region
parent_connection = google_cloudbuildv2_connection.main.name
remote_uri = "https://github.com/AriaAquamarine/gke-fleet-management.git"
}
resource "google_cloudbuild_trigger" "main" {
name = "manual-gke-fleet-mng-img-push"
project = var.project
location = local.region
filename = "fleet-argocd-plugin/cloudbuild.yaml"
source_to_build {
repository = google_cloudbuildv2_repository.main.id
ref = "refs/heads/main"
repo_type = "GITHUB"
}
}
Cloud Build が成功する様にフォークしたリポジトリの fleet-argocd-plugin/cloudbuild.yaml
ファイルを以下の様に修正します。
steps:
- name: 'gcr.io/cloud-builders/docker'
dir: 'fleet-argocd-plugin'
entrypoint: /bin/sh
args:
- -c
- |
docker build -t $LOCATION-docker.pkg.dev/$PROJECT_ID/argocd-fleet-sync/plugin:v1.0 .
images:
- $LOCATION-docker.pkg.dev/$PROJECT_ID/argocd-fleet-sync/plugin:v1.0
argocd-fleet-sync
イメージを保存する Artifact Registry を作成します。
resource "google_artifact_registry_repository" "main" {
repository_id = "argocd-fleet-sync"
format = "DOCKER"
project = var.project
location = local.region
}
作成した Cloud Build トリガーを Cloud コンールから実行ボタンをクリックして、Dockerfile をビルドさせます。
ビルドが成功すると、以下の様に argocd-fleet-sync
にイメージが作成されています。
ネットワークの作成
GKE クラスタを構築する VPC ネットワークを以下の様に作成します。
サブネットワーク名 | VPC ネットワーク名 | アドレス範囲 | 用途 |
---|---|---|---|
gke-primary | gke | 192.168.1.0/24 | Argo CD 用のクラスタが使うネットワーク |
gke-secondary | gke | 192.168.2.0/24 | アプリケーション(Nginx)をデプロイクラスタが使うネットワーク |
resource "google_compute_network" "main" {
name = "gke"
project = var.project
auto_create_subnetworks = false
}
resource "google_compute_subnetwork" "primary" {
name = "gke-primary"
project = var.project
ip_cidr_range = "192.168.0.0/24"
network = google_compute_network.main.self_link
region = "asia-northeast1"
}
resource "google_compute_subnetwork" "secondary" {
name = "gke-secondary"
project = var.project
ip_cidr_range = "192.168.1.0/24"
network = google_compute_network.main.self_link
region = "asia-northeast1"
}
GKE クラスタの作成
GKE クラスタを以下の様に作成します。今回、Autopilot クラスタを構築し、構築するプロジェクトのフリートに GKE クラスタを登録します。
GKE Cluster 名 | ネットワーク | サブネットワーク |
---|---|---|
primary | gke | gke-primary |
secondary | gke | gke-secondary |
resource "google_container_cluster" "primary" {
name = "primary"
project = var.project
location = "asia-northeast1"
enable_autopilot = true
network = google_compute_network.main.id
subnetwork = google_compute_subnetwork.primary.id
fleet {
project = var.project
}
}
resource "google_container_cluster" "secondary" {
name = "secondary"
project = var.project
location = "asia-northeast1"
enable_autopilot = true
network = google_compute_network.main.id
subnetwork = google_compute_subnetwork.secondary.id
fleet {
project = var.project
}
}
サービスアカウントの作成
Argo CD が他のクラスタに Kubernetes リソースをデプロイするために、Argo CD に Google Cloud の権限を付与する必要があります。その権限を付与するために Google Cloud 側でサービスアカウントを作成します。そして、作成した Google Cloud のサービスアカウントと、Kubernetes のサービスアカウントは Google Cloud の Workload Identity の機能で連携します。
作成するサービスアカウントに付与する権限は、以下の通りです。ここで、KSA は Kubernetes ServiceAccount の略です。
付与する権限 | 権限を有するリソース |
---|---|
roles/gkehub.viewer | Fleet ホストプロジェクト |
gkehub.gatewayEditor | Fleet ホストプロジェクト |
roles/container.developer | GKE のプロジェクト |
roles/iam.workloadIdentityUser | argocd ネームスペースの argocd-server KSA |
roles/iam.workloadIdentityUser | argocd ネームスペースの argocd-application-controller KSA |
roles/iam.workloadIdentityUser | argocd ネームスペースの argocd-fleet-sync KSA |
roles/iam.workloadIdentityUser | argocd ネームスペースの argocd-fleet-sync KSA |
resource "google_service_account" "main" {
depends_on = [
google_container_cluster.primary
]
account_id = <GOOGLE CLOUD SERVICEACCOUNT ID>
project = var.project
}
resource "google_service_account_iam_member" "workload_identity_argocd-server" {
service_account_id = google_service_account.main.id
role = "roles/iam.workloadIdentityUser"
member = format("serviceAccount:%s.svc.id.goog[%s/%s]", var.project, "argocd", "argocd-server")
}
resource "google_service_account_iam_member" "workload_identity_argocd-application-controller" {
service_account_id = google_service_account.main.id
role = "roles/iam.workloadIdentityUser"
member = format("serviceAccount:%s.svc.id.goog[%s/%s]", var.project, "argocd", "argocd-application-controller")
}
resource "google_service_account_iam_member" "workload_identity_argocd-fleet-sync" {
service_account_id = google_service_account.main.id
role = "roles/iam.workloadIdentityUser"
member = format("serviceAccount:%s.svc.id.goog[%s/%s]", var.project, "argocd", "argocd-fleet-sync")
}
resource "google_project_iam_member" "k8s-argocd_roles_gkehub_viewer" {
member = google_service_account.main.member
project = var.project
role = "roles/gkehub.viewer"
}
resource "google_project_iam_member" "k8s-argocd_roles_gkehub_gatewayEditor" {
member = google_service_account.main.member
project = var.project
role = "roles/gkehub.gatewayEditor"
}
resource "google_project_iam_member" "k8s-argocd_roles_container_developer" {
member = google_service_account.main.member
project = var.project
role = "roles/container.developer"
}
Argo CD をインターネットからアクセスさせるための設定
GKE 上にデプロイする Argo CD をインターネットから公開するため、以下のリソースを作成します。
- Certificate Manager によるサーバー証明書の作成
- Argo CD のドメインの DNS ゾーンへの登録
具体的な手順とコードは、GKE での Argo CD の構築の Argo CD のドメイン登録と Certificate Manager の作成と全く同じなので、そちらを参照ください。
Kubernetes 側の構築
Kubernetes に以下の設定をします。
- Argo CD の構築
-
argocd-fleet-sync
のインストール - Argo CD の設定
Argo CD の構築
Argo CD をプライマリクラスタにデプロイします。インターネットへの公開は、Gateway を使います。構築に必要なマニフェストは、GKE での Argo CD の構築の Argo CD の構築を参考にしてください。
今回は、さらに他のクラスタへの権限付与が必要なため、ServiceAccount リソースを作成して、サービスアカウントの作成
で作成した Google Cloud のサービスアカウントと紐づけます。
apiVersion: v1
kind: ServiceAccount
metadata:
name: argocd-application-controller
annotations:
iam.gke.io/gcp-service-account: <GOOGLE CLOUD SERVICEACCOUNT EMAIL>
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: argocd-server
annotations:
iam.gke.io/gcp-service-account: <GOOGLE CLOUD SERVICEACCOUNT EMAIL>
ServiceAccount リソースの設定を実施する様に kustomization.yaml を以下の様に修正します。
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: argocd
resources:
- gateway.yaml
- health-check-policy.yaml
- httproute.yaml
- https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
patches:
- path: configmap.yaml
- path: serviceaccount.yaml
以下のコマンドで Argo CD を構築します。
kubectl apply -f namespace.yaml
kustomize build . | kubectl apply -f -
暫くして、登録したドメインにアクセスすると、Argo CD のログイン画面にアクセスできるようになります。
argocd-fleet-sync
のインストール
Fleet に登録された GKE クラスタを Argo CD のクラスタに同期する argocd-fleet-sync
アプリケーションをデプロイして、Argo CD のクラスタに Fleet のメンバーの GKE クラスタを登録します。
以下の fleet-sync-install.yaml
と kustomization.yaml
を作成して、argocd-fleet-sync
Deployment を作成します。
apiVersion: v1
kind: ServiceAccount
metadata:
name: argocd-fleet-sync
namespace: argocd
annotations:
iam.gke.io/gcp-service-account: <GOOGLE CLOUD SERVICEACCOUNT EMAIL>
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: argocd
name: argocd-fleet-sync-secrets-role
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
namespace: argocd
name: argocd-fleet-sync-secrets-rolebinding
subjects:
- kind: ServiceAccount
name: argocd-fleet-sync
namespace: argocd
roleRef:
kind: Role
name: argocd-fleet-sync-secrets-role
apiGroup: rbac.authorization.k8s.io
---
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-fleet-sync
namespace: argocd
data:
token: '$argocd-fleet-sync:token'
baseUrl: "http://argocd-fleet-sync.argocd.svc.cluster.local:8888"
FLEET_PROJECT_NUMBER: "<PROJECT NUMBER>"
PORT: ":4356"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: argocd-fleet-sync
namespace: argocd
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: argocd-fleet-sync
template:
metadata:
labels:
app.kubernetes.io/name: argocd-fleet-sync
spec:
serviceAccountName: argocd-fleet-sync
nodeSelector:
iam.gke.io/gke-metadata-server-enabled: "true"
containers:
- name: argocd-fleet-sync
image: argocd-fleet-sync
imagePullPolicy: Always
envFrom:
- configMapRef:
name: argocd-fleet-sync
ports:
- containerPort: 4356
name: http
resources:
requests:
memory: "1Gi"
cpu: "500m"
ephemeral-storage: "1Gi"
limits:
memory: "1Gi"
cpu: "500m"
ephemeral-storage: "1Gi"
volumes:
- name: token
secret:
secretName: argocd-fleet-sync
---
apiVersion: v1
kind: Service
metadata:
name: argocd-fleet-sync
namespace: argocd
spec:
selector:
app.kubernetes.io/name: argocd-fleet-sync
ports:
- name: http
port: 8888
targetPort: 4356
---
apiVersion: v1
kind: Secret
metadata:
name: argocd-fleet-sync
namespace: argocd
labels:
app.kubernetes.io/part-of: argocd
stringData:
token: 'supersecret'
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- fleet-sync-install.yaml
images:
- name: argocd-fleet-sync
newName: "asia-northeast1-docker.pkg.dev/<PROJECT ID>/argocd-fleet-sync/plugin"
newTag: v1.0
以下のコマンドで、fleet-argocd-plugin
を作成します。
kustomize build . | kubectl apply -f -
デプロイに成功すると、argocd-fleet-sync
Deployment が作成されます。
Argo CD の Settings の Clusters を確認すると、Fleet に登録されているプライマリとセカンダリが以下の様に追加されています。
secondary クラスタの情報を確認すると、以下の様にクラスタ情報が登録されます。
Argo CD の設定
GKE の secondary クラスタへのデプロイするために Argo CD に以下を実施します。
- AppProject の作成
- GitHub リポジトリの接続
プライマリクラスタとセカンダリクラスタにデプロイできる AppProject を作成します。spec.destinations[]
にクラスタ情報を登録します。
server と name の情報は、Argo CD に登録されている クラスタの server と name を記載します。
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: <PROJECT NAME>
namespace: argocd
spec:
description: Multi Clusters (primary/secondary)
sourceRepos:
- <GITHUB REPOSITORY URL>
destinations:
- namespace: '*'
server: https://asia-northeast1-connectgateway.googleapis.com/v1/projects/<PROJECT NUMBER>/locations/asia-northeast1/gkeMemberships/primary
name: primary.asia-northeast1.<PROJECT NUMBER>
- namespace: '*'
server: https://asia-northeast1-connectgateway.googleapis.com/v1/projects/<PROJECT NUMBER>/locations/asia-northeast1/gkeMemberships/secondary
name: secondary.asia-northeast1.<PROJECT NUMBER>
clusterResourceWhitelist:
- group: '*'
kind: '*'
以下のコマンドでマニフェストをデプロイし、AppProject を作成します。
kubectl -f project.yaml
デプロイに成功すると、Argo CD の Settings の プロジェクトが以下の様に増えます。
続いて、Argo CD が参照する GitHub リポジトリを作成した プロジェクトに登録します。
設定項目 | 設定内容 |
---|---|
Choose your connection method | VIA HTTPS |
Type | git |
Name | 接続名 (任意の値) |
Project | 先に作成したプロジェクト名 |
Repository URL | GitHub リポジトリ URL |
Username | GitHub ユーザー名 |
Password | PAT※ |
※PAT は repo のスコープと admin:org の read:org スコープを付与して作成します。
接続に成功すると、以下の様に CONNECTION STATUS が Successful となります。
サンプルアプリケーションの作成
プロジェクトに登録した GitHub リポジトリに Nginx の Deployment を作成するマニフェストを登録し、このマニフェストを実行する Application を作成します。
登録したリポジトリを参照し、secondary クラスタに Pod をデプロイする以下の様な application.yaml を作成します。
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: <APPLICATION NAME>
namespace: argocd
spec:
destination:
namespace: argocd
server: https://asia-northeast1-connectgateway.googleapis.com/v1/projects/<PROJECT NUMBER>/locations/asia-northeast1/gkeMemberships/secondary
project: <PROJECT NAME>
source:
path: <KUBERNETES MANIFESTS PATH>
repoURL: <GITHUB REPOSITORY URL>
targetRevision: HEAD
syncPolicy:
automated: {}
接続した GitHub リポジトリに以下の様な nginx の Deployment を作成するマニフェストをプッシュします。
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: default
labels:
app: helloworld
backstage.io/kubernetes-id: helloworld
name: helloworld
spec:
selector:
matchLabels:
app: helloworld
template:
metadata:
labels:
app: helloworld
spec:
containers:
- name: helloworld
image: nginx
ports:
- containerPort: 80
Application を作成する前にデプロイ先の secondary GKE クラスタに Deployment がないことを確認します。
application.yaml のマニフェストを以下のコマンドでデプロイし、暫くすると Argo CD 上に Application が作成されます。また、Application は GitHub リポジトリに登録したマニフェストを自動で Sync をするので、helloworld の Deployment が以下の様に作成されます。
kubernetes apply -f application.yaml
最後に secondary クラスタを確認すると、helloworld の Deployment が作成されているのが確認されます。
さいごに
Argo CD を使って複数の GKE クラスタを管理する方法を紹介しました。Argo CD のプロジェクトへの権限設定を実施し運用すると、1つの Argo CD で複数のクラスタを安全に効率的に運用できます。複数の環境の GKE クラスタを管理するときは、fleet-argocd-plugin
の導入を検討してみてください。
Discussion