Initial commit
This commit is contained in:
282
skills/helm-scaffold/references/best-practices.md
Normal file
282
skills/helm-scaffold/references/best-practices.md
Normal 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/)
|
||||
507
skills/helm-scaffold/references/examples.md
Normal file
507
skills/helm-scaffold/references/examples.md
Normal 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.
|
||||
306
skills/helm-scaffold/references/templates.md
Normal file
306
skills/helm-scaffold/references/templates.md
Normal 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}
|
||||
```
|
||||
535
skills/helm-scaffold/references/testing-guide.md
Normal file
535
skills/helm-scaffold/references/testing-guide.md
Normal 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"!
|
||||
322
skills/helm-scaffold/references/workload-types.md
Normal file
322
skills/helm-scaffold/references/workload-types.md
Normal 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"
|
||||
```
|
||||
Reference in New Issue
Block a user