--- title: Tanzu Application Platformをkapp-controllerを使ったGitOpsでインストールするメモ tags: ["Kubernetes", "Cartographer", "kind", "Tanzu", "TAP", "Knative", "kapp-controller", "ytt", "GitOps"] categories: ["Dev", "CaaS", "Kubernetes", "TAP"] date: 2022-12-16T18:10:03Z updated: 2022-12-19T04:08:43Z --- TAPをGitOpsでインストールします。GitOpsにすることで、TAP自体のインストールを宣言的にでき、設定ファイルを管理でき、かつクラスタの操作ミスを防ぐことができます。 KubernetesでGitOpsを実現する方法はいくつかありますが、ここでは [kapp-controller](https://carvel.dev/kapp-controller/) を使用します。 kapp-controllerはTAPのインストールの前提条件になるので、GitOps用に追加でソフトウェアをインストールする必要はありません。 kapp-controllerについてはCloud Native Operator Days 2022で紹介しましたので、詳細は[スライド](https://docs.google.com/presentation/d/1mxsONks4IoQqEQ37YBPNS4hkKa71ScfsTHGt1odyMVc=)または[動画](https://www.youtube.com/watch?v=mRAKLIZ4bqs)を参考にしてください。 今回は[こちらの記事](https://ik.am/entries/716)で手動インストールしたTAPのGitOps版です。 **目次** ### Kindクラスタの作成 TAPをインストールする環境として[kind](https://kind.sigs.k8s.io/)を使用します。 Dockerには4 CPU, 4 GBメモリ以上を割り当ててください。 kindは次のバージョンを使用しています。古いkindではk8s 1.24のnodeが起動しませんでした。 ``` $ kind version kind v0.17.0 go1.19.2 darwin/amd64 ``` k8s 1.24を使用します。 ``` cat < kind-expose-port.yaml kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 nodes: - role: control-plane extraPortMappings: - containerPort: 31443 # expose port 31443 of the node to port 80 on the host for use later by Contour ingress (envoy) hostPort: 443 - containerPort: 31080 # expose port 31080 of the node to port 80 on the host for use later by Contour ingress (envoy) hostPort: 80 EOF kind create cluster --config kind-expose-port.yaml --image kindest/node:v1.24.7 ``` ### kapp-controllerのインストール TAPのドキュメントでは[Cluster Essentials for VMware Tanzu](https://network.tanzu.vmware.com/products/tanzu-cluster-essentials/)でkapp-controllerをインストールしますが、 ここでは最低限の手順でkapp-controllerを導入するため、Github上のマニフェストを使用します。 ``` kubectl apply -f https://github.com/vmware-tanzu/carvel-kapp-controller/releases/download/v0.44.1/release.yml ``` ### Service Accountの作成 kapp controllerが使用するService Accountを作成し、ClusterRoleBindingを設定します。 ここではcluster-adminをbindしますが、制限されたClusterRoleを使用しても良いです。 ``` NAMESPACE=kapp kubectl create ns ${NAMESPACE} kubectl create -n ${NAMESPACE} sa kapp kubectl create clusterrolebinding kapp-cluster-admin-${NAMESPACE} --clusterrole cluster-admin --serviceaccount=${NAMESPACE}:kapp ``` ### GitOps用のディレクトリ作成 kappディレクトリにはkapp-controllerのApp CRの定義を、configディレクトリにはデプロイされるk8sリソースの定義を格納します。 それを作成するクラスタ単位で管理します。今回はkind上にデプロイするiterater clusterを管理するためにkind-iterateディレクトリを作成します。 これらは後ほどgit pushします。 ``` mkdir -p tap-install-gitops/kind-iterate/kapp mkdir -p tap-install-gitops/kind-iterate/config ``` 次のようなファイル階層になります。 ``` $ tree tap-install-gitops tap-install-gitops `-- kind-iterate |-- config `-- kapp ``` ここではマニフェストは https://github.com/making/tap-install-gitops で管理します。 ### App CRの定義 kapp-controllerの [App CR](https://carvel.dev/kapp-controller/docs/v0.43.2/app-overview/) の定義と、App CRからデプロイされるリソースの定義を行います。 #### secretgen-controllerインストール用のApp CR まずはTAPの前提条件のもう一つである[secretgen-controller](https://github.com/vmware-tanzu/carvel-secretgen-controller)のインストールをApp CRで定義します。 ここではGithub上のマニフェストからsecretgen-controllerをデプロイするようにします。 ```yaml cat < tap-install-gitops/kind-iterate/kapp/secretgen-controller.yaml apiVersion: kappctrl.k14s.io/v1alpha1 kind: App metadata: name: secretgen-controller namespace: kapp annotations: kapp.k14s.io/change-group: "{name}" spec: serviceAccountName: kapp fetch: - http: url: https://github.com/vmware-tanzu/carvel-secretgen-controller/releases/download/v0.13.0/release.yml syncPeriod: 6h template: - ytt: { } deploy: - kapp: rawOptions: - --wait-timeout=5m - --diff-changes=true - --diff-mask=true EOF ``` 次のようなファイル階層になります。 ``` $ tree tap-install-gitops tap-install-gitops `-- kind-iterate |-- config `-- kapp `-- secretgen-controller.yaml ``` #### TAPのPackageRepositry用のApp CR TAPのPackageRepository及び、それを使用するのに必要なNamespaceとSecretをまとめてApp CRで定義します。 ``` mkdir -p tap-install-gitops/kind-iterate/config/tap-repository ``` Namespaceの定義 ```yaml cat < tap-install-gitops/kind-iterate/config/tap-repository/namespace.yaml apiVersion: v1 kind: Namespace metadata: name: tap-install EOF ``` Secretの定義。レジストリの情報は外部化しており、最後に外から渡します。 ```yaml cat < tap-install-gitops/kind-iterate/config/tap-repository/secret.yaml #@ load("@ytt:data", "data") apiVersion: v1 kind: Secret metadata: name: tap-registry namespace: tap-install type: kubernetes.io/dockerconfigjson stringData: #@yaml/text-templated-strings .dockerconfigjson: |- { "auths": { "(@= data.values.tap_registry.server @)": { "username": "(@= data.values.tap_registry.username @)", "password": "(@= data.values.tap_registry.password @)" } } } EOF ``` 上記のSecretを他のNamespaceにexportすることを許可するためのSecretExportの定義 ```yaml cat < tap-install-gitops/kind-iterate/config/tap-repository/secret-export.yaml apiVersion: secretgen.carvel.dev/v1alpha1 kind: SecretExport metadata: name: tap-registry namespace: tap-install spec: toNamespaces: - '*' EOF ``` PackageRepositoryの定義 (TAP 1.3.3用) ```yaml cat < tap-install-gitops/kind-iterate/config/tap-repository/pkgr.yaml #@ load("@ytt:data", "data") apiVersion: packaging.carvel.dev/v1alpha1 kind: PackageRepository metadata: name: tanzu-tap-repository-1.3.3 namespace: tap-install spec: fetch: imgpkgBundle: image: #@ "{}/tanzu-application-platform/tap-packages:1.3.3".format(data.values.tap_registry.server) EOF ``` ここまで用意したリソースをGitOpsで管理するためのApp CRの定義。secretgen-controllerの後にインストールされるように設定します。 6時間間隔でgit pullします。 ```yaml cat < tap-install-gitops/kind-iterate/kapp/tap-repository.yaml apiVersion: kappctrl.k14s.io/v1alpha1 kind: App metadata: name: tap-repository namespace: kapp annotations: kapp.k14s.io/change-group: "{name}" kapp.k14s.io/change-rule.create-order.0: "upsert after upserting secretgen-controller" kapp.k14s.io/change-rule.delete-order.0: "delete before deleting secretgen-controller" spec: syncPeriod: 6h serviceAccountName: kapp fetch: - git: url: https://github.com/making/tap-install-gitops.git ref: origin/main subPath: kind-iterate/config template: - ytt: paths: - tap-repository valuesFrom: - secretRef: name: tap-install-gitops deploy: - kapp: rawOptions: - --wait-timeout=5m - --diff-changes=false - --diff-mask=true EOF ``` 次のようなファイル階層になります。 ``` $ tree tap-install-gitops tap-install-gitops `-- kind-iterate |-- config | `-- tap-repository | |-- namespace.yaml | |-- pkgr.yaml | |-- secret-export.yaml | `-- secret.yaml `-- kapp |-- secretgen-controller.yaml `-- tap-repository.yaml ``` #### TAPのPackageInstall用のApp CR TAPのPackageInstall及びtap-values.yaml、及びoverlayのSecretをまとめてApp CRで定義します。 自己署名TLS証明書を発行するためCA証明書を作成します。 ``` mkdir -p certs rm -f certs/* docker run --rm -v ${PWD}/certs:/certs hitch openssl req -new -nodes -out /certs/ca.csr -keyout /certs/ca.key -subj "/CN=default-ca/O=TAP/C=JP" chmod og-rwx ca.key docker run --rm -v ${PWD}/certs:/certs hitch openssl x509 -req -in /certs/ca.csr -days 3650 -extfile /etc/ssl/openssl.cnf -extensions v3_ca -signkey /certs/ca.key -out /certs/ca.crt ``` ``` mkdir -p tap-install-gitops/kind-iterate/config/tap/overlays ``` ここではベースのドメイン名はKind用に127-0-0-1.sslip.ioを使用します。このドメイン及びサブドメインは全て127.0.0.1に解決されます。 ``` export BASE_DOMAIN=127-0-0-1.sslip.io ``` まずはTAPのインストールで使用するoverlayのSecretを定義します。 * デフォルトのTLS証明書を発行するためのoverlay ```yaml cat < tap-install-gitops/kind-iterate/config/tap/overlays/contour-default-tls.yaml #@ load("@ytt:data", "data") apiVersion: v1 kind: Secret metadata: name: contour-default-tls namespace: tap-install annotations: kapp.k14s.io/change-group: "tap-overlays" type: Opaque stringData: #@yaml/text-templated-strings contour-default-tls.yaml: | #@ load("@ytt:data", "data") #@ load("@ytt:overlay", "overlay") #@ namespace = data.values.namespace --- apiVersion: v1 kind: Secret metadata: name: default-ca namespace: #@ namespace type: kubernetes.io/tls stringData: tls.crt: "(@= data.values.default_ca.crt.replace('\n', '\\n') @)" tls.key: "(@= data.values.default_ca.key.replace('\n', '\\n') @)" --- apiVersion: cert-manager.io/v1 kind: Issuer metadata: name: default-ca-issuer namespace: #@ namespace spec: ca: secretName: default-ca --- apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: tap-default-tls namespace: #@ namespace spec: dnsNames: - #@ "*.${BASE_DOMAIN}" issuerRef: kind: Issuer name: default-ca-issuer secretName: tap-default-tls --- apiVersion: projectcontour.io/v1 kind: TLSCertificateDelegation metadata: name: contour-delegation namespace: #@ namespace spec: delegations: - secretName: tap-default-tls targetNamespaces: - "*" EOF ``` * KnativeでHTTPSでデフォルトで使用するためのoverlay ```yaml cat < tap-install-gitops/kind-iterate/config/tap/overlays/cnrs-https.yaml apiVersion: v1 kind: Secret metadata: name: cnrs-https namespace: tap-install annotations: kapp.k14s.io/change-group: "tap-overlays" type: Opaque stringData: cnrs-https.yaml: | #@ load("@ytt:overlay", "overlay") #@overlay/match by=overlay.subset({"metadata":{"name":"config-network"}, "kind": "ConfigMap"}) --- data: #@overlay/match missing_ok=True default-external-scheme: https #@overlay/match missing_ok=True http-protocol: redirected EOF ``` * TelemetryのPodを削除するためのoverlay ```yaml cat < tap-install-gitops/kind-iterate/config/tap/overlays/tap-telemetry-remove.yaml apiVersion: v1 kind: Secret metadata: name: tap-telemetry-remove namespace: tap-install annotations: kapp.k14s.io/change-group: "tap-overlays" type: Opaque stringData: tap-telemetry-remove.yaml: | #@ load("@ytt:overlay", "overlay") #@overlay/match by=overlay.subset({"metadata":{"namespace":"tap-telemetry"}}), expects="1+" #@overlay/remove --- EOF ``` 次に、`tanzu package install tap -n tap-install -f tap-values.yaml`と同等なインストールをYAMLで定義します。 > tanzu CLIでPackageをインストールする場合とPackageInstallを作成してPackageをインストールする場合との違いは[こちらの記事](https://ik.am/entries/681)を参照してください。 tap-values.yamlを格納するSecretの定義 ```yaml cat < tap-install-gitops/kind-iterate/config/tap/values.yaml #@ load("@ytt:data", "data") #@ load("@ytt:yaml", "yaml") #@ def tap_values(): shared: ingress_domain: ${BASE_DOMAIN} image_registry: project_path: #@ data.values.image_registry.project_path username: #@ data.values.image_registry.username password: #@ data.values.image_registry.password ca_cert_data: #@ data.values.default_ca.crt ceip_policy_disclosed: true profile: iterate supply_chain: basic contour: contour: replicas: 1 envoy: service: type: NodePort nodePorts: http: 31080 https: 31443 hostPorts: enable: true cnrs: domain_template: "{{.Name}}-{{.Namespace}}.{{.Domain}}" default_tls_secret: tanzu-system-ingress/tap-default-tls provider: local package_overlays: - name: contour secrets: - name: contour-default-tls - name: cnrs secrets: - name: cnrs-https - name: tap-telemetry secrets: - name: tap-telemetry-remove excluded_packages: - policy.apps.tanzu.vmware.com - image-policy-webhook.signing.apps.tanzu.vmware.com - eventing.tanzu.vmware.com - sso.apps.tanzu.vmware.com #@ end apiVersion: v1 kind: Secret metadata: name: tap-tap-install-values namespace: tap-install annotations: kapp.k14s.io/change-group: "tap-install-values" type: Opaque stringData: tap-values.yaml: #@ yaml.encode(tap_values()) EOF ``` PackageをインストールするためのRBACの定義 ```yaml cat < tap-install-gitops/kind-iterate/config/tap/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: annotations: tkg.tanzu.vmware.com/tanzu-package: tap-tap-install kapp.k14s.io/change-group: "tap-rbac" name: tap-tap-install-cluster-role rules: - apiGroups: - '*' resources: - '*' verbs: - '*' --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: annotations: tkg.tanzu.vmware.com/tanzu-package: tap-tap-install kapp.k14s.io/change-group: "tap-rbac" name: tap-tap-install-cluster-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: tap-tap-install-cluster-role subjects: - kind: ServiceAccount name: tap-tap-install-sa namespace: tap-install --- apiVersion: v1 kind: ServiceAccount metadata: annotations: tkg.tanzu.vmware.com/tanzu-package: tap-tap-install kapp.k14s.io/change-group: "tap-rbac" name: tap-tap-install-sa namespace: tap-install EOF ``` PackageInstallの定義 ```yaml cat < tap-install-gitops/kind-iterate/config/tap/pkgi.yaml apiVersion: packaging.carvel.dev/v1alpha1 kind: PackageInstall metadata: name: tap namespace: tap-install annotations: tkg.tanzu.vmware.com/tanzu-package-ClusterRole: tap-tap-install-cluster-role tkg.tanzu.vmware.com/tanzu-package-ClusterRoleBinding: tap-tap-install-cluster-rolebinding tkg.tanzu.vmware.com/tanzu-package-Secret: tap-tap-install-values tkg.tanzu.vmware.com/tanzu-package-ServiceAccount: tap-tap-install-sa kapp.k14s.io/change-group: "{name}" kapp.k14s.io/change-rule.create-order.1: "upsert after upserting tap-rbac" kapp.k14s.io/change-rule.delete-order.1: "delete before deleting tap-rbac" kapp.k14s.io/change-rule.create-order.2: "upsert after upserting tap-values" kapp.k14s.io/change-rule.delete-order.2: "delete before deleting tap-values" kapp.k14s.io/change-rule.create-order.3: "upsert after upserting tap-overlays" kapp.k14s.io/change-rule.delete-order.3: "delete before deleting tap-overlays" spec: syncPeriod: 3h serviceAccountName: tap-tap-install-sa packageRef: refName: tap.tanzu.vmware.com versionSelection: constraints: 1.3.3 prereleases: { } values: - secretRef: name: tap-tap-install-values EOF ``` ここまで用意したリソースをGitOpsで管理するためのApp CRの定義。PackageRepositoryの後にインストールされるように設定します。 1時間間隔でgit pullします。 ```yaml cat < tap-install-gitops/kind-iterate/kapp/tap.yaml apiVersion: kappctrl.k14s.io/v1alpha1 kind: App metadata: name: tap namespace: kapp annotations: kapp.k14s.io/change-group: "{name}" kapp.k14s.io/change-rule.create-order.0: "upsert after upserting tap-repository" kapp.k14s.io/change-rule.delete-order.0: "delete before deleting tap-repository" spec: syncPeriod: 1h serviceAccountName: kapp fetch: - git: url: https://github.com/making/tap-install-gitops.git ref: origin/main subPath: kind-iterate/config template: - ytt: paths: - tap valuesFrom: - secretRef: name: tap-install-gitops deploy: - kapp: rawOptions: - --wait-timeout=5m - --diff-changes=false - --diff-mask=true EOF ``` 次のようなファイル階層になります。 ``` $ tree tap-install-gitops tap-install-gitops `-- kind-iterate |-- config | |-- tap | | |-- overlays | | | |-- cnrs-https.yaml | | | |-- contour-default-tls.yaml | | | `-- tap-telemetry-remove.yaml | | |-- pkgi.yaml | | |-- rbac.yaml | | `-- values.yaml | `-- tap-repository | |-- namespace.yaml | |-- pkgr.yaml | |-- secret-export.yaml | `-- secret.yaml `-- kapp |-- secretgen-controller.yaml |-- tap-repository.yaml `-- tap.yaml ``` #### App CR群をまとめて管理する親App CR ここまで定義したApp CR群自体をGitOpsで管理するためのApp CRの定義。 10分間隔でgit pullします。 > ☝️ [app of apps pattern](https://argo-cd.readthedocs.io/en/stable/operator-manual/cluster-bootstrapping/)を実現します ```yaml cat < tap-install-gitops/kind-iterate/apps.yaml apiVersion: kappctrl.k14s.io/v1alpha1 kind: App metadata: name: apps namespace: kapp spec: serviceAccountName: kapp fetch: - git: url: https://github.com/making/tap-install-gitops.git ref: origin/main subPath: kind-iterate/kapp syncPeriod: 10m template: - ytt: paths: - '.' deploy: - kapp: rawOptions: - --wait-timeout=5m - --diff-changes=true - --diff-mask=false EOF ``` 次のようなファイル階層になります。 ``` $ tree tap-install-gitops tap-install-gitops `-- kind-iterate |-- apps.yaml |-- config | |-- tap | | |-- overlays | | | |-- cnrs-https.yaml | | | |-- contour-default-tls.yaml | | | `-- tap-telemetry-remove.yaml | | |-- pkgi.yaml | | |-- rbac.yaml | | `-- values.yaml | `-- tap-repository | |-- namespace.yaml | |-- pkgr.yaml | |-- secret-export.yaml | `-- secret.yaml `-- kapp |-- secretgen-controller.yaml |-- tap-repository.yaml `-- tap.yaml ``` ### マニフェストをgitにpush 作成したマニフェストを https://github.com/making/tap-install-gitops にpushします。 ``` GITHUB_USERNAME=... cd tap-install-gitops git init git add -A git commit -m "first commit" git branch -M main git remote add origin git@github.com:${GITHUB_USERNAME}/tap-install-gitops.git git push -u origin main cd .. ``` ### Credentials用Secretの作成 Gitで管理されるマニフェストにはCredentialsを含めず、外部化されたパラメータを参照するようにしていました。 外部化されたパラメータはSecretから参照するように設定されています。このSecretはGit管理外で、次のように作成します。 ```yaml TANZUNET_USERNAME=... TANZUNET_PASSWORD=... GITHUB_USERNAME=... GITHUB_API_TOKEN=... kubectl apply -f - < このブログの管理にはsopsを使用したGitOpsを採用しています。
> 本記事のファイル構成と若干異なりますが、 https://github.com/categolj/k8s-manifests が参考になるでしょう。 ### TAPのインストール Gitレポジトリと、外部化されたパラメータができたのでいよいよApp CRでTAPをインストールします。 親App CRを作成するだけで全てがインストールされます。 ``` kubectl apply -f tap-install-gitops/kind-iterate/apps.yaml ``` 次のコマンドで完了するまで進捗を確認します。 ``` while [ "$(kubectl -n kapp get app apps -o=jsonpath='{.status.friendlyDescription}')" != "Reconcile succeeded" ];do date kubectl get app -A echo "---------------------------------------------------------------------" sleep 10 done echo "✅ Install succeeded" ``` インストールが成功した後、各AppはsyncPeriodの間隔でreconcileします。このsyncPeriodを待てない場合は ``` kctrl app kick -n kapp -a ``` で強制的にreconcileさせることができます。`kctrl` CLIは [kapp-controllerのReleasesページ](https://github.com/vmware-tanzu/carvel-kapp-controller/releases) からダウンロードできます。 ### TAPのアンインストール 親App CRを削除するだけで全てがアンインストールされます。 ``` kubectl delete -f tap-install-gitops/kind-iterate/apps.yaml ``` --- kapp-controllerでTAPをGitOpsする方法を紹介しました。 この方法を用いると、TAPをインストールするのに * kapp-controllerをインストール * Service Accountを作成 * credentialsのSecretの登録 (sopsを使用する場合は、[GPG Private Key](https://carvel.dev/kapp-controller/docs/v0.43.2/sops/#using-gpg-2)の登録) * apps App CRの作成 だけで済みます。tanzu CLIによる操作は不要です。 TAP以外のソフトウェア(Prometheusなど)も同時にインストール可能なので、環境構築の自動化も楽になります。 TAPの検証環境を構築・破棄を繰り返し行いたい場合にも役に立つでしょう。