Skip to content

The Case for Helm Charts Over DevSpace/Loft

Helm Header

DevSpace and Loft are both tools designed to simplify the development and deployment of applications on Kubernetes. Both tools provide features such as local development environments, simplified deployment workflows, and streamlined debugging and logging. However, they have some differences in their approach and feature sets. DevSpace is more focused on providing an end-to-end development workflow, while Loft emphasizes multi-tenancy and collaboration features for teams.

Many of the features that DevSpace/Loft provides can be achieved by a more cost-effective approach: just by packaging and deploying Helm charts as normal.

Pre-requisites

Helm

Helm is a package manager for Kubernetes that allows you to package and deploy Kubernetes applications. Helm charts are a collection of files that describe a related set of Kubernetes resources. Helm charts can be used to deploy applications, but they can also be used to deploy other types of Kubernetes resources which are out of scope for this article.

To install Helm, follow the instructons for your OS on the Helm website. Once Helm is installed, you can verify that it is working by running the following command:

helm version

Helm charts are stored in a Helm chart repository, which is a collection of Helm charts. Helm chart repositories can be hosted on a public or private server, or they can be hosted on a cloud storage service, such as Amazon S3 or Google Cloud Storage. Helm chart repositories can be accessed using the Helm CLI, which can be installed using the following command:

curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash

Once the Helm CLI is installed, you can add a Helm chart repository using the following command, for example:

helm repo add stable https://kubernetes-charts.storage.googleapis.com/
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add janpreet https://janpreet.github.io/helm-charts/
helm repo update

Once a Helm chart repository has been added, you can search for Helm charts using the following command:

helm search repo stable

# Once you've found a Helm chart that you want to install, you can install it using the following command:
helm install stable/mysql  

We installed the MySQL Helm chart just for demonstration purposes. We will not be using it in this tutorial.

Kind

Kind is a tool for running local Kubernetes clusters using Docker container "nodes". Kind was primarily designed for testing Kubernetes itself, but it can also be used for local development or CI. Alternatives to Kind include Minikube, K3s, and MicroK8s. However, Kind is the most popular option for local development due to its speed, ease of use, and it's ability to offer a local container registry (not in scope of this article).

To install Kind, follow the instructions for your OS on the Kind website. Once Kind is installed, you can verify that it is working by running the following command:

kind version

Using namespaces is useful when you need to logically separate workloads within a single cluster, but still want to share resources such as nodes and network infrastructure. Namespace-level resource quotas can also be applied to prevent any one workload from using up too much of the shared resources.

While Kubernetes namespaces are used to isolate workloads and resources within a single cluster, creating separate clusters with tools like Kind creates isolated environments with their own Kubernetes API server, etcd storage, and worker nodes. Each cluster can have its own configuration, resource allocation, and security settings. Separate clusters are also useful when you need to create isolated environments for different purposes, such as development, testing, or production. Each cluster can have its own resources and configurations, making it easier to manage and isolate workloads.

With that said, let's create a namespace and cluster with the kubectl command. kubectl is typically installed together with kind and minikube since it is a necessary command-line tool for interacting with Kubernetes clusters. To create a these resources, run the following commands:

# To isolate our tutorial application from other workloads, we'll create a separate Kind cluster
kind create cluster --name tutorial-cluster
kubectl config use-context kind-tutorial-cluster # switch to the kind cluster

# create a namespace for all resources for our tutorial application
# i.e., frontend, backend, database, etc.
kubectl create namespace tutorial-app

Open Lens

OpenLens is an open-source project that provides a single dashboard to manage and monitor multiple Kubernetes clusters. It offers a graphical user interface that allows users to easily visualize and manage their resources. OpenLens supports a variety of Kubernetes distributions and is available for free.

For more information about OpenLens, read this blog post and installation instructions from the OpenLens website.

Our Tutorial Application

For this tutorial, we'll be using a simple Node.js application and "Dockerize" it. We can create this application by running the following commands:

mkdir tutorial-app && cd $_

echo "const http = require('http');

const hostname = '0.0.0.0';
const port = process.env.PORT || 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello, world!\n');
});

server.listen(port, hostname, () => {
  console.log(\`Server running at http://\${hostname}:\${port}/\`);
});" >| app.js

echo 'FROM node:18-alpine

WORKDIR /app

COPY . .

EXPOSE 3000

CMD [ "node", "app.js" ]' >| Dockerfile

Subsequently, let's build and push our image to the Docker container registry. We can do this by running the following commands:

docker login # Login to Docker Hub
docker build . -t <our Docker user name>/tutorial-app
docker push <our Docker user name>/tutorial-app

Next, let's create a Helm chart for our application. We can do this by executing:

helm create tutorial-app

# Replace the values.yml file with the following:
export our_DOCKER_USER_NAME=<our Docker user name>
sed -i "s/repository: nginx/repository: ${our_DOCKER_USER_NAME}\/tutorial-app/g" tutorial-app/values.yaml
sed -i 's/port: 80/port: 3000/g' tutorial-app/values.yaml

# Replace the deployment.yml file with the following:
export our_DOCKER_USER_NAME=<our Docker user name>
echo "apiVersion: apps/v1
kind: Deployment
metadata:
  name: tutorial-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: tutorial-app
  template:
    metadata:
      labels:
        app: tutorial-app
    spec:
      containers:
      - name: tutorial-app
        image: ${our_DOCKER_USER_NAME}/tutorial-app
        ports:
        - containerPort: 3000" >| tutorial-app/templates/deployment.yaml

Lets deploy our Helm chart to the cluster:

helm install tutorial-app ./tutorial-app  --namespace tutorial-app # install the chart into the tutorial-app namespace

Deployment to the Kubernetes cluster is done. We can verify this by running the following command:

kubectl get pods --namespace tutorial-app

For security reasons, the port of our application isn't forwarded. To forward the port of our application, run the following:

kubectl port-forward deployment/tutorial-app 3000:3000 --namespace tutorial-app &
curl localhost:3000  #  you should see "Hello, world!"

To view this all in Open Lens, click the blue button representing cluster catalogs in the upper left corner of the Open Lens UI. Then hen select the kind-tutorial-cluster to connect to it. After connected, select Deployments in the new left sidebar. You should see the tutorial-app deployment. This will open a new dialog on the right-hand side of the UI. Within this new dialog, scroll to the "3000/TCP" clickable link. You should see the Hello, world! message open within your defaut web browser.

Troubleshooting

If you have issues with steps in this tutorial, scorth earth and subsequently start over. We can do this by running the following commands:

kubectl delete namespace tutorial-app
kind delete cluster --name tutorial-cluster

kubectl create namespace tutorial-app
kind create cluster --name tutorial-cluster

docker build -t <our Docker user name>/tutorial-app .
docker push <our Docker user name>/tutorial-app

helm install tutorial-app ./tutorial-app  --namespace tutorial-app

For remote clusters, I suggest using GitHub Actions to automate the deployment process. We can use a GitHub Action to build and push a Docker image to the GitHub Container Registry (GHCR). When we push to the GHCR, we can set it to automatically generate a webhook event that we can use to trigger actions in other systems. For example, we can configure our deployment controller to periodically check the container registry for new versions of the image, or set up a webhook to trigger a deployment update whenever a new image is pushed to the registry.

Another option is to use a continuous deployment tool like Flux or Argo CD, which can monitor our container registry and automatically update our Kubernetes cluster when new images are available. These tools can also help us manage the deployment process, automate rollbacks, and provide other useful features for managing our clusters.

For more information on how this can be set up with Amazon's Elastic Kubernetes Service (EKS), see this article and this repository.

Installing a Helm chart can be a one-time task with subsequent upgrades. My suggestion would be to create a separate repository for all Helm charts within our organization. It may not be as flashy as Loft, but it's a workable idea. We can have a repository for all our Helm charts, and we can use Flux or Argo CD to deploy them to our clusters. We can also use Flux or Argo CD to deploy our application manifests to our clusters. Since it's in a repository, we can have a subset of our team responsible for maintaining the Helm charts and application manifests. This way, we can have a separation of concerns between the developers who are less experienced in DevOps and others who are more experienced. These more "DevOps"-y engineers can be responsible for code reviews when pull requests are opened for this repository.

Automatic application deployment

DevSpace/Loft provides a feature that automatically deploys our application to a Kubernetes cluster whenever you make changes to our code. You can achieve this same functionality using Helm by using a CI/CD pipeline that automatically builds and deploys our Helm chart whenever changes are pushed to our source code repository.

To achieve similar functionality to DevSpace's auto-deploy feature using Helm, you can set up a CI/CD pipeline that builds and deploys our Helm chart whenever changes are pushed to our source code repository. As mentioned earlier, we can use a continuous integration tool like GitHub Actions to build and push our Docker image to a container registry, and then use a continuous deployment tool like Flux or ArgoCD to automatically deploy our Helm chart to our Kubernetes cluster.

Local development environment

DevSpace/Loft allows you to run a local development environment that mirrors the configuration of our production environment.

Helm alone does not provide a way to create a local development environment that mirrors the production environment. However, we can achieve this same functionality wehen we deploy our application to a local Kubernetes cluster, such as Kind or Minikube, which can serve as a local development environment. Using only Helm and a local Kubernetes cluster can be a more cost-effective approach than using additional tools like DevSpace or Loft. Helm provides a simple and effective way to package, deploy, and manage applications on a Kubernetes cluster, while a local Kubernetes cluster like Minikube can provide a low-cost way to test and develop our application locally before deploying to a production environment.

Debugging and logging

DevSpace/Loft provides features that allow you to easily debug and view logs from our application running in a Kubernetes cluster.

Helm does not provide specific features for debugging or logging. However, we can achieve this same functionality for our Helm-deployed applications by using Kubernetes logging and debugging tools, such as kubectl logs, Open Lens, or Kubernetes Dashboard.

Using OpenLens, we can view the logs for our application by selecting the kind-tutorial-cluster cluster, and then selecting Pods in the sidebar. We can then select the tutorial-app* pod, and then click on the Logs button in the dialog's upper toolbar.

Application scaling

DevSpace/Loft provides features that allow you to easily scale our application running in a Kubernetes cluster.

We can achieve similar functionality as DevSpace/Loft for scaling our application in a Kubernetes cluster by using Helm. You can modify the replicaCount and resources fields in the values.yaml file to specify the desired number of replicas and resource requirements for our application.

replicaCount: 3

resources:
  limits:
    cpu: "1"
    memory: "1Gi"
  requests:
    cpu: "500m"
    memory: "256Mi"

Then, when we install or upgrade our Helm chart using helm install or helm upgrade commands, the Kubernetes Deployment object that is created will have the desired number of replicas and resource requirements set according to the values specified in the values.yaml file.