L’interface est localisée ; les présentations et documents des Skills sont traduits automatiquement par IA dans votre langue. Le code, les commandes, chemins et termes techniques restent en version originale, et le document de l’auteur fait foi.
Explorer les Skills
Déploiement et opérations · SKILL.md

helm-chart-development

helm-chart-development

Builds, templates, and manages Helm charts with Chart.yaml, values.yaml, and Go template files for reliable Kubernetes application deployment and versioned releases.

Independent GitHub snapshot · Automated classification · Original documentation governs

Documentation de la Skill

GitHub

name: helm-chart-development description: Builds, templates, and manages Helm charts with Chart.yaml, values.yaml, and Go template files for reliable Kubernetes application deployment and versioned releases. license: MIT compatibility: opencode metadata: version: "1.0.0" domain: cncf role: implementation scope: implementation output-format: code triggers: helm chart development, helm templating, helm values, helm debugging, chartmuseum, helm registry, helm lint, chart.yaml archetypes: - tactical - generation anti_triggers: - kustomize overlays - argo cd workflows response_profile: verbosity: medium directive_strength: high abstraction_level: operational related-skills: cncf-kubernetes, coding-kubernetes-deployments-management, cncf-prometheus

Helm Chart Development

Implements the full lifecycle of Helm chart creation, templating, packaging, and distribution — from initial helm create scaffolding through production-ready chart releases on a registry or ChartMuseum.

TL;DR Checklist

  • Chart.yaml declares apiVersion: v2, a stable version, and appVersion
  • values.yaml provides sensible defaults and documents every field with comments
  • Templates use {{- / -}} whitespace control to avoid blank lines in rendered YAML
  • {{ include }} helpers keep repeated logic DRY and namespaced per release
  • helm lint <chart-dir> passes before any commit
  • helm template . renders cleanly with default and overridden values
  • helm package <chart-dir> produces a tarball with correct semantic version
  • Chart is pushed to a registry or ChartMuseum with helm push or helm push chart.tgz chartmuseum://...

When to Use

Use this skill when:

  • You are authoring a new Helm chart from scratch or converting an existing deployment to a chart
  • You need to parameterize a Kubernetes manifest so teams can install it with custom values
  • You are debugging a chart that renders incorrectly or fails helm lint
  • You are packaging and distributing a chart to a Helm registry or ChartMuseum for team use
  • You need to create reusable template helpers (labels, ingress, service) across multiple template files

When NOT to Use

Avoid this skill for:

  • Pure Kustomize overlay management — use Kustomize-native tooling instead
  • Argo CD application-of-application workflows — Helm is for packaging, Argo CD is for GitOps sync
  • Generating one-off manifests for non-Helm users — output pure YAML directly instead
  • Managing cluster-wide operators with multi-chart dependencies — use helm install with proper requirements.yaml/dependencies in Chart.yaml

Core Workflow

Phase 1: Scaffolding and Structure

  1. Create the chart scaffold — Run helm create <chart-name> in an empty directory. This generates Chart.yaml, values.yaml, and templates/ with a starter deployment.yaml, service.yaml, and helpers.tpl.

  2. Audit the scaffold — Read every generated file. The scaffold is intentionally generic: update Chart.yaml metadata, remove files you do not need (e.g., ingress.yaml if the app has no ingress), and rename files to match your application's resources.

  3. Define the directory layout — A production chart follows this structure:

    my-chart/
    ├── Chart.yaml            # Chart metadata
    ├── values.yaml           # Default values
    ├── values.schema.json    # Optional JSON Schema validation
    ├── templates/
    │   ├── _helpers.tpl      # Reusable named templates
    │   ├── deployment.yaml
    │   ├── service.yaml
    │   └── NOTES.txt
    └── .helmignore           # Files to exclude from packaging
    

    Checkpoint: Run helm lint my-chart/ and verify it reports no errors or only warnings you have intentionally accepted.

Phase 2: Chart.yaml and Values

  1. Write Chart.yaml — Use apiVersion: v2 for modern Helm charts. Set version to a semantic version (e.g., 1.2.0) and appVersion to the application's version string:

    apiVersion: v2
    name: my-chart
    description: A Helm chart for deploying my application
    type: application
    version: 1.2.0
    appVersion: "3.1.0"
    keywords:
      - web
      - app
    maintainers:
      - name: Platform Team
        email: platform@example.com
    

    Checkpoint: Confirm version is valid semver and appVersion is quoted if it contains non-numeric characters.

  2. Write values.yaml — Provide defaults for every configurable aspect of your chart. Every top-level key in values.yaml is overridable at install time via --set or --values. Comment each section to document its purpose:

    # Replicas for the deployment
    replicaCount: 2
    
    # Container image configuration
    image:
      repository: myregistry/my-app
      pullPolicy: IfNotPresent
      # Override the image tag explicitly
      tag: ""
    
    # Service exposure
    service:
      type: ClusterIP
      port: 80
      targetPort: 8080
    
    # Resource limits and requests
    resources:
      limits:
        cpu: 500m
        memory: 512Mi
      requests:
        cpu: 250m
        memory: 256Mi
    
    # Autoscaling configuration (disabled by default)
    autoscaling:
      enabled: false
      minReplicas: 3
      maxReplicas: 10
      targetCPUUtilizationPercentage: 80
    

    Checkpoint: Run helm template my-chart/ --set replicaCount=5 and verify the rendered output reflects the override.

Phase 3: Templating

  1. Implement _helpers.tpl — Define named templates for labels, selectors, and any repeated patterns. Use the chart's fullname to namespace helper output:

    {{/*
    Generate basic labels including app.kubernetes.io/version.
    */}}
    {{- define "my-chart.labels" -}}
    include "my-chart.selectorLabels" .
    app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
    app.kubernetes.io/managed-by: {{ .Release.Service }}
    {{- end }}
    
    {{/*
    Selector labels used by Deployments and Services.
    */}}
    {{- define "my-chart.selectorLabels" -}}
    app.kubernetes.io/name: {{ .Chart.Name }}
    app.kubernetes.io/instance: {{ .Release.Name }}
    {{- end }}
    
  2. Write resource templates — In templates/deployment.yaml, reference values and helpers:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: {{ include "my-chart.fullname" . }}
      labels:
        {{- include "my-chart.labels" . | nindent 4 }}
    spec:
      {{- if not .Values.autoscaling.enabled }}
      replicas: {{ .Values.replicaCount }}
      {{- end }}
      selector:
        matchLabels:
          {{- include "my-chart.selectorLabels" . | nindent 8 }}
      template:
        metadata:
          labels:
            {{- include "my-chart.selectorLabels" . | nindent 10 }}
        spec:
          containers:
            - name: {{ .Chart.Name }}
              image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
              imagePullPolicy: {{ .Values.image.pullPolicy }}
              ports:
                - name: http
                  containerPort: {{ .Values.service.targetPort }}
                  protocol: TCP
              livenessProbe:
                httpGet:
                  path: /healthz
                  port: http
              readinessProbe:
                httpGet:
                  path: /ready
                  port: http
              resources:
                {{- toYaml .Values.resources | nindent 12 }}
    
  3. Handle conditional blocks — Use if/else for optional features (ingress, HPA, secrets) and range for lists:

    {{- if .Values.ingress.enabled }}
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: {{ include "my-chart.fullname" . }}
    spec:
      rules:
        {{- range .Values.ingress.hosts }}
        - host: {{ .host | quote }}
          http:
            paths:
              {{- range .paths }}
              - path: {{ .path }}
                pathType: {{ .pathType }}
                backend:
                  service:
                    name: {{ include "my-chart.fullname" $ }}
                    port:
                      number: {{ $.Values.service.port }}
              {{- end }}
        {{- end }}
    {{- end }}
    

    Checkpoint: Run helm template my-chart/ -f custom-values.yaml with at least two different value sets and verify both render correctly.

Phase 4: Validation, Packaging, and Distribution

  1. Validate the chart — Run the full validation suite:

    # Lint for structural issues
    helm lint my-chart/
    
    # Lint with strict mode (catches warnings as errors)
    helm lint --strict my-chart/
    
    # Render with default values
    helm template my-chart/
    
    # Render with custom overrides
    helm template my-chart/ --set replicaCount=3 --set image.tag=v2.0.0
    
    # Install in dry-run mode (no actual cluster interaction)
    helm install --dry-run --debug my-release my-chart/
    
  2. Package the chart — Create a versioned tarball for distribution:

    helm package my-chart/
    # Produces: my-chart-1.2.0.tgz
    
  3. Push to a registry or ChartMuseum — Distribute the packaged chart:

    # Push to a Helm OCI registry
    helm push my-chart-1.2.0.tgz oci://registry.example.com/charts
    
    # Push to a ChartMuseum instance
    helm push my-chart-1.2.0.tgz chartmuseum://http://chartmuseum.internal
    
    # Or upload manually
    curl --data-binary @"my-chart-1.2.0.tgz" http://chartmuseum.internal/api/charts
    

    Checkpoint: Install the packaged chart in a test cluster using helm install test-release ./my-chart-1.2.0.tgz and verify all resources are healthy.


Implementation Patterns

Pattern 1: Standard Deployment with Resource Management

This is the baseline deployment pattern for stateless workloads. It includes readiness/liveness probes, resource requests/limits, and proper label inheritance from helpers.

deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "my-chart.fullname" . }}
  labels:
    {{- include "my-chart.labels" . | nindent 4 }}
  {{- with .Values.annotations }}
  annotations:
    {{- toYaml . | nindent 4 }}
  {{- end }}
spec:
  {{- if not .Values.autoscaling.enabled }}
  replicas: {{ .Values.replicaCount }}
  {{- end }}
  strategy:
    {{- toYaml .Values.strategy | nindent 4 }}
  selector:
    matchLabels:
      {{- include "my-chart.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "my-chart.selectorLabels" . | nindent 8 }}
      {{- with .Values.podAnnotations }}
      annotations:
        {{- toYaml . | nindent 8 }}
      {{- end }}
    spec:
      serviceAccountName: {{ include "my-chart.serviceAccountName" . }}
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - name: http
              containerPort: {{ .Values.service.targetPort }}
              protocol: TCP
          livenessProbe:
            httpGet:
              path: /healthz
              port: http
            initialDelaySeconds: 15
            periodSeconds: 20
          readinessProbe:
            httpGet:
              path: /ready
              port: http
            initialDelaySeconds: 5
            periodSeconds: 10
          resources:
            {{- toYaml .Values.resources | nindent 12 }}
          env:
            {{- range $key, $value := .Values.env }}
            - name: {{ $key }}
              value: {{ $value | quote }}
            {{- end }}
      {{- with .Values.nodeSelector }}
      nodeSelector:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      {{- with .Values.affinity }}
      affinity:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      {{- with .Values.tolerations }}
      tolerations:
        {{- toYaml . | nindent 8 }}
      {{- end }}

values.yaml (deployment-related section)

replicaCount: 2

image:
  repository: myregistry/my-app
  pullPolicy: IfNotPresent
  tag: ""

strategy:
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 1
    maxUnavailable: 0

resources:
  limits:
    cpu: 500m
    memory: 512Mi
  requests:
    cpu: 250m
    memory: 256Mi

env:
  APP_ENV: production
  LOG_LEVEL: info

nodeSelector: {}

tolerations: []

affinity: {}

autoscaling:
  enabled: false
  minReplicas: 3
  maxReplicas: 10
  targetCPUUtilizationPercentage: 80

Pattern 2: Service Discovery with Headless and ClusterIP Services

This pattern covers two service types in one chart: a standard ClusterIP for internal communication and an optional headless service for direct pod addressing (useful for stateful apps or service meshes).

service.yaml

{{- if .Values.service.enabled }}
apiVersion: v1
kind: Service
metadata:
  name: {{ include "my-chart.fullname" . }}
  labels:
    {{- include "my-chart.labels" . | nindent 4 }}
spec:
  type: {{ .Values.service.type }}
  ports:
    - port: {{ .Values.service.port }}
      targetPort: http
      protocol: TCP
      name: http
  selector:
    {{- include "my-chart.selectorLabels" . | nindent 4 }}
{{- end }}

{{- if .Values.headlessService.enabled }}
---
apiVersion: v1
kind: Service
metadata:
  name: {{ include "my-chart.fullname" . }}-headless
  labels:
    {{- include "my-chart.labels" . | nindent 4 }}
spec:
  type: ClusterIP
  clusterIP: None
  ports:
    - port: {{ .Values.service.port }}
      targetPort: http
      protocol: TCP
      name: http
  selector:
    {{- include "my-chart.selectorLabels" . | nindent 4 }}
{{- end }}

values.yaml (service-related section)

service:
  enabled: true
  type: ClusterIP
  port: 80
  targetPort: 8080

headlessService:
  enabled: false

Pattern 3: HorizontalPodAutoscaler with Custom Metrics

Use this pattern when the application scales based on CPU, memory, or custom metrics (e.g., queue depth).

hpa.yaml

{{- if .Values.autoscaling.enabled }}
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: {{ include "my-chart.fullname" . }}
  labels:
    {{- include "my-chart.labels" . | nindent 4 }}
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: {{ include "my-chart.fullname" . }}
  minReplicas: {{ .Values.autoscaling.minReplicas }}
  maxReplicas: {{ .Values.autoscaling.maxReplicas }}
  metrics:
    {{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
    {{- end }}
    {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
    {{- end }}
    {{- if .Values.autoscaling.customMetrics }}
    {{- range .Values.autoscaling.customMetrics }}
    - type: Object
      object:
        metric:
          name: {{ .metricName }}
        identifiedBy:
          {{- range .identifiers }}
          - key: {{ .key }}
            value: {{ .value }}
          {{- end }}
        target:
          type: Value
          value: {{ .targetValue }}
    {{- end }}
    {{- end }}
{{- end }}

Pattern 4: ConfigMap and Secret as Environment Sources

This pattern externalizes configuration into ConfigMaps and secrets, keeping sensitive data out of deployment manifests.

configmap.yaml

{{- if .Values.configMap.enabled }}
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ include "my-chart.fullname" . }}-config
  labels:
    {{- include "my-chart.labels" . | nindent 4 }}
data:
  {{- range $key, $value := .Values.configMap.data }}
  {{ $key }}: {{ $value | quote }}
  {{- end }}
{{- end }}

secrets.yaml

{{- if .Values.secrets.create }}
apiVersion: v1
kind: Secret
metadata:
  name: {{ include "my-chart.fullname" . }}-secrets
  labels:
    {{- include "my-chart.labels" . | nindent 4 }}
type: Opaque
data:
  {{- range $key, $value := .Values.secrets.data }}
  {{ $key }}: {{ $value | b64enc }}
  {{- end }}
{{- end }}

Constraints

MUST DO

  • Always use apiVersion: v2 in Chart.yaml for modern Helm charts (supports dependencies without requirements.yaml).
  • Prefix all named templates in _helpers.tpl with the chart name to avoid collisions across releases (e.g., my-chart.labels).
  • Use {{- (dash-open) to strip leading whitespace and -}} (close-dash) to strip trailing whitespace in template conditionals.
  • Use nindent when embedding block output (like include results) inside YAML to maintain correct indentation levels.
  • Provide resources with both requests and limits — omitting requests prevents the scheduler from making correct decisions.
  • Use .Values.image.tag | default .Chart.AppVersion so that untagged installs fall back to the app version rather than latest.
  • Validate every chart with helm lint --strict and helm template before committing template changes.
  • Document every configurable key in values.yaml with a comment explaining its purpose and acceptable range.
  • Use --- YAML document separators only when multiple top-level resources are in one template file.
  • Test charts with helm test <release-name> if you define test hooks in templates/tests/.

MUST NOT DO

  • Never hardcode namespace names in templates — use {{ .Release.Namespace }} so the chart can be installed into any namespace.
  • Do not use .Values keys that shadow Helm built-in objects (e.g., avoid a key named Capabilities or Release).
  • Never embed base64-encoded secrets directly in values.yaml — use a Secret resource or an external secret manager (Sealed Secrets, External Secrets Operator).
  • Do not skip helm lint before packaging — a chart that fails lint will produce silent rendering errors in production.
  • Never use .Values without the .Values. prefix — bare variable names resolve to Go template variables, not Helm values, causing nil pointer errors.
  • Do not put Chart.yaml or values.yaml inside the templates/ directory — Helm only processes .yaml and .tpl files in templates/ during rendering.
  • Never use helm upgrade --install with --force in production scripts — it tears down and recreates resources, which can cause data loss for stateful workloads.
  • Do not use deprecated APIs (e.g., extensions/v1beta1 for Deployments or networking.k8s.io/v1beta1 for Ingress) in templates.

Output Template

When this skill is active, the model's output must contain:

  1. Chart Structure — The directory layout with file paths and brief descriptions of each file's role.
  2. Chart.yaml — Complete, valid YAML with all required fields and sensible metadata.
  3. values.yaml — Full defaults file with documented sections, typed keys, and commented explanations.
  4. Template Files — Complete, renderable Go template code with helper references, proper indentation, and conditional blocks.
  5. Validation Commands — Exact helm lint, helm template, and helm install --dry-run commands to verify the chart before distribution.
  6. Packaging Instructions — The helm package command and the appropriate push/install command for the target registry.

Related Skills

SkillPurpose
cncf-kubernetesCluster fundamentals, namespaces, and resource types referenced in charts
coding-kubernetes-deployments-managementAdvanced deployment strategies (canary, blue-green) that charts may implement
cncf-prometheusServiceMonitor and Prometheus annotations that charts can inject for observability

Live References

Skills enregistrées