mirror of https://github.com/helm/helm
Merge pull request #2007 from technosophos/docs/1993-chart-best-practices
docs(chart_best_practices): create best practices guidepull/2078/head
commit
df92425fa9
@ -0,0 +1,41 @@
|
||||
# General Conventions
|
||||
|
||||
This part of the Best Practices Guide explains general conventions.
|
||||
|
||||
## Chart Names
|
||||
|
||||
Chart names should be lower case letters and numbers. Words _may_ be separated with dashes (-):
|
||||
|
||||
Examples:
|
||||
|
||||
```
|
||||
drupal
|
||||
nginx-lego
|
||||
aws-cluster-autoscaler
|
||||
```
|
||||
|
||||
Neither uppercase letters nor underscores should be used in chart names. Dots should not be used in chart names.
|
||||
|
||||
The directory that contains a chart MUST have the same name as the chart. Thus, the chart `nginx-lego` MUST be created in a directory called `nginx-lego/`. This is not merely a stylistic detail, but a requirement of the Helm Chart format.
|
||||
|
||||
## Version Numbers
|
||||
|
||||
Wherever possible, Helm uses [SemVer 2](http://semver.org) to represent version numbers. (Note that Docker image tags do not necessarily follow SemVer, and are thus considered an unfortunate exception to the rule.)
|
||||
|
||||
When SemVer versions are stored in Kubernetes labels, we conventionally alter the `+` character to an `_` character, as labels do not allow the `+` sign as a value.
|
||||
|
||||
## Formatting YAML
|
||||
|
||||
YAML files should be indented using _two spaces_ (and never tabs).
|
||||
|
||||
## Usage of the Words Helm, Tiller, and Chart
|
||||
|
||||
There are a few small conventions followed for using the words Helm, helm, Tiller, and tiller.
|
||||
|
||||
- Helm refers to the project, and is often used as an umbrella term
|
||||
- `helm` refers to the client-side command
|
||||
- Tiller is the proper name of the backend
|
||||
- `tiller` is the name of the binary run on the backend
|
||||
- The term 'chart' does not need to be capitalized, as it is not a proper noun.
|
||||
|
||||
When in doubt, use _Helm_ (with an uppercase 'H').
|
@ -0,0 +1,20 @@
|
||||
# The Chart Best Practices Guide
|
||||
|
||||
This guide covers the Helm Team's considered best practices for creating charts.
|
||||
It focuses on how charts should be structured.
|
||||
|
||||
We focus primarily on best practices for charts that may be publicly deployed.
|
||||
We know that many charts are for internal-use only, and authors of such charts
|
||||
may find that their internal interests override our suggestions here.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [General Conventions](conventions.md): Learn about general chart conventions.
|
||||
- [Values Files](values.md): See the best practices for structuring `values.yaml`.
|
||||
- [Templates](templates.md): Learn some of the best techniques for writing templates.
|
||||
- [Requirements](requirements.md): Follow best practices for `requirements.yaml` files.
|
||||
- [Labels and Annotations](labels.md): Helm has a _heritage_ of labeling and annotating.
|
||||
- Kubernetes Resources:
|
||||
- [Pods and Pod Specs](pods.md): See the best practices for working with pod specifications.
|
||||
- [Third Party Resources](third_party_resources.md): Third Party Resources (TPRs) have their own associated best practices.
|
||||
|
@ -0,0 +1,37 @@
|
||||
# Labels and Annotations
|
||||
|
||||
This part of the Best Practices Guide discusses the best practices for using
|
||||
labels and annotations in your chart.
|
||||
|
||||
## Is it a Label or an Annotation?
|
||||
|
||||
An item of metadata should be a label under the following conditions:
|
||||
|
||||
- It is used by Kubernetes to identify this resource
|
||||
- It is useful to expose to operators for the purpose of querying the system.
|
||||
|
||||
For example, we suggest using `chart: NAME-VERSION` as a label so that operators
|
||||
can conveniently find all of the instances of a particular chart to use.
|
||||
|
||||
If an item of metadata is not used for querying, it should be set as an annotation
|
||||
instead.
|
||||
|
||||
Helm hooks are always annotations.
|
||||
|
||||
## Standard Labels
|
||||
|
||||
The following table defines common labels that Helm charts use. Helm itself never requires that a particular label be present. Labels that are marked REC
|
||||
are recommended, and _should_ be placed onto a chart for global consistency. Those marked OPT are optional. These are idiomatic or commonly in use, but are not relied upon frequently for operational purposes.
|
||||
|
||||
Name|Status|Description
|
||||
-----|------|----------
|
||||
heritage | REC | This should always be set to `Tiller`. It is for finding all things managed by Tiller.
|
||||
release | REC | This should be the `{{ .Release.Name }}`.
|
||||
chart | REC | This should be the chart name and version: `{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}`.
|
||||
app | OPT | This should be the app name, reflecting the entire app. Often the chart's `{{ .Chart.Name }}` is used for this. This is used by many Kubernetes manifests, and is not Helm-specific.
|
||||
component | OPT | This is a common label for marking the different roles that pieces may play in an application. For example, `component: frontend`
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,60 @@
|
||||
# Pods and PodTemplates
|
||||
|
||||
This part of the Best Practices Guide discusses formatting the Pod and PodTemplate
|
||||
portions in chart manifests.
|
||||
|
||||
The following (non-exhaustive) list of resources use PodTemplates:
|
||||
|
||||
- Deployment
|
||||
- ReplicationController
|
||||
- ReplicaSet
|
||||
- DaemonSet
|
||||
- StatefulSet
|
||||
|
||||
## Images
|
||||
|
||||
A container image should use a fixed tag or the SHA of the image. It should not use the tags `latest`, `head`, `canary`, or other tags that are designed to be "floating".
|
||||
|
||||
|
||||
Images _may_ be defined in the `values.yaml` file to make it easy to swap out images.
|
||||
|
||||
```
|
||||
image: {{ .Values.redisImage | quote }}
|
||||
```
|
||||
|
||||
An image and a tag _may_ be defined in `values.yaml` as two separate fields:
|
||||
|
||||
```
|
||||
image: "{{ .Values.redisImage }}:{{ .Values.redisTag }}"
|
||||
```
|
||||
|
||||
## ImagePullPolicy
|
||||
|
||||
The `imagePullPolicy` should default to an empty value, but allow users to override it:
|
||||
|
||||
```yaml
|
||||
imagePullPolicy: {{ default "" .Values.imagePullPolicy | quote }}
|
||||
```
|
||||
|
||||
## PodTemplates Should Declare Selectors
|
||||
|
||||
All PodTemplate sections should specify a selector. For example:
|
||||
|
||||
```yaml
|
||||
selector:
|
||||
matchLabels:
|
||||
app: MyName
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: MyName
|
||||
```
|
||||
|
||||
This is a good practice because it makes the relationship between the set and
|
||||
the pod.
|
||||
|
||||
But this is even more important for sets like Deployment.
|
||||
Without this, the _entire_ set of labels is used to select matching pods, and
|
||||
this will break if you use labels that change, like version or release date.
|
||||
|
||||
|
@ -0,0 +1,43 @@
|
||||
# Requirements Files
|
||||
|
||||
This section of the guide covers best practices for `requirements.yaml` files.
|
||||
|
||||
## Versions
|
||||
|
||||
Where possible, use version ranges instead of pinning to an exact version. The suggested default is to use a patch-level version match:
|
||||
|
||||
```yaml
|
||||
version: ^1.2.0
|
||||
```
|
||||
|
||||
This will match version 1.2.0 and any patches to that release (1.2.1, 1.2.999, and so on)
|
||||
|
||||
### Repository URLs
|
||||
|
||||
Where possible, use `https://` repository URLs, followed by `http://` URLs.
|
||||
|
||||
File URLs (`file://...`) are considered a "special case" for charts that are assembled by a fixed deployment pipeline. Charts that use `file://` in a `requirements.yaml` file are not allowed in the official Helm repository.
|
||||
|
||||
## Conditions and Tags
|
||||
|
||||
Conditions or tags should be added to any dependencies that _are optional_.
|
||||
|
||||
The preferred form of a condition is:
|
||||
|
||||
```yaml
|
||||
condition: somechart.enabled
|
||||
```
|
||||
|
||||
Where `somechart` is the chart name of the dependency.
|
||||
|
||||
When multiple subcharts (dependencies) together provide an optional or swappable feature, those charts should share the same tags.
|
||||
|
||||
For example, if both `nginx` and `memcached` together provided performance optimizations for the main app in the chart, and were required to both be present when that feature is enabled, then they might both have a
|
||||
tags section like this:
|
||||
|
||||
```
|
||||
tags:
|
||||
- webaccelerator
|
||||
```
|
||||
|
||||
This allows a user to turn that feature on and off with one tag.
|
@ -0,0 +1,197 @@
|
||||
# Templates
|
||||
|
||||
This part of the Best Practices Guide focuses on templates.
|
||||
|
||||
## Structure of templates/
|
||||
|
||||
The templates directory should be structured as follows:
|
||||
|
||||
- Template files should have the extension `.yaml` if they produce YAML output. The
|
||||
extension `.tpl` may be used for template files that produce no formatted content.
|
||||
- Template file names should use dashed notation (`my-example-configmap.yaml`), not camelcase.
|
||||
- Each resource definition should be in its own template file.
|
||||
- Template file names should reflect the resource kind in the name. e.g. `foo-pod.yaml`,
|
||||
`bar-svc.yaml`
|
||||
|
||||
## Names of Defined Templates
|
||||
|
||||
Defined templates (templates created inside a `{{ define }} ` directive) are
|
||||
globally accessible. That means that a chart and all of its subcharts will have
|
||||
access to all of the templates created with `{{ define }}`.
|
||||
|
||||
For that reason, _all defined template names should be namespaced._
|
||||
|
||||
Correct:
|
||||
|
||||
```yaml
|
||||
{{- define "nginx.fullname" }}
|
||||
{{/* ... */}}
|
||||
{{ end -}}
|
||||
```
|
||||
|
||||
Incorrect:
|
||||
|
||||
```yaml
|
||||
{{- define "fullname" -}}
|
||||
{{/* ... */}}
|
||||
{{ end -}}
|
||||
```
|
||||
|
||||
## Formatting Templates
|
||||
|
||||
Templates should be indented using _two spaces_ (never tabs).
|
||||
|
||||
Template directives should have whitespace after the opening braces and before the
|
||||
closing braces:
|
||||
|
||||
Correct:
|
||||
```
|
||||
{{ .foo }}
|
||||
{{ print "foo" }}
|
||||
{{- print "bar" -}}
|
||||
```
|
||||
|
||||
Incorrect:
|
||||
```
|
||||
{{.foo}}
|
||||
{{print "foo"}}
|
||||
{{-print "bar"-}}
|
||||
```
|
||||
|
||||
Templates should chomp whitespace where possible:
|
||||
|
||||
```
|
||||
foo:
|
||||
{{- range .Values.items }}
|
||||
{{ . }}
|
||||
{{ end -}}
|
||||
```
|
||||
|
||||
Blocks (such as control structures) may be indented to indicate flow of the template code.
|
||||
|
||||
```
|
||||
{{ if $foo -}}
|
||||
{{- with .Bar }}Hello{{ end -}}
|
||||
{{- end -}}
|
||||
```
|
||||
|
||||
However, since YAML is a whitespace-oriented language, it is often not possible for code indentation to follow that convention.
|
||||
|
||||
## Whitespace in Generated Templates
|
||||
|
||||
It is preferable to keep the amount of whitespace in generated templates to
|
||||
a minimum. In particular, numerous blank lines should not appear adjacent to each
|
||||
other. But occasional empty lines (particularly between logical sections) is
|
||||
fine.
|
||||
|
||||
This is best:
|
||||
|
||||
```yaml
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: example
|
||||
labels:
|
||||
first: first
|
||||
second: second
|
||||
```
|
||||
|
||||
This is okay:
|
||||
|
||||
```yaml
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
|
||||
metadata:
|
||||
name: example
|
||||
|
||||
labels:
|
||||
first: first
|
||||
second: second
|
||||
|
||||
```
|
||||
|
||||
But this should be avoided:
|
||||
|
||||
```yaml
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
|
||||
metadata:
|
||||
name: example
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
labels:
|
||||
first: first
|
||||
|
||||
second: second
|
||||
|
||||
```
|
||||
|
||||
## Comments (YAML Comments vs. Template Comments)
|
||||
|
||||
Both YAML and Helm Templates have comment markers.
|
||||
|
||||
YAML comments:
|
||||
```yaml
|
||||
# This is a comment
|
||||
type: sprocket
|
||||
```
|
||||
|
||||
Template Comments:
|
||||
```yaml
|
||||
{{- /*
|
||||
This is a comment.
|
||||
*/ -}}
|
||||
type: frobnitz
|
||||
```
|
||||
|
||||
Template comments should be used when documenting features of a template, such as explaining a defined template:
|
||||
|
||||
```yaml
|
||||
{{- /*
|
||||
mychart.shortname provides a 6 char truncated version of the release name.
|
||||
*/ }}
|
||||
{{ define "mychart.shortname" -}}
|
||||
{{ .Release.Name | trunc 6 }}
|
||||
{{- end -}}
|
||||
|
||||
```
|
||||
|
||||
Inside of templates, YAML comments may be used when it is useful for Helm users to (possibly) see the comments during debugging.
|
||||
|
||||
```
|
||||
# This may cause problems if the value is more than 100Gi
|
||||
memory: {{ .Values.maxMem | quote }}
|
||||
```
|
||||
|
||||
The comment above is visible when the user runs `helm install --debug`, while
|
||||
comments specified in `{{- /* */ -}}` sections are not.
|
||||
|
||||
## Use of JSON in Templates and Template Output
|
||||
|
||||
YAML is a superset of JSON. In some cases, using a JSON syntax can be more
|
||||
readable than other YAML representations.
|
||||
|
||||
For example, this YAML is closer to the normal YAML method of expressing lists:
|
||||
|
||||
```yaml
|
||||
arguments:
|
||||
- "--dirname"
|
||||
- "/foo"
|
||||
```
|
||||
|
||||
But it is easier to read when collapsed into a JSON list style:
|
||||
|
||||
```yaml
|
||||
arguments: ["--dirname", "/foo"]
|
||||
```
|
||||
|
||||
Using JSON for increased legibility is good. However, JSON syntax should not
|
||||
be used for representing more complex constructs.
|
||||
|
||||
When dealing with pure JSON embedded inside of YAML (such as init container
|
||||
configuration), it is of course appropriate to use the JSON format.
|
@ -0,0 +1,38 @@
|
||||
# Third Party Resources
|
||||
|
||||
This section of the Best Practices Guide deals with creating and using Third Party Resource
|
||||
objects.
|
||||
|
||||
When working with Third Party Resources (TPRs), it is important to distinguish
|
||||
two different pieces:
|
||||
|
||||
- There is a declaration of a TPR. This is the YAML file that has the kind `ThirdPartyResource`
|
||||
- Then there are resources that _use_ the TPR. Say a TPR defines `foo.example.com/v1`. Any resource
|
||||
that has `apiVersion: example.com/v1` and kind `Foo` is a resource that uses the
|
||||
TPR.
|
||||
|
||||
## Install a TPR Declaration Before Using the Resource
|
||||
|
||||
Helm is optimized to load as many resources into Kubernetes as fast as possible.
|
||||
By design, Kubernetes can take an entire set of manifests and bring them all
|
||||
online (this is called the reconciliation loop).
|
||||
|
||||
But there's a difference with TPRs.
|
||||
|
||||
For a TPR, the declaration must be registered before any resources of that TPRs
|
||||
kind(s) can be used. And the registration process sometimes takes a few seconds.
|
||||
|
||||
### Method 1: Separate Charts
|
||||
|
||||
One way to do this is to put the TPR definition in one chart, and then put any
|
||||
resources that use that TPR in _another_ chart.
|
||||
|
||||
In this method, each chart must be installed separately.
|
||||
|
||||
### Method 2: Pre-install Hooks
|
||||
|
||||
To package the two together, add a `pre-install` hook to the TPR definition so
|
||||
that it is fully installed before the rest of the chart is executed.
|
||||
|
||||
Note that if you create the TPR with a `pre-install` hook, that TPR definition
|
||||
will not be deleted when `helm delete` is run.
|
@ -0,0 +1,148 @@
|
||||
# Values
|
||||
|
||||
This part of the best practices guide covers using values. In this part of the
|
||||
guide, we provide recommendations on how you should structure and use your
|
||||
values, with focus on designing a chart's `values.yaml` file.
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
Variables names should begin with a lowercase letter, and words should be
|
||||
separated with camelcase:
|
||||
|
||||
Correct:
|
||||
|
||||
```yaml
|
||||
chicken: true
|
||||
chickenNoodleSoup: true
|
||||
```
|
||||
|
||||
Incorrect:
|
||||
|
||||
```yaml
|
||||
Chicken: true # initial caps may conflict with built-ins
|
||||
chicken-noodle-soup: true # do not use hyphens in the name
|
||||
```
|
||||
|
||||
Note that all of Helm's built-in variables begin with an uppercase letter to
|
||||
easily distinguish them from user-defined values: `.Release.Name`,
|
||||
`.Capabilities.KubeVersion`.
|
||||
|
||||
## Flat or Nested Values
|
||||
|
||||
YAML is a flexible format, and values may be nested deeply or flattened.
|
||||
|
||||
Nested:
|
||||
|
||||
```yaml
|
||||
server:
|
||||
name: nginx
|
||||
port: 80
|
||||
```
|
||||
|
||||
Flat:
|
||||
|
||||
```yaml
|
||||
serverName: nginx
|
||||
serverPort: 80
|
||||
```
|
||||
|
||||
In most cases, flat should be favored over nested. The reason for this is that
|
||||
it is simpler for template developers and users.
|
||||
|
||||
|
||||
For optimal safety, a nested value must be checked at every level:
|
||||
|
||||
```
|
||||
{{ if .Values.server }}
|
||||
{{ default "none" .Values.server.name }}
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
For every layer of nesting, an existence check must be done. But for flat
|
||||
configuration, such checks can be skipped, making the template easier to read
|
||||
and use.
|
||||
|
||||
```
|
||||
{{ default "none" .Values.serverName }}
|
||||
```
|
||||
|
||||
When there are a large number of related variables, and at least one of them
|
||||
is non-optional, nested values may be used to improve readability.
|
||||
|
||||
## Make Types Clear
|
||||
|
||||
YAML's type coercion rules are sometimes counterintuitive. For example,
|
||||
`foo: false` is not the same as `foo: "false"`. Large integers like `foo: 12345678`
|
||||
will get converted to scientific notation in some cases.
|
||||
|
||||
The easiest way to avoid type conversion errors is to be explicit about strings,
|
||||
and implicit about everything else. Or, in short, _quote all strings_.
|
||||
|
||||
Often, to avoid the integer casting issues, it is advantageous to store your
|
||||
integers as strings as well, and use `{{ int $value }}` in the template to convert
|
||||
from a string back to an integer.
|
||||
|
||||
In most cases, explicit type tags are respected, so `foo: !!string 1234` should
|
||||
treat `1234` as a string. _However_, the YAML parser consumes tags, so the type
|
||||
data is lost after one parse.
|
||||
|
||||
## Consider How Users Will Use Your Values
|
||||
|
||||
There are three potential sources of values:
|
||||
|
||||
- A chart's `values.yaml` file
|
||||
- A values file supplied by `helm install -f` or `helm upgrade -f`
|
||||
- The values passed to a `--set` flag on `helm install` or `helm upgrade`
|
||||
|
||||
When designing the structure of your values, keep in mind that users of your
|
||||
chart may want to override them via either the `-f` flag or with the `--set`
|
||||
option.
|
||||
|
||||
Since `--set` is more limited in expressiveness, the first guidelines for writing
|
||||
your `values.yaml` file is _make it easy to override from `--set`_.
|
||||
|
||||
For this reason, it's often better to structure your values file using maps.
|
||||
|
||||
Difficult to use with `--set`:
|
||||
|
||||
```yaml
|
||||
servers:
|
||||
- name: foo
|
||||
port: 80
|
||||
- name: bar
|
||||
port: 81
|
||||
```
|
||||
|
||||
Easy to use:
|
||||
|
||||
```yaml
|
||||
servers:
|
||||
foo:
|
||||
port: 80
|
||||
bar:
|
||||
port: 81
|
||||
```
|
||||
|
||||
## Document 'values.yaml'
|
||||
|
||||
Every defined property in 'values.yaml' should be documented. The documentation string should begin with the name of the property that it describes, and then give at least a one-sentence description.
|
||||
|
||||
Incorrect:
|
||||
|
||||
```
|
||||
# the host name for the webserver
|
||||
serverHost = example
|
||||
serverPort = 9191
|
||||
```
|
||||
|
||||
Correct:
|
||||
|
||||
```
|
||||
# serverHost is the host name for the webserver
|
||||
serverHost = example
|
||||
# serverPort is the HTTP listener port for the webserver
|
||||
serverPort = 9191
|
||||
|
||||
```
|
||||
|
||||
Beginning each comment with the name of the parameter it documents makes it easy to grep out documentation, and will enable documentation tools to reliably correlate doc strings with the parameters they describe.
|
Loading…
Reference in new issue