TECHSTEP

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

External Secrets Operatorを試す

External Secrets Operatorとは

External Secrets Operator (以降ESO) は、外部の秘匿情報管理サービスと連携するKubernetes Operatorで、外部サービスから取得した秘匿情報からKubernetes Secretリソースを作成・更新します。これにより、Secretリソースのマニフェストファイルを、GitHubなどのソースコード管理ツール上で直接管理せずに扱うことで、秘匿情報をセキュアに管理することが可能となります。

※図: ESO Docより

ESOと同様の機能を提供するものとして Kubernetes External Secrets (以降KES) がありましたが、メンテナの不在や技術的負債の解消の難しさなどからdeprecatedされ、現在はESOの利用が推奨されています。

ESOはKESと異なりGo言語で書かれていますが、それ以外に複数のCustom Resourceを組み合わせて機能を実現する、という点で大きく異なります。

利用する主なCustom Resourceは以下の2つです。

  • SecretStore: 利用する秘匿情報管理サービスやアクセス方法を指定する
  • ExternalSecret: 取得する対象のデータや生成するSecretなどを指定する

これ以外にも、Cluster-scopeなリソースである ClusterSecretStore ClusterExternalSecret なども存在します。

なお、Kubernetes Secretの課題感などは以前の投稿にも記載しているので、ここでは割愛します。

External Secrets Operatorを動かす

ここからESOを使ってみます。今回は ESOのドキュメントに記載のある方法を Amazon EKS 上で行います。また外部の秘匿情報管理サービスには AWS Secrets Manager を使用しました。

$ kubectl version
Client Version: version.Info{Major:"1", Minor:"22", GitVersion:"v1.22.0", GitCommit:"c2b5237ccd9c0f1d600d3072634ca66cefdf272f", GitTreeState:"clean", BuildDate:"2021-08-04T18:03:20Z", GoVersion:"go1.16.6", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"22+", GitVersion:"v1.22.16-eks-ffeb93d", GitCommit:"52e500d139bdef42fbc4540c357f0565c7867a81", GitTreeState:"clean", BuildDate:"2022-11-29T18:41:42Z", GoVersion:"go1.16.15", Compiler:"gc", Platform:"linux/amd64"}

AWS Secrets Managerに秘匿情報を登録する

ESOを使う前に、取得する秘匿情報を登録しておきます。今回はAWSマネジメントコンソールから、以下のような値を追加しました。

$ aws secretsmanager get-secret-value --secret-id test-secret
{
    "ARN": "arn:aws:secretsmanager:ap-northeast-1:111111111111:secret:test-secret-Rn6ufG",
    "Name": "test-secret",
    "VersionId": "93ed3aed-6048-41da-afec-48da94566aec",
    "SecretString": "{\"password\":\"1234\"}", # 使用する秘匿情報
    "VersionStages": [
        "AWSCURRENT"
    ],
    "CreatedDate": "2023-01-07T20:54:39.190000+09:00"
}

ESOをインストールする

次にESOをインストールします。ESOにはHelm Chartが用意されているのでこちらを使います。ESOが使用するリソースなどを知りたかったので、今回は helm template コマンドでマニフェストファイルを出力し、そちらを使いました。

# Helmリポジトリの追加
$ helm repo add external-secrets https://charts.external-secrets.io

# (既に追加されている場合)Helmリポジトリの更新
$ helm repo list
NAME                    URL
external-secrets        https://charts.external-secrets.io

$ helm repo update external-secrets

# テンプレートの生成
$ helm template external-secrets external-secrets/external-secrets --output-dir .

なお、生成されたファイルは以下のようになります。

helm template によって生成されたファイル一覧

$ tree external-secrets/templates/
external-secrets/templates/
├── cert-controller-deployment.yaml
├── cert-controller-rbac.yaml
├── cert-controller-serviceaccount.yaml
├── crds
│   ├── acraccesstoken.yaml
│   ├── clusterexternalsecret.yaml
│   ├── clustersecretstore.yaml
│   ├── ecrauthorizationtoken.yaml
│   ├── externalsecret.yaml
│   ├── fake.yaml
│   ├── gcraccesstoken.yaml
│   ├── password.yaml
│   ├── pushsecret.yaml
│   └── secretstore.yaml
├── deployment.yaml
├── rbac.yaml
├── serviceaccount.yaml
├── validatingwebhook.yaml
├── webhook-deployment.yaml
├── webhook-secret.yaml
├── webhook-service.yaml
└── webhook-serviceaccount.yaml

生成されたファイルを使い、インストールします。

# ESOのインストール
$ kubectl apply -f external-secrets/templates/
$ kubectl apply -f external-secrets/templates/crds/


# Podの起動を確認
$ kubectl get pods
NAME                                                READY   STATUS    RESTARTS   AGE
external-secrets-66dbbc7b45-rdgxw                   1/1     Running   0          113s
external-secrets-cert-controller-6c95b96976-fj9zt   1/1     Running   0          113s
external-secrets-webhook-647478f6dc-zdftz           1/1     Running   0          113s

AWSアクセス情報を登録する

ESOがSecretを作成するには、AWS Secrets Manager にアクセスし、秘匿情報を読み取る必要があります。ESOがAWSにアクセスする方法は2つ用意されていますが、ここではAWSへのアクセス情報を記載したSecretリソースを用意します (2つ目の方法も後述します)。

awssm-secret.yaml

apiVersion: v1
kind: Secret
metadata:
  name: awssm-secret
type: Opaque
stringData:
  access-key: XXXXXXXXXXXXXXXXXXXX
  secret-access-key: xxxxxxxxxxxxxxxxxxxx 

上記ファイルをデプロイします。

$ kubectl apply -f awssm-secret.yaml

$ kubectl describe secret awssm-secret
Name:         awssm-secret
Namespace:    default
Labels:       <none>
Annotations:  <none>

Type:  Opaque

Data
====
access-key:         20 bytes
secret-access-key:  40 bytes

SecretStoreのデプロイ

次に SecretStore を作成します。SecretStore は前述の通り、利用する秘匿情報管理サービスやアクセス方法を指定するリソースです。今回は AWS Secrets Manager へのアクセス方法を指定しますが、先ほど作成した awssm-secret Secretの情報を使用します。

今回使用したファイルは以下の通りです。

※参考: ESO Doc - SecretStore

secretstore-sample.yaml

apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: secretstore-sample
spec:
  provider: # 使用するサービスのプロバイダーを指定。同時に1つまで指定可能
    aws:
      service: SecretsManager
      region: ap-northeast-1
      auth:
        secretRef: # AWSへのアクセスで使用する認証情報を指定。ここでは Kubernetes Secretを使用
          accessKeyIDSecretRef: # AWSアクセスキーを登録したSecretとキーを指定
            name: awssm-secret
            key: access-key
          secretAccessKeySecretRef: # AWSシークレットアクセスキーを登録したSecretとキーを指定
            name: awssm-secret
            key: secret-access-key

上記ファイルをデプロイします。設定などに問題がなければ、リソースのステータスは Valid となるはずです。

$ kubectl apply -f secretstore-sample.yaml
secretstore.external-secrets.io/secretstore-sample created

$ kubectl get secretstore
NAME                 AGE   STATUS   CAPABILITIES   READY
secretstore-sample   10s   Valid    ReadWrite      True

ExternalSecretのデプロイ

次に ExternalSecret を作成します。 ExternalSecret は取得する対象のデータや生成するSecretなどを指定するリソースで、AWS Secrets Manager 上のkey/valueの情報や、前項で作成した SecretStore の情報などを指定します。

使用したファイルは以下の通りです。

※参考: ESO Doc - ExternalSecret

externalsecret-sample.yaml

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: example
spec:
  refreshInterval: 5m # SecretStoreプロバイダーから再度値を読み込む時間を指定
  secretStoreRef: # 参照する SecretStore を指定
    name: secretstore-sample
    kind: SecretStore
  target: # ExternalSecret から作成されるKubernetes Secretを指定する
    name: secret-to-be-created
    creationPolicy: Owner
  data: # 取得する秘匿情報を指定する
  - secretKey: secret-sample # 作成するSecretのkeyを指定
    remoteRef:
      key: test-secret # AWS Secrets Managerに登録したシークレット名
      property: password # シークレットに登録したキーを指定

上記ファイルをデプロイします。AWS Secrets Manager から秘匿情報を取れていれば、 ExternalSecret リソースは SecretSynced というステータスになり、Secretリソースが作成されます。

# ExternalSecretの作成
$ kubectl apply -f externalsecret-sample.yaml
externalsecret.external-secrets.io/example created

$ kubectl get externalsecret
NAME      STORE                REFRESH INTERVAL   STATUS         READY
example   secretstore-sample   5m                 SecretSynced   True

# Secretの確認
$ kubectl get secret
NAME                                           TYPE                                  DATA   AGE
awssm-secret                                   Opaque                                2      116s
default-token-hxtb6                            kubernetes.io/service-account-token   3      22m
external-secrets-cert-controller-token-k6ttv   kubernetes.io/service-account-token   3      4m24s
external-secrets-token-k4v7g                   kubernetes.io/service-account-token   3      4m24s
external-secrets-webhook                       Opaque                                4      4m24s
external-secrets-webhook-token-cn4mm           kubernetes.io/service-account-token   3      4m24s
secret-to-be-created                           Opaque                                1      17s # 新規作成されたSecret

$ kubectl get secret secret-to-be-created -oyaml
apiVersion: v1
data:
  secret-sample: MTIzNA==

(以降割愛)

$ echo "MTIzNA==" | base64 --decode
1234

その他

EKS: IRSAを使用する場合

EKSの場合、IAM Role for Service Account (IRSA) を使用し、AWSリソースにアクセスする権限をPodだけに持たせることができます。ESOはIRSAにも対応しており、AWSアクセスキーを使用するよりもセキュアに運用することが可能になります。

ここからIRSAを使用する場合も試してみます。IRSAを使用するため、まずは以下のようなJSONファイルを使用してIAMポリシーを作成します。

iam-policy.json

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "secretsmanager:GetSecretValue",
                "secretsmanager:DescribeSecret"
            ],
            "Resource": "arn:aws:secretsmanager:ap-northeast-1:111111111111:secret:*"
        }
    ]
}

上記JSONファイルを使用してIAMポリシーを作成します。

# IAM policyの作成
$ aws iam create-policy --policy-name eso-policy --policy-document file://iam-policy.json

続いて eksctl コマンドを使用し、IAM OIDCプロバイダーの作成と、IAMポリシーに紐づいたService Accountの作成を行います。

# OIDCプロバイダーの作成
$ eksctl get clusters
NAME            REGION          EKSCTL CREATED
eks-cluster     ap-northeast-1  True

$ eksctl utils associate-iam-oidc-provider \
    --cluster eks-cluster \
    --approve


# Service Accountの作成
$ eksctl create iamserviceaccount \
    --name eso-irsa \
    --namespace default \
    --cluster eks-cluster \
    --role-name "eso-role" \
    --attach-policy-arn arn:aws:iam::111111111111:policy/eso-policy \
    --approve


# Service Accountの確認
$ kubectl get sa
NAME                               SECRETS   AGE
default                            1         23m
eso-irsa                           1         13s # 作成したService Account
external-secrets                   1         5m27s
external-secrets-cert-controller   1         5m27s
external-secrets-webhook           1         5m27s

$ kubectl describe sa eso-irsa
Name:                eso-irsa
Namespace:           default
Labels:              app.kubernetes.io/managed-by=eksctl
Annotations:         eks.amazonaws.com/role-arn: arn:aws:iam::111111111111:role/eso-role
Image pull secrets:  <none>
Mountable secrets:   eso-irsa-token-mp7kf
Tokens:              eso-irsa-token-mp7kf
Events:              <none>

IRSAを使用する場合、先ほど使用した SecretStorespec.provider.aws.auth を修正する必要があります。eksctl コマンドで新しく作成したService Accountを指定することで、IRSA経由でAWS Secrets Managerにアクセスできるようになります。

ここで使用した SecretStore は以下の通りです。

※参考: ESO Doc - AWS Secrets Manager

secretstore-sample-irsa.yaml

apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: secretstore-sample
spec:
  provider:
    aws:
      service: SecretsManager
      region: ap-northeast-1
      auth: # 以降を修正
        jwt:
          serviceAccountRef:
            name: eso-irsa

こちらのファイルを使用し、リソースを作成します。なお、デプロイ前にはクラスターにある SecretStore ExternalSecret をあらかじめ削除しておきました。

$ kubectl apply -f secretstore-sample-irsa.yaml
secretstore.external-secrets.io/secretstore-sample created

$ kubectl get secretstore
NAME                 AGE   STATUS   CAPABILITIES   READY
secretstore-sample   8s    Valid    ReadWrite      True

あとは、すでに作成していた ExternalSecret 用のファイルをデプロイすれば、先ほどと同様にSecrets Managerから値を取得し、Secretリソースを作成することが確認できます。

$ kubectl apply -f externalsecret-sample.yaml
externalsecret.external-secrets.io/example created

$ kubectl get externalsecret
NAME      STORE                REFRESH INTERVAL   STATUS         READY
example   secretstore-sample   5m                 SecretSynced   True

$ kubectl get secret
NAME                                           TYPE                                  DATA   AGE
default-token-dpgft                            kubernetes.io/service-account-token   3      27m
eso-irsa-token-mp7kf                           kubernetes.io/service-account-token   3      4m
external-secrets-cert-controller-token-z7pqx   kubernetes.io/service-account-token   3      9m14s
external-secrets-token-572s4                   kubernetes.io/service-account-token   3      9m14s
external-secrets-webhook                       Opaque                                4      9m14s
external-secrets-webhook-token-7v49c           kubernetes.io/service-account-token   3      9m13s
secret-to-be-created                           Opaque                                1      23s

Flux+ArgoCD? Flamingoを試す

今回は、ArgoCD GUIからFluxのリソースの可視化を実現する、Flamingoというプロダクトを動かしてみます。

Flamingoとは

Flamingoは、Flux subsystem for Argo (FSA) という名称を持っており、その名前の通りFluxとArgoCDを組み合わせて利用することを可能にします。Flamingoのコンテナイメージは既存のArgoCDと置き換え可能で、FluxのワークロードをArgoCD UIから可視化することを可能にします。

以前Weave GitOpsを試したとき、GUIからリソースの操作やログの確認ができないことを記載しましたが、Flamingoを導入することで、Fluxの管理するリソースをArgoCD UIで操作し、これらの課題を解消することが期待できます。

また、Flux OCI Repositoryや、以前扱ったTerraform Controllerなど、Fluxの利用を前提としたリソースをArgoCD UIから操作したり、逆にFluxの管理するリソースのためにApplicationsetを使ったりと、ArgoCDとFluxの機能を組み合わせることも期待できます。

FlamingoはもともとArgoCDベースなので、通常のArgoCDと同じく Application リソースを管理することができます。FSAはそれに加えてArgoCD ApplicationをFlux Objectに変換する機能が追加されており、これによってFluxのリソースをArgoCD UIから管理することを実現します。

図: Flamingo Doc

なお、リリースページを見る限りかなり新しいプロジェクトのようなので、今後仕様変更などが発生する可能性もありそうです。

Flamingoを動かす

ここからFlamingoを実際に動かします。Flamingoを使うため、あらかじめFluxをインストールしたKubernetesクラスターを用意します。

$ kubectl version
Client Version: version.Info{Major:"1", Minor:"22", GitVersion:"v1.22.0", GitCommit:"c2b5237ccd9c0f1d600d3072634ca66cefdf272f", GitTreeState:"clean", BuildDate:"2021-08-04T18:03:20Z", GoVersion:"go1.16.6", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"22+", GitVersion:"v1.22.16-eks-ffeb93d", GitCommit:"52e500d139bdef42fbc4540c357f0565c7867a81", GitTreeState:"clean", BuildDate:"2022-11-29T18:41:42Z", GoVersion:"go1.16.15", Compiler:"gc", Platform:"linux/amd64"}

 flux install
✚ generating manifests
✔ manifests build completed
► installing components in flux-system namespace

(省略)

◎ verifying installation
✔ helm-controller: deployment ready
✔ kustomize-controller: deployment ready
✔ notification-controller: deployment ready
✔ source-controller: deployment ready
✔ install finished

Demoの実施

まずはDemo用の操作をなぞってみます。このDemoでは、Flamingoのイメージをインストールし、GUIからリソースが確認できるところまで実施します。

※参考: GitHub - Flamingo: Getting Started with a fresh KIND cluster

ここでは以下のマニフェストファイルを使用します。ここでは、Flamingoのインストールに加え、デモ用のアプリケーションも合わせて定義されています。

---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: OCIRepository
metadata:
  name: fsa-demo
  namespace: flux-system
spec:
  interval: 30s
  url: oci://ghcr.io/flux-subsystem-argo/flamingo/manifests
  ref:
    tag: v2.5
---
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
  name: fsa-demo
  namespace: flux-system
spec:
  prune: true
  interval: 2m
  path: "./demo"
  sourceRef:
    kind: OCIRepository
    name: fsa-demo
  timeout: 3m

上記ファイルをデプロイします。

$ kubectl apply -f fsa-demo.yaml
ocirepository.source.toolkit.fluxcd.io/fsa-demo created
kustomization.kustomize.toolkit.fluxcd.io/fsa-demo created

$ kubectl get ocirepository -n flux-system
NAME       URL                                                    READY   STATUS                                                                                               AGE
fsa-demo   oci://ghcr.io/flux-subsystem-argo/flamingo/manifests   True    stored artifact for digest 'v2.5/b6a0b0e388b930d2f2b0c24e6f794e438e06e3da2142d25baf537655d38c95f4'   12s

$ kubectl get kustomization -n flux-system
NAME       AGE   READY   STATUS
fsa-demo   28s   True    Applied revision: v2.5/b6a0b0e388b930d2f2b0c24e6f794e438e06e3da2142d25baf537655d38c95f4

デプロイ後、ArgoCDが起動していることも確認できます。

$ kubectl get ns
NAME              STATUS   AGE
argocd            Active   34s
default           Active   19m
flux-system       Active   5m50s
kube-node-lease   Active   19m
kube-public       Active   19m
kube-system       Active   19m

$ kubectl get pods -n argocd
NAME                                               READY   STATUS    RESTARTS   AGE
argocd-application-controller-0                    1/1     Running   0          33s
argocd-applicationset-controller-7888c4567-c5p8l   1/1     Running   0          33s
argocd-dex-server-8446698bbb-fpz5g                 1/1     Running   0          33s
argocd-notifications-controller-698d9ff4f5-zp57w   1/1     Running   0          33s
argocd-redis-6d67ff987b-g4pzd                      1/1     Running   0          33s
argocd-repo-server-7f5488d584-2dxl9                1/1     Running   0          33s
argocd-server-86764d6945-ls6m2                     1/1     Running   0          33s

$ kubectl get application -n argocd
NAME               SYNC STATUS   HEALTH STATUS
default-app        Synced        Healthy
fsa-installation   Synced        Healthy

デプロイが完了したので、Flamingo GUIにアクセスします。ここではport-forwardを使いました。

Flamingo GUIにアクセスすると、以下のようにArgoCDのようなGUIを確認できます (このDemoではログイン操作は不要です)。

$ kubectl -n argocd port-forward svc/argocd-server 8080:443
Forwarding from 127.0.0.1:8080 -> 8080
Forwarding from [::1]:8080 -> 8080
Handling connection for 8080
Handling connection for 8080

Terraform Controllerの利用

次に、Terraform Controller (以降 tf-controller) を使用し、Terraformリソースを操作してみます。先ほど使ったDemo用のリソースを先に削除しておきます。tf-controllerは先日投稿したこちらの記事を参考にしてください。

※参考: Flamingo Doc - GitOps Terraform Resources with Argo CD and Flux Subsystem for Argo

$ kubectl delete -f fsa-demo.yaml
ocirepository.source.toolkit.fluxcd.io "fsa-demo" deleted
kustomization.kustomize.toolkit.fluxcd.io "fsa-demo" deleted

次にFlamingoをデプロイします。まずは argocd Namespaceを作成し、その後GitHubに記載されたインストールコマンドを実行します。

$ kubectl create ns argocd
namespace/argocd created

$ kubectl -n argocd apply -k https://github.com/flux-subsystem-argo/flamingo//release?ref=v2.2.8-fl.1-main-305be5e6
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-redis 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-redis 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-cmd-params-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 created
service/argocd-server-metrics created
deployment.apps/argocd-dex-server created
deployment.apps/argocd-redis created
deployment.apps/argocd-repo-server created
deployment.apps/argocd-server created
statefulset.apps/argocd-application-controller created
networkpolicy.networking.k8s.io/argocd-application-controller-network-policy created
networkpolicy.networking.k8s.io/argocd-dex-server-network-policy created
networkpolicy.networking.k8s.io/argocd-redis-network-policy created
networkpolicy.networking.k8s.io/argocd-repo-server-network-policy created
networkpolicy.networking.k8s.io/argocd-server-network-policy created

インストール後のリソースを確認します。

$ kubectl get ns
NAME              STATUS   AGE
argocd            Active   5m15s
default           Active   44m
flux-system       Active   30m
kube-node-lease   Active   44m
kube-public       Active   44m
kube-system       Active   44m

$ kubectl get pods -n argocd
NAME                                 READY   STATUS    RESTARTS   AGE
argocd-application-controller-0      1/1     Running   0          3m28s
argocd-dex-server-5489d874b-2fn5j    1/1     Running   0          3m28s
argocd-redis-d486999b7-2h8hr         1/1     Running   0          3m28s
argocd-repo-server-9f4d7498f-sl7ks   1/1     Running   0          3m28s
argocd-server-d58f5c6b6-4rflx        1/1     Running   0          3m28s

# ログインパスワードの確認
$ kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d; echo
MxzAJVkfj5qdcmCn

続いてtf-controllerをインストールします。ここでは以下の Application 定義ファイルを利用し、用意されたtf-controllerマニフェストファイルを使用してインストールします。

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: tf-controller
spec:
  destination:
    namespace: flux-system
    server: 'https://kubernetes.default.svc'
  source:
    repoURL: 'https://weaveworks.github.io/tf-controller'
    targetRevision: 0.9.6
    chart: tf-controller
  project: default
  syncPolicy:
    syncOptions:
      - ApplyOutOfSyncOnly=true
      - FluxSubsystem=true
      - AutoCreateFluxResources=true

上記ファイルをデプロイし、argocd CLIGUIからSyncをかけ、tf-controllerがインストールされたことを確認します。

$ kubectl apply -n argocd -f fsa-tf-controller.yaml
application.argoproj.io/tf-controller created

# Sync後
$ kubectl get application -n argocd
NAME            SYNC STATUS   HEALTH STATUS
tf-controller   Synced        Healthy

# リソースの確認
$ kubectl get helmrelease -n flux-system
NAME            AGE     READY   STATUS
tf-controller   3m40s   True    Release reconciliation succeeded

$ kubectl get helmrepository -n flux-system
NAME            URL                                          AGE   READY   STATUS
tf-controller   https://weaveworks.github.io/tf-controller   4m    True    stored artifact for revision '483cdb04ff510d91707adfeca19a91a4bdf3a19506074195d300ff6866a6fc5b'

$ kubectl get pods -n flux-system
NAME                                       READY   STATUS    RESTARTS   AGE
helm-controller-85b89f8958-7jszl           1/1     Running   0          77m
kustomize-controller-59d6498d5-5tw8h       1/1     Running   0          77m
notification-controller-7cf6f94f46-z9llj   1/1     Running   0          77m
source-controller-b8bf68987-r2bvd          1/1     Running   0          77m
tf-controller-795f76f645-8rk4j             1/1     Running   0          26m

tf-controllerがインストールできたので、tf-controllerの扱う Terraform リソースとGitHubリポジトリを用意します。ここでは あらかじめ用意されているこちらのリポジトリをForkし、必要な箇所を書き換えておきます。書き換えるのはリポジトリ中にある ./infra/setup.yaml ファイル中の以下の箇所です。

---
apiVersion: source.toolkit.fluxcd.io/v1beta1
kind: GitRepository
metadata:
  name: helloworld-tf
spec:
  interval: 1m
  url: https://github.com/flux-subsystem-argo/tf-controller-helloworld  # このURLをFork後のURLに書き換える
  ref:
    branch: main
---
apiVersion: infra.contrib.fluxcd.io/v1alpha1
kind: Terraform

(以降割愛)

次に、上記リポジトリを指定した Application ファイルを用意し、デプロイします。マニフェストファイルは以下の通りです。

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: helloworld
spec:
  destination:
    namespace: dev
    server: 'https://kubernetes.default.svc'
  source:
    path: ./infra
    repoURL: 'https://github.com/<repository name>/tf-controller-helloworld'
    targetRevision: main
  project: default
  syncPolicy:
    syncOptions:
      - CreateNamespace=true
      - ApplyOutOfSyncOnly=true
      - FluxSubsystem=true
      - AutoCreateFluxResources=true
$ kubectl apply -n argocd -f fsa-tf-helloworld.yaml
application.argoproj.io/helloworld created

$ kubectl get app -n argocd
NAME            SYNC STATUS   HEALTH STATUS
helloworld      Synced        Healthy
tf-controller   Synced        Healthy

しばらくすると dev Namespaceが作成され、tf-controllerの管理するtfstate/tfplanなどが確認できます。

$ kubectl get secret -n dev
NAME                              TYPE                                  DATA   AGE
default-token-xvtlk               kubernetes.io/service-account-token   3      51m
helloworld-outputs                Opaque                                1      89s
terraform-runner.tls-1672559922   kubernetes.io/tls                     4      45m
tf-runner-token-zlkwq             kubernetes.io/service-account-token   3      11m
tfplan-default-helloworld-tf      Opaque                                1      89s
tfstate-default-helloworld-tf     Opaque                                1      45m

tfstateファイルや helloworld-outputs から、実行結果を確認することができます。

$ kubectl get secret -n dev tfstate-default-helloworld-tf -ojsonpath='{.data.tfstate}' | base64 -d | gzip -d

{
  "version": 4,
  "terraform_version": "1.3.1",
  "serial": 1,
  "lineage": "133da118-1101-8ef4-00e2-31b527b932f5",
  "outputs": {
    "hello_world": {
      "value": "hey hey ya, tfctl-rc!",
      "type": "string"
    }
  },
  "resources": [],
  "check_results": []
}

$ kubectl -n dev get secret helloworld-outputs -o jsonpath="{.data.hello_world}" | base64 -d; echo
hey hey ya, tfctl-rc!

GUIから見た結果は以下の通りです。

Terraform Controllerを試す

今回はTerraformリソースをGitOpsで管理することを可能にする、 Terraform Controllerを試してみます。

Terraform Controllerとは

Terraform Controller (以降 tf-controller) は、Fluxなどを開発するWeave works社が開発するKubernetes Controllerの一つで、名称の通りTerraformリソースを管理するControllerです。tf-controllerはFluxと組み合わせることを前提としており、TerraformリソースをGitOpsのスタイルで管理することを実現します。

Terraform Controllerの機能

tf-controllerは以下のような機能を持ちます。

GitOps Automation for Terraform

tf-controllerは Terraform というカスタムリソースを管理します。 Terraform リソースには .spec.approvePlan というパラメータがあり、このパラメータを変更するとtf-controllerがどのタイミングで terraform apply を実行するか変更することができます。例えば .spec.approvePlan: auto にすると自動的にapplyを実行するため、起動中のリソースと定義ファイルに差分 (Drift) を検知すると自動的に適用することができます。

なお、 .spec.approvePlan で指定できる値は、主に以下の3種類があるようです。

  • auto: controllerは自動的にすべてのplanを承認し、applyを実行する
  • disable: driftの検知のみするようcontrollerに伝え、plan/apply stageをスキップする
  • 空欄: planのみ実行するようcontrollerに伝え、planの実行とapproveに必要な文字列の生成を行う。plan実行後に指定の文字列を追加すると、再度Sync時にapplyを実行する

Drift detection

前項の繰り返しになりますが、tf-controllerは定義ファイルとリソースの設定に差分があればそれを検知します (Drift detection)。これを制御する方法は主に2つあり、一つは spec.approvePlan: disable と設定することで、こうするとDrift detectionのみを行い、plan/applyを実行しなくなります。

もう一つは spec.disableDriftDetection というパラメータを変更します。このパラメータはデフォルトで false に設定されていますが、これを true にすることでDrift detectionをdisableにすることができます。

Yaml-based Terraform

tf-controllerは、Tofu-jetというジェネレータを利用して、初めから利用可能なTerraform module (primitive module) を用意しています。これを使用すると、 Terraform リソースの中でパラメータを指定することで、AWS IAMなどのクラウドリソースを管理することができます。

tfstate management

tf-controllerはTerraformリソースを作成するとき、デフォルトではKubernetes Secretリソースとして tfstateファイルを生成します。ただ、spec.backendConfig.customConfiguration を設定すると、Amazon S3Google Cloud Storageなどの外部リソースを利用することもできます。

dependency management

tf-controllerで管理する Terraform リソースは、別のリソースとの間で依存関係を設定することができます。これはFluxのKustomize controllerをベースにしており、 .spec.dependsOn のパラメータに Terraform リソースを指定すると、対象のリソースが存在する場合のみリソースが作成されます。

Terraform Controllerを動かす

ここからtf-controllerを動かしてみます。

前提条件

tf-controllerはFluxを前提に動作するcontrollerなので、事前にFluxのインストールされたAmazon EKSクラスターを用意します。

Client Version: version.Info{Major:"1", Minor:"22", GitVersion:"v1.22.0", GitCommit:"c2b5237ccd9c0f1d600d3072634ca66cefdf272f", GitTreeState:"clean", BuildDate:"2021-08-04T18:03:20Z", GoVersion:"go1.16.6", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"22+", GitVersion:"v1.22.16-eks-ffeb93d", GitCommit:"52e500d139bdef42fbc4540c357f0565c7867a81", GitTreeState:"clean", BuildDate:"2022-11-29T18:41:42Z", GoVersion:"go1.16.15", Compiler:"gc", Platform:"linux/amd64"}

$ kubectl get pods -n flux-system
NAME                                       READY   STATUS    RESTARTS   AGE
helm-controller-85b89f8958-zrx9j           1/1     Running   0          36s
kustomize-controller-59d6498d5-lhskt       1/1     Running   0          36s
notification-controller-7cf6f94f46-q5w2k   1/1     Running   0          36s
source-controller-b8bf68987-tn69v          1/1     Running   0          36s

そのほかの前提条件は以下の通りです。

  • Fluxのバージョンは、一定のバージョンより新しい必要がある (tf-controller 0.13.0の場合、Fluxは 0.32.0 以上)
  • tf-controllerが各種Podと通信できるよう、一部ポートの通信を許可する必要がある
    • Runner Podの起動するNamepsace: 30000
    • Flux source-controller: 80
    • Flux notification-controller: 80

tf-controllerをインストール

tf-controllerのインストーラーはFluxの HelmRepository HelmRelease リソースで定義されたものが提供されているので、こちらを使います。

$ kubectl apply -f https://raw.githubusercontent.com/weaveworks/tf-controller/main/docs/release.yaml
helmrepository.source.toolkit.fluxcd.io/tf-controller created
helmrelease.helm.toolkit.fluxcd.io/tf-controller created

$ kubectl get helmrepository -n flux-system
NAME            URL                                           AGE   READY   STATUS
tf-controller   https://weaveworks.github.io/tf-controller/   24s   True    stored artifact for revision '483cdb04ff510d91707adfeca19a91a4bdf3a19506074195d300ff6866a6fc5b'

$ kubectl get helmrelease -n flux-system
NAME            AGE   READY   STATUS
tf-controller   43s   True    Release reconciliation succeeded

しばらくするとtf-controller Podが起動して完了です。

$ kubectl get crd
NAME                                         CREATED AT
alerts.notification.toolkit.fluxcd.io        2022-12-24T08:01:06Z
buckets.source.toolkit.fluxcd.io             2022-12-24T08:01:06Z
eniconfigs.crd.k8s.amazonaws.com             2022-12-24T07:39:57Z
gitrepositories.source.toolkit.fluxcd.io     2022-12-24T08:01:06Z
helmcharts.source.toolkit.fluxcd.io          2022-12-24T08:01:06Z
helmreleases.helm.toolkit.fluxcd.io          2022-12-24T08:01:06Z
helmrepositories.source.toolkit.fluxcd.io    2022-12-24T08:01:06Z
kustomizations.kustomize.toolkit.fluxcd.io   2022-12-24T08:01:06Z
ocirepositories.source.toolkit.fluxcd.io     2022-12-24T08:01:07Z
providers.notification.toolkit.fluxcd.io     2022-12-24T08:01:07Z
receivers.notification.toolkit.fluxcd.io     2022-12-24T08:01:07Z
securitygrouppolicies.vpcresources.k8s.aws   2022-12-24T07:40:01Z
terraforms.infra.contrib.fluxcd.io           2022-12-24T08:04:19Z  # 追加されたCRD


$ kubectl get pods -n flux-system
NAME                                       READY   STATUS    RESTARTS   AGE
helm-controller-85b89f8958-zrx9j           1/1     Running   0          4m47s
kustomize-controller-59d6498d5-lhskt       1/1     Running   0          4m47s
notification-controller-7cf6f94f46-q5w2k   1/1     Running   0          4m47s
source-controller-b8bf68987-tn69v          1/1     Running   0          4m47s
tf-controller-5d44bdcbf7-pfnqm             1/1     Running   0          96s   # 新規Pod

Hello Worldリソースの作成

ここから実際にTerraformリソースの管理を試します。

まずはTerraformファイルの配置されたGitリポジトリを登録するため、以下のマニフェストファイルをデプロイします。

apiVersion: source.toolkit.fluxcd.io/v1beta1
kind: GitRepository
metadata:
  name: helloworld
  namespace: flux-system
spec:
  interval: 30s
  url: https://github.com/tf-controller/helloworld
  ref:
    branch: main
$ kubectl apply -f gitrepo-helloworld.yaml
gitrepository.source.toolkit.fluxcd.io/helloworld created

$ kubectl get gitrepository -n flux-system
NAME         URL                                           AGE   READY   STATUS
helloworld   https://github.com/tf-controller/helloworld   12s   True    stored artifact for revision 'main/d9c5cc348e555526ea563fb82fc901e37de4d732'

Hello worldチュートリアルでは、以下のような Terraform ファイルを使用します。

apiVersion: infra.contrib.fluxcd.io/v1alpha1
kind: Terraform
metadata:
  name: helloworld
  namespace: flux-system
spec:
  interval: 1m
  approvePlan: auto
  path: ./
  sourceRef:
    kind: GitRepository
    name: helloworld
    namespace: flux-system

上記ファイルをデプロイします。

$ kubectl apply -f tf-helloworld.yaml
terraform.infra.contrib.fluxcd.io/helloworld created

# 作成中
$ kubectl get terraform -n flux-system
NAME         READY     STATUS                       AGE
helloworld   Unknown   Reconciliation in progress   11s

# 作成完了
$ kubectl get terraform -n flux-system
NAME         READY   STATUS                                                                AGE
helloworld   True    Applied successfully: main/d9c5cc348e555526ea563fb82fc901e37de4d732   23s

tf-controllerは、管理するTerraformリソースの状態 (tfstate) や planなどをSecretリソースに管理しています。Secretリソースを見ると、新たなリソースが作成されていることを確認できます。

$ kubectl get secret -n flux-system
NAME                                  TYPE                                  DATA   AGE
default-token-wm64p                   kubernetes.io/service-account-token   3      13m
helm-controller-token-vsw84           kubernetes.io/service-account-token   3      13m
kustomize-controller-token-czctc      kubernetes.io/service-account-token   3      13m
notification-controller-token-7btz4   kubernetes.io/service-account-token   3      13m
sh.helm.release.v1.tf-controller.v1   helm.sh/release.v1                    1      10m
source-controller-token-tt56n         kubernetes.io/service-account-token   3      13m
terraform-runner.tls-1671955464       kubernetes.io/tls                     4      2m56s
tf-controller-token-b4lsh             kubernetes.io/service-account-token   3      10m
tf-runner-token-tfvsh                 kubernetes.io/service-account-token   3      10m
tfplan-default-helloworld             Opaque                                1      2m40s  # 新規作成
tfstate-default-helloworld            Opaque                                1      2m40s  # 新規作成

これらSecretリソースの中を見ると、tfstate/planの情報が取得できます。tfplan-default-helloworld はなぜか文字化けして読めなかったのですが、 tfstate-default-helloworld のほうは以下のように確認することができました。

$ kubectl get secret -n flux-system tfstate-default-helloworld -o jsonpath='{.dat
a.tfstate}' | base64 -d | gzip -d
{
  "version": 4,
  "terraform_version": "1.3.1",
  "serial": 1,
  "lineage": "4d62fa7c-68ed-f488-b2c7-e699d0b97e98",
  "outputs": {
    "hello_world": {
      "value": "Hello, World!",
      "type": "string"
    }
  },
  "resources": [],
  "check_results": []
}

primitive moduleによるS3バケットの作成

ここからはAmazon S3バケットをtf-controllerから作成し、手動で設定を変更したのちにDrift Detectionをトリガーに設定が修正されるかを見てみます。

今回は primitive moduleを利用し、 Terraform リソースの中で必要なパラメータを指定する形で新規S3バケットを作成します。

まず、tf-controllerがAWSリソースを操作できるよう、AWS環境へのアクセス権限を付与します。Amazon EKSの場合はIAM Role for Service Account (IRSA) を利用することも可能ですが、今回はドキュメントに記載のある、Access Key/Secret Access KeyをSecretリソースに定義する方法で行いました。

以下のようなマニフェストファイルを用意し、あらかじめクラスター上に作成しておきます。

apiVersion: v1
kind: Secret
metadata:
  name: aws-credentials
  namespace: flux-system
type: Opaque
stringData:
  AWS_ACCESS_KEY_ID: XXXXXXXXXXXXXXXXXXXX
  AWS_SECRET_ACCESS_KEY: xxxxxxxxxxxxxxxxxxxx
  AWS_REGION: ap-northeast-1
$ kubectl apply -f aws-credentials.yml
secret/aws-credentials created

続いて、primitive moduleを格納するOCI リポジトリを追加し、primitive moduleを利用可能にします。以下のファイルをデプロイすることで、primitive moduleを利用可能になります。

apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: OCIRepository
metadata:
  name: aws-package
  namespace: flux-system
spec:
  interval: 30s
  url: oci://ghcr.io/tf-controller/aws-primitive-modules
  ref:
    tag: v4.38.0-v1alpha9
$ kubectl apply -f aws-package.yaml
Warning: resource ocirepositories/aws-package is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.
ocirepository.source.toolkit.fluxcd.io/aws-package configured

$ kubectl get ocirepository -n flux-system
NAME          URL                                                 READY   STATUS                                                                                                           AGE
aws-package   oci://ghcr.io/tf-controller/aws-primitive-modules   True    stored artifact for digest 'v4.38.0-v1alpha9/b9e0b7aefd72bec52f1ee922ca76fda112263f037a239ac969e042fde8810332'   37m

最後にS3バケットを作成するため、以下のファイルを使用します。 .spec.valuesバケット名やタグなどの情報を指定します。また先ほど作成した aws-credentials Secretを使用するため、spec.runnerPodTemplate でSecretを指定します。

apiVersion: infra.contrib.fluxcd.io/v1alpha1
kind: Terraform
metadata:
  name: aws-s3-bucket
  namespace: flux-system
spec:
  path: aws_s3_bucket
  values:
    bucket: my-tf-controller-test-bucket-20221224
    tags:
      Environment: Dev
      Name: My bucket
  sourceRef:
    kind: OCIRepository
    name: aws-package
  approvePlan: auto
  interval: 1h0m
  destroyResourcesOnDeletion: true
  runnerPodTemplate:
    spec:
      envFrom:
      - secretRef:
          name: aws-credentials
$ kubectl apply -f aws-s3.yaml
terraform.infra.contrib.fluxcd.io/aws-s3-bucket created

$ kubectl get terraform -n flux-system
NAME            READY     STATUS                                                    AGE
aws-s3-bucket   Unknown   Applying                                                  25s
helloworld      True      No drift: main/d9c5cc348e555526ea563fb82fc901e37de4d732   32m

aws s3 ls コマンドでバケット一覧を表示すると、確かにS3バケットが作成されていることを確認できます。

$ aws s3 ls

(抜粋)

2022-12-24 17:43:38 my-tf-controller-test-bucket-20221224

Drift detectionのテスト

先ほど作成したS3バケットを使って、Drift detectionのテストをしてみます。S3バケットのタグをAWSマネジメントコンソールから変更し、以下のような設定にします。

# 変更前のタグ
$ aws s3api get-bucket-tagging --bucket my-tf-controller-test-bucket-20221224
{
    "TagSet": [
        {
            "Key": "Environment",
            "Value": "Dev"
        },
        {
            "Key": "Name",
            "Value": "My bucket"
        }
    ]
}

# 変更後のタグ
$ date && aws s3api get-bucket-tagging --bucket my-tf-controller-test-bucket-20221224
Sat Dec 24 17:48:31 JST 2022
{
    "TagSet": [
        {
            "Key": "Environment",
            "Value": "Dev modified"
        },
        {
            "Key": "Name",
            "Value": "My bucket modified"
        }
    ]
}

しばらくするとtf-controllerがリソースの状態を確認し、Driftを検知します。 ここでtf-controllerがリソースの状態を確認する頻度は、 Terraform リソースの .spec.interval に指定された時間ごとになります。今回使用した定義ファイルでは、1時間ごとにリソースの状態を確認します。

kubectl get terraform コマンドを実行すると、以下のようにDriftの検知と再デプロイの様子を確認できます。

$ kubectl get terraform -n flux-system -w
NAME            READY   STATUS                                                                                                    AGE
aws-s3-bucket   True    Applied successfully: v4.38.0-v1alpha9/b9e0b7aefd72bec52f1ee922ca76fda112263f037a239ac969e042fde8810332   59m
helloworld      True    No drift: main/d9c5cc348e555526ea563fb82fc901e37de4d732                                                   91m


aws-s3-bucket   Unknown   Reconciliation in progress                                                                                60m
aws-s3-bucket   Unknown   Initializing                                                                                              60m
aws-s3-bucket   False     ...                                                                                                       60m
aws-s3-bucket   Unknown   Terraform Planning                                                                                        60m
aws-s3-bucket   Unknown   Plan generated                                                                                            61m
aws-s3-bucket   Unknown   Applying                                                                                                  61m
aws-s3-bucket   Unknown   Applying                                                                                                  61m
aws-s3-bucket   Unknown   Applied successfully: v4.38.0-v1alpha9/b9e0b7aefd72bec52f1ee922ca76fda112263f037a239ac969e042fde8810332   61m
aws-s3-bucket   True      Applied successfully: v4.38.0-v1alpha9/b9e0b7aefd72bec52f1ee922ca76fda112263f037a239ac969e042fde8810332   61m

S3バケットを見ると、元のタグの値に修正されていることもわかります。

$ date && aws s3api get-bucket-tagging --bucket my-tf-controller-test-bucket-20221224
Sat Dec 24 18:45:08 JST 2022
{
    "TagSet": [
        {
            "Key": "Environment",
            "Value": "Dev"
        },
        {
            "Key": "Name",
            "Value": "My bucket"
        }
    ]
}

Weave GitOpsを試す

今回はWeave GitOpsというFluxのリソースを可視化するツールを試してみます。

Weave GitOpsとは

Weave GitOpsはWeaveworks社が開発しているGUIツールで、OSSのほかEnterprise用も用意されています。同社が開発元となったFluxはデフォルトではWeb GUI機能を提供しておらず、リソースの管理はflux CLIツールで行う形となっています。Weave GitOpsを使用することで、FluxにWeb GUIの機能を追加し、Fluxのオブジェクト間の関係を把握しやすくなるなどの効果を期待できます。

Weave GitOpsは主に以下のような機能を提供します。

  • Application View: クラスター全体のデプロイの状態を表示する。具体的にはFluxの Kustomization HelmRelease リソースの情報を表示する。
  • Sources View: Kustomization などが管理するリソース (GitRepository HelmRepository など) の同期状態などを表示する。
  • Flux Runtime View: Flux自身のリソース ( helm-controllerなど) の状態を表示する

※参考リンク:

Weave GitOpsを動かす

ここからWeave GitOpsを試してみます。

0. Fluxのインストール

Weave GitOpsを使うため、FluxをインストールしたKubernetesクラスターを用意します。Kubernetesのバージョンは以下の通り、Amazon EKSを使います。

$ kubectl version
Client Version: version.Info{Major:"1", Minor:"22", GitVersion:"v1.22.0", GitCommit:"c2b5237ccd9c0f1d600d3072634ca66cefdf272f", GitTreeState:"clean", BuildDate:"2021-08-04T18:03:20Z", GoVersion:"go1.16.6", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"22+", GitVersion:"v1.22.15-eks-fb459a0", GitCommit:"be82fa628e60d024275efaa239bfe53a9119c2d9", GitTreeState:"clean", BuildDate:"2022-10-24T20:33:23Z", GoVersion:"go1.16.15", Compiler:"gc", Platform:"linux/amd64"}

Fluxのインストールは flux CLIで行います。 Weave GitOpsを使うにはFluxのバージョンを 0.32.0 以上にする必要があります。必要に応じて flux CLIのバージョンを更新しておきます。

※参考リンク:

$ flux --version
flux version 0.24.1

# flux CLIのバージョンを更新
$ curl -s https://fluxcd.io/install.sh | sudo bash
[INFO]  Downloading metadata https://api.github.com/repos/fluxcd/flux2/releases/latest
[INFO]  Using 0.37.0 as release
[INFO]  Downloading hash https://github.com/fluxcd/flux2/releases/download/v0.37.0/flux_0.37.0_checksums.txt
[INFO]  Downloading binary https://github.com/fluxcd/flux2/releases/download/v0.37.0/flux_0.37.0_linux_amd64.tar.gz
[INFO]  Verifying binary download
[INFO]  Installing flux to /usr/local/bin/flux

$ flux --version
flux version 0.37.0

Fluxのインストールを行います。

# Fluxをインストールするための前提条件を確認
$ flux check --pre
► checking prerequisites
✔ Kubernetes 1.22.15-eks-fb459a0 >=1.20.6-0
✔ prerequisites checks passed

# Fluxインストール
$ flux install
✚ generating manifests
✔ manifests build completed
► installing components in flux-system namespace

(一部割愛)

◎ verifying installation
✔ helm-controller: deployment ready
✔ kustomize-controller: deployment ready
✔ notification-controller: deployment ready
✔ source-controller: deployment ready
✔ install finished

# インストール後
$ kubectl get pods -n flux-system
NAME                                       READY   STATUS    RESTARTS   AGE
helm-controller-85b89f8958-55tcf           1/1     Running   0          41s
kustomize-controller-59d6498d5-8vzkh       1/1     Running   0          41s
notification-controller-7cf6f94f46-75znj   1/1     Running   0          41s
source-controller-b8bf68987-xtcjw          1/1     Running   0          41s

1. gitops CLIのインストール

Weave GitOpsは gitops というCLIツールを提供しており、インストールなどの管理を行います。まずは gitops CLIのインストールを行います。

$ curl --silent --location "https://github.com/weaveworks/weave-gitops/releases/download/v0.10.2/gitops-$(uname)-$(uname -m).tar.gz" | tar xz -C /tmp
$ sudo mv /tmp/gitops /usr/local/bin

# インストール後の確認
$ gitops version
Current Version: 0.10.2
GitCommit: 56d11cb9a8d14670eec713e9e3fe105da0e18476
BuildTime: 2022-11-10T15:22:34Z
Branch: releases/v0.10.2

Weave GitOpsをインストールするため、今回は gitops CLIからマニフェストファイルを生成します。

$ mkdir weave-gitops-dashboard-test
$ cd weave-gitops-dashboard-test/

# Weave GitOpsへのログイン用パスワードを設定
$ PASSWORD="password1234"

# マニフェストファイルを生成
$ gitops create dashboard ww-gitops \
>   --password=$PASSWORD \
>   --export > ./weave-gitops-dashboard.yaml
$ 

生成されたマニフェストファイルの内容は以下の通りです。

---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
  annotations:
    metadata.weave.works/description: This is the Weave GitOps Dashboard.  It provides
      a simple way to get insights into your GitOps workloads.
  labels:
    app.kubernetes.io/component: ui
    app.kubernetes.io/created-by: weave-gitops-cli
    app.kubernetes.io/name: weave-gitops-dashboard
    app.kubernetes.io/part-of: weave-gitops
  name: ww-gitops
  namespace: flux-system
spec:
  interval: 1h0m0s
  type: oci
  url: oci://ghcr.io/weaveworks/charts
---
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
  name: ww-gitops
  namespace: flux-system
spec:
  chart:
    spec:
      chart: weave-gitops
      sourceRef:
        kind: HelmRepository
        name: ww-gitops
  interval: 1h0m0s
  values:
    adminUser:
      create: true
      passwordHash: $2a$10$hHCPY3uBf5p63hZUL1mCe.Y6M8W3/H/G0wa.szdarMYIVUIgLyCOC
      username: admin

※参考リンク:

2. Weave GitOpsのインストール

上記マニフェストファイルをデプロイし、Weave GitOpsをインストールします。

$ kubectl apply -n flux-system -f weave-gitops-dashboard.yaml
helmrepository.source.toolkit.fluxcd.io/ww-gitops created
helmrelease.helm.toolkit.fluxcd.io/ww-gitops created

# インストール後のリソース
$ kubectl get helmrepository -n flux-system
NAME        URL                               AGE     READY   STATUS
ww-gitops   oci://ghcr.io/weaveworks/charts   3m40s   True    Helm repository is ready

$ kubectl get helmrelease -n flux-system
NAME        AGE     READY   STATUS
ww-gitops   3m54s   True    Release reconciliation succeeded

$ kubectl get pods -n flux-system
NAME                                       READY   STATUS    RESTARTS   AGE
helm-controller-85b89f8958-55tcf           1/1     Running   0          9m48s
kustomize-controller-59d6498d5-8vzkh       1/1     Running   0          9m48s
notification-controller-7cf6f94f46-75znj   1/1     Running   0          9m48s
source-controller-b8bf68987-xtcjw          1/1     Running   0          9m48s
ww-gitops-weave-gitops-5f6876dd6d-kt7vs    1/1     Running   0          14s

$ kubectl get svc -n flux-system
NAME                      TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
notification-controller   ClusterIP   10.100.108.183   <none>        80/TCP     10m
source-controller         ClusterIP   10.100.175.133   <none>        80/TCP     10m
webhook-receiver          ClusterIP   10.100.26.54     <none>        80/TCP     10m
ww-gitops-weave-gitops    ClusterIP   10.100.208.91    <none>        9001/TCP   49s

$ kubectl get secret -n flux-system
NAME                                  TYPE                                  DATA   AGE
cluster-user-auth                     Opaque                                2      86s
default-token-rp2vz                   kubernetes.io/service-account-token   3      11m
helm-controller-token-sjmld           kubernetes.io/service-account-token   3      11m
kustomize-controller-token-nxkgf      kubernetes.io/service-account-token   3      11m
notification-controller-token-s85bq   kubernetes.io/service-account-token   3      11m
sh.helm.release.v1.ww-gitops.v1       helm.sh/release.v1                    1      86s
source-controller-token-nhtww         kubernetes.io/service-account-token   3      11m
ww-gitops-weave-gitops-token-6ntjv    kubernetes.io/service-account-token   3      86s

$ kubectl get secret -n flux-system cluster-user-auth -oyaml
apiVersion: v1
data:
  password: JDJhJDEwJGhIQ1BZM3VCZjVwNjNoWlVMMW1DZS5ZNk04VzMvSC9HMHdhLnN6ZGFyTVlJVlVJZ0x5Q09D
  username: YWRtaW4=
kind: Secret
metadata:
  annotations:
    meta.helm.sh/release-name: ww-gitops
    meta.helm.sh/release-namespace: flux-system
  creationTimestamp: "2022-12-03T04:52:54Z"
  labels:
    app.kubernetes.io/managed-by: Helm
    helm.toolkit.fluxcd.io/name: ww-gitops
    helm.toolkit.fluxcd.io/namespace: flux-system
  name: cluster-user-auth
  namespace: flux-system
  resourceVersion: "5178"
  uid: 15933161-0af9-46db-99e5-8c3bfae2d47b
type: Opaque

Web GUIにアクセスするため、port-forwardします。

$ kubectl port-forward svc/ww-gitops-weave-gitops -n flux-system 9001:9001
Forwarding from 127.0.0.1:9001 -> 9001
Forwarding from [::1]:9001 -> 9001

http://localhost:9001 にアクセスすると以下の画面が表示されるので、設定したパスワードでログインします。

※参考リンク:

3. Weave GitOpsを眺める

ここからWeave GitOpsの画面を見てみます。

※参考リンク:

Application View

Applications Viewは、 Kustomization HelmRelease のリソースの状態を表示します。この時点では Weave GitOpsを管理する HelmRelease のみが表示されます。

ww-gitops を選択すると、このリソース自身の情報や、管理しているリソースの名称と状態が表示されます。

HelmRelease に対しては、リソースの同期状態を操作することができます。

ここで管理しているリソースを選択すると、そのリソースのマニフェストの情報が表示されます。なお、ここでマニフェストファイルを編集することはできないようです。

EVENTS タブを開くと、ww-gitops自体のKubernetesイベントを表示します。

GRAPH タブを開くと、ww-gitops の管理するリソース間の関係がグラフで表示されます。

DEPENDENCIES タブを開くと、Kustomization HelmRelease 内で定義した依存関係が表示されます。ここでは何も依存関係がないので表示されませんが、後程試してみます。

YAML タブを開くと、 ww-gitops 自身のマニフェストファイルが表示されます。

Sources View

Sources Viewは、 Kustomization HelmRelease の管理するリソースの状態を表示します。

各リソースを選択すると、以下のように Applications Viewと似たような画面が表示され、リソース自体の情報やKubernetesイベント、マニフェストファイルなどが確認できます。

Flux Runtime View

Flux Runtime Viewは、Flux自身のリソースの状態を表示します。

4. 実際にアプリケーションを管理した状態を眺める

ここで、実際にアプリケーションをデプロイしてみます。今回は以下のようにマニフェストファイルを生成し、それを使いました。

# マニフェストファイルの生成
$ flux create source git podinfo \
>   --url=https://github.com/stefanprodan/podinfo \
>   --branch=master \
>   --interval=30s \
>   --export > ./podinfo-source.yaml

$ flux create kustomization podinfo \
>   --target-namespace=default \
>   --source=podinfo \
>   --path="./kustomize" \
>   --prune=true \
>   --interval=5m \
>   --export > ./podinfo-kustomization.yaml

# アプリケーションのデプロイ
$ kubectl apply -n flux-system -f podinfo-source.yaml
gitrepository.source.toolkit.fluxcd.io/podinfo created
$ kubectl apply -n flux-system -f podinfo-kustomization.yaml
kustomization.kustomize.toolkit.fluxcd.io/podinfo created

Weave GitOpsから見ると、上記アプリケーションが確認できます。

なお、この時点ではMetrics Serverをクラスターにデプロイしていないため、HPAのリソースの状態は Not Ready となっています。

DEPENDENCIES を試す

最後に DEPENDENCIES がどう表示されるか試してみます。Fluxでは spec.dependsOnという項目でリソース間の依存関係を定義することができます。今回は以下のようなマニフェストファイルを用意し、先ほど作成した podinfo リソースと依存関係のある状態を作ります。

---
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
  name: podinfo-2
  namespace: flux-system
spec:
  dependsOn:
    - name: podinfo
  interval: 5m0s
  path: ./kustomize
  prune: true
  sourceRef:
    kind: GitRepository
    name: podinfo
  targetNamespace: flux-system

上記ファイルを使ってリソースを作成すると、 Weave GitOpsの DEPENDENCIES 配下のように表示されます。

感想

今回はFluxのリソースを可視化するWeave GitOpsを試してみました。これまでGitOpsの可視化といえばArgoCDのUIばかり使っていたのですが、ArgoCDと同じ感覚で使い始めるとかなり勝手が違うので、最初は少し戸惑いました。

Weave GitOpsを触った限り、GUIから管理するリソースを編集したり、管理するPodのログを確認することはできないため、各リソースの詳細な状態確認やトラブル時の暫定対応をすることは難しいと感じました。あくまでWeave GitOpsはFluxの管理するリソースの状態を要約して表示してくれるものであり、Flux自体をサポートするためのもの、ととらえて使ったほうがよさそうに感じました。

ただ、リソース間の依存関係を表す DEPENDENCIES などは、現状Weave GitOpsにしかない機能であり、複数のリソースを組み合わせてシステムを構築する場合は便利そうだなとも感じました。

そもそも、ArgoCDとFluxではGUIへのアプローチが異なる、という指摘があります。

However, Flux and Argo have a different approach to GUIs, Richardson noted. Flux has an API-led approach that enables multiple GUIs. Platforms such as Weaveworks, AWS, Azure and D2IQ provide GUIs that are integrated with their own security and workflows, he said, adding Weave GitOps is “the main open source GUI.”

※引用元: Argo CD and Flux Are CNCF Grads: But What Now?

なので、ArgoCD UIとは使い道が違うことを理解したうえで、採用するか否かを検討するほうがよさそうです。

ArgoCD Projectから一部のDestinationをDenyする

今回はArgoCD ver 2.5で追加された、Projectレベルでのセキュリティ機能を試してみます。

以前のArgoCD Projectの記事にも記載をしているのですが、ArgoCDのProjectは Application を論理的にグルーピングする機能で、source/destinationやroleなどを設定できます。

今回のアップデートでは、destinationdeny の設定を追加できるようになりました。これを利用するケースとしては、一部のnamespace( kube-system など)やclusterが機密情報を扱っており、管理者以外が該当のnamespaceへアクセスできないようにしたい場合などが挙げられます。

※参考:

ではProject-level securityを試していきます。今回は以下のマニフェストファイルを使ってテスト用のProjectを作成しています。

Denyの設定をするには、宛先の名称の先頭に ! を付ければ実現できます。なお、同じDestinationにAllow/Deny両方設定するとDenyのほうが優先されます。そのため、今回のテスト用ファイルのように記載すると、kube-system 以外のNamespaceはすべて許可する、といった設定ができます。

apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: test-project
  namespace: argocd
spec:
  sourceRepos:
  - '*'
  destinations:
  - namespace: '*'
    server: '*'
  - namespace: '!kube-system' # Denyの設定
    server: '*'

上記マニフェストファイルをデプロイします。

$ kubectl apply -f proj-test.yaml
appproject.argoproj.io/test-project created

$ kubectl describe appproject test-project -n argocd
Name:         test-project
Namespace:    argocd

(割愛)

Spec:
  Destinations:
    Namespace:  *
    Server:     *
    Namespace:  !kube-system
    Server:     *
  Source Repos:
    *
Events:  <none>

上記Projectを指定して Application の作成を行います。今回はDestinationのNamespaceを変えた2つの Application を作成してみます。片方は default 、もう片方は以下にあるように kube-systemDestinationに指定します。

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: guestbook-kube-system
  namespace: argocd
spec:
  project: test-project
  source:
    repoURL: https://github.com/argoproj/argocd-example-apps.git
    targetRevision: HEAD
    path: guestbook
  destination:
    server: https://kubernetes.default.svc
    namespace: kube-system # ProjectでDenyしたkube-systemを指定
  syncPolicy:
    automated:
      prune: false
      selfHeal: false

default を指定したほうはSyncも正常に実行し、Statusも Healthy となることが確認できます。

# namespace: defaultを指定したリソース
$ kubectl apply -f app-default.yaml
application.argoproj.io/guestbook-default created

$ kubectl get app -n argocd
NAME                SYNC STATUS   HEALTH STATUS
guestbook-default   Synced        Progressing

$ kubectl get app -n argocd
NAME                SYNC STATUS   HEALTH STATUS
guestbook-default   Synced        Healthy

kube-system を指定したほうは、 Application を作成してもStatusが Unknown の状態でキープされます。 Application の状態を確認すると、指定したDestinationが許可されていない旨を表すメッセージが表示されました。

# namespace: kube-systemを指定したリソース
$ kubectl apply -f app-kube-system.yaml
application.argoproj.io/guestbook-kube-system created

$ kubectl get app -n argocd
NAME                    SYNC STATUS   HEALTH STATUS
guestbook-default       Synced        Healthy
guestbook-kube-system   Unknown       Unknown

$ kubectl describe app guestbook-kube-system -n argocd
Name:         guestbook-kube-system
Namespace:    argocd

(割愛)

Status:
  Conditions:
    Last Transition Time:  2022-11-05T06:35:37Z
    Message:               application destination {https://kubernetes.default.svc kube-system} is not permitted in project 'test-project'
    Type:                  InvalidSpecError
  Health:
    Status:  Unknown
  Sync:
    Status:  Unknown
Events:
  Type    Reason           Age   From                           Message
  ----    ------           ----  ----                           -------
  Normal  ResourceUpdated  22s   argocd-application-controller  Updated sync status:  -> Unknown
  Normal  ResourceUpdated  22s   argocd-application-controller  Updated health status:  -> Unknown

上記の通り、kube-systemdestinationに利用できないことが確認できました。 ちなみにArgoCD UIのほうからも、同様のメッセージを確認できます。

ArgoCD Server-side Applyを試す

今回は ArgoCD ver 2.5でBeta版になったServer-side Apply機能について簡単に触れてみます。

Server-side Applyとは

Server-side Applyは、Kubernetesでv 1.14から利用可能になった、リソースの作成・管理方法の1つです。

Kubernetesはリソースを宣言的手法で管理するコマンドとして kubectl apply を利用することができます。

リソースを宣言的に管理するための差分を検出・比較するのは、従来は kubectl コマンドが行っていました (Client-sdie Apply) 。この手法ではいくつかの課題が知られており、これを解決するための方法として Server-side Apply (以降 SSAと記載) という機能が実装されました。

SSAはリソースの管理に metadata.managedField というフィールドを使用します。このフィールドには、kubectlKubernetesコンポーネントなど、誰がこのリソースを管理・更新したのかを記録します。そして前回更新したのと別の誰かがフィールドを更新しようとすると、コンフリクトを発生させて上書きを防止します。

SSAの詳細な説明はこちらのドキュメント等をご確認ください。

ArgoCD Server-side Applyとは

さて、ArgoCDのドキュメントにもClient-side Applyでの課題は記載されており、大きく2つの課題が紹介されています。

  1. 管理するリソースのマニフェストファイルのサイズが巨大な場合: Kubernetesリソースのannotationsには、256kbのサイズ制限があります。Client-side Applyでは、差分管理のため、annotationの last-applied-configuration という値にリソースの状態を記録しています。そのためマニフェストのサイズが巨大になると256kbを超える場合があります(例: Fixing Argo CD “Too long must have at most 262144 bytes” error)。

  2. ArgoCDが完全には管理しないリソースにパッチを当てる場合: ArgoCDだけでなく、それ以外の方法でリソースを更新したい場合も、Server-Side Applyを適用したほうが管理しやすくなります。

ArgoCDのアップデートによりSSAが実装されたことで、これらの課題を解消することが期待できます。

ArgoCDでのSSA機能は、Application からリソースを作成・更新する時、対象のリソースにSSAを適用します。SSAの設定はArgoCDの Application リソースの定義で行い、 Application 適用後に kubectl apply 等で変更しようとすると、差分があればコンフリクトを返します。

なお、 ArgoCDから Application 経由でリソースを更新する際は、現在は差分があっても 強制的に更新されるようです( --force-conflicts オプションを付けて更新するようなイメージ)。

※参考: GitHub - argoproj/argo-cd: Server-Side Apply support for ArgoCD

ArgoCD Server-side Applyを試す

ここからArgoCD SSAを試してみます。今回は EKS ver.1.21上に ArgoCD ver 2.5をデプロイしています。

$ kubectl version
Client Version: version.Info{Major:"1", Minor:"22", GitVersion:"v1.22.0", GitCommit:"c2b5237ccd9c0f1d600d3072634ca66cefdf272f", GitTreeState:"clean", BuildDate:"2021-08-04T18:03:20Z", GoVersion:"go1.16.6", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"21+", GitVersion:"v1.21.14-eks-fb459a0", GitCommit:"b07006b2e59857b13fe5057a956e86225f0e82b7", GitTreeState:"clean", BuildDate:"2022-10-24T20:32:54Z", GoVersion:"go1.16.15", Compiler:"gc", Platform:"linux/amd64"}

$ argocd version
argocd: v2.4.2+c6d0c8b
  BuildDate: 2022-06-21T21:03:41Z
  GitCommit: c6d0c8baaa291cd68465acd7ad6bef58b2b6f942
  GitTreeState: clean
  GoVersion: go1.18.3
  Compiler: gc
  Platform: linux/amd64
argocd-server: v2.5.1+504da42
  BuildDate: 2022-11-01T21:14:30Z
  GitCommit: 504da424c2c9bb91d7fb2ebf3ae72162e7a5a5be
  GitTreeState: clean
  GoVersion: go1.18.8
  Compiler: gc
  Platform: linux/amd64
  Kustomize Version: v4.5.7 2022-08-02T16:35:54Z
  Helm Version: v3.10.1+g9f88ccb
  Kubectl Version: v0.24.2
  Jsonnet Version: v0.18.0

今回はServer-side Applyを有効にした Application を作成し、SSAが適用されていることを確認します。ArgoCDでSSAを有効にするには、以下のように Applicationspec.syncPolicy.syncOptions に設定を追加します。

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: guestbook-ssa
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/argoproj/argocd-example-apps.git
    targetRevision: HEAD
    path: guestbook
  destination:
    server: https://kubernetes.default.svc
    namespace: default
  syncPolicy:
    syncOptions:
    - ServerSideApply=true

上記 app-ssa.yaml をデプロイします。

$ kubectl apply -f app-ssa.yaml
application.argoproj.io/guestbook-ssa created

デプロイ後Syncを行い、Statusを確認すると、作成されたDeploymentは annotations.last-applied-configuration がなく、 metadata.managedFiled が追加されていることが確認できます。

$ kubectl get deploy guestbook-ui -oyaml --show-managed-fields
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    deployment.kubernetes.io/revision: "1"
  creationTimestamp: "2022-11-05T03:22:23Z"
  generation: 1
  labels:
    app.kubernetes.io/instance: guestbook-ssa
  managedFields:
  - apiVersion: apps/v1
    fieldsType: FieldsV1
    fieldsV1:
      f:metadata:
        f:labels:
          f:app.kubernetes.io/instance: {}
      f:spec:
        f:replicas: {}
        f:revisionHistoryLimit: {}
        f:selector: {}
        f:template:
          f:metadata:
            f:labels:
              f:app: {}
          f:spec:
            f:containers:
              k:{"name":"guestbook-ui"}:
                .: {}
                f:image: {}
                f:name: {}
                f:ports:
                  k:{"containerPort":80,"protocol":"TCP"}:
                    .: {}
                    f:containerPort: {}
    manager: argocd-controller
    operation: Apply
    time: "2022-11-05T03:22:23Z"

(以下割愛)

また、SSAの動きを確認するため、上記 Application で作られたDeploymentのイメージタグを事前に変更してから、 Application で指定するマニフェストファイルと同じ内容のファイルをapplyすると、想定通りコンフリクトが発生しました。

# edit前のイメージタグを確認
$ kubectl get deploy -oyaml | grep image:
        - image: gcr.io/heptio-images/ks-guestbook-demo:0.2

# kubectl editでイメージタグを変更
$ kubectl edit deploy guestbook-ui
deployment.apps/guestbook-ui edited

$ kubectl get deploy -oyaml | grep image:
        - image: gcr.io/heptio-images/ks-guestbook-demo:0.1

# kubectl applyで更新すると差分を検知してコンフリクトを発生
$ kubectl apply -f guestbook-ui-deploy.yaml --server-side
error: Apply failed with 1 conflict: conflict with "kubectl-edit" using apps/v1: .spec.template.spec.containers[name="guestbook-ui"].image
Please review the fields above--they currently have other managers. Here
are the ways you can resolve this warning:
* If you intend to manage all of these fields, please re-run the apply
  command with the `--force-conflicts` flag.
* If you do not intend to manage all of the fields, please edit your
  manifest to remove references to the fields that should keep their
  current managers.
* You may co-own fields by updating your manifest to match the existing
  value; in this case, you'll become the manager if the other manager(s)
  stop managing the field (remove it from their configuration).
See https://kubernetes.io/docs/reference/using-api/server-side-apply/#conflicts

なお ArgoCDからSyncをした場合は、上記イメージタグも上書きされることも確認できました。

# Syncして更新
$ argocd app sync guestbook-ssa
TIMESTAMP                  GROUP        KIND   NAMESPACE                  NAME    STATUS    HEALTH        HOOK  MESSAGE
2022-11-05T12:32:43+09:00            Service     default          guestbook-ui    Synced   Healthy
2022-11-05T12:32:43+09:00   apps  Deployment     default          guestbook-ui  OutOfSync  Healthy
2022-11-05T12:32:43+09:00   apps  Deployment     default          guestbook-ui    Synced  Progressing
2022-11-05T12:32:43+09:00            Service     default          guestbook-ui    Synced  Healthy                  service/guestbook-ui serverside-applied
2022-11-05T12:32:43+09:00   apps  Deployment     default          guestbook-ui    Synced  Progressing              deployment.apps/guestbook-ui serverside-applied

(以下割愛)

# イメージタグは元に戻った
$ kubectl get deploy -oyaml | grep image:
        - image: gcr.io/heptio-images/ks-guestbook-demo:0.2

ArgoCD Web-based Terminalを試す

ArgoCDでのWeb Terminalは、ArgoCD ver 2.4から導入された新しい機能です。 これまでもUIへの機能追加は行われており、Kubernetesリソースのマニフェストの表示やログの出力などをサポートしていました。今回のリリースにより、起動中のPodに含まれるコンテナ上でshellを開始することができるようになりました

これまで起動中のPodでのトラブル調査を行う際は、ArgoCD UIとは別のインターフェイスからPodにアクセスする必要がありましたが、この機能によりArgoCD UI上で作業を完結できるケースを増やせると期待できます。

検証環境

Web Terminal機能を利用するために、あらかじめArgoCDをデプロイしたKubernetes環境を用意します。つい先日ArgoCD ver 2.5がリリースされたので、こちらを使ってみました。

あらかじめサンプル用のアプリケーションを作成しておきます。

※参考: ArgoCD Document - Getting Started

Web-based Terminalを試す

ここからWeb terminalを利用するため、設定変更を行います。

Web terminalを利用するためには、Web terminalの機能を有効にするのに加え、ArgoCDとユーザーの権限を変更する必要があります。

1. Web terminalの機能の有効化

Web terminalはセキュリティの観点から、デフォルトでは無効になっています。有効にするには argocd-cm ConfigMapに以下の項目を追加し、設定を変更します。

$ kubectl edit cm argocd-cm -n argocd

# 以下の項目を追加
data:
  exec.enabled: "true"

# 変更を保存
configmap/argocd-cm edited

# 変更後
$ kubectl get cm argocd-cm -n argocd -oyaml
apiVersion: v1
data:
  exec.enabled: "true"
kind: ConfigMap
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","kind":"ConfigMap","metadata":{"annotations":{},"labels":{"app.kubernetes.io/name":"argocd-cm","app.kubernetes.io/part-of":"argocd"},"name":"argocd-cm","namespace":"argocd"}}
  creationTimestamp: "2022-10-28T01:21:48Z"
  labels:
    app.kubernetes.io/name: argocd-cm
    app.kubernetes.io/part-of: argocd
  name: argocd-cm
  namespace: argocd
  resourceVersion: "5528"
  uid: 3446d288-a15e-4ca5-96ee-68225e5c82f1

※参考: GitHub: argoproj/argo-cd - argocd-cm.yaml

2. ArgoCDの権限変更

次に、argocd-server Podで exec を利用するため、RoleまたはClusterRoleの設定を変更します。今回は こちらのドキュメント の通りArgoCDをインストールしており、Clusterに対する権限が付与されているため、 ClusterRoleのほうを修正します。

argocd-server ClusterRoleを修正し、以下の権限を追加します。

$ kubectl edit clusterrole argocd-server

# 以下の項目を追加
- apiGroups:
  - ""
  resources:
  - pods/exec
  verbs:
  - create

# 変更を保存
clusterrole.rbac.authorization.k8s.io/argocd-server edited

# 変更後
$ kubectl describe clusterrole argocd-server
Name:         argocd-server
Labels:       app.kubernetes.io/component=server
              app.kubernetes.io/name=argocd-server
              app.kubernetes.io/part-of=argocd
Annotations:  <none>
PolicyRule:
  Resources                 Non-Resource URLs  Resource Names  Verbs
  ---------                 -----------------  --------------  -----
  pods/exec                 []                 []              [create]
  *.*                       []                 []              [delete get patch]
  applications.argoproj.io  []                 []              [get list watch]
  pods/log                  []                 []              [get]
  pods                      []                 []              [get]
  events                    []                 []              [list]

3. ユーザーの権限変更

ArgoCDにログインするユーザーに権限が足りなければ、argocd-rbac-cm ConfigMapに以下の権限を追加します。今回は admin ユーザーを使うため、この操作は不要です。

# 必要なら以下の項目を追加
p, role:myrole, exec, create, */*, allow

※参考: ArgoCD Document - RBAC Configuration

設定変更後の機能確認

設定変更したのでUIを見てみます。argocd-server Podを再起動する必要があるかと思ったのですが、特に何もせずに Terminal という項目が追加されていることを確認しました。

正しく設定ができていれば、以下のようにコンソール画面が表示されます。ドキュメントに記載のあるように、kubectl exec 実行時と同じような状態です。

簡単なコマンドを試したところ、問題なく実行されました。

なお、ANSI対応とのことで、こんな風に出力を操作することもできます。

※参考: Qiita - 環境毎にプロンプトの色を変える

以上の通り、ArgoCD UIからコンテナにログインし、コマンドを実行することができました。サイドカーコンテナが含まれているときはどうなるのかなど、気になる点もありますが、開発環境などで簡単にコンテナの状態をチェックするには役立ちそうです。

参考リンク