Skip to content

Commit

Permalink
docs(chart_best_practices): create best practices guide
Browse files Browse the repository at this point in the history
This adds a basic Chart Best Practices guide.

Closes helm#1993
  • Loading branch information
technosophos committed Feb 22, 2017
1 parent 1113a10 commit a602d6b
Show file tree
Hide file tree
Showing 7 changed files with 377 additions and 0 deletions.
36 changes: 36 additions & 0 deletions docs/chart_best_practices/conventions.md
@@ -0,0 +1,36 @@
# General Conventions

This part of the Best Practices Guide explains conventions.

## Formatting YAML

YAML files should be indented using _two spaces_ (and never tabs).

## Formatting Templates

Templates should be indented using _two spaces_.


Template directives should have whitespace after the opening braces and before the
closing braces:

Correct:
```
{{ print "foo" }}
{{- print "bar" -}}
```

Incorrect:
```
{{print "foo"}}
{{-print "bar"-}}
```

Templates should chomp whitespace where possible:

```
foo:
{{- range .Values.items }}
{{ . }}
{{ end -}}
```
18 changes: 18 additions & 0 deletions docs/chart_best_practices/index.md
@@ -0,0 +1,18 @@
# 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 Convntions](conventions.md)
[Values Files](values.md)
[Templates](templates.md)
[Labels and Annotations](labels.md)
[Pods and PodTemplates](pods.md)
[Third Party Resources](third_party_resources.md):w

37 changes: 37 additions & 0 deletions docs/chart_best_practices/labels.md
@@ -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 a label
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`





60 changes: 60 additions & 0 deletions docs/chart_best_practices/pods.md
@@ -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.


39 changes: 39 additions & 0 deletions docs/chart_best_practices/templates.md
@@ -0,0 +1,39 @@
# 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 -}}
```

39 changes: 39 additions & 0 deletions docs/chart_best_practices/third_party_resources.md
@@ -0,0 +1,39 @@
# 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. We strongly
suggest using the `--wait` flag when installing this way.

Note that if you create the TPR with a `pre-install` hook, that TPR definition
will not be deleted when `helm delete` is run.
148 changes: 148 additions & 0 deletions docs/chart_best_practices/values.md
@@ -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.

0 comments on commit a602d6b

Please sign in to comment.