Chapter 4. Building a Chart

Charts are at the heart of Helm. In addition to installing them into a Kubernetes cluster or managing the instances of charts you’ve installed, you can build new charts or alter existing ones. In the next three chapters we will cover a lot of details about charts including creating them, the elements inside them, templating Kubernetes manifests, testing charts, dependencies, and more.

In this chapter you will learn how to create a new chart and learn about the many parts of a chart. This will include the use of several built-in commands that can help you in the chart development process.

Charts are the packages Helm works with. They are conceptually similar to Debian packages used by APT or Formula used by Homebrew for macOS. The conceptual similarity is where the similarities end. Charts are designed to target Kubernetes as a platform that has its own unique style. At the heart of charts are templates to generate Kubernetes manifests that can be installed and managed in a cluster.

Before we dig into templates in Chapter 5, let’s start by creating a basic fully functional chart. To do that we will cover an example chart named anvil. Using that chart you will learn about using Helm to generate a chart, the structure of charts and files within them, packaging charts, and linting charts. Reference the online source for this chart at https://github.com/Masterminds/learning-helm/tree/main/chapter4/anvil.

The Chart Creation Command

Helm includes the create command to make it easy for you to create a chart of your own, and it’s a great way to get started. This command creates a new Nginx chart, with a name of your choice, following best practices for a chart layout. Since Kubernetes clusters can have different methods to expose an application, this chart makes the way Nginx is exposed to network traffic configurable so it can be exposed in a wide variety of clusters.

The create command creates a chart for you, with all the required chart structure and files. These files are documented to help you understand what is needed, and the templates it provides showcase multiple Kubernetes manifests working together to deploy an application. In addition, you can install and test this chart right out of the box.

Throughout this chapter we will look at an example application named anvil. It is a simple application that will show you the structure of a chart and provide you the chance to alter a chart for a different application. To create the new chart, run the following command from a command prompt:

$ helm create anvil

This will create a new chart as a subdirectory of your current directory with the name anvil.

The new chart is a directory containing a number of files and folders. This does not include every file and folder—you will discover some more in the next couple chapters. These are the basic ones needed for a functioning chart:

anvil
├── Chart.yaml 1
├── charts 2
├── templates 3
│   ├── NOTES.txt 4
│   ├── _helpers.tpl
│   ├── deployment.yaml
│   ├── ingress.yaml
│   ├── service.yaml
│   ├── serviceaccount.yaml
│   └── tests
│       └── test-connection.yaml 5
└── values.yaml 6
1

The Chart.yaml file contains metadata and some functionality controls for the chart.

2

Dependent charts can optionally be held in the charts directory. Chart dependencies are covered in Chapter 6. For now this will be an empty directory.

3

Templates used to generate Kubernetes manifests are stored in the templates directory.

4

The NOTES.txt file is a special template. When a chart is installed, the NOTES.txt template is rendered and displayed rather than being installed into a cluster.

5

Templates can include tests that are not installed as part of the install or upgrade commands. This chart includes a test that is used by the helm test command. Testing is covered in Chapter 6.

6

Default values passed to the templates when Helm is rendering the manifests are in the values.yaml file. When you instantiate a chart, these values can be overridden.

You can install this newly created chart without any modifications by running the following command:

$ helm install myapp anvil

When you run this command Helm will create an instance of the chart running in the cluster with the name myapp. It will install it using the currently configured connection and context you use for Kubernetes. Helm is using the same configuration you’re using when you use kubectl, the command-line application for Kubernetes. In that command the final argument of anvil is the directory where the chart is located.

The output from this command includes content generated by rendering the NOTES.txt template, as shown here:

NAME: myapp
LAST DEPLOYED: Sun Apr  5 08:12:59 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
1. Get the application URL by running these commands:
  export POD_NAME=$(kubectl get pods --namespace default ↵
    -l "app.kubernetes.io/name=anvil,app.kubernetes.io/instance=myapp" ↵
    -o jsonpath="{.items[0].metadata.name}")
  echo "Visit http://127.0.0.1:8080 to use your application"
  kubectl --namespace default port-forward $POD_NAME 8080:80

The NOTES section contains information on connecting to the application. Depending on the values you pass into the chart when it is instantiated, this information can be very different. This chart can be configured to use a ClusterIP, NodePort, LoadBalancer, and Ingress to expose an application. By default, a ClusterIP is used.

If you follow the directions in the notes you will see the default Nginx web page to show you it’s running, as shown in Figure 4-1.

Nginx
Figure 4-1. Default Nginx web page when you visit the running application

The methods to expose the application are tied to built-in Kubernetes resource types rather than features of the application. That makes them portable to your custom applications. The methods to expose applications include:

ClusterIP

A configuration option on the Kubernetes Service resource type that exposes the service on a cluster-level internal IP address.

NodePort

An alternative option for Kubernetes Service resources that exposes the service on a static port of each node. A ClusterIP is automatically created as well.

LoadBalancer

A Kubernetes Service configuration option that exposes an application externally using a load balancer provided by the hosting provider.

Ingress

Ingress resources are additional resources to Services that expose a service over HTTP and HTTPS. An Ingress Controller, such as ingress-nginx, is required for this to work.

If you installed this chart into your cluster to test it, you can delete the instance from your cluster by running the following command:

$ helm delete myapp
Note

When the chart is installed the image used for Nginx, by default, is the latest version of the image from the Docker Official Images. If the Kubernetes cluster you are working with does not have access to hub.docker.com you won’t be able to install the image. You would need to set the image to one your cluster has access to.

Now that a working chart has been scaffolded, let’s take a look at what’s inside and modify it for the Anvil application.

The Chart.yaml File

Look inside the anvil directory and you’ll find a file named Chart.yaml. The Chart.yaml file tells Helm and other tools about your chart. Other tools include Kubeapps (an on-premise catalog and application installer), the Artifact Hub (a listing of cloud native artifacts), and many others.

When you open the Chart.yaml file, you will see the contents shown in Example 4-1.

Example 4-1. The generated Chart.yaml file
apiVersion: v2 1
name: anvil 2
description: A Helm chart for Kubernetes

# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into ↵
  versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer.↵
  They're included as
# a dependency of application charts to inject those utilities and functions ↵
  into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be ↵
  deployed.
type: application

# This is the chart version. This version number should be incremented each ↵
  time you make changes
# to the chart and its templates, including the app version.
version: 0.1.0 3

# This is the version number of the application being deployed. This version ↵
  number should be
# incremented each time you make changes to the application. Versions are not ↵
  expected to
# follow Semantic Versioning. They should reflect the version the application ↵
  is using.
appVersion: 1.16.0
1

The apiVersion tells Helm what structure the chart is using. An apiVerison of v2 is designed for Helm v3.

2

The name is used to identify the chart in various places.

3

Charts can have many versions. Helm uses the version information to order and identify charts.

This Chart.yaml file contains numerous keys, of which only three are required. The apiVersion property tells Helm which version of a chart this is. Helm v3 can work with charts whose apiVersion is v1 or v2. v1 charts are those designed to work with previous versions of Helm. If your charts are designed to work with Helm v3 or newer you should set this to v2. The value of name is typically used as part of the name for Kubernetes resources. This means names are limited to lowercase alphanumeric, -, and . characters and must start and end with an alphanumeric character. Names are typically lowercase alphanumeric characters. The final required key is version, containing the version of the chart. Versions are expected to follow Semantic Versioning, which was covered in Chapter 2.

You might notice that the style of a Chart.yaml file is similar but mildly different from those of Kubernetes manifests. Chart.yaml files are not the same format as custom resources but do contain some of the same properties. The original Chart.yaml files were designed back in 2015, long before Kubernetes custom resource definitions existed. While Helm has progressed in major versions, it has maintained a certain amount of backward compatibility over time to not disrupt users too much. This has led to differences between the Chart.yaml file format and Kubernetes manifests.

Chart.yaml files also contain descriptive information, which is useful as it’s presented in user interfaces. The description field in Example 4-1 is one such field, but you can add additional fields, such as the following:

  • home is a URL to the chart or projects homepage.

  • icon is an image (e.g., PNG or SVG file) in the form of a URL.

  • maintainers contains a list of maintainers. Each maintainer on the list can have a name, email, and URL.

  • keywords can hold a list of keywords about the project.

  • sources is for a list of URLs to source code for the project or chart.

A full description of the properties in the Chart.yaml file are available in Appendix A, for reference.

The generated Chart.yaml file can be modified for the Anvil application. The following modifications update the required fields, add some descriptive files, and remove comments:

apiVersion: v2
name: anvil
description: A surprise to catch something speedy.
version: 0.1.0
appVersion: 9.17.49
icon: https://wile.example.com/anvil.svg
keywords:
  - road runner
  - anvil
home: https://wile.example.com/
sources:
  - https://github.com/Masterminds/learning-helm/tree/main/chapter4/anvil
maintainers:
  - name: ACME Corp
    email: maintainers@example.com
  - name: Wile E. Coyote
    email: wile@example.com

One property that was in the generated Chart.yaml file but is not in the one for Anvil is type. Anvil is an application which is the default value for the type field, so the type field is not required. The other type of chart is a library chart, which is covered in Chapter 7.

The appVersion property is unique. It is both descriptive and regularly used within the templates. The appVersion property represents the version of the primary or combined application. For example, if the application being packaged was WordPress, it would be the version of WordPress.

Tip

The icon property is a URL, and that can include data URLs. Data URLs enable you to embed small files in URL form. This is especially useful if the logo is a small SVG file. If a chart may be run in air-gapped on-premise environments or you do not want user interfaces constantly downloading a file from your web server, a data URL is a useful choice.

Modifying Templates

In order to modify this chart for the Anvil application or your own custom application, you will need to understand and modify templates. Out of the box, the templates created by the helm create command run Nginx as a stateless application. In the example we are working through, Nginx will need to be replaced with Anvil.

Helm is written in the Go programming language, and Go includes template packages. Helm leverages the text template package as the foundation for its templates. This template language is similar to other template languages and includes loops, if/then logic, functions, and more. An example template of a YAML file follows:

product: {{ .Values.product | default "rocket" | quote }}

In this YAML file there is a key name of product. The value is generated using a template. {{ and }} are the opening and closing brackets to enter and exit template logic. There are three parts to the template logic separated by a |. This is called a pipeline, and it works the same way as a pipeline in Unix-based systems. The value or output of a function on the left is passed in as the last argument to the next item in the pipeline. In this case, the pipeline starts with the value from the property in .Values.product. This comes from the data object passed in when the templates are rendered. The value of this data is piped as the last argument to the default function, which is one of the functions provided by Helm. If the value passed in is empty, the default function uses the default value of "rocket", ensuring there is a value. This is then sent to the quote function, which ensures the string is wrapped in quotes before writing it to the template.

The . at the start of .Values.product is important. This is considered the root object in the current scope. .Values is a property on the root object.

The Deployment

Helm charts can hold templates for any Kubernetes resource type you might use. That includes StatefulSets, Jobs, PersistentVolumeClaims, Services, and much more. The chart created with helm create is designed to run a stateless service as a Kubernetes Deployment. The example application we are using here for Anvil is a stateless application, which means it will work well as a deployment.

To understand the Deployment template, we can take a look at the deployment.yaml file in the templates directory of the chart. The following is the templated version of the Deployment up to the spec section:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "anvil.fullname" . }}
  labels:
    {{- include "anvil.labels" . | nindent 4 }}

This looks very similar to the start of a Kubernetes manifest. It has an apiVersion, the kind, and metadata. Once you get into the metadata you’ll notice the templating begins.

Tip

If you are unfamiliar with the structure of Kubernetes Deployments, you can read about them in the Kubernetes documentation.

The include template function enables including the output of one template in another template, and this works in pipelines. The first argument to the include function is the name of the template to use. The . passed in as the second argument is the root object. This is passed in so the properties and functions on the root object can be used within the called template.

anvil.fullname and anvil.labels are two reusable templates included in the chart via the _helpers.tpl file. (The _ at the start of the name causes it to bubble up to the top of directory listings so you can easily find it among your templates; Helm does not render them into Kubernetes manifests but does make templates in them available for use.) anvil.fullname provides a name based on the name chosen when the chart is instantiated, and anvil.labels provides labels following Kubernetes best practices. The functions are covered in more depth in Chapter 5.

After the metadata section of the template is the spec section, which reads as follows:

spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      {{- include "anvil.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "anvil.selectorLabels" . | nindent 8 }}
    spec:
    {{- with .Values.imagePullSecrets }}
      imagePullSecrets:
        {{- toYaml . | nindent 8 }}
    {{- end }}
      serviceAccountName: {{ include "anvil.serviceAccountName" . }}
      securityContext:
        {{- toYaml .Values.podSecurityContext | nindent 8 }}
      containers:
        - name: {{ .Chart.Name }}
          securityContext:
            {{- toYaml .Values.securityContext | nindent 12 }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default↵
            .Chart.AppVersion }}" 1
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
          livenessProbe:
            httpGet:
              path: /
              port: http
          readinessProbe:
            httpGet:
              path: /
              port: http
          resources:
            {{- toYaml .Values.resources | nindent 12 }}
      {{- with .Values.nodeSelector }}
      nodeSelector:
        {{- toYaml . | nindent 8 }}
      {{- end }}
    {{- with .Values.affinity }}
      affinity:
        {{- toYaml . | nindent 8 }}
    {{- end }}
    {{- with .Values.tolerations }}
      tolerations:
        {{- toYaml . | nindent 8 }}
    {{- end }}
1

The location and version of the container image is configurable via values.

The spec section completes the deployment. Most of this section is filling in data with the properties on .Values. There are a few elements that are hardcoded, such as the ports used to expose the application. Anvil is exposed over HTTP on port 80, so we do not need to change the port. If your containers are exposed on different ports, you will need to make changes here.

The value of image for the container is set using values. You won’t find the location of the image hardcoded here. This is useful for those cases where the image location needs to be set to a different location when a chart is instantiated. It means we need to change the location in the default values.

The properties on .Values are computed based on a number of factors. The default values and starting point are based on the values provided by the values.yaml file in the chart. The values.yaml file is covered in the next section. These values can be overridden by values passed in when the chart is instantiated. The helm CLI has flags to pass in values directly (i.e., --set, --set-file, and --set-string) or to pass in a file with values (i.e., -f or --values). The values are merged together, with those being passed in later taking precedence.

Templates are a large topic and typically make up the bulk of a chart. Chapter 5 is dedicated to templates.

Using the Values File

When someone instantiates an application in a Kubernetes cluster from a chart, they don’t need to supply all the values used in the templates. If they did, it would provide for a difficult user experience. This is where the values.yaml file comes in.

Charts include a values.yaml file that sits alongside the Chart.yaml file in the root of a chart. The values.yaml file contains the default values used by the chart, and it is a form of documentation for the custom values that can be passed into a chart.

values.yaml is an unstructured YAML file. There are some common and useful practices, which will be covered shortly, but nothing is required in the format of the YAML. This enables chart creators to provide a structure and information that works well for them. A values.yaml file can contain numerous things, from simple substitution for Kubernetes manifest properties to elements needed for application-specific business logic.

Container Images

The opening part of the values.yaml file created by helm create contains the image information along with some opening documentation and information on replicas:

# Default values for anvil.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.

replicaCount: 1

image:
  repository: ghcr.io/masterminds/learning-helm/anvil-app 1
  pullPolicy: IfNotPresent 2
  # Overrides the image tag whose default is the chart version.
  tag: "" 3

imagePullSecrets: [] 4
1

The location of the image. It has been updated to reflect the location of Anvil.

2

A policy of IfNotPresent means that the image will be cached in the Kubernetes cluster by the version being used. Always is another option that bypasses the cache and always downloads from the repository.

3

By default this chart uses the appVersion as the tag. If an image tag is specified, it is used instead of the appVersion.

4

A list of pull secrets is used when credentials are needed to access a container registry location that is protected with a username and password.

This chart and the values represent an application bundled as a single image. The patterns used in the values.yaml file are designed with that in mind. For example, there is only one image location. If your applications have multiple images, each image would have a section containing much of the information here. This includes replicaCount, which is the number of replicas Kubernetes will use when the Deployment is created.

The image section contains details about the image. The repository contains the location of the image to use while the pullPolicy tells Kubernetes how often to fetch or cache the images. If a moving tag, such as stable, is used, the pullPolicy should be set to Always so that changes are picked up. Since a version is being used, the default pullPolicy is set to IfNotPresent so that a cached version can be used if available. The tag property provides an opportunity to set a tag that is different from the appVersion set in the Chart.yaml file.

You might notice there is no method to set a digest when fetching an image. Digests can be different when images are in different repositories. For example, if the Anvil image were copied from Docker Hub to Quay, another image repository, the digest would change for the same image even if the tag and content remained the same. Chapter 5 provides an example of adding in support for a digest to a chart, if that is desired.

If you need to pull an image from a container registry with access controls, Kubernetes needs to know how to do that. This happens through the use of pull secrets. imagePullSecrets allows you to list the names of pull secrets with access to private registries. Reference the documentation for creating a pull secret.

The generated chart has some security considerations built in that can be enabled or otherwise configured. A service account for the chart instance is created by default, while the other options are opt-in. The following is what is generated by helm create:

serviceAccount:
  # Specifies whether a service account should be created
  create: true
  # Annotations to add to the service account
  annotations: {}
  # The name of the service account to use.
  # If not set and create is true, a name is generated using the fullname ↵
    template
  name:

podSecurityContext: {}
  # fsGroup: 2000

securityContext: {}
  # capabilities:
  #   drop:
  #   - ALL
  # readOnlyRootFilesystem: true
  # runAsNonRoot: true
  # runAsUser: 1000

You will notice that most of the properties in the configuration are comments and are inactive. When the chart is rendered with the values as comments, there is no value for those properties. The value is empty. By having a structure and values as comments the chart is documenting the structure and default values that can be used but isn’t turning on those features.

Exposing Services

The next section of the values.yaml file deals with exposing the application for others to consume:

service:
  type: ClusterIP
  port: 80

ingress:
  enabled: false
  annotations: {}
    # kubernetes.io/ingress.class: nginx
    # kubernetes.io/tls-acme: "true"
  hosts:
    - host: chart-example.local
      paths: []
  tls: []
  #  - secretName: chart-example-tls
  #    hosts:
  #      - chart-example.local

In Kubernetes there are two built-in objects you can use to expose applications. The first is a Service. The service property will let you select the type of Service being used. While ClusterIP is used by default, other options such as NodePort and LoadBalancer can be used. The few lines of YAML in the service section are paired with the generated service.yaml template to create a full Service manifest to upload to Kubernetes.

The second built-in object is the Ingress manifest, which can be paired with a Service, and the chart has the capability to generate them. Ingress configuration provides a means to show off a common pattern found in charts: the use of an enabled property to turn features on and off. In this case ingress.enabled is set to false. When Helm renders the templates and sees a value of false, the Ingress manifest is skipped. This is due to the use of an if logic statement in the Ingress template found in the generated ingress.yaml file.

Resource Limits

When you run applications in production, it is a good practice to set resource limits. This prevents, for example, a memory leak in one container from disrupting other containers. When a chart author creates a chart that others are going to use, they may not know where it will be installed and how many resources will be available there. Could this be installed on a laptop by a developer or someone testing out the chart? Or, might this be installed on large production servers? To handle this variance in environment, the recommendation is to put in resource limits and then turn them into comments. This can be found in the next section of the values.yaml file:

resources: {}
  # We usually recommend not to specify default resources and to leave this as
  # a conscious choice for the user. This also increases chances charts run on
  # environments with little resources, such as Minikube. If you do want to
  # specify resources, uncomment the following lines, adjust them as necessary,
  # and remove the curly braces after 'resources:'.
  # limits:
  #   cpu: 100m
  #   memory: 128Mi
  # requests:
  #   cpu: 100m
  #   memory: 128Mi

Those who install applications use these numbers as recommendations when they instantiate a chart. These numbers are the default values that have been set for a simple Nginx setup as it was generated. They work for the Anvil application. If your application will need different values, you will need to update these.

Workloads have the ability to specify details about where they are executed in a cluster by the settings node selector, tolerations, and affinity. Although these more advanced features are often not used, it is a good idea to include them in a chart for those who need them. The generated values.yaml file and templates take this into account. The following example has generated YAML keys for these advanced features. The values are empty by default with an expectation that the person who installs the chart will set values as appropriate for their installation:

nodeSelector: {}

tolerations: []

affinity: {}

Packaging the Chart

You can package the files and directories of a chart into a single archive file. This is useful for many reasons, including:

  • For distribution to other people. One of the powerful aspects of a package manager is where someone with knowledge of running an application packages it up so that others, who don’t have intimate knowledge of the platform or application, can run it.

  • When a version of an application needs to be taken through a multienvironment test process. An example of this process is where there are development, quality assurance (QA), and production environments and the application needs to pass QA prior to going into production.

  • When developing a multiservice application and developers need to run services built or otherwise handled by others as part of their development setup.

In each of these situations it is often simpler to pass around a single file for the chart than a directory structure.

Chart versions bring another wrinkle to the way you distribute and consume charts. You or someone consuming your chart may need to use different versions of the chart. This is why it’s useful to store and share different versions using chart repositories or Open Container Initiative (OCI) registries, covered in Chapter 7. In these environments, storing and sharing many files in a collection of directory structures for each version is far from simple.

Helm has the ability to build a chart archive. Each chart archive is a gzipped TAR file with the extension .tgz. Any tool that can create, extract, and otherwise work on gzipped TAR files will work with Helm’s chart archives.

When Helm generates the archive files, they are named using a pattern of chart name-version.tgz. Helm expects this same pattern when consuming them. The chart name is the name you will find inside the Chart.yaml file and the version is the chart version. This enables multiple versions of the same chart to be stored alongside each other. You can package Anvil as an archive by running:

$ helm package anvil

In this case anvil is the path to the location where the anvil chart source is located. By default, the helm package command will place the archive in the directory you were in when you ran the command.

There are some useful flags you can use when packaging a chart:

--dependency-update (-u)

Tells Helm to update the dependent charts prior to creating the archive. This will update the Chart.lock file and place a copy of the dependent charts in the chart directory. Dependencies are covered in more detail in Chapter 6.

--destination (-d)

Enables you to set the location to put the chart archive if it is different from the current working directory.

--app-version

Can be used to set the appVersion property of the Chart.yaml file. This is especially useful if you create new releases of the chart for each new release of your application running within the container and there is no other change to the chart. Automation can use a flag like this as part of the process to build the new version.

--version

Updates the chart’s version. This is useful if you’re updating the appVersion using the command line as part of the process to package a chart.

Flags for Pretty Good Privacy (PGP) signing charts

Helm charts can be cryptographically signed and verified. The package command has flags for the signing portion of the process, while commands like install and upgrade have flags for the verification portion of the process. Chapter 6 covers this process.

Sometimes you will have files in a chart directory that you do not want to include in the chart archive. Optionally, in a chart directory there can be a .helmignore file. This is similar to a .gitignore file for Git. The helm create command used earlier created one with the following contents:

# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/

Many of these extensions and patterns may look familiar because they come from various version control systems and code editors.

When the chart archive is created, you usually don’t want to include elements like your version control system data. The .helmignore file provides a place to specify what to skip. This file needs to be at the top level of the chart.

Helm is designed to work with the archive files the same way it works with directory structures. Commands like helm install and helm lint, which will be covered shortly, can be passed an archive file the same way they can be passed a directory.

Linting Charts

When developing charts, especially when working with YAML templates, it can be easy to make a mistake or miss something. To help you catch errors, bugs, style issues, and other suspicious elements, the Helm client includes a linter. This linter can be used during chart development and as part of any testing processes.

To use the linter, use the lint command on a chart as a directory or a packaged archive:

$ helm lint anvil
==> Linting anvil

1 chart(s) linted, 0 chart(s) failed

The first line is the command you run, while the following lines are output by Helm. In this case there were no issues. You could use this command on an archive file like the one in the previous section. To do that, change the anvil argument, set to the directory location for the chart, to the archive file anvil-0.1.0.tgz.

This command is able to lint multiple charts in a single command. For example, if you had a second chart called mychart and wanted to lint it alongside anvil, you could run the following command:

$ helm lint anvil mychart

The three levels of actionable feedback about charts Helm provides are info, warning, and errors. Info-level feedback is informational; charts can be installed with info-level feedback. Info-level feedback causes Helm to have an exit code of 0. Error-level feedback means there is a problem with the chart. If a chart generates an Invalid manifest for Kubernetes, such as YAML being invalid, Helm will generate an error. Errors cause Helm to have a nonzero exit code, which is useful to catch issues in automated testing tools. In the middle are warning messages. These messages address findings that may cause issues. By default, warning messages cause Helm to have an exit code of 0, but Helm adds a --strict flag that causes the exit codes to be nonzero. You can choose how to handle these in automation.

In this case there were no issues found with the anvil chart. A default chart, created by helm create, will have a single info message about a missing icon property in the Chart.yaml file. This is an info-level notice so that people are aware it is missing. The missing icon will not affect the operation of the chart, but it will affect the way it is displayed in user interfaces.

Conclusion

Creating a simple chart for your application is straightforward when you use the helm create command. Even when your applications are more complicated, the structure of charts is able to accommodate them, and the helm create command can help you. With a few minor modifications made in this chapter you can install the Anvil chart using helm install and see the custom application running in your cluster. You can use this same flow to create your own charts.

In the next chapter you will learn about creating templates with an emphasis on how the template language works and how you can apply it to Kubernetes templates stored in charts. Templates are usually the largest part of a chart where you will spend the most time. Understanding what you have available to you when you create templates will make the process of developing them faster and easier.

Get Learning Helm now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.