About Helm

Helm is the popular package manager for Kubernetes application deployment, not new to me as I had tried many charts previously with Kafka, and Redis helm charts installation, today I am going to explore how to build my own helm chart for this zack blog, also deep dive into the chart development for advanced templating, together with helm release management and version control, finally integrate my own chart with CI/CD for GitOps.  

Common Helm command

  • helm list -A   # list releases across all namespaces

  • helm pull bitnami/postgresql-ha –untar  # untar the chart after pull online chart

  • helm repo add bitnami https://charts.bitnami.com/bitnami  # add a repo

  • helm create zackblog-helm  # create a new chart

  • helm install zackblog-helm ~/zackblog-helm -n NAMESPACE -f dev-values.yaml # define ns and override with a new value file

  • helm upgrade zackblog-helm ~/zackblog-helm –set image.repository= --set image.tag= # --set to upgrade chart with override a new value

  • helm lint ~/zackblog-helm  # lint syntax

  • helm rollback zackblog-helm 2   # rollback to revision 2 of a release

  • helm uninstall zackblog-helm -n Production  # uninstall a chart from a ns

Start with own chart

  • create a new helm chart
[root@freeipa-server ~]# helm create zackblog-helm
Creating zackblog-helm

# modify values.yaml
[root@freeipa-server zackblog]# vim values.yaml

replicaCount: 3

image:
  repository: zackz001/gitops-jekyll
  pullPolicy: IfNotPresent
  # Overrides the image tag.
  tag: "latest"

service:
  type: NodePort
  port: 80
  • Lint chart syntacx before install
# lint syntax
[root@freeipa-server ~]# helm lint zackblog-helm/
==> Linting zackblog-helm/
[INFO] Chart.yaml: icon is recommended

1 chart(s) linted, 0 chart(s) failed

# install own chart

[root@freeipa-server ~]# helm install zackblog-helm zackblog-helm
NAME: zackblog-helm
LAST DEPLOYED: Mon May 13 21:27:14 2024
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
1. Get the application URL by running these commands:
  export NODE_PORT=$(kubectl get --namespace default -o jsonpath="{.spec.ports[0].nodePort}" services zackblog-helm)
  export NODE_IP=$(kubectl get nodes --namespace default -o jsonpath="{.items[0].status.addresses[0].address}")
  echo http://$NODE_IP:$NODE_PORT

[root@freeipa-server ~]# helm list -a
NAME            NAMESPACE   REVISION    UPDATED                                     STATUS      CHART               APP VERSION
argo-cd         default     1           2024-04-29 06:14:18.117158759 +0000 UTC     deployed    argo-cd-6.7.17      v2.10.8    
zackblog-helm   default     1           2024-05-13 21:27:14.825391301 +1000 AEST    deployed    zackblog-helm-0.1.0 1.16.0 

[root@freeipa-server ~]# kubectl get deployments.apps | grep zack
zackblog-helm                              3/3     3            3           113s
[root@freeipa-server ~]# kubectl get svc | grep zack
zackblog-helm                              NodePort    10.43.90.209    <none>        80:31070/TCP                 2m7s
  • Customize vaule.yaml by change replica and image tag
# modify vaule.yaml to scale down and change image to v138
[root@freeipa-server ~]# vim zackblog-helm/values.yaml
replicaCount: 1

image:
  repository: zackz001/gitops-jekyll
  pullPolicy: IfNotPresent
  # Overrides the image tag.
  tag: "v139"

[root@freeipa-server ~]# helm list -a
NAME            NAMESPACE   REVISION    UPDATED                                     STATUS      CHART               APP VERSION
argo-cd         default     1           2024-04-29 06:14:18.117158759 +0000 UTC     deployed    argo-cd-6.7.17      v2.10.8    
zackblog-helm   default     2           2024-05-13 21:31:16.523093364 +1000 AEST    deployed    zackblog-helm-0.1.0 1.16.0     
[root@freeipa-server ~]# kubectl get deployments.apps | grep zack
zackblog-helm                              1/1     1            1           4m31s
  • Override values.yaml by -f and deploy same chart to different environments
# create a dev ns then deploy and override with dev-values.yaml
[root@freeipa-server ~]# vim zackblog-helm/dev-values.yaml
image:
  repository: zackz001/gitops-jekyll
  tag: v140
replicaCount: 2
service:
  type: NodePort
  port: 80

[root@freeipa-server ~]# kubectl create ns dev
namespace/dev created
[root@freeipa-server ~]# helm install dev-zackblog-helm zackblog-helm -f dev-values.yaml -n dev
Error: INSTALLATION FAILED: open dev-values.yaml: no such file or directory
[root@freeipa-server ~]# helm install dev-zackblog-helm zackblog-helm -f zackblog-helm/dev-values.yaml -n dev
NAME: dev-zackblog-helm
LAST DEPLOYED: Mon May 13 21:36:39 2024
NAMESPACE: dev
STATUS: deployed
REVISION: 1
NOTES:
1. Get the application URL by running these commands:
  export NODE_PORT=$(kubectl get --namespace dev -o jsonpath="{.spec.ports[0].nodePort}" services dev-zackblog-helm)
  export NODE_IP=$(kubectl get nodes --namespace dev -o jsonpath="{.items[0].status.addresses[0].address}")
  echo http://$NODE_IP:$NODE_PORT

[root@freeipa-server ~]# kubectl get deployments.apps -n dev
NAME                READY   UP-TO-DATE   AVAILABLE   AGE
dev-zackblog-helm   2/2     2            2           96s
[root@freeipa-server ~]# kubectl get svc -n dev
NAME                TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
dev-zackblog-helm   NodePort   10.43.239.229   <none>        80:31391/TCP   103s
  • Advanced templating to add pvc into chart
# add templates/pvc.yaml
[root@freeipa-server ~]# vim zackblog-helm/templates/pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: longhron-pvc
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: longhorn
  resources:
    requests:
      storage: 1Gi

# add pvc in values.yaml
[root@freeipa-server ~]# vim zackblog-helm/values.yaml
pvc:
  enabled: true
  templateFiles:
    - pvc.yaml

# add persistentVolumeClaim in templates/deployment.yaml
[root@freeipa-server ~]# vim zackblog-helm/templates/deployment.yaml
...
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: longhron-pvc
...
[root@freeipa-server ~]# helm upgrade zackblog-helm zackblog-helm
Release "zackblog-helm" has been upgraded. Happy Helming!

CICD integration to deploy chart with ArgoCD

Now commit and upload “zackblog-helm” folder into github repo, create ArgoCD application manifest to sync with from path of own helm chart

# argoCD application manifest
project: default
source:
  repoURL: 'https://github.com/ZackZhouHB/zack-gitops-project.git'
  path: argo-helm-zackblog
  targetRevision: editing
  helm:
    valueFiles:
      - values.yaml
destination:
  server: 'https://kubernetes.default.svc'
  namespace: helm
syncPolicy:
  automated: {}
  syncOptions:
    - CreateNamespace=true

image tooltip here

Conclusion

Finally, I had a chance to go over helm, it makes package management easier and more convenient, through charts, k8s deployment can be more flexible with values and templates that can be deployed and reusable into different environments, it provides versioning and rollbacks, also allow customization of the template. however using on-line chart can also be risky in a production environment with quality, dependency and security risks.

Overall I think helm chart is a very good way to start deployment into k8s.