Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 09:05:12 +08:00
commit 74928623b2
25 changed files with 3741 additions and 0 deletions

View File

@@ -0,0 +1,282 @@
# Helm Chart Best Practices
CNCF and Helm community standards for production-ready charts.
## Chart Metadata Standards
### Chart.yaml Requirements
- `apiVersion: v2` (Helm 3)
- Semantic versioning (version, appVersion)
- Meaningful description
- Keywords for discoverability
- Maintainer information
### Naming Conventions
- Chart names: lowercase, hyphens (no underscores)
- Resource names: `{{ template "name.fullname" . }}`
- Avoid hardcoding names
## Kubernetes Label Standards
**Required labels (app.kubernetes.io/* namespace):**
```yaml
labels:
app.kubernetes.io/name: {{ include "chart.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
helm.sh/chart: {{ include "chart.chart" . }}
```
**Selector labels (must be immutable):**
```yaml
selector:
matchLabels:
app.kubernetes.io/name: {{ include "chart.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
```
## Security Best Practices
### Pod Security Context
```yaml
podSecurityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
seccompProfile:
type: RuntimeDefault # Production
```
### Container Security Context
```yaml
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
capabilities:
drop:
- ALL
```
### Security Guidelines
- Never run as root (UID 0)
- Drop all Linux capabilities by default
- Use read-only root filesystem when possible
- Apply seccomp profiles in production
- Avoid privileged containers
- Don't expose host ports or namespaces
## Resource Management
### Always Define Resources
```yaml
resources:
limits:
cpu: 500m
memory: 256Mi
requests:
cpu: 50m
memory: 64Mi
```
### Resource Sizing Guidelines
- **Small apps**: 50m CPU / 64Mi memory (requests)
- **Medium apps**: 100m CPU / 128Mi memory (requests)
- **Large apps**: 250m+ CPU / 256Mi+ memory (requests)
- Limits should be 2-10x requests
- Monitor and adjust based on actual usage
## Health Checks
### Liveness Probe
Detects when container needs restart:
```yaml
livenessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
```
### Readiness Probe
Detects when container can accept traffic:
```yaml
readinessProbe:
httpGet:
path: /ready
port: http
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
```
### Probe Best Practices
- Always define both liveness and readiness
- Use appropriate initialDelaySeconds for slow-starting apps
- Health endpoints should be lightweight
- Don't use same endpoint for liveness and readiness if startup is slow
## Values.yaml Organization
### Structure
```yaml
# 1. Replica configuration
replicaCount: 1
# 2. Image configuration
image:
repository: example/app
pullPolicy: IfNotPresent
tag: "" # Defaults to Chart.appVersion
# 3. Service account
serviceAccount:
create: true
name: ""
# 4. Security contexts
podSecurityContext: {}
securityContext: {}
# 5. Service configuration
service:
type: ClusterIP
port: 80
# 6. Resources
resources: {}
# 7. Autoscaling
autoscaling:
enabled: false
# 8. Additional features (Ingress, ConfigMaps, etc.)
```
### Documentation
- Comment every major section
- Provide examples for complex values
- Document accepted value types
- Explain default behavior
## Template Best Practices
### Use Helper Functions
```yaml
# _helpers.tpl
{{- define "app.fullname" -}}
{{- printf "%s-%s" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-" }}
{{- end }}
```
### Conditional Resources
```yaml
{{- if .Values.ingress.enabled -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
...
{{- end }}
```
### Checksum Annotations
Force pod restart on config changes:
```yaml
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
```
## NOTES.txt Guidelines
Provide clear post-installation instructions:
```
1. How to access the application
2. Default credentials (if any)
3. Next steps for configuration
4. Links to documentation
5. Troubleshooting commands
```
## Multi-Environment Patterns
### Base + Override Pattern
- `values.yaml`: Base defaults
- `values-dev.yaml`: Development overrides
- `values-prod.yaml`: Production overrides
### Environment-Specific Settings
- **Dev**: Debug enabled, minimal resources, verbose logging
- **Staging**: Production-like, moderate resources
- **Prod**: HA, autoscaling, security hardening, monitoring
## Common Pitfalls to Avoid
**Don't:**
- Hardcode values in templates
- Forget resource limits
- Run containers as root
- Skip health checks
- Use `latest` image tag
- Expose secrets in values.yaml
- Create resources without labels
- Ignore security contexts
**Do:**
- Use template functions
- Define all resources
- Use non-root users
- Configure probes
- Pin specific versions
- Reference external secrets
- Apply standard labels
- Enable security contexts
## Testing Checklist
Before deploying:
- [ ] `helm lint` passes
- [ ] `helm template` renders correctly
- [ ] All required labels present
- [ ] Security contexts configured
- [ ] Resource limits defined
- [ ] Health checks configured
- [ ] NOTES.txt provides clear instructions
- [ ] README documents all values
- [ ] Dry run succeeds
- [ ] Test deployment in dev environment
## Validation Commands
```bash
# Lint chart
helm lint .
# Template rendering
helm template myrelease .
# Dry run
helm install myrelease . --dry-run --debug
# Install to test namespace
kubectl create ns test
helm install myrelease . -n test
# Verify
kubectl get all -n test
helm test myrelease -n test
# Cleanup
helm uninstall myrelease -n test
kubectl delete ns test
```
## References
- [Helm Best Practices](https://helm.sh/docs/chart_best_practices/)
- [Kubernetes Labels](https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/)
- [CNCF Security Whitepaper](https://github.com/cncf/tag-security)
- [Pod Security Standards](https://kubernetes.io/docs/concepts/security/pod-security-standards/)

View File

@@ -0,0 +1,507 @@
# Helm Chart Examples
Real-world chart examples for common use cases.
## Example 1: Simple Web Application
**Scenario**: Node.js API microservice
**Chart.yaml**:
```yaml
apiVersion: v2
name: user-api
description: User management API service
type: application
version: 0.1.0
appVersion: "1.0.0"
```
**values.yaml** (key sections):
```yaml
replicaCount: 2
image:
repository: myorg/user-api
tag: "1.0.0"
service:
type: ClusterIP
port: 80
targetPort: 3000
ingress:
enabled: true
className: nginx
hosts:
- host: api.example.com
paths:
- path: /users
pathType: Prefix
resources:
limits: {cpu: 500m, memory: 256Mi}
requests: {cpu: 100m, memory: 128Mi}
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 70
```
**Usage**:
```bash
helm install user-api . -n production
```
---
## Example 2: Database (StatefulSet)
**Scenario**: PostgreSQL with persistent storage
**Chart.yaml**:
```yaml
apiVersion: v2
name: postgresql
description: PostgreSQL database
type: application
version: 0.1.0
appVersion: "15"
```
**values.yaml** (key sections):
```yaml
replicaCount: 1
image:
repository: postgres
tag: "15"
persistence:
enabled: true
storageClass: "standard"
accessMode: ReadWriteOnce
size: 20Gi
resources:
limits: {cpu: 1000m, memory: 2Gi}
requests: {cpu: 250m, memory: 512Mi}
env:
- name: POSTGRES_DB
value: myapp
- name: POSTGRES_USER
value: postgres
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secret
key: password
```
**StatefulSet template** (key parts):
```yaml
spec:
serviceName: postgresql-headless
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: standard
resources:
requests:
storage: 20Gi
```
---
## Example 3: Scheduled Job (CronJob)
**Scenario**: Nightly database backup
**Chart.yaml**:
```yaml
apiVersion: v2
name: db-backup
description: Automated database backup job
type: application
version: 0.1.0
appVersion: "1.0.0"
```
**values.yaml** (key sections):
```yaml
cronjob:
schedule: "0 2 * * *" # 2 AM daily
successfulJobsHistoryLimit: 7
failedJobsHistoryLimit: 3
concurrencyPolicy: Forbid
image:
repository: myorg/backup-tool
tag: "latest"
resources:
limits: {cpu: 500m, memory: 512Mi}
requests: {cpu: 100m, memory: 128Mi}
env:
- name: BACKUP_RETENTION_DAYS
value: "30"
- name: S3_BUCKET
value: "my-backups"
```
---
## Example 4: Multi-Environment Configuration
**Scenario**: Application deployed to dev, staging, prod
**values.yaml** (base):
```yaml
replicaCount: 1
image:
repository: myorg/myapp
tag: ""
resources:
limits: {cpu: 500m, memory: 256Mi}
requests: {cpu: 50m, memory: 64Mi}
env:
- name: LOG_LEVEL
value: "info"
```
**values-dev.yaml**:
```yaml
replicaCount: 1
image:
tag: "dev"
resources:
limits: {cpu: 200m, memory: 128Mi}
requests: {cpu: 25m, memory: 32Mi}
env:
- name: LOG_LEVEL
value: "debug"
- name: DEBUG
value: "true"
ingress:
enabled: true
hosts:
- host: dev.myapp.example.com
```
**values-prod.yaml**:
```yaml
replicaCount: 3
image:
tag: "1.0.0"
resources:
limits: {cpu: 1000m, memory: 512Mi}
requests: {cpu: 100m, memory: 128Mi}
env:
- name: LOG_LEVEL
value: "warn"
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 10
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values: [myapp]
topologyKey: kubernetes.io/hostname
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: myapp.example.com
tls:
- secretName: myapp-tls
hosts:
- myapp.example.com
```
**Deployment commands**:
```bash
# Development
helm install myapp . -f values-dev.yaml -n dev
# Production
helm install myapp . -f values-prod.yaml -n production
```
---
## Example 5: Converting Manifest to Helm
**Original Kubernetes manifest**:
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
spec:
replicas: 2
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: frontend
image: myorg/frontend:1.0.0
ports:
- containerPort: 8080
resources:
limits:
cpu: "500m"
memory: "256Mi"
```
**Converted Helm template** (deployment.yaml):
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "frontend.fullname" . }}
labels:
{{- include "frontend.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "frontend.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "frontend.labels" . | nindent 8 }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
ports:
- name: http
containerPort: {{ .Values.service.targetPort }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
```
**Extracted values.yaml**:
```yaml
replicaCount: 2
image:
repository: myorg/frontend
tag: "1.0.0"
service:
targetPort: 8080
resources:
limits:
cpu: 500m
memory: 256Mi
```
**What was parameterized**:
- Replica count → `.Values.replicaCount`
- Image name/tag → `.Values.image.*`
- Port → `.Values.service.targetPort`
- Resources → `.Values.resources`
- Labels → Helper templates
- Resource name → Template function
---
## Example 6: Organizational Standard Template
**Scenario**: Platform team creates standard chart for all services
**Required organizational standards**:
- All services must have cost center label
- Security scanning required
- Must use org-wide naming convention
- Mandatory resource limits
- Required network policies
**Modified _helpers.tpl**:
```yaml
{{- define "org.labels" -}}
app.kubernetes.io/name: {{ include "chart.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
org.example.com/cost-center: {{ .Values.org.costCenter | required "Cost center is required" }}
org.example.com/team: {{ .Values.org.team | required "Team name is required" }}
org.example.com/security-scan: "required"
{{- end }}
```
**Required values**:
```yaml
org:
costCenter: "" # MUST be provided
team: "" # MUST be provided
# Resource limits are mandatory
resources:
limits:
cpu: 500m
memory: 256Mi
requests:
cpu: 50m
memory: 64Mi
# Security context is required
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
capabilities:
drop: [ALL]
```
**Usage**:
```bash
helm install myapp . \
--set org.costCenter=eng-001 \
--set org.team=platform
```
---
## Example 7: With Subchart Dependency
**Scenario**: Application that needs PostgreSQL database
**Chart.yaml**:
```yaml
apiVersion: v2
name: myapp
version: 0.1.0
appVersion: "1.0.0"
dependencies:
- name: postgresql
version: "12.1.0"
repository: "https://charts.bitnami.com/bitnami"
condition: postgresql.enabled
```
**values.yaml**:
```yaml
# Application values
replicaCount: 2
image:
repository: myorg/myapp
tag: "1.0.0"
# Subchart values
postgresql:
enabled: true
auth:
database: myapp
username: myapp
primary:
persistence:
size: 10Gi
```
**Install**:
```bash
# Download dependencies
helm dependency update
# Install with subchart
helm install myapp .
```
---
## Common Patterns
### ConfigMap from Files
```yaml
# values.yaml
config:
app.conf: |
server_port=8080
log_level=info
db.conf: |
host=postgres
port=5432
# templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "app.fullname" . }}
data:
{{- range $key, $value := .Values.config }}
{{ $key }}: |
{{- $value | nindent 4 }}
{{- end }}
```
### External Secret Reference
```yaml
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Values.externalSecret.name }}
key: password
```
### Horizontal Pod Autoscaler
```yaml
{{- if .Values.autoscaling.enabled }}
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "app.fullname" . }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ include "app.fullname" . }}
minReplicas: {{ .Values.autoscaling.minReplicas }}
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
{{- end }}
```
These examples demonstrate common patterns and can be adapted for specific use cases.

View File

@@ -0,0 +1,306 @@
# Helm Chart Templates
Complete template library. Load sections as needed. {{ CN }} = {{ CHART_NAME }} (abbreviated).
## Chart.yaml
```yaml
apiVersion: v2
name: {{ CHART_NAME }}
description: A Helm chart for {{ APP_DESCRIPTION }}
type: application
version: 0.1.0
appVersion: "1.0.0"
```
## values.yaml
```yaml
replicaCount: 1
image:
repository: {{ IMAGE_REPO }}
pullPolicy: IfNotPresent
tag: "{{ IMAGE_TAG }}"
serviceAccount:
create: true
automount: true
podSecurityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: [ALL]
service:
type: ClusterIP
port: {{ SERVICE_PORT }}
targetPort: {{ CONTAINER_PORT }}
resources:
limits: {cpu: 500m, memory: 256Mi}
requests: {cpu: 50m, memory: 64Mi}
livenessProbe:
httpGet: {path: /health, port: http}
initialDelaySeconds: 30
readinessProbe:
httpGet: {path: /ready, port: http}
initialDelaySeconds: 5
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 10
targetCPUUtilizationPercentage: 80
```
## _helpers.tpl
```yaml
{{- define "{{ CN }}.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- define "{{ CN }}.fullname" -}}
{{- if .Values.fullnameOverride }}{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}{{- end }}{{- end }}{{- end }}
{{- define "{{ CN }}.labels" -}}
helm.sh/chart: {{ include "{{ CN }}.chart" . }}
{{ include "{{ CN }}.selectorLabels" . }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{- define "{{ CN }}.selectorLabels" -}}
app.kubernetes.io/name: {{ include "{{ CN }}.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{- define "{{ CN }}.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}{{- default (include "{{ CN }}.fullname" .) .Values.serviceAccount.name }}
{{- else }}{{- default "default" .Values.serviceAccount.name }}{{- end }}{{- end }}
```
## Deployment
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "{{ CN }}.fullname" . }}
labels: {{- include "{{ CN }}.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}replicas: {{ .Values.replicaCount }}{{- end }}
selector:
matchLabels: {{- include "{{ CN }}.selectorLabels" . | nindent 6 }}
template:
metadata:
labels: {{- include "{{ CN }}.labels" . | nindent 8 }}
spec:
serviceAccountName: {{ include "{{ CN }}.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 }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.targetPort }}
livenessProbe: {{- toYaml .Values.livenessProbe | nindent 12 }}
readinessProbe: {{- toYaml .Values.readinessProbe | nindent 12 }}
resources: {{- toYaml .Values.resources | nindent 12 }}
```
## StatefulSet
```yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: {{ include "{{ CN }}.fullname" . }}
labels: {{- include "{{ CN }}.labels" . | nindent 4 }}
spec:
serviceName: {{ include "{{ CN }}.fullname" . }}-headless
replicas: {{ .Values.replicaCount }}
selector:
matchLabels: {{- include "{{ CN }}.selectorLabels" . | nindent 6 }}
template:
metadata:
labels: {{- include "{{ CN }}.labels" . | nindent 8 }}
spec:
serviceAccountName: {{ include "{{ CN }}.serviceAccountName" . }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
volumeMounts:
- name: data
mountPath: /data
volumeClaimTemplates:
- metadata: {name: data}
spec:
accessModes: [{{ .Values.persistence.accessMode | quote }}]
storageClassName: {{ .Values.persistence.storageClass | quote }}
resources:
requests:
storage: {{ .Values.persistence.size | quote }}
```
Add to values: `persistence: {storageClass: standard, accessMode: ReadWriteOnce, size: 10Gi}`
## Job
```yaml
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "{{ CN }}.fullname" . }}
labels: {{- include "{{ CN }}.labels" . | nindent 4 }}
spec:
backoffLimit: {{ .Values.job.backoffLimit | default 3 }}
completions: {{ .Values.job.completions | default 1 }}
template:
metadata:
labels: {{- include "{{ CN }}.labels" . | nindent 8 }}
spec:
restartPolicy: OnFailure
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
```
Add to values: `job: {backoffLimit: 3, completions: 1, parallelism: 1}`
## CronJob
```yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: {{ include "{{ CN }}.fullname" . }}
labels: {{- include "{{ CN }}.labels" . | nindent 4 }}
spec:
schedule: {{ .Values.cronjob.schedule | quote }}
successfulJobsHistoryLimit: {{ .Values.cronjob.successfulJobsHistoryLimit | default 3 }}
failedJobsHistoryLimit: {{ .Values.cronjob.failedJobsHistoryLimit | default 1 }}
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
```
Add to values: `cronjob: {schedule: "0 2 * * *", successfulJobsHistoryLimit: 3, failedJobsHistoryLimit: 1}`
## Service
```yaml
apiVersion: v1
kind: Service
metadata:
name: {{ include "{{ CN }}.fullname" . }}
labels: {{- include "{{ CN }}.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
name: http
selector: {{- include "{{ CN }}.selectorLabels" . | nindent 4 }}
```
## ServiceAccount
```yaml
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "{{ CN }}.serviceAccountName" . }}
labels: {{- include "{{ CN }}.labels" . | nindent 4 }}
automountServiceAccountToken: {{ .Values.serviceAccount.automount }}
{{- end }}
```
## Ingress
```yaml
{{- if .Values.ingress.enabled -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "{{ CN }}.fullname" . }}
labels: {{- include "{{ CN }}.labels" . | nindent 4 }}
spec:
{{- if .Values.ingress.className }}ingressClassName: {{ .Values.ingress.className }}{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
pathType: {{ .pathType }}
backend:
service: {name: {{ include "{{ CN }}.fullname" $ }}, port: {number: {{ $.Values.service.port }}}}
{{- end }}
{{- end }}
{{- end }}
```
## HPA
```yaml
{{- if .Values.autoscaling.enabled }}
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "{{ CN }}.fullname" . }}
spec:
scaleTargetRef: {apiVersion: apps/v1, kind: Deployment, name: {{ include "{{ CN }}.fullname" . }}}
minReplicas: {{ .Values.autoscaling.minReplicas }}
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
metrics:
- type: Resource
resource: {name: cpu, target: {type: Utilization, averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}}}
{{- end }}
```
## NOTES.txt
```
Thank you for installing {{ .Chart.Name }}!
Release: {{ .Release.Name }}
{{- if .Values.ingress.enabled }}
URL: http{{ if $.Values.ingress.tls }}s{{ end }}://{{ (index .Values.ingress.hosts 0).host }}
{{- else }}
Port forward: kubectl port-forward svc/{{ include "{{ CN }}.fullname" . }} {{ .Values.service.port }}
{{- end }}
```
## .helmignore
```
.DS_Store
.git/
*.swp
.idea/
.vscode/
```
## README.md
```markdown
# {{ CHART_NAME }}
## Installation
helm install {{ CN }} .
## Testing
helm lint .
helm template {{ CN }} .
helm install {{ CN }} . --dry-run
```
## Environment Values
values-dev.yaml:
```yaml
replicaCount: 1
resources: {limits: {cpu: 200m, memory: 128Mi}, requests: {cpu: 25m, memory: 32Mi}}
```
values-prod.yaml:
```yaml
replicaCount: 3
resources: {limits: {cpu: 1000m, memory: 512Mi}, requests: {cpu: 100m, memory: 128Mi}}
autoscaling: {enabled: true, minReplicas: 3, maxReplicas: 10}
```

View File

@@ -0,0 +1,535 @@
# Helm Chart Testing Guide
## Overview
This guide provides testing templates and commands for validating Helm charts before deployment. Always test charts with dry-run and template rendering before actual deployment.
## Testing Workflow
```
1. Helm Lint → Validate chart structure
2. Helm Template → Preview rendered manifests
3. Dry-run Install → Test against cluster API
4. Kubectl Dry-run → Final validation
5. Actual Install → Deploy to cluster
```
## Mock Values by Workload Type
### Deployment (Web Application)
```yaml
# values-test.yaml
image:
repository: nginx
tag: "1.25"
pullPolicy: IfNotPresent
replicaCount: 2
service:
type: ClusterIP
port: 80
ingress:
enabled: true
className: "nginx"
hosts:
- host: test.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: test-tls
hosts:
- test.example.com
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
livenessProbe:
enabled: true
path: /healthz
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
enabled: true
path: /ready
initialDelaySeconds: 5
periodSeconds: 10
env:
- name: ENVIRONMENT
value: "test"
- name: LOG_LEVEL
value: "debug"
- name: PORT
value: "80"
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 5
targetCPUUtilizationPercentage: 80
nodeSelector: {}
tolerations: []
affinity: {}
```
### StatefulSet (Database)
```yaml
# values-test.yaml
image:
repository: postgres
tag: "15"
pullPolicy: IfNotPresent
replicaCount: 3
service:
type: ClusterIP
port: 5432
persistence:
enabled: true
storageClass: "standard"
size: 10Gi
resources:
requests:
cpu: 250m
memory: 512Mi
limits:
cpu: 1000m
memory: 2Gi
livenessProbe:
enabled: true
path: /
initialDelaySeconds: 60
periodSeconds: 30
readinessProbe:
enabled: true
path: /
initialDelaySeconds: 10
periodSeconds: 10
env:
- name: POSTGRES_DB
value: "testdb"
- name: POSTGRES_USER
value: "testuser"
- name: POSTGRES_PASSWORD
value: "testpass123"
- name: PGDATA
value: "/data/pgdata"
podManagementPolicy: OrderedReady
volumeMounts:
- name: data
mountPath: /data
nodeSelector: {}
tolerations: []
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values:
- postgres
topologyKey: kubernetes.io/hostname
```
### Job (Batch Processing)
```yaml
# values-test.yaml
image:
repository: busybox
tag: "1.36"
pullPolicy: IfNotPresent
job:
backoffLimit: 3
activeDeadlineSeconds: 600
ttlSecondsAfterFinished: 86400
restartPolicy: OnFailure
parallelism: 1
completions: 1
command:
- /bin/sh
args:
- -c
- |
echo "Starting batch job..."
echo "Processing data..."
sleep 30
echo "Job completed successfully!"
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 1000m
memory: 1Gi
env:
- name: JOB_TYPE
value: "batch-process"
- name: BATCH_SIZE
value: "1000"
- name: OUTPUT_PATH
value: "/output"
nodeSelector: {}
tolerations: []
```
### CronJob (Scheduled Task)
```yaml
# values-test.yaml
image:
repository: busybox
tag: "1.36"
pullPolicy: IfNotPresent
cronJob:
schedule: "*/5 * * * *" # Every 5 minutes for testing
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
startingDeadlineSeconds: 200
suspend: false
backoffLimit: 2
activeDeadlineSeconds: 300
restartPolicy: OnFailure
command:
- /bin/sh
args:
- -c
- |
echo "Running scheduled task at $(date)"
echo "Performing backup/cleanup/sync..."
sleep 10
echo "Task completed at $(date)"
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
env:
- name: TASK_NAME
value: "scheduled-backup"
- name: RETENTION_DAYS
value: "7"
- name: TARGET
value: "/backup"
nodeSelector: {}
tolerations: []
```
## Testing Commands
### 1. Validate Chart Structure
```bash
# Basic lint
helm lint my-chart
# Lint with custom values
helm lint my-chart -f values-test.yaml
# Lint with value overrides
helm lint my-chart --set image.tag=latest
```
**Expected output:**
```
==> Linting my-chart
[INFO] Chart.yaml: icon is recommended
1 chart(s) linted, 0 chart(s) failed
```
### 2. Render Templates Locally
```bash
# Render all templates
helm template my-release my-chart
# Render with test values
helm template my-release my-chart -f values-test.yaml
# Render with inline overrides
helm template my-release my-chart \
--set image.tag=latest \
--set replicaCount=3
# Show only specific resource
helm template my-release my-chart | grep -A 30 "kind: Deployment"
# Save rendered manifests to file
helm template my-release my-chart -f values-test.yaml > rendered.yaml
# Render for specific namespace
helm template my-release my-chart --namespace production
```
### 3. Dry-run Install
```bash
# Dry-run against cluster API (requires cluster access)
helm install my-release my-chart --dry-run --debug
# Dry-run with custom values
helm install my-release my-chart \
--dry-run \
--debug \
-f values-test.yaml
# Dry-run with namespace
helm install my-release my-chart \
--dry-run \
--debug \
--namespace test \
--create-namespace
```
**Expected behavior:**
- Validates against cluster API
- Shows what would be installed
- Catches API version issues
- Does NOT create resources
### 4. Validate with Kubectl
```bash
# Validate rendered manifests
helm template my-release my-chart | kubectl apply --dry-run=client -f -
# Validate with specific values
helm template my-release my-chart -f values-test.yaml | kubectl apply --dry-run=client -f -
# Server-side dry-run (requires cluster)
helm template my-release my-chart | kubectl apply --dry-run=server -f -
```
### 5. Diff Against Existing Release
```bash
# Install helm diff plugin first
helm plugin install https://github.com/databus23/helm-diff
# Compare with existing release
helm diff upgrade my-release my-chart -f values-test.yaml
# Show only changes
helm diff upgrade my-release my-chart -f values-test.yaml --suppress-secrets
```
## Testing Checklist
### Pre-deployment Validation
- [ ] Chart passes `helm lint` without warnings
- [ ] Templates render successfully with `helm template`
- [ ] Dry-run install completes without errors
- [ ] Image names are valid and accessible
- [ ] Resource limits are appropriate
- [ ] Labels and selectors match correctly
- [ ] Health check paths are correct
- [ ] Environment variables are set
- [ ] Secrets are not in values.yaml
- [ ] Ingress hostnames are valid
- [ ] Service ports match container ports
### Workload-Specific Checks
#### Deployment
- [ ] Replica count is appropriate
- [ ] Rolling update strategy configured
- [ ] HPA settings (if enabled) are sensible
- [ ] Pod disruption budget considered
#### StatefulSet
- [ ] Persistence configuration correct
- [ ] Storage class exists
- [ ] Headless service defined
- [ ] Pod management policy appropriate
- [ ] Volume claim templates valid
#### Job
- [ ] Backoff limit reasonable
- [ ] Active deadline set
- [ ] TTL for cleanup configured
- [ ] Restart policy appropriate
- [ ] Command/args correct
#### CronJob
- [ ] Schedule syntax valid
- [ ] Concurrency policy appropriate
- [ ] History limits set
- [ ] Starting deadline configured
- [ ] Job template valid
## Common Testing Scenarios
### Test 1: Minimal Values (Defaults)
```bash
# Test with only required values
helm template test my-chart --set image.repository=nginx
```
### Test 2: Production-like Values
```bash
# Test with production configuration
helm template test my-chart -f values-prod.yaml
```
### Test 3: Multiple Environments
```bash
# Test dev environment
helm template test my-chart -f values-dev.yaml
# Test staging environment
helm template test my-chart -f values-staging.yaml
# Test production environment
helm template test my-chart -f values-prod.yaml
```
### Test 4: Value Overrides
```bash
# Test with inline overrides
helm template test my-chart \
--set image.tag=v2.0.0 \
--set replicaCount=5 \
--set ingress.enabled=true
```
### Test 5: Resource Validation
```bash
# Check resource limits
helm template test my-chart | grep -A 5 "resources:"
# Check security contexts
helm template test my-chart | grep -A 10 "securityContext:"
# Check probes
helm template test my-chart | grep -A 5 "Probe:"
```
## Troubleshooting
### Issue: Template rendering fails
```bash
# Debug with verbose output
helm template test my-chart --debug
# Check specific template
helm template test my-chart --show-only templates/deployment.yaml
```
### Issue: Validation errors
```bash
# Validate individual resources
helm template test my-chart | kubectl apply --dry-run=client -f - --validate=strict
# Check for deprecated APIs
helm template test my-chart | kubectl apply --dry-run=server -f -
```
### Issue: Values not applied
```bash
# Verify values are loaded
helm template test my-chart -f values-test.yaml --debug | grep -A 5 "USER-SUPPLIED VALUES"
# Check final computed values
helm template test my-chart -f values-test.yaml --debug | grep -A 20 "COMPUTED VALUES"
```
## Best Practices
1. **Always test before deploying** - Use dry-run and template rendering
2. **Use realistic test data** - Mock values should resemble production
3. **Test all environments** - Validate dev, staging, prod configurations
4. **Validate security** - Check security contexts and RBAC
5. **Check resource limits** - Ensure requests/limits are appropriate
6. **Test failure scenarios** - Invalid values, missing fields
7. **Document test process** - Share testing commands with team
8. **Automate testing** - Include in CI/CD pipeline
## Automated Testing Example
```bash
#!/bin/bash
# test-chart.sh
set -e
CHART_DIR="my-chart"
VALUES_FILE="values-test.yaml"
echo "Testing Helm chart: $CHART_DIR"
# Test 1: Lint
echo "1. Running helm lint..."
helm lint $CHART_DIR -f $VALUES_FILE
# Test 2: Template rendering
echo "2. Rendering templates..."
helm template test $CHART_DIR -f $VALUES_FILE > /tmp/rendered.yaml
# Test 3: Kubectl validation
echo "3. Validating with kubectl..."
kubectl apply --dry-run=client -f /tmp/rendered.yaml
# Test 4: Check for secrets in values
echo "4. Checking for secrets in values..."
if grep -i "password\|secret\|token" $VALUES_FILE; then
echo "WARNING: Potential secrets found in values file!"
fi
echo "✅ All tests passed!"
```
## Summary
Always follow this testing sequence:
1. **Lint** the chart structure
2. **Render** templates to preview
3. **Dry-run** to validate against API
4. **Review** rendered manifests
5. **Deploy** to test environment first
6. **Monitor** after deployment
Never skip testing, even for "small changes"!

View File

@@ -0,0 +1,322 @@
# Helm Chart Workload Types
## Overview
Different application types require different Kubernetes workload resources. This guide helps choose the right workload type and understand its specific requirements.
## Workload Type Decision Tree
```
Is it a long-running process?
├─ YES: Does it need stable network identity or persistent storage?
│ ├─ YES: Use StatefulSet
│ └─ NO: Use Deployment
└─ NO: Is it scheduled to run repeatedly?
├─ YES: Use CronJob
└─ NO: Use Job
```
## Deployment
**Use for:** Stateless applications, microservices, web servers, APIs
### Characteristics
- No persistent identity
- Pods are interchangeable
- Can be scaled horizontally
- Rolling updates and rollbacks
- No guaranteed ordering
### Best For
- REST APIs
- Web applications
- Microservices
- Stateless workers
- Frontend applications
### values.yaml Specific Fields
```yaml
replicaCount: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 10
```
### Example Use Cases
- Node.js API server
- Nginx web server
- Python Flask application
- React/Vue frontend
- Stateless background workers
## StatefulSet
**Use for:** Stateful applications requiring stable identity or persistent storage
### Characteristics
- Stable, unique network identifiers
- Stable, persistent storage
- Ordered, graceful deployment and scaling
- Ordered, automated rolling updates
- Requires headless service
### Best For
- Databases (MySQL, PostgreSQL, MongoDB)
- Message queues (RabbitMQ, Kafka)
- Distributed systems (Elasticsearch, Cassandra)
- Applications requiring stable hostnames
### values.yaml Specific Fields
```yaml
replicaCount: 3
updateStrategy:
type: RollingUpdate
persistence:
enabled: true
storageClass: "fast-ssd"
size: 10Gi
accessMode: ReadWriteOnce
podManagementPolicy: OrderedReady # or Parallel
```
### Template Additions
```yaml
serviceName: {{ include "chart.fullname" . }}-headless
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: {{ .Values.persistence.storageClass }}
resources:
requests:
storage: {{ .Values.persistence.size }}
```
### Headless Service Required
```yaml
apiVersion: v1
kind: Service
metadata:
name: {{ include "chart.fullname" . }}-headless
spec:
clusterIP: None
selector:
{{- include "chart.selectorLabels" . | nindent 4 }}
```
### Example Use Cases
- PostgreSQL cluster
- Redis with persistent storage
- Elasticsearch cluster
- Kafka broker
- ZooKeeper ensemble
## Job
**Use for:** Run-to-completion tasks, one-time executions
### Characteristics
- Runs until completion
- Pods are not restarted after successful completion
- Can run multiple pods in parallel
- Automatic cleanup options
- Suitable for batch processing
### Best For
- Database migrations
- Batch processing
- Data imports/exports
- One-time setup tasks
- Report generation
### values.yaml Specific Fields
```yaml
job:
backoffLimit: 4 # Retry attempts
activeDeadlineSeconds: 600 # Timeout
ttlSecondsAfterFinished: 86400 # Cleanup after 24h
restartPolicy: OnFailure # or Never
parallelism: 1 # Parallel pods
completions: 1 # Required completions
command: []
args: []
```
### Template Specifics
```yaml
spec:
backoffLimit: {{ .Values.job.backoffLimit }}
ttlSecondsAfterFinished: {{ .Values.job.ttlSecondsAfterFinished }}
template:
spec:
restartPolicy: {{ .Values.job.restartPolicy }}
```
### Example Use Cases
- Database schema migration
- Data ETL job
- Image processing batch
- Cache warming
- Backup operations
## CronJob
**Use for:** Scheduled, recurring tasks
### Characteristics
- Scheduled execution (cron syntax)
- Creates Jobs on schedule
- Concurrency control
- History management
- Automatic cleanup
### Best For
- Scheduled backups
- Report generation
- Data synchronization
- Cache clearing
- Periodic cleanup tasks
### values.yaml Specific Fields
```yaml
cronJob:
schedule: "0 2 * * *" # Daily at 2 AM
concurrencyPolicy: Forbid # Forbid, Allow, or Replace
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
startingDeadlineSeconds: 200 # Deadline for missed runs
suspend: false # Pause scheduling
backoffLimit: 4
activeDeadlineSeconds: 600
restartPolicy: OnFailure
command: []
args: []
```
### Cron Schedule Examples
```yaml
"*/5 * * * *" # Every 5 minutes
"0 * * * *" # Every hour
"0 0 * * *" # Daily at midnight
"0 2 * * *" # Daily at 2 AM
"0 0 * * 0" # Weekly on Sunday
"0 0 1 * *" # Monthly on 1st
"0 0 1 1 *" # Yearly on Jan 1st
```
### Concurrency Policies
- **Forbid**: Don't start new job if previous still running (recommended)
- **Allow**: Allow concurrent jobs
- **Replace**: Cancel running job and start new one
### Example Use Cases
- Nightly database backup
- Daily report generation
- Hourly cache refresh
- Weekly cleanup tasks
- Monthly billing runs
## Comparison Matrix
| Feature | Deployment | StatefulSet | Job | CronJob |
|---------|-----------|-------------|-----|---------|
| **Replicas** | Yes | Yes | Parallelism | Parallelism |
| **Persistent Identity** | No | Yes | No | No |
| **Persistent Storage** | Optional | Yes | Optional | Optional |
| **Ordered Operations** | No | Yes | No | No |
| **Auto-restart** | Yes | Yes | Optional | Optional |
| **Scaling** | Easy | Ordered | N/A | N/A |
| **Updates** | Rolling | Rolling | N/A | N/A |
| **Service** | Yes | Headless | Optional | Optional |
| **Typical Replicas** | 2-100+ | 1-10 | 1-1000 | 1-100 |
## When to Use Each Type
### Use Deployment When:
- Application is stateless
- Pods are interchangeable
- No need for stable network identity
- Need rapid scaling
- Standard web application pattern
### Use StatefulSet When:
- Need stable, unique network identifiers
- Require persistent storage per pod
- Need ordered deployment/scaling
- Running clustered databases
- Pods need to discover each other
### Use Job When:
- Task runs to completion
- One-time execution needed
- Batch processing work
- Database migration
- Don't need scheduling
### Use CronJob When:
- Need scheduled execution
- Recurring tasks
- Time-based triggers
- Periodic maintenance
- Regular backups or reports
## Migration Considerations
### Deployment → StatefulSet
**Required Changes:**
- Add `serviceName` pointing to headless service
- Create headless service
- Add `volumeClaimTemplates`
- Update selector labels (StatefulSet selector is immutable)
- Plan for ordered rollout
### StatefulSet → Deployment
**Consider:**
- Loss of stable network identity
- Need to externalize persistent data
- Pods become interchangeable
- No ordered operations
## Resource Recommendations by Type
### Deployment
```yaml
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
```
### StatefulSet
```yaml
resources:
requests:
memory: "512Mi" # Higher for databases
cpu: "250m"
limits:
memory: "2Gi"
cpu: "1000m"
```
### Job/CronJob
```yaml
resources:
requests:
memory: "256Mi"
cpu: "200m"
limits:
memory: "1Gi"
cpu: "1000m"
```