📋 JayJay's DevOps Diaries ..

Chronicling my journey through Cloud Native Infrastructure.. one step and tool at a time...

May 2, 2026 - 12 minute read - Kubernetes DevOps Networking

☸️ For the HELM of it - PART 1

Intro

In a cheeky 🥴 bid to demonstrate a simple app deployment 🧐 utilizing Helm Charts, 🤔 I would be plagiarising a friends article on Helm Charts that actaully got me started, provided the ground work and basic understanding on them.. I really loved his take on it.

🤓 Getting the hang of ☸️ HELM CHARTs: whats cool 😎 and whats not🥴..


Ok, the said friend of mine wrote this banging article about deploying an app using Helm Charts templates. And I really loved it. It was simple, concise and to the very point.

Here’s a link to his famous article that I loved soo much

https://www.linkedin.com/pulse/helm-template-chart-kubernetes-components-john-ikeson-lxxoe/

Right now, I am inspired to take a leaf from this branch and pretty much do the same. I am going to do exactly what he did. But, a little different, with a little twist.

In this article, I am going to give a simple demonstration on how to deploy any app from a DockerHub Artifactory and have it running in a pod(s), attached to a service, aided by an ingress controller and an ingress, I would expose the app via web browser to the world.

The cool twist

Now the cool twist is, I would be deploying not just one but 2 apps and, exposing them simultaneously. Why keep it boring right ? when you can deploy multiple apps at once. A fun way to understand the potentials of ingress paths.

Now the fun part.


To achieve this cool feat, there are a few prerequisites we have to attain.

First : -

  • A running Kubernetes cluster (Minikube, Kind, EKS, GKE, AKS, etc.) By the way, see this article for my home stack.
  • kubectl configured and working
  • 2 versions of WebApps at random from DockerHub Artifactory. Like I said, I would be shooting 2 birds with one stone. I mean, deploying 2 apps with 1 Helm Chart. By the way, I dont need chatgpt to show I got a cool sense of humour.

Secondly : -

  • Would need amend the hosts file (/etc/hosts) on the MiniKube host. This would assist the resolution of the FQDN of the ingress

  • We would also need the MiniKube-IP. This is what the FQDN would resolve to. And they both of to reflect in the hosts file (/etc/hosts).

  • Since Helm is the topic of discusion, its best to familiarise ourselves with a version of Helm Charts CHEAT’s SHEET

Enough chit-chat, now to get your hands dirty.

At this point, I say its time to start issuing in the commands that would create and execute the Helm templates. So lets get the ball rolling. It would help to admit I would be plagairising steps created in my friends article, to speed up deployment

so here goes nothing.

Step 1 :


Install and ensure you got helm up and running . I predominantly use Linux btw. So …

# first, install helm
sudo snap install helm --classic

# Confirm installation
helm version

Step 2 :


Create a folder that would act a base directory to store all our apps, I think I would call it helm-templates This step is not necessary but it helps to keep our code tidy.

# Make directory :)
mkdir -p helm-templates

# Change directory
cd helm-templates

Step 3 :


In the spirit of keeping things neat and tidy. I am going to create a namespace (call it multi-app) to segregate my helm’s deployment from all previous deployments. Generally making debugging a lot easier.

# create namespace
 kubectl create namespace multi-app

actual screenshot

Step 4 :


Now create the actual template. I would call this project multina

# create helm template
 helm create multina -n multi-app

# Change directory
cd multina

# list files in multina directory
ll
# OR
ls -al

actual screenshot

From screenshots we can establish that we have gotten a directory called multina that contains 5 items in total. 2 directories (charts & templates), 2 yaml files (Chart.yaml & Values.yaml) and lastly 1 helmignore file.

Going forward is where the magic happens 👀

Step 5 :


Of all the files and directories in the multina parent directory, I would be start with the Values.yaml file.

SO.., BEHOLD !!! I present to you the contents of Values.yaml

please draw your attention to line 10 and 14 as we would be dealing with them in a moment as soon as I provide a clearer screenshots of the file, depicting the stipulated lines.

Values.yaml


# Default values for multina.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.

# This will set the replicaset count more information can be found here: https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/
replicaCount: 1

# This sets the container image more information can be found here: https://kubernetes.io/docs/concepts/containers/images/
image:
  repository: nginx
  # This sets the pull policy for images.
  pullPolicy: IfNotPresent
  # Overrides the image tag whose default is the chart appVersion.
  tag: ""

# This is for the secrets for pulling an image from a private repository more information can be found here: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/
imagePullSecrets: []
# This is to override the chart name.
nameOverride: ""
fullnameOverride: ""

# This section builds out the service account more information can be found here: https://kubernetes.io/docs/concepts/security/service-accounts/
serviceAccount:
  # Specifies whether a service account should be created
  create: true
  # Automatically mount a ServiceAccount's API credentials?
  automount: true
  # Annotations to add to the service account
  annotations: {}
  # The name of the service account to use.
  # If not set and create is true, a name is generated using the fullname template
  name: ""

# This is for setting Kubernetes Annotations to a Pod.
# For more information checkout: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/
podAnnotations: {}
# This is for setting Kubernetes Labels to a Pod.
# For more information checkout: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/
podLabels: {}

podSecurityContext: {}
  # fsGroup: 2000

securityContext: {}
  # capabilities:
  #   drop:
  #   - ALL
  # readOnlyRootFilesystem: true
  # runAsNonRoot: true
  # runAsUser: 1000

# This is for setting up a service more information can be found here: https://kubernetes.io/docs/concepts/services-networking/service/
service:
  # This sets the service type more information can be found here: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types
  type: ClusterIP
  # This sets the ports more information can be found here: https://kubernetes.io/docs/concepts/services-networking/service/#field-spec-ports
  port: 80

# This block is for setting up the ingress for more information can be found here: https://kubernetes.io/docs/concepts/services-networking/ingress/
ingress:
  enabled: false
  className: ""
  annotations: {}
    # kubernetes.io/ingress.class: nginx
    # kubernetes.io/tls-acme: "true"
  hosts:
    - host: chart-example.local
      paths:
        - path: /
          pathType: ImplementationSpecific
  tls: []
  #  - secretName: chart-example-tls
  #    hosts:
  #      - chart-example.local

resources: {}
  # We usually recommend not to specify default resources and to leave this as a conscious
  # choice for the user. This also increases chances charts run on environments with little
  # resources, such as Minikube. If you do want to specify resources, uncomment the following
  # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
  # limits:
  #   cpu: 100m
  #   memory: 128Mi
  # requests:
  #   cpu: 100m
  #   memory: 128Mi

# This is to setup the liveness and readiness probes more information can be found here: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/
livenessProbe:
  httpGet:
    path: /
    port: http
readinessProbe:
  httpGet:
    path: /
    port: http

# This section is for setting up autoscaling more information can be found here: https://kubernetes.io/docs/concepts/workloads/autoscaling/
autoscaling:
  enabled: false
  minReplicas: 1
  maxReplicas: 100
  targetCPUUtilizationPercentage: 80
  # targetMemoryUtilizationPercentage: 80

# Additional volumes on the output Deployment definition.
volumes: []
# - name: foo
#   secret:
#     secretName: mysecret
#     optional: false

# Additional volumeMounts on the output Deployment definition.
volumeMounts: []
# - name: foo
#   mountPath: "/etc/foo"
#   readOnly: true

nodeSelector: {}

tolerations: []

affinity: {}

As promised, do find below, a pretty cool screenshot around the Lines we would like to talk about. 10 and 14

Taking a closer look, while having a clearer view, you can derive that on Line 10, nginx has been stipulated as the default app to create should we run this template as it is. And Line 14 designated to habour its version tag. (yes, its empty, meaning when run, it would look for and pull the latest version of Ngnix)

However, we’ve already established that for most forseable demonstrations, we would be utilising the custom-made nginx image called WebApps ( by yours truly 😉 )

Cool Screenshot here.. gaze upon its beauty and then lets proceed to the application of WebApps

By Now, your anticipation, of the application WebApps would have been at its peak, so much so that you would have been inebriated by its documentation found here. therefore familiarising you with its proceedure.

From the documentation, you can tell its application is pretty straight forward. The container comes in two parts. The image path and the version tag. Both of which we need for our template. especially the version tag. Because, thats what we would be using to distiguish the apps apart.

On the repository, it kinda looks like this : burgxy/webapps:v3 with anything after the colon(:) as the version tag. However, Helm templates prefers it a bit different. you literally have to specifer which repository you’ll be pulling from. So in turn, it would look more like this : docker.io/burgxy/webapps and version tags v3 (for version 3.) they are many versions there 7 or 8. I lost count.

You know what? how about I just populate the Values.yaml file and present you another picasso of a screenshot. I guess thats why we are all on here. getting the show on the road.

So here goes nothing ..

Viola !!! Picasso.. love it !!

There you go!! we love it. Line 10 and 14 nicely populated as intended and its looking good.

Step 6 :


Its too, early to celebrate now, we still got a few editing to do. Somewhere along the begining, I did mention we would have to amend the systems hosts file (/etc/hosts) to accommodate the minikube IP so it resolves against a FQDN of our choosing, well I geuss that time is now. But first, lets go back to the Values.yaml file once more. By default it should have some sample FQDN like chart-example.local which we could leave it as it is, but I would rather change it to match the theme of the project.

Therefore, since we intend to run 2 apps in one Helm charts, its best we edit them both and be done with it.

I called the project maltina, so why dont I go with maltina.app1 and maltina.app2. This would be the names that when put into the browser, it the WebApps would be displayed.

But first another picasso of the affected lines ..

Aha!! Look at it ! Have you found what I am looking for? There it is at Line 67! The FQDN of the host listed under the ingress tab is set to chart-example.local. As promised we are going to set to the host name to maltina.app1 (dont worry about the second app for now, I just want to prove that one app works at this point. Not 2.

And since we are configuring the FQDN of the ingress, we might a well not forget to enable the ingress and mention the ingress class name as provisioned on line 61 and line 62 .

We would not be setting up auto-scaling at this point in time. But we can set the replica count to 3 so we could have 3 pods running the same WebApp for this first demonstration.

OK, I think we’re good. The template has been configured, lets see if we can test and deploy this template.

Step 7 :


So we’ve come to the part where we would start issuing in deployment commands. Where the actions actually takes place. so here goes nothing.

OH, before I forget, we would have to map the hostnames maltina.app1 and maltina.app2 to the IP address of the MiniKube host. And to retrieve the IP address, we query the minikube ip command.

from the screenshot we can acertain that 192.168.20.10 is the host ip for minikube. Therefore we are going to map that to the maltina.app1 and maltina.app2 hostnames respectively in the /etc/hosts file. And this is how we’ll do it.

by running this command (as root or try sudo)

echo "192.168.20.10    maltina.app1   maltina.app2" >> /etc/hosts

Now that we have setup and prepared /etc/hosts to resolve maltina.app1 and maltina.app2 as needs be, I am going to proceed with a dry run of the template installation command. Just to be sure we got all our configurations right.

helm install --dry-run --debug multina -n multi-app .

I ran the command and it was successful! The output is too long to screenshot all at once but I would attempt to grab the head and tail of the output, to give you an idea.

and the tail

Now lets run the actual deployment commands. We would be omitting the --dry-run and --debug flags in the previous command and deploy the template for real.

helm install multina -n multi-app .

The execution logs

In the ancient words of Apache Tomcat Webserver .. It Works :) !!!

Step 8 :


Ok its a success !! We got one of the apps working as promised. The FQDN http://maltina.app1/ was placed into a web browser, and we achieved a screenshot of the outcome which you can see above.

But just to tidy up in the kitchen before we leave, it would be nice to know what we have been cooking in the background. Therefore, lets achieve this by listing a few these things we’ve cooked along the way.

  • Running Pods
  • Describe Deployment
  • Describe Service
  • A clearer ScreenShot of the web browser
  • A clearer FQDN in a web browser.

So lets start with the listing of the running pods.

( one of the pod’s name can be matched to the Pod/Container ID on the confirmation page of the web-browser )


Next lets decribe the service called maltina

You can see that one of the IP addresses on the endpoints in the description matches the Local IP Address on WebApps confirmation page.


Next we have the decription of the deployment. stating the 3 replica pods, confirming the WebApps image pulled and version tag (:v3)

Image docker.io/burgxy/webapps:v3


Now a closer look at both Local IP Address and Pod/Container ID on the web browser

The FQDN on the web browser

Final Conclusion :


Right now, we can establish we got one of the applications working. However I promised to have 2 applications up and running on the ingress of our deployment, so definately, we can mark this task as INCOMPLETE.

But do not dispair as I have commenced the continuation of this project in the next article: For the Helm of it - PART 2

So in our next project, we are going to be :

  • Modifying Helm templates to accomodate additional applications.
  • Modifying ingresses for the same reason above.
  • Using any of these images to provide distinction of images
    • docker.io/burgxy/webapps:v3
    • docker.io/burgxy/webapps:v4
    • docker.io/burgxy/webapps:v5
    • docker.io/burgxy/webapps:v6
    • docker.io/burgxy/webapps:v7

So see you in the Next Article: For the Helm of it - PART 2