README ¶
helmfile
Deploy Kubernetes Helm Charts
status
Even though Helmfile is used in production environments across multiple organizations, it is still in its early stage of development, hence versioned 0.x.
Helmfile complies to Semantic Versioning 2.0.0 in which v0.x means that there could be backward-incompatible changes for every release.
Note that we will try our best to document any backward incompatibility.
about
Helmfile is a declarative spec for deploying helm charts. It lets you...
- Keep a directory of chart value files and maintain changes in version control.
- Apply CI/CD to configuration changes.
- Periodically sync to avoid skew in environments.
To avoid upgrades for each iteration of helm
, the helmfile
executable delegates to helm
- as a result, helm
must be installed.
configuration syntax
CAUTION: This documentation is for the development version of helmfile. If you are looking for the documentation for any of releases, please switch to the corresponding release tag like v0.31.0.
The default helmfile is helmfile.yaml
:
repositories:
- name: roboll
url: http://roboll.io/charts
certFile: optional_client_cert
keyFile: optional_client_key
username: optional_username
password: optional_password
context: kube-context # kube-context (--kube-context)
#default values to set for args along with dedicated keys that can be set by contributers, cli args take precedence overe these
helmDefaults:
tillerNamespace: tiller-namespace #dedicated default key for tiller-namespace
kubeContext: kube-context #dedicated default key for kube-context
# additional and global args passed to helm
args:
- "--set k=v"
# defaults for verify, wait, force, timeout and recreatePods under releases[]
verify: true
wait: true
timeout: 600
recreatePods: true
force: true
releases:
# Published chart example
- name: vault # name of this release
namespace: vault # target namespace
labels: # Arbitrary key value pairs for filtering releases
foo: bar
chart: roboll/vault-secret-manager # the chart being installed to create this release, referenced by `repository/chart` syntax
version: ~1.24.1 # the semver of the chart. range constraint is supported
values:
# value files passed via --values
- vault.yaml
# inline values, passed via a temporary values file and --values
- address: https://vault.example.com
db:
username: {{ requiredEnv "DB_USERNAME" }}
# value taken from environment variable. Quotes are necessary. Will throw an error if the environment variable is not set. $DB_PASSWORD needs to be set in the calling environment ex: export DB_PASSWORD='password1'
password: {{ requiredEnv "DB_PASSWORD" }}
proxy:
# Interpolate environment variable with a fixed string
domain: {{ requiredEnv "PLATFORM_ID" }}.my-domain.com
scheme: {{ env "SCHEME" | default "https" }}
set:
# single value loaded from a local file, translates to --set-file foo.config=path/to/file
- name: foo.config
file: path/to/file
# set a single array value in an array, translates to --set bar[0]={1,2}
- name: bar[0]
values:
- 1
- 2
# will attempt to decrypt it using helm-secrets plugin
secrets:
- vault_secret.yaml
# wait for k8s resources via --wait. Defaults to `false`
wait: true
# time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks, and waits on pod/pvc/svc/deployment readiness) (default 300)
timeout: 60
# performs pods restart for the resource if applicable
recreatePods: true
# forces resource update through delete/recreate if needed
force: true
# Local chart example
- name: grafana # name of this release
namespace: another # target namespace
chart: ../my-charts/grafana # the chart being installed to create this release, referenced by relative path to local chart
values:
- "../../my-values/grafana/values.yaml" # Values file (relative path to manifest)
- ./values/{{ requiredEnv "PLATFORM_ENV" }}/config.yaml # Values file taken from path with environment variable. $PLATFORM_ENV must be set in the calling environment.
wait: true
Templating
Helmfile uses Go templates for templating your helmfile.yaml. While go ships several built-in functions, we have added all of the functions in the Sprig library.
We also added one special template function: requiredEnv
.
The requiredEnv
function allows you to declare a particular environment variable as required for template rendering.
If the environment variable is unset or empty, the template rendering will fail with an error message.
Using environment variables
Environment variables can be used in most places for templating the helmfile. Currently this is supported for name
, namespace
, value
(in set), values
and url
(in repositories).
Examples:
respositories:
- name: your-private-git-repo-hosted-charts
url: https://{{ requiredEnv "GITHUB_TOKEN"}}@raw.githubusercontent.com/kmzfs/helm-repo-in-github/master/
releases:
- name: {{ requiredEnv "NAME" }}-vault
namespace: {{ requiredEnv "NAME" }}
chart: roboll/vault-secret-manager
values:
- db:
username: {{ requiredEnv "DB_USERNAME" }}
password: {{ requiredEnv "DB_PASSWORD" }}
set:
- name: proxy.domain
value: {{ requiredEnv "PLATFORM_ID" }}.my-domain.com
- name: proxy.scheme
value: {{ env "SCHEME" | default "https" }}
installation
getting started
Let's start with a simple helmfile
and gradually improve it to fit your use-case!
Suppose the helmfile.yaml
representing the desired state of your helm releases looks like:
releases:
- name: prom-norbac-ubuntu
namespace: prometheus
chart: stable/prometheus
set:
- name: rbac.create
value: false
Sync your Kubernetes cluster state to the desired one by running:
helmfile sync
Congratulations! You now have your first Prometheus deployment running inside your cluster.
Iterate on the helmfile.yaml
by referencing the configuration syntax and the cli reference.
cli reference
NAME:
helmfile -
USAGE:
helmfile [global options] command [command options] [arguments...]
COMMANDS:
repos sync repositories from state file (helm repo add && helm repo update)
charts sync releases from state file (helm upgrade --install)
diff diff releases from state file against env (helm diff)
lint lint charts from state file (helm lint)
sync sync all resources from state file (repos, releases and chart deps)
apply apply all resources from state file only when there are changes
status retrieve status of releases in state file
delete delete releases from state file (helm delete)
test test releases from state file (helm test)
GLOBAL OPTIONS:
--helm-binary value, -b value path to helm binary
--file helmfile.yaml, -f helmfile.yaml load config from file or directory. defaults to helmfile.yaml or `helmfile.d`(means `helmfile.d/*.yaml`) in this preference
--quiet, -q Silence output. Equivalent to log-level warn
--kube-context value Set kubectl context. Uses current context by default
--log-level value Set log level, default info
--namespace value, -n value Set namespace. Uses the namespace set in the context by default
--selector value, -l value Only run using the releases that match labels. Labels can take the form of foo=bar or foo!=bar.
A release must match all labels in a group in order to be used. Multiple groups can be specified at once.
--selector tier=frontend,tier!=proxy --selector tier=backend. Will match all frontend, non-proxy releases AND all backend releases.
The name of a release can be used as a label. --selector name=myrelease
--help, -h show help
--version, -v print the version
sync
The helmfile sync
sub-command sync your cluster state as described in your helmfile
. The default helmfile is helmfile.yaml
, but any yaml file can be passed by specifying a --file path/to/your/yaml/file
flag.
Under the covers, Helmfile executes helm upgrade --install
for each release
declared in the manifest, by optionally decrypting secrets to be consumed as helm chart values. It also updates specified chart repositories and updates the
dependencies of any referenced local charts.
For Helm 2.9+ you can use a username and password to authenticate to a remote repository.
diff
The helmfile diff
sub-command executes the helm-diff plugin across all of
the charts/releases defined in the manifest.
To supply the diff functionality Helmfile needs the helm-diff plugin v2.9.0+1 or greater installed. For Helm 2.3+
you should be able to simply execute helm plugin install https://github.com/databus23/helm-diff
. For more details
please look at their documentation.
apply
The helmfile apply
sub-command begins by executing diff
. If diff
finds that there is any changes, sync
is executed after prompting you for an confirmation. --auto-approve
skips the confirmation.
An expected use-case of apply
is to schedule it to run periodically, so that you can auto-fix skews between the desired and the current state of your apps running on Kubernetes clusters.
delete
The helmfile delete
sub-command deletes all the releases defined in the manfiests
Note that delete
doesn't purge releases. So helmfile delete && helmfile sync
results in sync failed due to that releases names are not deleted but preserved for future references. If you really want to remove releases for reuse, add --purge
flag to run it like helmfile delete --purge
.
secrets
The secrets
parameter in a helmfile.yaml
causes the helm-secrets plugin to be executed to decrypt the file.
To supply the secret functionality Helmfile needs the helm secrets
plugin installed. For Helm 2.3+
you should be able to simply execute helm plugin install https://github.com/futuresimple/helm-secrets
.
test
The helmfile test
sub-command runs a helm test
against specified releases in the manifest, default to all
Use --cleanup
to delete pods upon completion.
lint
The helmfile lint
sub-command runs a helm lint
across all of the charts/releases defined in the manifest. Non local charts will be fetched into a temporary folder which will be deleted once the task is completed.
Paths Overview
Using manifest files in conjunction with command line argument can be a bit confusing.
A few rules to clear up this ambiguity:
- Absolute paths are always resolved as absolute paths
- Relative paths referenced in the helmfile manifest itself are relative to that manifest
- Relative paths referenced on the command line are relative to the current working directory the user is in
For additional context, take a look at paths examples
Labels Overview
A selector can be used to only target a subset of releases when running helmfile. This is useful for large helmfiles with releases that are logically grouped together.
Labels are simple key value pairs that are an optional field of the release spec. When selecting by label, the search can be inverted. tier!=backend
would match all releases that do NOT have the tier: backend
label. tier=fronted
would only match releases with the tier: frontend
label.
Multiple labels can be specified using ,
as a separator. A release must match all selectors in order to be selected for the final helm command.
The selector
parameter can be specified multiple times. Each parameter is resolved independently so a release that matches any parameter will be used.
--selector tier=frontend --selector tier=backend
will select all the charts
Templates
You can use go's text/template expressions in helmfile.yaml
and values.yaml
(helm values files).
In addition to built-in ones, the following custom template functions are available:
readFile
reads the specified local file and generate a golang stringfromYaml
reads a golang string and generates a mapsetValuleAtPath PATH NEW_VALUE
traverses a golang map, replaces the value at the PATH with NEW_VALUEtoYaml
marshals a map into a string
Values Files Templates
You can reference a template of values file in your helmfile.yaml
like below:
releases
- name: myapp
chart: mychart
values:
- values.yaml.gotmpl
Every values file whose file extension is .gotmpl
is considered as a tempalte file.
Suppose values.yaml.gotmpl
was something like:
{{ readFile "values.yaml" | fromYaml | setValueAtPath "foo.bar" "FOO_BAR" | toYaml }}
And values.yaml
was:
foo:
bar: ""
The resulting, temporary values.yaml that is generated from values.yaml.tpl
would become:
foo:
# Notice `setValueAtPath "foo.bar" "FOO_BAR"` in the template above
bar: FOO_BAR
Refactoring helmfile.yaml
with values files templates
One of expected use-cases of values files templates is to keep helmfile.yaml
small and concise.
See the example helmfile.yaml
below:
releases:
- name: {{ requiredEnv "NAME" }}-vault
namespace: {{ requiredEnv "NAME" }}
chart: roboll/vault-secret-manager
values:
- db:
username: {{ requiredEnv "DB_USERNAME" }}
password: {{ requiredEnv "DB_PASSWORD" }}
set:
- name: proxy.domain
value: {{ requiredEnv "PLATFORM_ID" }}.my-domain.com
- name: proxy.scheme
value: {{ env "SCHEME" | default "https" }}
The values
and set
sections of the config file can be separated out into a template:
helmfile.yaml
:
releases:
- name: {{ requiredEnv "NAME" }}-vault
namespace: {{ requiredEnv "NAME" }}
chart: roboll/vault-secret-manager
values:
- values.yaml.tmpl
values.yaml.tmpl
:
db:
username: {{ requiredEnv "DB_USERNAME" }}
password: {{ requiredEnv "DB_PASSWORD" }}
proxy:
domain: {{ requiredEnv "PLATFORM_ID" }}.my-domain.com
scheme: {{ env "SCHEME" | default "https" }}
Environment
When you want to customize the contents of helmfile.yaml
or values.yaml
files per environment, use this feature.
You can define as many environments as you want under environments
in helmfile.yaml
.
The environment name defaults to default
, that is, helmfile sync
implies the default
environment.
The selected environment name can be referenced from helmfile.yaml
and values.yaml.gotmpl
by {{ .Environment.Name }}
.
If you want to specify a non-default environment, provide a --environment NAME
flag to helmfile
like helmfile --environment production sync
.
The below example shows how to define a production-only release:
environments:
default:
production:
releases:
{{ if (eq .Environment.Name "production" }}
- name: newrelic-agent
# snip
{{ end }}
- name: myapp
# snip
Environment Values
Environment Values allows you to inject a set of values specific to the selected environment, into values.yaml templates. Use it to inject common values from the environment to multiple values files, to make your configuration DRY.
Suppose you have three files helmfile.yaml
, production.yaml
and values.yaml.gotmpl
:
helmfile.yaml
environments:
production:
values:
- production.yaml
releases:
- name: myapp
values:
- values.yaml.gotmpl
production.yaml
domain: prod.example.com
values.yaml.gotmpl
domain: {{ .Environment.Values.domain | default "dev.example.com" }}
helmfile sync
installs myapp
with the value domain=dev.example.com
,
whereas helmfile --environment production sync
installs the app with the value domain=production.example.com
.
Environment Secrets
Environment Secrets are encrypted versions of Environment Values
.
You can list any number of secrets.yaml
files created using helm secrets
or sops
, so that
helmfile could automatically decrypt and merge the secrets into the environment values.
Suppose you have environment secrets defined in hemlfile.yaml
:
environments:
production:
secrets:
- environments/produdction/secrets.yaml
releases:
- name: myapp
chart: mychart
values:
- values.yaml.gotmpl
an environment secret foo.bar
can be referenced by the below template expression in your values.yaml.gotmpl
:
{{ .Values.foo.bar }
Separating helmfile.yaml into multiple independent files
Once your helmfile.yaml
got to contain too many releases,
split it into multiple yaml files.
Recommended granularity of helmfile.yaml files is "per microservice" or "per team". And there are two ways to organize your files.
- Single directory
- Glob patterns
Single directory
helmfile -f path/to/directory
loads and runs all the yaml files under the specified directory, each file as an independent helmfile.yaml.
The default helmfile directory is helmfile.d
, that is,
in case helmfile is unable to locate helmfile.yaml
, it tries to locate helmfile.d/*.yaml
.
All the yaml files under the specified directory are processed in the alphabetical order. For example, you can use a <two digit number>-<microservice>.yaml
naming convention to control the sync order.
helmfile.d
/00-database.yaml
00-backend.yaml
01-frontend.yaml
Glob patterns
In case you want more control over how multiple helmfile.yaml
files are organized, use helmfiles:
configuration key in the helmfile.yaml
:
Suppose you have multiple microservices organized in a Git reposistory that looks like:
myteam/
(sometimes it is equivalent to a k8s ns, that iskube-system
forclusterops
team)apps/
filebeat/
helmfile.yaml
(nocharts/
exists, because it depends on the stable/filebeat chart hosted on the official helm charts repository)README.md
(each app managed by my team has a dedicated README maintained by the owners of the app)
metricbeat/
helmfile.yaml
README.md
elastalert-operator/
helmfile.yaml
README.md
charts/
elastalert-operator/
<the content of the local helm chart>
The benefits of this structure is that you can run git diff
to locate in which directory=microservice a git commit has changes.
It allows your CI system to run a workflow for the changed microservice only.
A downside of this is that you don't have an obvious way to sync all microservices at once. That is, you have to run:
for d in apps/*; do helmfile -f $d diff; if [ $? -eq 2 ]; then helmfile -f $d sync; fi; done
At this point, you'll start writing a Makefile
under myteam/
so that make sync-all
will do the job.
It does work, but you can rely on the helmfile's feature instead.
Put myteam/helmfile.yaml
that looks like:
helmfiles:
- apps/*/helmfile.yaml
So that you can get rid of the Makefile
and the bash snippet.
Just run helmfile sync
inside myteam/
, and you are done.
All the files are sorted alphabetically per group = array item inside helmfiles:
, so that you have granular control over ordering, too.
Importing values from any source
The exec
template function that is available in values.yaml.gotmpl
is useful for importing values from any source
that is accessible by running a command:
An usual usage of exec
would look like this:
mysetting: |
{{ exec "./mycmd" (list "arg1" "arg2" "--flag1") | indent 2 }}
Or even with a pipeline:
mysetting: |
{{ yourinput | exec "./mycmd-consume-stdin" (list "arg1" "arg2") | indent 2 }}
The possibility is endless. Try importing values from your golang app, bash script, jsonnet, or anything!
Using env files
helmfile itself doesn't have an ability to load env files. But you can write some bash script to achieve the goal:
set -a; . .env; set +a; helmfile sync
Please see #203 for more context.
Running helmfile without an Internet connection
Once you download all required charts into your machine, you can run helmfile charts
to deploy your apps.
It basically run only helm upgrade --install
with your already-downloaded charts, hence no Internet connection is required.
See #155 for more information on this topic.
Examples
For more examples, see the examples/README.md or the helmfile.d
distribution of helmfiles by Cloud Posse.
Documentation ¶
There is no documentation for this package.