Helm Chart Wrapper
A standardized Helm chart wrapper that provides consistent deployment patterns, configuration management, and best practices for Kubernetes applications across different environments.
Overview
This archetype creates a wrapper Helm chart that encapsulates common deployment patterns and configurations, making it easier to deploy applications consistently across multiple environments while maintaining flexibility and customization options.
Technology Stack
- Helm 3.x: Kubernetes package manager
- Kubernetes: Container orchestration platform
- YAML: Configuration and templating
- Go Templates: Helm templating engine
- Chart Testing: Automated chart validation
- ArgoCD/Flux: GitOps deployment (optional)
Key Features
Standardized Deployment
- Common Patterns: Deployment, Service, Ingress, ConfigMap, Secret
- Multi-Environment: Environment-specific configurations
- Resource Management: CPU, memory, and storage specifications
- Security Policies: Pod security standards and RBAC
Configuration Management
- Values Hierarchy: Global, environment, and application-specific values
- Secret Management: External secret integration
- Feature Flags: Toggle functionality through configuration
- Environment Promotion: Consistent configuration across environments
Operational Excellence
- Health Checks: Readiness and liveness probes
- Monitoring: ServiceMonitor and alerting integration
- Scaling: HPA and resource management
- Backup: Persistent volume backup configurations
Project Structure
helm-chart-wrapper/
├── Chart.yaml # Chart metadata
├── values.yaml # Default values
├── values/ # Environment-specific values
│ ├── development.yaml
│ ├── staging.yaml
│ └── production.yaml
├── templates/
│ ├── deployment.yaml # Application deployment
│ ├── service.yaml # Service definition
│ ├── ingress.yaml # Ingress configuration
│ ├── configmap.yaml # Configuration data
│ ├── secret.yaml # Secret data
│ ├── serviceaccount.yaml # Service account
│ ├── rbac.yaml # RBAC configuration
│ ├── hpa.yaml # Horizontal Pod Autoscaler
│ ├── pdb.yaml # Pod Disruption Budget
│ ├── networkpolicy.yaml # Network policies
│ ├── servicemonitor.yaml # Prometheus monitoring
│ ├── _helpers.tpl # Template helpers
│ └── tests/
│ ├── test-connection.yaml
│ └── test-deployment.yaml
├── charts/ # Subcharts directory
├── crds/ # Custom Resource Definitions
├── docs/ # Documentation
│ ├── README.md
│ ├── configuration.md
│ └── examples/
├── ci/ # CI test values
│ ├── default-values.yaml
│ └── test-values.yaml
└── .helmignore # Helm ignore patterns
Chart Configuration
Chart.yaml
apiVersion: v2
name: application-wrapper
description: A standardized Helm chart wrapper for application deployment
type: application
version: 1.0.0
appVersion: "1.0.0"
home: https://github.com/your-org/helm-chart-wrapper
sources:
- https://github.com/your-org/helm-chart-wrapper
maintainers:
- name: DevOps Team
email: devops@your-org.com
keywords:
- application
- deployment
- wrapper
annotations:
category: Infrastructure
dependencies:
- name: postgresql
version: "12.x.x"
repository: "https://charts.bitnami.com/bitnami"
condition: postgresql.enabled
- name: redis
version: "17.x.x"
repository: "https://charts.bitnami.com/bitnami"
condition: redis.enabled
Default Values (values.yaml)
# Global configuration
global:
imageRegistry: ""
imagePullSecrets: []
storageClass: ""
# Application configuration
app:
name: myapp
version: "1.0.0"
image:
registry: docker.io
repository: myorg/myapp
tag: "1.0.0"
pullPolicy: IfNotPresent
pullSecrets: []
# Deployment configuration
deployment:
replicaCount: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
# Security context
securityContext:
runAsNonRoot: true
runAsUser: 1001
fsGroup: 1001
# Pod security context
podSecurityContext:
runAsNonRoot: true
runAsUser: 1001
fsGroup: 1001
# Resource management
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
# Horizontal Pod Autoscaler
hpa:
enabled: true
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 70
targetMemoryUtilizationPercentage: 80
# Service configuration
service:
type: ClusterIP
port: 80
targetPort: 8080
annotations: {}
# Ingress configuration
ingress:
enabled: true
className: nginx
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: myapp.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: myapp-tls
hosts:
- myapp.example.com
# Health checks
healthChecks:
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
# Environment variables
env:
- name: APP_ENV
value: production
- name: LOG_LEVEL
value: info
# Configuration and secrets
configMap:
enabled: true
data:
app.properties: |
server.port=8080
logging.level.root=INFO
secret:
enabled: true
type: Opaque
data:
database-password: ""
api-key: ""
# Persistent volumes
persistence:
enabled: false
accessMode: ReadWriteOnce
size: 1Gi
storageClass: ""
# ServiceAccount and RBAC
serviceAccount:
create: true
annotations: {}
name: ""
rbac:
create: true
rules: []
# Pod Disruption Budget
podDisruptionBudget:
enabled: true
minAvailable: 1
# Network policies
networkPolicy:
enabled: false
ingress: []
egress: []
# Monitoring
monitoring:
enabled: true
serviceMonitor:
enabled: true
interval: 30s
path: /metrics
port: metrics
# Node selector and affinity
nodeSelector: {}
tolerations: []
affinity: {}
# Dependencies
postgresql:
enabled: false
auth:
postgresPassword: ""
database: myapp
redis:
enabled: false
auth:
password: ""
Template Examples
Deployment Template (templates/deployment.yaml)
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "chart.fullname" . }}
labels:
{{- include "chart.labels" . | nindent 4 }}
spec:
{{- if not .Values.hpa.enabled }}
replicas: {{ .Values.deployment.replicaCount }}
{{- end }}
strategy:
{{- toYaml .Values.deployment.strategy | nindent 4 }}
selector:
matchLabels:
{{- include "chart.selectorLabels" . | nindent 6 }}
template:
metadata:
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }}
labels:
{{- include "chart.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.image.pullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "chart.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.deployment.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.deployment.securityContext | nindent 12 }}
image: "{{ include "chart.image" . }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.targetPort }}
protocol: TCP
{{- if .Values.monitoring.enabled }}
- name: metrics
containerPort: 9090
protocol: TCP
{{- end }}
{{- if .Values.healthChecks.livenessProbe }}
livenessProbe:
{{- toYaml .Values.healthChecks.livenessProbe | nindent 12 }}
{{- end }}
{{- if .Values.healthChecks.readinessProbe }}
readinessProbe:
{{- toYaml .Values.healthChecks.readinessProbe | nindent 12 }}
{{- end }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
env:
{{- toYaml .Values.env | nindent 12 }}
{{- if .Values.configMap.enabled }}
volumeMounts:
- name: config
mountPath: /app/config
readOnly: true
{{- end }}
{{- if .Values.persistence.enabled }}
- name: data
mountPath: /app/data
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
volumes:
{{- if .Values.configMap.enabled }}
- name: config
configMap:
name: {{ include "chart.fullname" . }}
{{- end }}
{{- if .Values.persistence.enabled }}
- name: data
persistentVolumeClaim:
claimName: {{ include "chart.fullname" . }}
{{- end }}
Service Template (templates/service.yaml)
apiVersion: v1
kind: Service
metadata:
name: {{ include "chart.fullname" . }}
labels:
{{- include "chart.labels" . | nindent 4 }}
{{- with .Values.service.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: {{ .Values.service.targetPort }}
protocol: TCP
name: http
{{- if .Values.monitoring.enabled }}
- port: 9090
targetPort: 9090
protocol: TCP
name: metrics
{{- end }}
selector:
{{- include "chart.selectorLabels" . | nindent 4 }}
Helper Templates (templates/_helpers.tpl)
{{/*
Expand the name of the chart.
*/}}
{{- define "chart.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
*/}}
{{- define "chart.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 }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "chart.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "chart.labels" -}}
helm.sh/chart: {{ include "chart.chart" . }}
{{ include "chart.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "chart.selectorLabels" -}}
app.kubernetes.io/name: {{ include "chart.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "chart.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "chart.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
{{/*
Create image name
*/}}
{{- define "chart.image" -}}
{{- $registry := .Values.global.imageRegistry | default .Values.image.registry -}}
{{- $repository := .Values.image.repository -}}
{{- $tag := .Values.image.tag | default .Chart.AppVersion -}}
{{- printf "%s/%s:%s" $registry $repository $tag -}}
{{- end }}
Environment-Specific Configurations
Development (values/development.yaml)
deployment:
replicaCount: 1
hpa:
enabled: false
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 256Mi
ingress:
hosts:
- host: myapp-dev.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: myapp-dev-tls
hosts:
- myapp-dev.example.com
env:
- name: APP_ENV
value: development
- name: LOG_LEVEL
value: debug
monitoring:
enabled: false
postgresql:
enabled: true
auth:
postgresPassword: devpassword
database: myapp_dev
redis:
enabled: true
auth:
password: devpassword
Production (values/production.yaml)
deployment:
replicaCount: 5
hpa:
enabled: true
minReplicas: 5
maxReplicas: 20
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 1000m
memory: 1Gi
ingress:
annotations:
nginx.ingress.kubernetes.io/rate-limit: "100"
nginx.ingress.kubernetes.io/rate-limit-window: "1m"
hosts:
- host: myapp.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: myapp-prod-tls
hosts:
- myapp.example.com
env:
- name: APP_ENV
value: production
- name: LOG_LEVEL
value: warn
monitoring:
enabled: true
serviceMonitor:
enabled: true
podDisruptionBudget:
enabled: true
minAvailable: 2
networkPolicy:
enabled: true
ingress:
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
ports:
- protocol: TCP
port: 8080
postgresql:
enabled: false # Use external database
redis:
enabled: false # Use external cache
Deployment Commands
Basic Deployment
# Install the chart
helm install myapp ./helm-chart-wrapper \
--namespace myapp \
--create-namespace \
--values values/production.yaml
# Upgrade the deployment
helm upgrade myapp ./helm-chart-wrapper \
--namespace myapp \
--values values/production.yaml
# Rollback to previous version
helm rollback myapp 1 --namespace myapp
Advanced Deployment
# Deploy with custom values
helm install myapp ./helm-chart-wrapper \
--namespace myapp \
--create-namespace \
--values values/production.yaml \
--set image.tag=2.0.0 \
--set deployment.replicaCount=10
# Deploy with secrets from external source
helm install myapp ./helm-chart-wrapper \
--namespace myapp \
--create-namespace \
--values values/production.yaml \
--set-file secret.data.database-password=/path/to/db-password \
--set-file secret.data.api-key=/path/to/api-key
# Dry run deployment
helm install myapp ./helm-chart-wrapper \
--namespace myapp \
--values values/production.yaml \
--dry-run --debug
Testing and Validation
Chart Testing
# Lint the chart
helm lint ./helm-chart-wrapper
# Template and validate
helm template myapp ./helm-chart-wrapper \
--values values/production.yaml \
--validate
# Test with chart testing tool
ct lint --config .github/ct.yaml
ct install --config .github/ct.yaml
Test Configuration (.github/ct.yaml)
remote: origin
target-branch: main
chart-dirs:
- .
chart-repos:
- bitnami=https://charts.bitnami.com/bitnami
helm-extra-args: --timeout 600s
check-version-increment: true
debug: true
GitOps Integration
ArgoCD Application
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/your-org/helm-chart-wrapper
targetRevision: main
path: .
helm:
valueFiles:
- values/production.yaml
parameters:
- name: image.tag
value: "2.0.0"
destination:
server: https://kubernetes.default.svc
namespace: myapp
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
Flux Kustomization
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
name: myapp
namespace: flux-system
spec:
interval: 5m
sourceRef:
kind: GitRepository
name: helm-chart-wrapper
path: "./environments/production"
prune: true
wait: true
timeout: 5m
postBuild:
substitute:
image_tag: "2.0.0"
Quick Start
-
Generate the chart:
archetect render git@github.com:p6m-archetypes/helm-chart-wrapper.archetype.git -
Customize values:
# Edit values.yaml and environment-specific files
vim values/production.yaml -
Validate chart:
helm lint .
helm template myapp . --values values/production.yaml -
Deploy application:
helm install myapp . \
--namespace myapp \
--create-namespace \
--values values/production.yaml -
Verify deployment:
kubectl get pods -n myapp
kubectl get svc -n myapp
kubectl get ingress -n myapp
Best Practices
Security
- Use non-root containers and security contexts
- Implement network policies for traffic control
- Use RBAC with least privilege principle
- Store secrets securely using external secret management
Performance
- Configure appropriate resource requests and limits
- Use HPA for automatic scaling
- Implement proper health checks
- Configure Pod Disruption Budgets
Maintainability
- Use semantic versioning for chart releases
- Document all configuration options
- Implement comprehensive testing
- Follow Helm best practices and conventions