Skip to main content

Define Preflight Checks for Helm Charts

This topic provides a basic understanding and some key considerations about preflight checks for Helm charts with KOTS and Helm installations, and to help guide you in defining them for your application.

note

A KOTS entitlement is required to create KOTS releases.

About Preflight Checks for Helm Charts

You define preflight checks based on your application needs. Replicated recommends that you use preflight checks to help ensure that your customers' environments support your application before installing the application.

Preflight checks are not included by default, so you must enable them.

For Helm installations, preflight checks run using a helm template command before running the installation to confirm the target cluster has the resources required for a successful installation. For more information about running the helm install command, see Running Preflight Checks for Helm Installations.

For KOTS installations with Helm charts, preflight checks run automatically.

Choose an Input Kind

You run preflight checks with the open source preflight kubectl plugin. For information about the preflight plugin, see Getting Started in the open source Troubleshoot documentation.

The plugin requires a preflight check specification as input. For Helm installations, you provide this specification by running helm template to produce a stream of stdout and pipe the result to preflight -. The preflight plugin automatically finds and runs preflight specifications by filtering the stream of stdout for the following input kinds:

  • Secret (kind: Secret)
  • ConfigMap (kind: ConfigMap)
  • Preflight custom resource (kind: Preflight)

All of these input options allow customization of preflight checks based on values unique to the customer, using Helm templates with conditional statements.

In KOTS v1.101.0 and later, a KOTS installation looks for the preflights specified in the Helm chart archive.

Replicated recommends writing your preflight checks specification in one or more Secrets. Using Secrets protects secure information in a cluster.

note

Alternatively, you can use a ConfigMap (kind: ConfigMap) if the specification will not collect private information from the cluster.

To create a Secret for the preflight checks specification:

  1. Create a Secret as a YAML file with kind: Secret and apiVersion: v1. The Secret must include the following:

    • The label troubleshoot.sh/kind: preflight
    • A stringData field with a key named preflight.yaml so that the preflight binary can use this Secret when it runs from the CLI

    Template:

    apiVersion: v1
    kind: Secret
    metadata:
    labels:
    troubleshoot.sh/kind: preflight
    name: "{{ .Release.Name }}-preflight-config"
    stringData:
    preflight.yaml: |
    apiVersion: troubleshoot.sh/v1beta2
    kind: Preflight
    metadata:
    name: preflight-sample
    spec:
    collectors: []
    analyzers: []
  2. Add the Secret to your Helm chart templates/ directory.

Next, define the preflight checks specification by adding collectors and analyzers. For more information, see Define the Preflight Checks Specification.

Create a Preflight Custom Resource

The Preflight custom resource lets you provide preflight checks in a Helm template without using stringData. If you use this input kind, you must use conditional statements because the custom resource definition is not installed in the cluster.

For more information about the Preflight custom resource, see Preflight and Support Bundle.

To define a Preflight custom resource as a template:

  1. Create a Preflight custom resource YAML file using kind: Preflight. Wrap the custom resource template in an {{ if so that it is only rendered when specified in the values.yaml file or on the Helm CLI.

    Example:

    {{ if .Values.renderpreflights }}
    apiVersion: troubleshoot.sh/v1beta2
    kind: Preflight
    metadata:
    name: example-name
    spec:
    collectors: []
    analyzers: []
    {{ end }}
  2. Configure your values.yaml file to enable the Preflight custom resource.

    Example:

    renderpreflights: true

Next, define the preflight checks specification by adding collectors and analyzers. Optionally, you can add to the default redactors. For more information, see Define the Preflight Checks Specification.

Define the Preflight Checks Specification

Any preflight checks you run are dependent on your application needs. This section gives some guidance about how to think about using collectors and analyzers to design preflight checks.

For more information about defining collectors and analyzers, see Collecting Data and Analyzing Data in the Troubleshoot documentation.

For more information about using Helm templates with collectors and analyzers, see Using Helm Templates in Specifications.

Collectors

Add collectors to define information to be collected for analysis during the analyze phase. For example, you can collect information about the MySQL version that is running in a cluster:

spec:
{{ if eq .Values.global.mariadb.enabled false }}
collectors:
- mysql:
collectorName: mysql
uri: '{{ .Values.global.externalDatabase.user }}:{{ .Values.global.externalDatabase.password }}@tcp({{ .Values.global.externalDatabase.host }}:{{ .Values.global.externalDatabase.port }})/{{ .Values.global.externalDatabase.database }}?tls=false'
{{ end }}

A Helm template is used for the URI field to avoid exposing sensitive information. The Helm template maps to the values.yaml file to populate the connection string for the MySQL database.

For more information about defining collectors and analyzers, see Collecting Data and Analyzing Data in the Troubleshoot documentation.

Analyzers

You must add analyzers to analyze the data from the collectors that you specified. Define the criteria for the pass, fail, and warn outcomes, and specify custom messages for each.

For example, you can set a fail outcome if the MySQL version is less than the minimum required. Then, specify a message to display that informs your customer of the reasons for the failure and steps they can take to fix the issue.

The following example shows an analyzer for the MySQL collector above. This analyzer specifies a fail outcome if the MySQL version is less than the minimum required. Then, specify a message to display that informs your customer of the reasons for the failure and steps they can take to fix the issue.

spec:
collectors:
- mysql:
collectorName: mysql
uri: '{{ .Values.global.externalDatabase.user }}:{{ .Values.global.externalDatabase.password }}@tcp({{ .Values.global.externalDatabase.host }}:{{ .Values.global.externalDatabase.port }})/{{ .Values.global.externalDatabase.database }}?tls=false'
analyzers:
- mysql:
checkName: Must be MySQL 8.x or later
collectorName: mysql
outcomes:
- fail:
when: connected == false
message: Cannot connect to MySQL server
- fail:
when: version < 8.x
message: The MySQL server must be at least version 8
- pass:
message: The MySQL server is ready

For more information about defining collectors and analyzers, see Collecting Data and Analyzing Data in the Troubleshoot documentation.

For basic examples of checking CPU, memory, and disk capacity, see Node Resources Analyzer in the Troubleshoot documentation.

Example

The following example shows a preflight specification in a Secret for a Helm installation. For more examples, see the Troubleshoot example repository in GitHub.

For more information about defining collectors and analyzers, see Collecting Data and Analyzing Data in the Troubleshoot documentation.

apiVersion: v1
kind: Secret
metadata:
labels:
troubleshoot.sh/kind: preflight
name: "{{ .Release.Name }}-preflight-config"
stringData:
# This is the preflight spec that will be used to run the preflight checks
# Note: here we demonstrate using Helm's templating to conditionally run a preflight check based on a value
# plus getting some configuration from the local values.yaml file
preflight.yaml: |
apiVersion: troubleshoot.sh/v1beta2
kind: Preflight
metadata:
name: preflight-sample
spec:
{{ if eq .Values.global.mariadb.enabled false }}
collectors:
- mysql:
collectorName: mysql
uri: '{{ .Values.global.externalDatabase.user }}:{{ .Values.global.externalDatabase.password }}@tcp({{ .Values.global.externalDatabase.host }}:{{ .Values.global.externalDatabase.port }})/{{ .Values.global.externalDatabase.database }}?tls=false'
{{ end }}
analyzers:
- nodeResources:
checkName: Node Count Check
outcomes:
- fail:
when: 'count() > {{ .Values.global.maxNodeCount }}'
message: "The cluster has more than {{ .Values.global.maxNodeCount }} nodes."
- pass:
message: You have the correct number of nodes.
- clusterVersion:
outcomes:
- fail:
when: "< 1.22.0"
message: The application requires at least Kubernetes 1.22.0, and recommends 1.23.0.
uri: https://kubernetes.io
- warn:
when: "< 1.23.0"
message: Your cluster meets the minimum version of Kubernetes, but we recommend you update to 1.18.0 or later.
uri: https://kubernetes.io
- pass:
message: Your cluster meets the recommended and required versions of Kubernetes.
{{ if eq .Values.global.mariadb.enabled false }}
- mysql:
checkName: Must be MySQL 8.x or later
collectorName: mysql
outcomes:
- fail:
when: connected == false
message: Cannot connect to MySQL server
- fail:
when: version < 8.x
message: The MySQL server must be at least version 8
- pass:
message: The MySQL server is ready
{{ end }}

Next Step

Test your preflight checks in a development environment. For more information, see Running Helm Preflight Checks.