Creating a Custom Tanzu Package and install it on a Tanzu Kubernetes Cluster using tanzu CLI

In this post, i will help you to create a tanzu package from scratch, then add that package in a repository and finally install the package on a Tanzu Community Edition Standalone cluster using tanzu cli. I was really interested to understand this in detail because there was change in TKG v1.4 where all the extensions are not packaged as a package and installed using tanzu cli. Let’s get started.

Few Checks

NOTE:

If you are looking for a steps to create Tanzu Community Edition Standalone cluster on your laptop, Refer my earlier post: https://mappslearning.wordpress.com/2021/10/06/setting-up-tanzu-community-edition-tce-on-a-mac/

If you are looking to understand more about ytt, I think this is a very good tutorial https://ik.am/entries/544/en

If you are looking for a guided web based lab, you can refer https://katacoda.com/carvel/scenarios/kapp-controller-package-management

Install Carvel tools, You can run wget -O- https://raw.githubusercontent.com/vmware-tanzu/carvel-kapp-controller/fc5458fe2102d67e85116c26534a35e265b28125/hack/install-deps.sh | bash

Once you have created a cluster, you will have following in place.

  • Check the kapp controller pod status
$ k get po -n tkg-system
NAME                                                     READY   STATUS    RESTARTS   AGE
kapp-controller-6499b8866-qdx4n                          1/1     Running   0          15h
tanzu-capabilities-controller-manager-6ff97656b8-v2dpg   1/1     Running   0          15h
  • Check the created CRD’s

$ kubectl api-resources --api-group packaging.carvel.dev
NAME                  SHORTNAMES   APIGROUP               NAMESPACED   KIND
packageinstalls       pkgi         packaging.carvel.dev   true         PackageInstall
packagerepositories   pkgr         packaging.carvel.dev   true         PackageRepository

$ kubectl api-resources --api-group data.packaging.carvel.dev
NAME               SHORTNAMES   APIGROUP                    NAMESPACED   KIND
packagemetadatas   pkgm         data.packaging.carvel.dev   true         PackageMetadata
packages           pkg          data.packaging.carvel.dev   true         Package

$ kubectl api-resources --api-group kappctrl.k14s.io
NAME   SHORTNAMES   APIGROUP           NAMESPACED   KIND
apps                kappctrl.k14s.io   true         App

Package creation process

Creating a package is two step process:

  1. Creating a Package Content bundle
  2. Creating a Package Repository bundle

1. Creating a Package Content bundle

A package bundle is an imgpkg bundle that holds package contents such as Kubernetes YAML configuration, ytt templates etc.

  • Create a kubernetes resource yaml file for POD and Service
$ cat > config.yml << EOF
#@ load("@ytt:data", "data")

#@ def labels():
simple-app: ""
#@ end

---
apiVersion: v1
kind: Service
metadata:
  namespace: default
  name: simple-app
spec:
  ports:
  - port: #@ data.values.svc_port
    targetPort: #@ data.values.app_port
  selector: #@ labels()
---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: default
  name: simple-app
spec:
  selector:
    matchLabels: #@ labels()
  template:
    metadata:
      labels: #@ labels()
    spec:
      containers:
      - name: simple-app
        image: docker.io/dkalinin/k8s-simple-app@sha256:4c8b96d4fffdfae29258d94a22ae4ad1fe36139d47288b8960d9958d1e63a9d0
        env:
        - name: HELLO_MSG
          value: #@ data.values.hello_msg
EOF
  • View the created file
$ ls
carvel-kapp config.yml
  • Create a yaml file that will have variables and values
$ cat > values.yml <<- EOF
#@data/values
---
svc_port: 80
app_port: 80
hello_msg: stranger
EOF
  • View the Created files
$ l
total 16
drwxr-xr-x    4 dinetrip  staff   128B Oct 31 14:21 .
drwxr-xr-x+ 229 dinetrip  staff   7.2K Oct 31 14:22 ..
-rw-r--r--    1 dinetrip  staff   711B Oct 31 14:21 config.yml
-rw-r--r--    1 dinetrip  staff    64B Oct 31 14:21 values.yml
  • Create a required directory structure and copy the files
$ mkdir -p package-contents/config/
$ cp config.yml package-contents/config/config.yml
$ cp values.yml package-contents/config/values.yml

$ ls -l package-contents/config                                                     
total 16
-rw-r--r--  1 dinetrip  staff  711 Oct 31 14:22 config.yml
-rw-r--r--  1 dinetrip  staff   64 Oct 31 14:22 values.yml
  • Locate the used images and update the images.yml file
$ kbld -f package-contents/config/ --imgpkg-lock-output package-contents/.imgpkg/images.yml

resolve | final: docker.io/dkalinin/k8s-simple-app@sha256:4c8b96d4fffdfae29258d94a22ae4ad1fe36139d47288b8960d9958d1e63a9d0 -> index.docker.io/dkalinin/k8s-simple-app@sha256:4c8b96d4fffdfae29258d94a22ae4ad1fe36139d47288b8960d9958d1e63a9d0
---
simple-app: ""
---
apiVersion: v1
kind: Service
metadata:
  name: simple-app
  namespace: default
spec:
  ports:
  - port: null
    targetPort: null
  selector: null
---
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    kbld.k14s.io/images: |
      null
  name: simple-app
  namespace: default
spec:
  selector:
    matchLabels: null
  template:
    metadata:
      labels: null
    spec:
      containers:
      - env:
        - name: HELLO_MSG
          value: null
        image: index.docker.io/dkalinin/k8s-simple-app@sha256:4c8b96d4fffdfae29258d94a22ae4ad1fe36139d47288b8960d9958d1e63a9d0
        name: simple-app
---
app_port: 80
hello_msg: stranger
svc_port: 80

Succeeded
  • View the content of image.yml file
$ cat  package-contents/.imgpkg/images.yml
---
apiVersion: imgpkg.carvel.dev/v1alpha1
images:
- annotations:
    kbld.carvel.dev/id: docker.io/dkalinin/k8s-simple-app@sha256:4c8b96d4fffdfae29258d94a22ae4ad1fe36139d47288b8960d9958d1e63a9d0
  image: index.docker.io/dkalinin/k8s-simple-app@sha256:4c8b96d4fffdfae29258d94a22ae4ad1fe36139d47288b8960d9958d1e63a9d0
kind: ImagesLock
  • Once these files have been added, our package contents bundle is ready to be pushed!

  • Push the content bundle to a nimage registry

$ imgpkg push -b dineshtripathi30/simple-app:1.0.0 -f package-contents/ 
dir: .
dir: .imgpkg
file: .imgpkg/images.yml
dir: config
file: config/config.yml
file: config/values.yml
Pushed 'index.docker.io/dineshtripathi30/simple-app@sha256:8b199051061fcfef126446e82ddcf024c3c014b150a7fd916b62fe246eb3798b'
Succeeded
  • Now, The package content bundle is created and pushed to an image registry.

2. Creating a Package Repository bundle

A package repository bundle is an imgpkg bundle that holds PackageMetadata and Package CRs.

  • Create a metadata.yml file
$ cat > metadata.yml << EOF
apiVersion: data.packaging.carvel.dev/v1alpha1
kind: PackageMetadata
metadata:
  # This will be the name of our package
  name: simple-app.corp.com
spec:
  displayName: "Simple App"
  longDescription: "Simple app consisting of a k8s deployment and service"
  shortDescription: "Simple app for demoing"
  categories:
  - demo
EOF
  • Create a package CR
cat 1.0.0.yml       
---
apiVersion: data.packaging.carvel.dev/v1alpha1
kind: Package
metadata:
  name: simple-app.corp.com.1.0.0
spec:
  refName: simple-app.corp.com
  version: 1.0.0
  releaseNotes: |
        Initial release of the simple app package
  valuesSchema:
    openAPIv3:
      title: simple-app.corp.com values schema
      examples:
      - svc_port: 80
        app_port: 80
        hello_msg: stranger
      properties:
        svc_port:
          type: integer
          description: Port number for the service.
          default: 80
          examples:
          - 80
        app_port:
          type: integer
          description: Target port for the application.
          default: 80
          examples:
          - 80
        hello_msg:
          type: string
          description: Name used in hello message from app when app is pinged.
          default: stranger
          examples:
          - stranger
  template:
    spec:
      fetch:
      - imgpkgBundle:
          image: dineshtripathi30/simple-app:1.0.0
      template:
      - ytt:
          paths:
          - "config/"
      - kbld:
          paths:
          - "-"
          - ".imgpkg/images.yml"
      deploy:
      - kapp: {}
  • Create the required package folder structure and copy the files
$ mkdir -p my-pkg-repo/.imgpkg my-pkg-repo/packages/simple-app.corp.com

$ cp 1.0.0.yml my-pkg-repo/packages/simple-app.corp.com
$ cp metadata.yml my-pkg-repo/packages/simple-app.corp.com
  • Relocate the image and update image.yml file

$ kbld -f my-pkg-repo/packages/ --imgpkg-lock-output my-pkg-repo/.imgpkg/images.yml

resolve | final: dineshtripathi30/simple-app:1.0.0 -> index.docker.io/dineshtripathi30/simple-app@sha256:8b199051061fcfef126446e82ddcf024c3c014b150a7fd916b62fe246eb3798b
---
apiVersion: data.packaging.carvel.dev/v1alpha1
kind: Package
metadata:
  annotations:
    kbld.k14s.io/images: |
      - Metas:
        - Tag: 1.0.0
          Type: resolved
          URL: dineshtripathi30/simple-app:1.0.0
        URL: index.docker.io/dineshtripathi30/simple-app@sha256:8b199051061fcfef126446e82ddcf024c3c014b150a7fd916b62fe246eb3798b
  name: simple-app.corp.com.1.0.0
spec:
  refName: simple-app.corp.com
  releaseNotes: |
    Initial release of the simple app package
  template:
    spec:
      deploy:
      - kapp: {}
      fetch:
      - imgpkgBundle:
          image: index.docker.io/dineshtripathi30/simple-app@sha256:8b199051061fcfef126446e82ddcf024c3c014b150a7fd916b62fe246eb3798b
      template:
      - ytt:
          paths:
          - config/
      - kbld:
          paths:
          - '-'
          - .imgpkg/images.yml
  valuesSchema:
    openAPIv3:
      examples:
      - app_port: 80
        hello_msg: stranger
        svc_port: 80
      properties:
        app_port:
          default: 80
          description: Target port for the application.
          examples:
          - 80
          type: integer
        hello_msg:
          default: stranger
          description: Name used in hello message from app when app is pinged.
          examples:
          - stranger
          type: string
        svc_port:
          default: 80
          description: Port number for the service.
          examples:
          - 80
          type: integer
      title: simple-app.corp.com values schema
  version: 1.0.0
---
apiVersion: data.packaging.carvel.dev/v1alpha1
kind: PackageMetadata
metadata:
  name: simple-app.corp.com
spec:
  categories:
  - demo
  displayName: Simple App
  longDescription: Simple app consisting of a k8s deployment and service
  shortDescription: Simple app for demoing

Succeeded
  • Push the bundle in a container registry
$ imgpkg push -b ${REPO_HOST}/my-pkg-repo:1.0.0 -f my-pkg-repo 

dir: .
dir: .imgpkg
file: .imgpkg/images.yml
dir: packages
dir: packages/simple-app.corp.com
file: packages/simple-app.corp.com/1.0.0.yml
file: packages/simple-app.corp.com/metadata.yml
Pushed 'index.docker.io/dineshtripathi30/my-pkg-repo@sha256:f876115b629c87b6735070f6f747631fb727d127c3fe2f3fcfd9d063919186ba'
Succeeded

  • Now, We have created both artifacts and ready to install the package on a tanzu standalone cluster

Installing the newly created package

Add the package repository


$ tanzu package repository add simple-package-repository --url dineshtripathi30/my-pkg-repo:1.0.0 --namespace default
/ Adding package repository 'simple-package-repository'... 
 Added package repository 'simple-package-repository'

Install the package


$ tanzu package install simple-app --package-name simple-app.corp.com --version 1.0.0
/ Installing package 'simple-app.corp.com' 
| Getting namespace 'default' 
/ Getting package metadata for 'simple-app.corp.com' 
| Creating service account 'simple-app-default-sa' 
| Creating cluster admin role 'simple-app-default-cluster-role' 
| Creating cluster role binding 'simple-app-default-cluster-rolebinding' 
- Creating package resource 
- Package install status: Reconciling 


Added installed package 'simple-app' in namespace 'default'

Validating the created resources

$ k get po,svc                                         
NAME                             READY   STATUS    RESTARTS   AGE
pod/simple-app-cc8f98744-dfm4c   1/1     Running   0          9s

NAME                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   100.64.0.1     <none>        443/TCP   21h
service/simple-app   ClusterIP   100.66.92.86   <none>        80/TCP    9s

  • Port forward the service to access it
$ k port-forward service/simple-app 30000:80
Forwarding from 127.0.0.1:30000 -> 80
Forwarding from [::1]:30000 -> 80
Handling connection for 30000


$ curl localhost:30000
<h1>Hello stranger!</h1>%                                                                                                                                                                          

So, We have been able to install the newly created package. We can use same method and create any new package and install it.

2 thoughts on “Creating a Custom Tanzu Package and install it on a Tanzu Kubernetes Cluster using tanzu CLI

  1. Hey! This post could not be written any better! Reading through this post reminds me of my good old room mate! He always kept talking about this. I will forward this article to him. Pretty sure he will have a good read. Thank you for sharing!

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s