Helm Charts
Helm charts package your module for deployment to Kubernetes. This guide covers chart creation and customization.
Chart Structure
charts/my-module/
├── Chart.yaml # Chart metadata
├── values.yaml # Default values
├── templates/
│ ├── _helpers.tpl # Template helpers
│ ├── deployment.yaml # Deployment manifest
│ ├── service.yaml # Service manifest
│ ├── serviceaccount.yaml
│ ├── rbac.yaml # RBAC rules
│ └── NOTES.txt # Post-install notes
└── crds/ # Custom Resource Definitions (optional)
├── tinymodule.yaml
├── tinynode.yaml
└── tinysignal.yamlChart.yaml
yaml
apiVersion: v2
name: my-module
description: My TinySystems module
type: application
version: 1.0.0 # Chart version
appVersion: "1.0.0" # Module version
keywords:
- tinysystems
- workflow
- automation
home: https://github.com/myorg/my-module
sources:
- https://github.com/myorg/my-module
maintainers:
- name: My Name
email: me@example.com
dependencies: []values.yaml
yaml
# Image configuration
image:
repository: ghcr.io/myorg/my-module
tag: "" # Defaults to appVersion
pullPolicy: IfNotPresent
imagePullSecrets: []
# Replica count
replicaCount: 2
# Module settings
module:
name: my-module
namespace: tinysystems
# Service configuration
service:
type: ClusterIP
grpcPort: 50051
# Resource limits
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
# Pod settings
podAnnotations: {}
podSecurityContext: {}
securityContext: {}
# Node selection
nodeSelector: {}
tolerations: []
affinity: {}
# Service account
serviceAccount:
create: true
name: ""
annotations: {}
# RBAC
rbac:
create: true
# Leader election
leaderElection:
enabled: true
leaseDuration: 15s
renewDeadline: 10s
retryPeriod: 2s
# Observability
metrics:
enabled: true
port: 8080
# Probes
probes:
liveness:
enabled: true
initialDelaySeconds: 10
periodSeconds: 10
readiness:
enabled: true
initialDelaySeconds: 5
periodSeconds: 5Templates
deployment.yaml
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "my-module.fullname" . }}
labels:
{{- include "my-module.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "my-module.selectorLabels" . | nindent 6 }}
template:
metadata:
annotations:
{{- with .Values.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "my-module.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "my-module.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: grpc
containerPort: {{ .Values.service.grpcPort }}
{{- if .Values.metrics.enabled }}
- name: metrics
containerPort: {{ .Values.metrics.port }}
{{- end }}
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: MODULE_NAME
value: {{ .Values.module.name }}
{{- if .Values.leaderElection.enabled }}
- name: LEADER_ELECTION_ENABLED
value: "true"
- name: LEADER_ELECTION_LEASE_DURATION
value: {{ .Values.leaderElection.leaseDuration }}
- name: LEADER_ELECTION_RENEW_DEADLINE
value: {{ .Values.leaderElection.renewDeadline }}
- name: LEADER_ELECTION_RETRY_PERIOD
value: {{ .Values.leaderElection.retryPeriod }}
{{- end }}
{{- if .Values.probes.liveness.enabled }}
livenessProbe:
httpGet:
path: /healthz
port: metrics
initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
{{- end }}
{{- if .Values.probes.readiness.enabled }}
readinessProbe:
httpGet:
path: /readyz
port: metrics
initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
{{- end }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
securityContext:
{{- toYaml .Values.securityContext | 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 }}service.yaml
yaml
apiVersion: v1
kind: Service
metadata:
name: {{ include "my-module.fullname" . }}
labels:
{{- include "my-module.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.grpcPort }}
targetPort: grpc
protocol: TCP
name: grpc
{{- if .Values.metrics.enabled }}
- port: {{ .Values.metrics.port }}
targetPort: metrics
protocol: TCP
name: metrics
{{- end }}
selector:
{{- include "my-module.selectorLabels" . | nindent 4 }}rbac.yaml
yaml
{{- if .Values.rbac.create }}
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: {{ include "my-module.fullname" . }}
rules:
- apiGroups: ["operator.tinysystems.io"]
resources: ["tinynodes", "tinymodules", "tinysignals"]
verbs: ["*"]
- apiGroups: [""]
resources: ["services", "configmaps", "secrets"]
verbs: ["*"]
- apiGroups: ["networking.k8s.io"]
resources: ["ingresses"]
verbs: ["*"]
- apiGroups: ["coordination.k8s.io"]
resources: ["leases"]
verbs: ["*"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ include "my-module.fullname" . }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: {{ include "my-module.fullname" . }}
subjects:
- kind: ServiceAccount
name: {{ include "my-module.serviceAccountName" . }}
namespace: {{ .Release.Namespace }}
{{- end }}_helpers.tpl
yaml
{{/*
Expand the name of the chart.
*/}}
{{- define "my-module.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
*/}}
{{- define "my-module.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- printf "%s" $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "my-module.labels" -}}
helm.sh/chart: {{ include "my-module.chart" . }}
{{ include "my-module.selectorLabels" . }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "my-module.selectorLabels" -}}
app.kubernetes.io/name: {{ include "my-module.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Service account name
*/}}
{{- define "my-module.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "my-module.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}Installation
Install Chart
bash
# From local directory
helm install my-module ./charts/my-module
# With custom values
helm install my-module ./charts/my-module -f custom-values.yaml
# Override specific values
helm install my-module ./charts/my-module \
--set replicaCount=3 \
--set image.tag=1.0.0Upgrade
bash
helm upgrade my-module ./charts/my-module --set image.tag=1.1.0Uninstall
bash
helm uninstall my-modulePackaging
Package Chart
bash
helm package ./charts/my-module
# Creates my-module-1.0.0.tgzPublish to Repository
bash
# Add to Helm repo
helm repo index . --url https://charts.example.com
# Push to OCI registry
helm push my-module-1.0.0.tgz oci://registry.io/chartsBest Practices
1. Use Semantic Versioning
yaml
# Chart.yaml
version: 1.0.0 # Chart version - bump for chart changes
appVersion: "1.2.3" # Module version2. Provide Sensible Defaults
yaml
# values.yaml
replicaCount: 2
resources:
requests:
cpu: 100m
memory: 128Mi3. Document Values
yaml
# values.yaml with comments
# -- Number of replicas
replicaCount: 2
# -- Container image settings
image:
# -- Image repository
repository: ghcr.io/myorg/my-module
# -- Image tag (defaults to appVersion)
tag: ""4. Test Chart
bash
# Lint
helm lint ./charts/my-module
# Template locally
helm template my-module ./charts/my-module
# Dry run
helm install my-module ./charts/my-module --dry-runNext Steps
- Registry Publishing - Publish chart
- Versioning - Version strategy
- Building Modules - Build images