With infrastructure becoming increasingly complex, achieving the right level of isolation and reproducibility is crucial for any software development environment. When it’s not the case, issues like “works on my machine” or “push-and-pray” can make life much more challenging.
Helm and Kubernetes are no exception: ensuring you use the correct versions and settings across all environments can be challenging. If you’ve ever had to push multiple CI or pipeline changes, hoping it would turn green, you know exactly what I mean.
In this post, I’ll show you how to use Dagger to achieve isolation and reproducibility with Helm (including running linters, tests, and pushing to a registry) so you can run everything on your machine exactly as it will run in CI.
You can find the final version of the code here.
Preparations
Make sure to install the latest version of Dagger (0.13.3 at the time of writing) and Docker on your machine. Installing your chosen language environment can also improve user experience and IDE integration, but aside from that (and Git), you won’t need any other dependencies on your machine—one of the great advantages of using Dagger.
As a first step, set up a new project and a Dagger module:
After initializing the module, replace the contents of .dagger/main.go
with
the following:
We will add more code to that file as we go through the tutorial.
Next, generate a new chart using Helm’s create
command. You can use my Helm
module for Dagger:
Alternatively, you can try Dagger’s new core
command (available since 0.13.0)
to run the official Helm container image directly:
To test the Helm chart, we will need a test application. To simplify things, we’ll use Nginx, as it’s the default application used by the generated Helm chart.
Add the following piece of code to .dagger/main.go
to build your own Nginx
container image:
If you want, you can add a custom index.html
to verify your chart uses your
custom container. The Build
function should look like this in that case:
You can test the application by running the following command and then visiting
http://localhost:8080
in your browser:
That concludes the preparation phase. You are now ready to move on to the next chapter. You can always check the example code here if you get stuck.
CI: Testing and Linting Helm charts
The next step is setting up linting and testing. While these could be part of your CI pipeline, what makes Dagger especially powerful is that you can run them on your own machine exactly as they would run in CI, making the feedback loop much quicker.
To do so, you are going to need the Helm module from Daggerverse:
Note: you don’t have to use these exact versions, but these are the ones used in the example repository.
As a first step, create a Helm chart object using the Helm module by adding the
following to .dagger/main.go
:
As you can see from the *dagger.HelmChart
return type, the returned object
will be more than one of the usual core types of Dagger. It allows you to call
higher-level functions on the Helm chart loaded from the source directory.
For example, you can call the Lint
function on it, which is equivalent to
running helm lint
:
You can run the linter by executing the following:
Helm also comes with a test framework that allows you to install the application and run tests against a running instance. (One such test is in the templates/tests/ directory of your chart.) Installing the chart requires an actual Kubernetes cluster and a way to get your application image inside that cluster.
Fortunately, there are modules that can help you with this task:
The k3s module (lightweight Kubernetes) will run a k3s cluster in Dagger, while the registry will allow you to push your application image to it and Kubernetes to pull it from.
Let’s start putting together our test function. First, we are going to need to build the application and the chart:
Next, we are going to need a registry service that we can push the application image to:
Set up a k3s cluster with the registry acting as a mirror where Kubernetes can pull the image from:
Finally, install the chart on the cluster and run the tests:
You can run the tests by executing the following command:
It may take a few minutes for the first time, but it should successfully launch a k3s cluster, install the chart, and run the default tests. You can always consult the example code here if you get stuck.
Releasing Helm charts
Pushing the chart to an OCI registry is the final step in the development
lifecycle. Once again, we will use the Helm module to do this. To make things
more realistic, you can also push the application container image to a registry
along with the chart. I’ll use ghcr.io
in this example, but you can use any
registry you prefer.
The nice thing about this workflow is you can easily run it from your machine:
While releases are typically handled by a CI system, it's often easier to run the initial release from a local machine. It's also convenient to have the ability to publish releases even if the CI system is down.
Conclusion
Dagger is an excellent tool for developing a Helm chart, supporting the entire development lifecycle—from creating the chart to pushing the final artifact to a registry.
Be sure to check out the example repository for the final code and some bonus components, such as generating documentation with helm-docs and a GitHub Actions workflow.