162 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Markdown
		
	
	
	
			
		
		
	
	
			162 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Markdown
		
	
	
	
| # The Helmfile Best Practices Guide
 | ||
| 
 | ||
| This guide covers the Helmfile’s considered patterns for writing advanced helmfiles. It focuses on how helmfile should be structured and executed.
 | ||
| 
 | ||
| ## Missing keys and Default values
 | ||
| 
 | ||
| helmfile tries its best to inform users for noticing potential mistakes.
 | ||
| 
 | ||
| One example of how helmfile achieves it is that, `helmfile` fails when you tried to access missing keys in environment values.
 | ||
| 
 | ||
| That is, the following example let `helmfile` fail when you have no `eventApi.replicas` defined in environment values.
 | ||
| 
 | ||
| ```
 | ||
| {{ .Environment.Values.eventApi.replicas | default 1 }}
 | ||
| ```
 | ||
| 
 | ||
| In case it isn't a mistake and you do want to allow missing keys, use the `getOrNil` template function:
 | ||
| 
 | ||
| ```
 | ||
| {{ .Environment.Values | getOrNil "eventApi.replicas" }}
 | ||
| ```
 | ||
| 
 | ||
| This result in printing `<no value` in your template, that may or may not result in a failure.
 | ||
| 
 | ||
| If you want a kind of default values that is used when a missing key was referenced, use `default` like:
 | ||
| 
 | ||
| ```
 | ||
| {{ .Environment.Values | getOrNil "eventApi.replicas" | default 1 }}
 | ||
| ```
 | ||
| 
 | ||
| Now, you get `1` when there is no `eventApi.replicas` defined in environment values.
 | ||
| 
 | ||
| ## Release Template / Conventional Directory Structure
 | ||
| 
 | ||
| Introducing helmfile into a large-scale project that involes dozens of releases often results in a lot of repetitions in `helmfile.yaml` files.
 | ||
| 
 | ||
| The example below shows repetitions in `namespace`, `chart`, `values`, and `secrets`:
 | ||
| 
 | ||
| ```yaml
 | ||
| releases:
 | ||
| # *snip*
 | ||
| - name: heapster
 | ||
|   namespace: kube-system
 | ||
|   chart: stable/heapster
 | ||
|   version: 0.3.2
 | ||
|   values:
 | ||
|   - "./config/heapster/values.yaml"
 | ||
|   - "./config/heapster/{{ .Environment.Name }}.yaml"
 | ||
|   secrets:
 | ||
|   - "./config/heapster/secrets.yaml"
 | ||
|   - "./config/heapster/{{ .Environment.Name }}-secrets.yaml"
 | ||
| 
 | ||
|  - name: kubernetes-dashboard
 | ||
|   namespace: kube-system
 | ||
|   chart: stable/kubernetes-dashboard
 | ||
|   version: 0.10.0
 | ||
|   values:
 | ||
|   - "./config/kubernetes-dashboard/values.yaml"
 | ||
|   - "./config/kubernetes-dashboard/{{ .Environment.Name }}.yaml"
 | ||
|   values:
 | ||
|   - "./config/kubernetes-dashboard/secrets.yaml"
 | ||
|   - "./config/kubernetes-dashboard/{{ .Environment.Name }}-secrets.yaml"
 | ||
| ```
 | ||
| 
 | ||
| This is where Helmfile's advanced feature called Release Template comes handy.
 | ||
| 
 | ||
| It allows you to abstract away the repetitions in releases into a template, which is then included and executed by using YAML anchor/alias:
 | ||
| 
 | ||
| ```yaml
 | ||
| templates:
 | ||
|   default: &default
 | ||
|     chart: stable/{{`{{ .Release.Name }}`}}
 | ||
|     namespace: kube-system
 | ||
|     # This prevents helmfile exiting when it encounters a missing file
 | ||
|     # Valid values are "Error", "Warn", "Info", "Debug". The default is "Error"
 | ||
|     # Use "Debug" to make missing files errors invisible at the default log level(--log-level=INFO)
 | ||
|     missingFileHandler: Warn
 | ||
|     values:
 | ||
|     - config/{{`{{ .Release.Name }}`}}/values.yaml
 | ||
|     - config/{{`{{ .Release.Name }}`}}/{{`{{ .Environment.Name }}`}}.yaml
 | ||
|     secrets:
 | ||
|     - config/{{`{{ .Release.Name }}`}}/secrets.yaml
 | ||
|     - config/{{`{{ .Release.Name }}`}}/{{`{{ .Environment.Name }}`}}-secrets.yaml
 | ||
| 
 | ||
| releases:
 | ||
| - name: heapster
 | ||
|   <<: *default
 | ||
| - name: kubernetes-dashboard
 | ||
|   <<: *default
 | ||
| ```
 | ||
| 
 | ||
| See the [issue 428](https://github.com/roboll/helmfile/issues/428) for more context on how this is supposed to work.
 | ||
| 
 | ||
| ## Layering
 | ||
| 
 | ||
| You may occasionally end up with many helmfiles that shares common parts like which repositories to use, and whichi release to be bundled by default.
 | ||
| 
 | ||
| Use Layering to extract te common parts into a dedicated *library helmfile*s, so that each helmfile becomes DRY.
 | ||
| 
 | ||
| Let's assume that your `helmfile.yaml` looks like:
 | ||
| 
 | ||
| ```
 | ||
| { readFile "commons.yaml" }}
 | ||
| ---
 | ||
| {{ readFile "environments.yaml" }}
 | ||
| ---
 | ||
| releases:
 | ||
| - name: myapp
 | ||
|   chart: mychart
 | ||
| ```
 | ||
| 
 | ||
| Whereas `commons.yaml` contained a monitoring agent:
 | ||
| 
 | ||
| ```yaml
 | ||
| releases:
 | ||
| - name: metricbaet
 | ||
|   chart: stable/metricbeat
 | ||
| ```
 | ||
| 
 | ||
| And `environments.yaml` contained well-known environments:
 | ||
| 
 | ||
| ```yaml
 | ||
| environments:
 | ||
|   development:
 | ||
|   production:
 | ||
| ```
 | ||
| 
 | ||
| At run time, template expressions in your `helmfile.yaml` are executed:
 | ||
| 
 | ||
| ```yaml
 | ||
| releases:
 | ||
| - name: metricbaet
 | ||
|   chart: stable/metricbeat
 | ||
| ---
 | ||
| environments:
 | ||
|   development:
 | ||
|   production:
 | ||
| ---
 | ||
| releases:
 | ||
| - name: myapp
 | ||
|   chart: mychart
 | ||
| ```
 | ||
| 
 | ||
| Resulting YAML documents are merged in the order of occurrence,
 | ||
| so that your `helmfile.yaml` becomes:
 | ||
| 
 | ||
| ```yaml
 | ||
| environments:
 | ||
|   development:
 | ||
|   production:
 | ||
| 
 | ||
| releases:
 | ||
| - name: metricbaet
 | ||
|   chart: stable/metricbeat
 | ||
| - name: myapp
 | ||
|   chart: mychart
 | ||
| ```
 | ||
| 
 | ||
| Great!
 | ||
| 
 | ||
| Now, repeat the above steps for each your `helmfile.yaml`, so that all your helmfiles becomes DRY.
 |