zheludov.com:/$ blog creating-generic-helm-chart-for-web-app

// 2023-11-20

Creating a Generic Helm Chart for a Web Application

This blog post will guide you through creating a Helm chart for a generic web application. We'll cover how to include a deployment, service, NGINX ingress, and a horizontal pod autoscaler in your Helm chart.

Introduction to Helm

Helm is a Kubernetes package manager that simplifies deploying and managing applications. Helm charts are templates used to deploy applications, defining necessary Kubernetes resources.

Setting Up Your Helm Chart

First, ensure Helm is installed:

helm version

Then, create a new chart:

helm create webapp

This command creates a new directory webapp with the structure for your chart.

Chart Structure Overview

  • Chart.yaml: Contains metadata about your chart.
  • values.yaml: Defines default values for your chart.
  • templates/: Kubernetes resource templates.

Customizing Your Chart

This Helm chart contains several key Kubernetes resources, each serving a specific purpose in deploying and managing your web application. Below is a breakdown of these resources and the modifications we'll make:

Resources Overview

  1. Deployment: The Deployment resource is crucial for managing the stateless pods of your web application. It ensures that a specified number of pod replicas are running and handles rolling updates to your application. In the deployment.yaml file, we will customize the container image, set the desired number of replicas, and define necessary environment variables for your application.

  2. Service: The Service resource exposes your application within the Kubernetes cluster or to the external world. It provides a stable endpoint for accessing the pods managed by the Deployment. In the service.yaml file, we'll specify the type of service (like ClusterIP or LoadBalancer) and configure the ports through which the application is accessible.

  3. Ingress: If external access to your web application is required, the Ingress resource comes into play. It manages external access to the services in the cluster, providing load balancing, SSL termination, and host-based routing. In the ingress.yaml file, we'll define the rules for routing external traffic to your service, including hostnames and paths.

  4. Horizontal Pod Autoscaler (HPA): The HPA automatically adjusts the number of pod replicas in the Deployment based on observed CPU utilization or other selected metrics. This ensures that your application scales properly in response to traffic. In the hpa.yaml file, we'll set up scaling policies, including the minimum and maximum number of replicas and the target CPU utilization for scaling.

Configuration Details

Each of these resources is defined in a template file in the templates/ directory of your Helm chart. We'll make specific changes to these files to tailor the deployment process of your web application. These changes will ensure that your application is deployed with the right settings, resources, and scaling behavior to meet your requirements.

With these resources and configurations in place, your Helm chart will be a powerful tool for deploying and managing your web application in a Kubernetes environment.

Now, let's dive into the specifics of customizing each resource...

Customizing the Deployment Configuration

Location: templates/deployment.yaml

Parameters:

  1. Container Image: Define the Docker image for your web application's container.

  2. Replica Count: Set the number of pod replicas to run.

  3. Environment Variables: Configure environment variables as key-value pairs.

  4. Resource Requests and Limits: Specify the CPU and memory usage for the container.

  5. Readiness and Liveness Probes: Set up health checks for your application.

  6. Volume Mounts: Configure one or more volumes for your container.

Example Deployment Configuration:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "webapp.fullname" . }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app: {{ include "webapp.fullname" . }}
  template:
    metadata:
      labels:
        app: {{ include "webapp.fullname" . }}
    spec:
      containers:
        - name: {{ .Values.container.name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          ports:
            - containerPort: {{ .Values.container.port }}
          env:
            {{- range $key, $value := .Values.env }}
            - name: {{ $key }}
              value: {{ $value }}
            {{- end }}
          resources:
            requests:
              memory: {{ .Values.resources.requests.memory }}
              cpu: {{ .Values.resources.requests.cpu }}
            limits:
              memory: {{ .Values.resources.limits.memory }}
              cpu: {{ .Values.resources.limits.cpu }}
          livenessProbe:
            httpGet:
              path: {{ .Values.livenessProbe.path }}
              port: {{ .Values.livenessProbe.port }}
          readinessProbe:
            httpGet:
              path: {{ .Values.readinessProbe.path }}
              port: {{ .Values.readinessProbe.port }}
          volumeMounts:
            {{- range .Values.volumes }}
            - name: {{ .name }}
              mountPath: {{ .mountPath }}
            {{- end }}
      volumes:
        {{- range .Values.volumes }}
        - name: {{ .name }}
          {{- with .configMap }}
          configMap:
            name: {{ .name }}
            items:
              {{- range .items }}
              - key: {{ .key }}
                path: {{ .path }}
              {{- end }}
          {{- end }}
        {{- end }}

Example Values Configuration for deployment:

In values.yaml, define the values for images, environment variables, resources, probes, and volumes for potentially multiple containers:

replicaCount: 3

container:
  name: webapp
  port: 80

image:
  repository: your-webapp-image
  tag: latest

env:
  ENV_VAR_NAME: "Value"

resources:
  requests:
    memory: "256Mi"
    cpu: "250m"
  limits:
    memory: "512Mi"
    cpu: "500m"

livenessProbe:
  path: /health
  port: 80

readinessProbe:
  path: /health
  port: 80

volumes:
  - name: config-vol
    mountPath: /etc/config
    configMap:
      name: log-config
      items:
        - key: log_level
          path: log_level

Customizing the Service Configuration

Location: templates/service.yaml

Parameters:

  • Service Type: Specify the type of service (e.g., ClusterIP, NodePort, LoadBalancer).

  • Port Configuration: Define the ports and target ports for the service.

Example Service Configuration:

apiVersion: v1
kind: Service
metadata:
  name: {{ include "webapp.fullname" . }}
spec:
  type: {{ .Values.service.type }}
  ports:
    - port: {{ .Values.service.port }}
      targetPort: {{ .Values.container.port }}
      protocol: TCP
  selector:
    app: {{ include "webapp.fullname" . }}

Example Values Configuration for Service:

service:
  type: ClusterIP
  port: 80

Customizing Ingress Configuration

Location: templates/ingress.yaml

Parameters:

  • Hosts: Define the hostnames for the ingress rules.

  • Paths: Specify the paths and corresponding backend services.

Example Ingress Configuration:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: {{ include "webapp.fullname" . }}
  annotations:
    kubernetes.io/ingress.class: nginx
spec:
  rules:
    {{- range .Values.ingress.hosts }}
    - host: {{ .host }}
      http:
        paths:
          {{- range .paths }}
          - path: {{ .path }}
            pathType: Prefix
            backend:
              service:
                name: {{ include "webapp.fullname" $ }}
                port:
                  number: {{ $.Values.service.port }}
          {{- end }}
    {{- end }}

Example Values Configuration for Ingress:

ingress:
  hosts:
    - host: "example.com"
      paths:
        - path: "/"

Customizing the Horizontal Pod Autoscaler Configuration

Location: templates/hpa.yaml

Parameters:

  • Minimum and Maximum Replicas: Configure the scaling limits.
  • CPU Utilization: Set the target CPU utilization for scaling.
  • Memory Utilization: Define the target memory utilization for scaling.

Example HPA Configuration:

apiVersion: autoscaling/v2beta2
kind: HorizontalPod Autoscaler
metadata:
  name: {{ include "webapp.fullname" . }}
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: {{ include "webapp.fullname" . }}
  minReplicas: {{ .Values.autoscaling.minReplicas }}
  maxReplicas: {{ .Values.autoscaling.maxReplicas }}
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}

Example Values Configuration for HPA:

autoscaling:
  minReplicas: 1
  maxReplicas: 100
  targetCPUUtilizationPercentage: 80
  targetMemoryUtilizationPercentage: 75

When effectively utilizing an HPA for a web application, it's crucial to consider both the application's resource usage patterns and its request volume. Initially, you should set the HPA metrics (CPU and memory utilization percentages) based on typical usage under average load. Monitoring tools can help you understand these patterns. For instance, if your application is memory-intensive, setting a lower target for memory utilization in the HPA configuration will ensure timely scaling. Conversely, for CPU-intensive applications, the CPU utilization threshold is more critical. Additionally, it's important to consider the request volume. Metrics like the number of concurrent users or requests per second can be indicative of the load. Using custom metrics from Kubernetes metrics servers can provide more insight into scaling based on request volume. I will dive into scaling web application based on custom metrics in the future article.

Testing and Deploying Your Helm Chart

Testing

Before deploying your Helm chart, it's essential to test it to ensure that it generates the correct Kubernetes resources. You can do this using the helm template command. This command renders your templates locally and outputs the generated Kubernetes manifests, allowing you to verify their correctness.

Run the following command in the root directory of your Helm chart:

helm template .

Examine the output to ensure that all resources (Deployments, Services, Ingress, and HPA) are correctly configured and that values from values.yaml are properly substituted. Pay special attention to your customizations, like environment variables, resource requests and limits, and metrics for HPA.

Deploying

Once you're satisfied with the output of the helm template command, you can proceed to deploy your Helm chart in your Kubernetes cluster. Use the helm install command for this purpose:

helm install my-webapp-release .

Replace my-webapp-release with a name for your deployment. This command deploys your web application to the Kubernetes cluster, creating all the necessary resources as defined in your chart.

Conclusion

Creating a Helm chart for a web application involves careful planning and attention to detail. By customizing your chart for Deployments, Services, Ingress, and HPAs, you can ensure that your application scales efficiently and remains stable under different loads. Testing the chart with helm template helps catch issues before deployment, and careful monitoring after deployment ensures optimal performance. Remember, Helm charts are not only about deploying your application but also about maintaining it effectively in a Kubernetes environment. With this Helm chart in place, you have taken a significant step towards a robust and scalable deployment strategy for your web application.

Full helm chart can be found here: https://github.com/alex-zheludov/helm-generic-web

Type 'blog' + Enter -- to get a list of my blog posts.
Type 'help' + Enter -- for available commands.
zheludov.com$