348 lines
12 KiB
Markdown
348 lines
12 KiB
Markdown
# Environments
|
|
|
|
## 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`.
|
|
`environments` section should be separated from `releases` with `---`.
|
|
|
|
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:
|
|
|
|
```yaml
|
|
environments:
|
|
default:
|
|
production:
|
|
|
|
---
|
|
|
|
releases:
|
|
- name: newrelic-agent
|
|
installed: {{ eq .Environment.Name "production" | toYaml }}
|
|
# snip
|
|
- name: myapp
|
|
# snip
|
|
```
|
|
|
|
### Environment Values
|
|
Helmfile supports 3 values languages :
|
|
- Straight yaml
|
|
- Go templates to generate straight yaml
|
|
- HCL
|
|
|
|
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`
|
|
|
|
```yaml
|
|
environments:
|
|
production:
|
|
values:
|
|
- production.yaml
|
|
|
|
---
|
|
|
|
releases:
|
|
- name: myapp
|
|
values:
|
|
- values.yaml.gotmpl
|
|
```
|
|
|
|
`production.yaml`
|
|
|
|
```yaml
|
|
domain: prod.example.com
|
|
releaseName: prod
|
|
```
|
|
|
|
`values.yaml.gotmpl`
|
|
|
|
```yaml
|
|
domain: {{ .Values | get "domain" "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=prod.example.com`.
|
|
|
|
For even more flexibility, you can now use values declared in the `environments:` section in other parts of your helmfiles:
|
|
|
|
consider:
|
|
`default.yaml`
|
|
|
|
```yaml
|
|
domain: dev.example.com
|
|
releaseName: dev
|
|
```
|
|
|
|
```yaml
|
|
environments:
|
|
default:
|
|
values:
|
|
- default.yaml
|
|
production:
|
|
values:
|
|
- production.yaml # bare .yaml file, content will be used verbatim
|
|
- other.yaml.gotmpl # template directives with potential side-effects like `exec` and `readFile` will be honoured
|
|
|
|
---
|
|
|
|
releases:
|
|
- name: myapp-{{ .Values.releaseName }} # release name will be one of `dev` or `prod` depending on selected environment
|
|
values:
|
|
- values.yaml.gotmpl
|
|
- name: production-specific-release
|
|
# this release would be installed only if selected environment is `production`
|
|
installed: {{ eq .Values.releaseName "prod" | toYaml }}
|
|
...
|
|
```
|
|
|
|
#### Merge strategy
|
|
|
|
By default, when several files are listed under `values:`, later files override earlier files. Set `mergeStrategy: fallback` on the environment to flip the precedence so earlier files win and later files only fill missing keys:
|
|
|
|
```yaml
|
|
environments:
|
|
production:
|
|
mergeStrategy: fallback
|
|
values:
|
|
- cluster-specific.yaml # wins on every key it defines
|
|
- shared-defaults.yaml.gotmpl
|
|
```
|
|
|
|
Under `fallback`, explicit non-nil values in the earlier file (including zero values like `false`, `0`, `""`, and empty list) are preserved against any later file, while maps are deep-merged so later files may still add nested keys. A later `.gotmpl` file can also reference values from earlier files via `.Values`. See [Merge Strategy: override vs fallback](values-and-merging.md#4a-merge-strategy-override-vs-fallback) for the full semantics, including how explicit `null` is handled.
|
|
|
|
#### HCL specifications
|
|
|
|
Since Helmfile v0.164.0, HCL language is supported for environment values only.
|
|
HCL values supports interpolations and sharing values across files
|
|
|
|
* Only `.hcl` suffixed files will be interpreted as is
|
|
* Helmfile supports 2 different blocks: `values` and `locals`
|
|
* `values` block is a shared block where all values are accessible everywhere in all loaded files
|
|
* `locals` block can't reference external values apart from the ones in the block itself, and where its defined values are only accessible in its local file
|
|
* Only values in `values` blocks are made available to the final root `.Values` (e.g : ` values { myvar = "var" }` is accessed through `{{ .Values.myvar }}`)
|
|
* There can only be 1 `locals` block per file
|
|
* Helmfile hcl `values` are referenced using the `hv` accessor.
|
|
* Helmfile hcl `locals` are referenced using the `local` accessor.
|
|
* When the same key is defined multiple times across imported `.hcl` files in `values` blocks, values from later files override those from earlier files (last file loaded wins). Map values are merged per key, while list values are replaced as a whole (i.e. not deep-merged). Mixed-types overrides (e.g. bool -> string) are supported (latest value/type wins).
|
|
* All cty [standard library functions](`https://pkg.go.dev/github.com/zclconf/go-cty@v1.14.3/cty/function/stdlib`) are available and custom functions could be created in the future
|
|
|
|
Consider the following example :
|
|
|
|
```terraform
|
|
# values1.hcl
|
|
locals {
|
|
hostname = "host1"
|
|
}
|
|
values {
|
|
domain = "DEV.EXAMPLE.COM"
|
|
hostnameV1 = "${local.hostname}.${lower(hv.domain)}" # "host1.dev.example.com"
|
|
}
|
|
```
|
|
```terraform
|
|
# values2.hcl
|
|
locals {
|
|
hostname = "host2"
|
|
}
|
|
|
|
values {
|
|
hostnameV2 = "${local.hostname}.${hv.domain}" # "host2.DEV.EXAMPLE.COM"
|
|
}
|
|
```
|
|
#### Note on Environment.Values vs Values
|
|
|
|
The `{{ .Values.foo }}` syntax is the recommended way of using environment values.
|
|
|
|
Prior to this [pull request](https://github.com/roboll/helmfile/pull/647), environment values were made available through the `{{ .Environment.Values.foo }}` syntax.
|
|
This is still working but is **deprecated** and the new `{{ .Values.foo }}` syntax should be used instead.
|
|
|
|
You can read more infos about the feature proposal [here](https://github.com/roboll/helmfile/issues/640).
|
|
|
|
### Environment Secrets
|
|
|
|
Environment Secrets *(not to be confused with Kubernetes 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.
|
|
|
|
First you must have the [helm-secrets](https://github.com/jkroepke/helm-secrets) plugin installed along with a
|
|
`.sops.yaml` file to configure the method of encryption (this can be in the same directory as your helmfile or
|
|
in the subdirectory containing your secrets files).
|
|
|
|
Then suppose you have a secret `foo.bar` defined in `environments/production/secrets.yaml`:
|
|
|
|
```yaml
|
|
foo.bar: "mysupersecretstring"
|
|
```
|
|
|
|
You can then encrypt it with `helm secrets enc environments/production/secrets.yaml`
|
|
|
|
Then reference that encrypted file in `helmfile.yaml`:
|
|
|
|
```yaml
|
|
environments:
|
|
production:
|
|
secrets:
|
|
- environments/production/secrets.yaml
|
|
|
|
---
|
|
|
|
releases:
|
|
- name: myapp
|
|
chart: mychart
|
|
values:
|
|
- values.yaml.gotmpl
|
|
```
|
|
|
|
Then the environment secret `foo.bar` can be referenced by the below template expression in your `values.yaml.gotmpl`:
|
|
|
|
```yaml
|
|
{{ .Values.foo.bar }}
|
|
```
|
|
|
|
#### Loading remote Environment secrets files
|
|
|
|
Since Helmfile v0.149.0, you can use `go-getter`-style URLs to refer to remote secrets files, the same way as in values files:
|
|
```yaml
|
|
environments:
|
|
staging:
|
|
secrets:
|
|
- git::https://{{ env "GITHUB_PAT" }}@github.com/org/repo.git@/environments/staging.secret.yaml?ref=main
|
|
- http://$HOSTNAME/artifactory/example-repo-local/test.tgz@environments/staging.secret.yaml
|
|
production:
|
|
secrets:
|
|
- git::https://{{ env "GITHUB_PAT" }}@github.com/org/repo.git@/environments/production.secret.yaml?ref=main
|
|
- http://$HOSTNAME/artifactory/example-repo-local/test.tgz@environments/production.secret.yaml
|
|
```
|
|
|
|
### Loading remote Environment values files
|
|
|
|
Since Helmfile v0.118.8, you can use `go-getter`-style URLs to refer to remote values files:
|
|
|
|
```yaml
|
|
environments:
|
|
cluster-azure-us-west:
|
|
values:
|
|
- git::https://git.company.org/helmfiles/global/azure.yaml?ref=master
|
|
- git::https://git.company.org/helmfiles/global/us-west.yaml?ref=master
|
|
- git::https://gitlab.com/org/repository-name.git@/config/config.test.yaml?ref=main # Public Gilab Repo
|
|
cluster-gcp-europe-west:
|
|
values:
|
|
- git::https://git.company.org/helmfiles/global/gcp.yaml?ref=master
|
|
- git::https://git.company.org/helmfiles/global/europe-west.yaml?ref=master
|
|
- git::https://ci:{{ env "CI_JOB_TOKEN" }}@gitlab.com/org/repository-name.git@/config.dev.yaml?ref={{ env "APP_COMMIT_SHA" }} # Private Gitlab Repo
|
|
staging:
|
|
values:
|
|
- git::https://{{ env "GITHUB_PAT" }}@github.com/[$GITHUB_ORGorGITHUB_USER]/repository-name.git@/values.dev.yaml?ref=main #Github Private repo
|
|
- http://$HOSTNAME/artifactory/example-repo-local/test.tgz@values.yaml #Artifactory url
|
|
---
|
|
|
|
releases:
|
|
- ...
|
|
```
|
|
|
|
Since Helmfile v0.158.0, support more protocols, such as: s3, https, http
|
|
```
|
|
values:
|
|
- s3::https://helm-s3-values-example.s3.us-east-2.amazonaws.com/values.yaml
|
|
- s3://helm-s3-values-example/subdir/values.yaml
|
|
- https://john:doe@helm-s3-values-example.s3.us-east-2.amazonaws.com/values.yaml
|
|
- http://helm-s3-values-example.s3.us-east-2.amazonaws.com/values.yaml
|
|
```
|
|
|
|
For more information about the supported protocols see: [go-getter Protocol-Specific Options](https://github.com/hashicorp/go-getter#protocol-specific-options-1).
|
|
|
|
This is particularly useful when you co-locate helmfiles within your project repo but want to reuse the definitions in a global repo.
|
|
|
|
### Environment values precedence
|
|
With the introduction of HCL, a new value precedence was introduced over environment values.
|
|
Here is the order of precedence from least to greatest (the last one overrides all others)
|
|
1. `yaml` / `yaml.gotmpl`
|
|
2. `hcl`
|
|
3. `yaml` secrets
|
|
|
|
Example:
|
|
|
|
---
|
|
|
|
```yaml
|
|
# values1.yaml
|
|
domain: "dev.example.com"
|
|
```
|
|
|
|
```terraform
|
|
# values2.hcl
|
|
values {
|
|
domain = "overdev.example.com"
|
|
env = "dev"
|
|
willBeOverridden = "override_me"
|
|
}
|
|
```
|
|
|
|
```terraform
|
|
# values3.hcl
|
|
values {
|
|
env = "local"
|
|
}
|
|
```
|
|
|
|
```yaml
|
|
# secrets.yml (assuming this one has been encrypted)
|
|
willBeOverridden: overridden
|
|
```
|
|
|
|
```
|
|
# helmfile.yaml.gotmpl
|
|
environments:
|
|
default:
|
|
values:
|
|
- values1.yaml
|
|
- values2.hcl
|
|
- values3.hcl
|
|
secrets:
|
|
- secrets.yml
|
|
---
|
|
releases:
|
|
- name: random-release
|
|
[...]
|
|
values:
|
|
domain: "{{ .Values.domain }}" # == "overdev.example.com"
|
|
env: "{{ .Values.env }}" # == "local"
|
|
willBeOverridden: "{{ .Values.willBeOverridden }}" # == "overridden"
|
|
```
|
|
|
|
### Environment defaults
|
|
|
|
In addition to `values`, environments support a `defaults` block that provides a separate layer of default values. These are merged **before** `values`, giving `values` higher priority:
|
|
|
|
```yaml
|
|
environments:
|
|
default:
|
|
defaults:
|
|
- cluster: dev
|
|
replicas: 1
|
|
values:
|
|
- replicas: 3
|
|
```
|
|
|
|
The merge order for environment values is:
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ 1. Environment defaults (merged first, lowest priority) │
|
|
│ 2. Environment values (yaml/yaml.gotmpl) │
|
|
│ 3. Environment values (HCL) │
|
|
│ 4. Environment secrets (non-HCL, decrypted) │
|
|
│ 5. CLI overrides (--state-values-set, --state-values-file) │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
In the example above, `{{ .Values.replicas }}` would be `3` (values overrides defaults) and `{{ .Values.cluster }}` would be `dev` (only defined in defaults).
|