If you are working with infrastructure as code (IaC), tools like Terraform and Helm become quite handy. But what if you would like to create a CI/CD process that automates the deployment of your application as well as the provisioning of infrastructure? Basically, there are three options:
- deploy infrastructure via Terraform and create a separate pipeline for the application
- deploy infrastructure via Terraform and use helm provider to deploy the application
- deploy infrastructure via Terraform and use a Terraform wrapper module to deploy the application
Today we will cover the third approach.
TLDR; Checkout Terraform Helm release deployment module on github bery/terraform-helm-release or Terraform module page.
Let’s assume that you have to deploy a simple website to a Kubernetes cluster built on top of the official Bitnami Drupal chart. The steps that need to be taken:
- Find and inspect the chart
- Design the solution — e.g. decide which database and storage will be used
- Provision the infrastructure
- Deploy the website
- [OPTIONAL] Consider using a custom domain and SSL
Bitnami Helm charts are well-documented and actually highly configurable charts with decent support for customization.
It can seem a bit overwhelming at a first glance but there are only a few parameters that really need to be configured. In a nutshell, the config parameters are usually set with the
command. A more complex example for the chart might look like
As you can see in the example above and if you check the documentation of the chart, you will quickly realize that using a plain
setcommand is neither efficient nor easily maintainable and will differ for every chart used — lots of writing, passing complex or nested values such as lists, bash interpolation, and special/reserved characters (e.g. passing float numbers to helm charts can be fun), etc. This can be easily overcome by using Helm provider for Terraform and a few neat tricks.
Designing the solution should be the first phase of any project. Considering the nature of Drupal, the solution will not be a complex one — we could live even with one node and one replica, but a general Kubernetes deployment of (any) web application is illustrated on the following diagram:
Once the solution is finished, the key technologies are Terraform, Helm and Kubernetes. The development flow will be:
- Code commit to git (SCM)
- Trigger a pipeline
- Execute terraform (terraform)
- Deploy the application (helm via terraform)
- [OPT] Run tests
Terraform helm provider
Hang on — call helm from Terraform? Why would I do that? Why should you use Helm provider instead of using a separate pipeline or a simple bash script? Apart from obvious — having one pipeline and one tool responsible for the deployment — reasoning here for that is simple:
- be lazy
- introduce another layer of abstraction
- bash scripts are not developer-friendly
- bash scripts are flaky and lead to many issues, such as variable expansions, working with variables
- having a single source of truth helps visibility, maintainability and increases durability and/or stability
- having control over what is going to be deployed is a must-have
- not having to worry about what Helm actually does.
In the example above, you can see how to pass set a chart, repository, version, and a values file or set individual values. Merely any chart from the official public helm chart repository can be deployed. This solution is suitable for most of the trivial use-cases such as quickly installing a dev environment, developing your charts, or trying out a community helm chart. But can we take this any further and create a reliable production-ready process? There are major drawbacks of this solution:
- passing individual helm chart values is long and repetitive
- the code is messy and confusing, considering that helm charts have usually quite a few values to be set
- it is not possible to enforce any sort of best practice or standardize
- it is very difficult to pass dynamic values to the Helm chart
- doing replacements on values file using terraform’s template function is not an option — variables need to be passed individually and enumerated
Terraform Helm module
Especially to increase reusability and readability or to leverage Terraform’s built-in capabilities, a simple Terraform module can be used.
The first step is to create the module which is straightforward and consists of only one file — main.tf. The initialization of the module will look similar to this:
When you execute the code, the output should look like this:
Let’s explore the code at hand after the initialization of Terraform:
- download the module
- set helm values
- iterate over the values in the chart
- set any number of variables or sensitive (secret) values as a map.
This approach overcomes the drawbacks of using the plain resource in Terraform:
- forces standardizationacross all projects
- is easy to read
- pass helm valuesas module parameters in one map
- terraform resources can be passed via references
- can be distributed, reused, easily updated (note the version constraint!)
- a new release version will be created on every call by default — detecting changes in a release is tricky and not very reliable in terraform
Check out Terraform Helm release deployment module on GitHub bery/terraform-helm-release or Terraform module page.
- shareable, maintainable
- easy to read
- share knowledge and reuse code across the board
- write less code
- for complete charts produces loads of changes and output of Terraform plan can get difficult to interpret (applies to all approaches)
- sometimes produces unexpected results due to the nature of Terraform
Terraform and Helm are amazing tools when it comes to provisioning and deployment automation but used lightly, the whole eco-system might very quickly become difficult to manage and produce various issues with deployment, upgradeability, and reliability. If you want to create a solid basis for your deployments, you should definitely consider introducing similar Terraform modules and standardize the way how individual Terraform resources are being used in your across your projects and in your codebase.
Helm release is just the beginning!
Lukáš Beránek, Solutions Architect