IK.AM

@making's tech note


imgpkg bundle化したHelm ChartをCarvel Package化する

🗃 {Dev/Carvel/kapp-controller}
🏷 Kubernetes 🏷 Carvle 🏷 air-gapped 🏷 kbld 🏷 imgpkg 🏷 Helm 🏷 kapp-controller 
🗓 Updated at 2021-12-17T00:00:00+09:00  🗓 Created at 2021-12-17T00:00:00+09:00 {✒️️ Edit  ⏰ History  🗑 Delete}

TUNA-JP Advent Calendar 2021 その2の17日目のエントリです。

前の記事で、imgpkg / kbldを使ったHelm Chartのimgpkg bundle化、及びair-gapped環境へのインストールができることを確認しました。 今回は前回作成したHelm Chartのimgpkg bundleをCarvel Package 化し、PackageInstall CRを使ってair-gapped環境にinstallします。

Carvel Packageに関しては CFCNのWebinar がわかりやすいです。 Webinarで使われたサンプルのレポジトリは https://github.com/k14s/cncf-packaging-webinar です。

目次

imgpkg bundle化したHelm ChartのCarvel Package作成

Webinarの 7-pkg-cr8-pkg-cr-helm-chart の内容を組み合わせます。

Webinarでは作成されませんでしたが、PackageMetadata CR を作っておきます。 pkg-metadata.yamlに次の内容を記述します。

apiVersion: data.packaging.carvel.dev/v1alpha1
kind: PackageMetadata
metadata:
  name: nginx.chart.pkg.maki.lol
  namespace: default
spec:
  displayName: "Nginx"
  longDescription: "Nginx Bitnami Helm Chart"
  shortDescription: "Nginx Bitnami Helm Chart"
  categories:
  - nginx
  - bitnami
  - helm

次に Package CR を作成します。 fetchimgpkgBundletemplatehelmTemplate + kbldを指定します。 pkg.yamlに次の内容を記述します。

apiVersion: data.packaging.carvel.dev/v1alpha1
kind: Package
metadata:
  name: nginx.chart.pkg.maki.lol.9.5.7
  namespace: default
spec:
  refName: nginx.chart.pkg.maki.lol
  version: 9.5.7
  releaseNotes: |
    Supprt nginx bitnami helm chart 9.5.7
  template:
    spec:
      fetch:
      - imgpkgBundle:
          image: harbor-10-213-232-22.sslip.io/library/nginx-chart-bundle:9.5.7
      template:
      - helmTemplate: {}
      - kbld:
          paths:
          - "-"
          - ".imgpkg/images.yml"
      deploy:
      - kapp: {}

ちなみに既存のHelm ChartをCarvel Package化したいだけであれば、imgpkg bundleを作る必要はなく、次のYAMLのようにfetchhelmChartを指定すれば良いです。air-gapped環境にインストールしたい場合は、imgpkg bundle化する

apiVersion: data.packaging.carvel.dev/v1alpha1
kind: Package
metadata:
  name: nginx.chart.pkg.maki.lol.9.5.7
  annotations:
    kapp.k14s.io/change-group: pkg
spec:
  refName: nginx.chart.pkg.maki.lol
  version: 9.5.7
  releaseNotes: |
    Supprt nginx bitnami helm chart 9.5.7
  template:
    spec:
      fetch:
      - helmChart:
          name: nginx
          version: 9.5.7
          repository:
            url: https://charts.bitnami.com/bitnami
      template:
      - helmTemplate: {}
      - kbld:
          paths:
          - "-"
          - ".imgpkg/images.yml"
      deploy:
      - kapp: {}

pkg-metadata.yamlpkg.yamlをapplyします。

kubectl apply -f pkg-metadata.yaml -f pkg.yaml

PackageとPackageMetadata一覧にnginx.chart.pkg.maki.lolが現れることを確認します。

$ kubectl get package        
NAME                                                 PACKAGEMETADATA NAME            VERSION                 AGE
cert-manager.tanzu.vmware.com.1.1.0+vmware.1-tkg.2   cert-manager.tanzu.vmware.com   1.1.0+vmware.1-tkg.2    79h34m56s
contour.tanzu.vmware.com.1.17.1+vmware.1-tkg.1       contour.tanzu.vmware.com        1.17.1+vmware.1-tkg.1   79h34m56s
external-dns.tanzu.vmware.com.0.8.0+vmware.1-tkg.1   external-dns.tanzu.vmware.com   0.8.0+vmware.1-tkg.1    79h34m56s
fluent-bit.tanzu.vmware.com.1.7.5+vmware.1-tkg.1     fluent-bit.tanzu.vmware.com     1.7.5+vmware.1-tkg.1    79h34m56s
grafana.tanzu.vmware.com.7.5.7+vmware.1-tkg.1        grafana.tanzu.vmware.com        7.5.7+vmware.1-tkg.1    79h34m56s
harbor.tanzu.vmware.com.2.2.3+vmware.1-tkg.1         harbor.tanzu.vmware.com         2.2.3+vmware.1-tkg.1    79h34m56s
multus-cni.tanzu.vmware.com.3.7.1+vmware.1-tkg.1     multus-cni.tanzu.vmware.com     3.7.1+vmware.1-tkg.1    79h34m56s
prometheus.tanzu.vmware.com.2.27.0+vmware.1-tkg.1    prometheus.tanzu.vmware.com     2.27.0+vmware.1-tkg.1   79h34m55s
nginx.chart.pkg.maki.lol.9.5.7                       nginx.chart.pkg.maki.lol        9.5.7                   1m6s

$ kubectl get packagemetadata
NAME                            DISPLAY NAME   CATEGORIES                 SHORT DESCRIPTION                                    AGE
cert-manager.tanzu.vmware.com   cert-manager   certificate management     Certificate management                               79h34m58s
contour.tanzu.vmware.com        Contour        ingress                    An ingress controller                                79h34m58s
external-dns.tanzu.vmware.com   external-dns   dns                        This package provides DNS synchronization funct...   79h34m58s
fluent-bit.tanzu.vmware.com     fluent-bit     logging,observability      Fluent Bit is a fast Log Processor and Forwarder     79h34m58s
grafana.tanzu.vmware.com        grafana        monitoring,observability   Visualization and analytics software                 79h34m58s
harbor.tanzu.vmware.com         Harbor         OCI registry               OCI Registry                                         79h34m57s
multus-cni.tanzu.vmware.com     multus-cni     networking                 This package provides the ability for enabling ...   79h34m57s
prometheus.tanzu.vmware.com     prometheus     monitoring,observability   A time series database for your metrics              79h34m58s
nginx.chart.pkg.maki.lol        Nginx          nginx,bitnami,helm         Nginx Bitnami Helm Chart                             1m9s

【補足情報】
PackageもPackageMetadataもNamespacedなリソースです。上記の例ではnginxはdefault namespaceにインストールされました。 しかし、一覧表示にはtanzu-package-repo-globalにインストールされたPackageとPackageMetadataも出力されています。
kapp controllerの-packaging-global-namespaceオプションに指定されたnamespaceは他のnamespaceからもアクセスできるようになります。
TKGのkapp-controllerに-packaging-global-namespace=tanzu-package-repo-globalオプションが指定されているため、このnamespaceにインストールされたPackageやPackageMetadataは他のnamespaceからも見えるようになっています。
詳細は ドキュメント に記載されています。

PackageInstall CRの作成

インストールしたPackage CRはPackageInstall CRを作成することで App CR が作成され、Deploymentなどの実際のリソースが作成されます。 PackageInstallの作り方は、YAMLをkubectlで作成する方法とtanzu CLIを使用する方法があります。

kubectlでPackageInstall CRを作成

まずはYAMLでPackageInstall CRを作成します。インストールするPackageの指定と、Packageに渡すパラメータ(ここではhelm templateに渡すvalues.yamlそのもの)をSecret経由で指定します。

pkg-install.yamlに次の内容を記述します。

apiVersion: packaging.carvel.dev/v1alpha1
kind: PackageInstall
metadata:
  name: nginx
  namespace: default
spec:
  serviceAccountName: default-ns-sa
  packageRef:
    refName: nginx.chart.pkg.maki.lol
    versionSelection:
      constraints: 9.5.7
  values:
  - secretRef:
      name: nginx-values
---
apiVersion: v1
kind: Secret
metadata:
  name: nginx-values
  namespace: default
stringData:
  data.yml: |
    serverBlock: |-
      server {
        listen 0.0.0.0:8080;
        location / {
          return 200 "hello from kapp-controller!";
        }
      }

ここで指定したsecretRefはPackage CRのtemplateの一つ目(ここではhelmTemplate)に渡されます。

以下のコマンドでPackageInstall CRを作成します。その前にPackageInstall CRが使用するService AccountとそのRBACを作成します。

kubectl apply -f https://github.com/k14s/cncf-packaging-webinar/raw/develop/3-app-cr-config-map/rbac/default-ns.yml
kubectl apply -f pkg-install.yaml

nginxのPackage Install CRとそれによって作成されるApp CRを確認します。

$ kubectl get packageinstall,app                  
NAME                                        PACKAGE NAME               PACKAGE VERSION   DESCRIPTION           AGE
packageinstall.packaging.carvel.dev/nginx   nginx.chart.pkg.maki.lol   9.5.7             Reconcile succeeded   71s

NAME                         DESCRIPTION           SINCE-DEPLOY   AGE
app.kappctrl.k14s.io/nginx   Reconcile succeeded   55s            71s

実際にどのようなリソースが作成されたかは次のようにApp CRのstatusで確認できます。

$ kubectl get app nginx -oyaml
apiVersion: kappctrl.k14s.io/v1alpha1
kind: App
metadata:
  creationTimestamp: "2021-12-14T11:39:07Z"
  finalizers:
  - finalizers.kapp-ctrl.k14s.io/delete
  generation: 1
  name: nginx
  namespace: default
  ownerReferences:
  - apiVersion: packaging.carvel.dev/v1alpha1
    blockOwnerDeletion: true
    controller: true
    kind: PackageInstall
    name: nginx
    uid: 2aa16ab1-0637-4584-b952-9c2f8d16cf78
  resourceVersion: "970989"
  uid: f35ddafe-858a-415c-bc74-bffa42a5799f
spec:
  deploy:
  - kapp: {}
  fetch:
  - imgpkgBundle:
      image: harbor-10-213-232-22.sslip.io/library/nginx-chart-bundle:9.5.7
  serviceAccountName: default-ns-sa
  template:
  - helmTemplate:
      valuesFrom:
      - secretRef:
          name: nginx-values
  - kbld:
      paths:
      - '-'
      - .imgpkg/images.yml
status:
  conditions:
  - status: "True"
    type: ReconcileSucceeded
  consecutiveReconcileSuccesses: 3
  deploy:
    exitCode: 0
    finished: true
    startedAt: "2021-12-14T11:40:30Z"
    stdout: |-
      Target cluster 'https://100.64.0.1:443'
      11:40:30AM: info: Resources: Ignoring group version: schema.GroupVersionResource{Group:"stats.antrea.tanzu.vmware.com", Version:"v1alpha1", Resource:"networkpolicystats"}
      11:40:30AM: info: Resources: Ignoring group version: schema.GroupVersionResource{Group:"stats.antrea.tanzu.vmware.com", Version:"v1alpha1", Resource:"antreanetworkpolicystats"}
      Changes
      Namespace  Name  Kind  Conds.  Age  Op  Op st.  Wait to  Rs  Ri
      Op:      0 create, 0 delete, 0 update, 0 noop
      Wait to: 0 reconcile, 0 delete, 0 noop
      Succeeded
    updatedAt: "2021-12-14T11:40:31Z"
  fetch:
    exitCode: 0
    startedAt: "2021-12-14T11:40:28Z"
    stdout: |
      apiVersion: vendir.k14s.io/v1alpha1
      directories:
      - contents:
        - imgpkgBundle:
            image: harbor-10-213-232-22.sslip.io/library/nginx-chart-bundle@sha256:d9ef393cf39246e623e833581f7fb9dbf8547b610152da958d80221a23899a84
          path: .
        path: "0"
      kind: LockConfig
    updatedAt: "2021-12-14T11:40:29Z"
  friendlyDescription: Reconcile succeeded
  inspect:
    exitCode: 0
    stdout: |-
      Target cluster 'https://100.64.0.1:443'
      11:40:31AM: info: Resources: Ignoring group version: schema.GroupVersionResource{Group:"stats.antrea.tanzu.vmware.com", Version:"v1alpha1", Resource:"antreanetworkpolicystats"}
      11:40:31AM: info: Resources: Ignoring group version: schema.GroupVersionResource{Group:"stats.antrea.tanzu.vmware.com", Version:"v1alpha1", Resource:"networkpolicystats"}
      Resources in app 'nginx-ctrl'
      Namespace  Name                    Kind           Owner    Conds.  Rs  Ri  Age
      default    nginx                   Deployment     kapp     2/2 t   ok  -   1m
      ^          nginx                   Endpoints      cluster  -       ok  -   1m
      ^          nginx                   Service        kapp     -       ok  -   1m
      ^          nginx-6d58d959c7        ReplicaSet     cluster  -       ok  -   1m
      ^          nginx-6d58d959c7-sbs8f  Pod            cluster  4/4 t   ok  -   1m
      ^          nginx-ltxf4             EndpointSlice  cluster  -       ok  -   1m
      ^          nginx-server-block      ConfigMap      kapp     -       ok  -   1m
      Rs: Reconcile state
      Ri: Reconcile information
      7 resources
      Succeeded
    updatedAt: "2021-12-14T11:40:31Z"
  observedGeneration: 1
  template:
    exitCode: 0
    stderr: |
      resolve | final: docker.io/bitnami/nginx:1.21.3-debian-10-r29 -> harbor-10-213-232-22.sslip.io/library/nginx-chart-bundle@sha256:f21acb4cd83d4b55930d8e290ebd3064fb0e019b0f06cd10ebc297cd67901747
    updatedAt: "2021-12-14T11:40:30Z"

helmTemplatesecretRefが設定されていることも確認できました。

デプロイされたNginxにアクセスし、data.yamlで設定した内容が反映されていることを確認します。

$ curl $(kubectl get svc nginx -o jsonpath='{.status.loadBalancer.ingress[0].ip}')   
hello from kapp-controller!

確認できたら、PackageInstallとRBACの設定を削除します。

kubectl delete -f pkg-install.yaml
kubectl delete -f https://github.com/k14s/cncf-packaging-webinar/raw/develop/3-app-cr-config-map/rbac/default-ns.yml

Tanzu CLIでPackageInstall CRを作成

PackageInstall CRはtanzu CLIを使うことでも作成できます。この場合PackageInstall CRのYAMLは不要です。 また、RBACの設定も自動で行われます。

次のコマンドで対象のnamespaceにインストール可能なPackage一覧を取得します、

$ tanzu package available list
\ Retrieving available packages... 
  NAME                           DISPLAY-NAME  SHORT-DESCRIPTION                                                                                           LATEST-VERSION         
  cert-manager.tanzu.vmware.com  cert-manager  Certificate management                                                                                      1.1.0+vmware.1-tkg.2   
  contour.tanzu.vmware.com       Contour       An ingress controller                                                                                       1.17.1+vmware.1-tkg.1  
  external-dns.tanzu.vmware.com  external-dns  This package provides DNS synchronization functionality.                                                    0.8.0+vmware.1-tkg.1   
  fluent-bit.tanzu.vmware.com    fluent-bit    Fluent Bit is a fast Log Processor and Forwarder                                                            1.7.5+vmware.1-tkg.1   
  grafana.tanzu.vmware.com       grafana       Visualization and analytics software                                                                        7.5.7+vmware.1-tkg.1   
  harbor.tanzu.vmware.com        Harbor        OCI Registry                                                                                                2.2.3+vmware.1-tkg.1   
  multus-cni.tanzu.vmware.com    multus-cni    This package provides the ability for enabling attaching multiple network interfaces to pods in Kubernetes  3.7.1+vmware.1-tkg.1   
  prometheus.tanzu.vmware.com    prometheus    A time series database for your metrics                                                                     2.27.0+vmware.1-tkg.1  
  nginx.chart.pkg.maki.lol       Nginx         Nginx Bitnami Helm Chart                                                                                    9.5.7  

Packageに渡すパラメータ(ここではhelm templateに渡すvalues.yamlそのもの)のみYAMLで次のように作成します。

cat <<EOF > data.yml 
serverBlock: |-
  server {
    listen 0.0.0.0:8080;
    location / {
      return 200 "hello from kapp-controller!";
    }
  }
EOF  

次のコマンドでPackageをインストールします。

tanzu package install nginx -p nginx.chart.pkg.maki.lol -v 9.5.7 -f data.yml -n default

次のようなログが出力され、RBACの設定やPackageInstall CRが作成されていることがわかります。

| Installing package 'nginx.chart.pkg.maki.lol' 
| Getting package metadata for 'nginx.chart.pkg.maki.lol' 
/ Creating service account 'nginx-default-sa' 
/ Creating cluster admin role 'nginx-default-cluster-role' 
/ Creating cluster role binding 'nginx-default-cluster-rolebinding' 
/ Creating secret 'nginx-default-values' 
/ Creating package resource 
\ Waiting for 'PackageInstall' reconciliation for 'nginx' 
| 'PackageInstall' resource install status: Reconciling 


 Added installed package 'nginx'

tanzu CLIでインストールされたPackage一覧を確認します。

$ tanzu package installed list
/ Retrieving installed packages... 
  NAME   PACKAGE-NAME              PACKAGE-VERSION  STATUS               
  nginx  nginx.chart.pkg.maki.lol  9.5.7            Reconcile succeeded  

kubectlでPackageInstall CRとApp CRを確認します。

$ kubectl get packageinstall,app     
NAME                                        PACKAGE NAME               PACKAGE VERSION   DESCRIPTION           AGE
packageinstall.packaging.carvel.dev/nginx   nginx.chart.pkg.maki.lol   9.5.7             Reconcile succeeded   3m53s

NAME                         DESCRIPTION           SINCE-DEPLOY   AGE
app.kappctrl.k14s.io/nginx   Reconcile succeeded   13s            3m53s

実際にどのようなリソースが作成されたかは次のようにApp CRのstatusで確認できます。

$ kubectl get app nginx -oyaml
apiVersion: kappctrl.k14s.io/v1alpha1
kind: App
metadata:
  creationTimestamp: "2021-12-14T11:47:28Z"
  finalizers:
  - finalizers.kapp-ctrl.k14s.io/delete
  generation: 1
  name: nginx
  namespace: default
  ownerReferences:
  - apiVersion: packaging.carvel.dev/v1alpha1
    blockOwnerDeletion: true
    controller: true
    kind: PackageInstall
    name: nginx
    uid: 0aa10a91-7134-4564-85a3-243d2456f51b
  resourceVersion: "973411"
  uid: d776a09d-0acf-4c0e-a0a0-0dffd49ae719
spec:
  deploy:
  - kapp: {}
  fetch:
  - imgpkgBundle:
      image: harbor-10-213-232-22.sslip.io/library/nginx-chart-bundle:9.5.7
  serviceAccountName: nginx-default-sa
  template:
  - helmTemplate:
      valuesFrom:
      - secretRef:
          name: nginx-default-values
  - kbld:
      paths:
      - '-'
      - .imgpkg/images.yml
status:
  conditions:
  - status: "True"
    type: ReconcileSucceeded
  consecutiveReconcileSuccesses: 8
  deploy:
    exitCode: 0
    finished: true
    startedAt: "2021-12-14T11:51:42Z"
    stdout: |-
      Target cluster 'https://100.64.0.1:443' (nodes: cheetah-control-plane-xtwcj, 1+)
      11:51:43AM: info: Resources: Ignoring group version: schema.GroupVersionResource{Group:"stats.antrea.tanzu.vmware.com", Version:"v1alpha1", Resource:"antreaclusternetworkpolicystats"}
      11:51:43AM: info: Resources: Ignoring group version: schema.GroupVersionResource{Group:"stats.antrea.tanzu.vmware.com", Version:"v1alpha1", Resource:"antreanetworkpolicystats"}
      11:51:43AM: info: Resources: Ignoring group version: schema.GroupVersionResource{Group:"stats.antrea.tanzu.vmware.com", Version:"v1alpha1", Resource:"networkpolicystats"}
      Changes
      Namespace  Name  Kind  Conds.  Age  Op  Op st.  Wait to  Rs  Ri
      Op:      0 create, 0 delete, 0 update, 0 noop
      Wait to: 0 reconcile, 0 delete, 0 noop
      Succeeded
    updatedAt: "2021-12-14T11:51:43Z"
  fetch:
    exitCode: 0
    startedAt: "2021-12-14T11:51:41Z"
    stdout: |
      apiVersion: vendir.k14s.io/v1alpha1
      directories:
      - contents:
        - imgpkgBundle:
            image: harbor-10-213-232-22.sslip.io/library/nginx-chart-bundle@sha256:d9ef393cf39246e623e833581f7fb9dbf8547b610152da958d80221a23899a84
          path: .
        path: "0"
      kind: LockConfig
    updatedAt: "2021-12-14T11:51:42Z"
  friendlyDescription: Reconcile succeeded
  inspect:
    exitCode: 0
    stdout: |-
      Target cluster 'https://100.64.0.1:443' (nodes: cheetah-control-plane-xtwcj, 1+)
      11:51:43AM: info: Resources: Ignoring group version: schema.GroupVersionResource{Group:"stats.antrea.tanzu.vmware.com", Version:"v1alpha1", Resource:"antreaclusternetworkpolicystats"}
      11:51:43AM: info: Resources: Ignoring group version: schema.GroupVersionResource{Group:"stats.antrea.tanzu.vmware.com", Version:"v1alpha1", Resource:"networkpolicystats"}
      11:51:43AM: info: Resources: Ignoring group version: schema.GroupVersionResource{Group:"stats.antrea.tanzu.vmware.com", Version:"v1alpha1", Resource:"antreanetworkpolicystats"}
      Resources in app 'nginx-ctrl'
      Namespace  Name                   Kind           Owner    Conds.  Rs  Ri  Age
      default    nginx                  Deployment     kapp     2/2 t   ok  -   4m
      ^          nginx                  Endpoints      cluster  -       ok  -   4m
      ^          nginx                  Service        kapp     -       ok  -   4m
      ^          nginx-59b74db6b        ReplicaSet     cluster  -       ok  -   4m
      ^          nginx-59b74db6b-wsh68  Pod            cluster  4/4 t   ok  -   4m
      ^          nginx-j9wh8            EndpointSlice  cluster  -       ok  -   4m
      ^          nginx-server-block     ConfigMap      kapp     -       ok  -   4m
      Rs: Reconcile state
      Ri: Reconcile information
      7 resources
      Succeeded
    updatedAt: "2021-12-14T11:51:43Z"
  observedGeneration: 1
  template:
    exitCode: 0
    stderr: |
      resolve | final: docker.io/bitnami/nginx:1.21.3-debian-10-r29 -> harbor-10-213-232-22.sslip.io/library/nginx-chart-bundle@sha256:f21acb4cd83d4b55930d8e290ebd3064fb0e019b0f06cd10ebc297cd67901747
    updatedAt: "2021-12-14T11:51:42Z"

デプロイされたNginxにアクセスし、data.yamlで設定した内容が反映されていることを確認します。

$ curl $(kubectl get svc nginx -o jsonpath='{.status.loadBalancer.ingress[0].ip}')   
hello from kapp-controller!

確認できたら、PackageInstallとRBACの設定をtanzu CLIで削除します。

tanzu package installed delete nginx -y

imgpkg bundle化したHelmChartをCarvel Package化しました。 このPackageはair-gapped環境でもインストール可能です。