Chuyển đến nội dung chính

LESSON 29: HELM CHARTS FOR MICROSERVICES — TEMPLATE, VALUES, DEPENDENCIES

Build reusable Helm charts for microservices: template functions, values ​​management, chart dependencies, library charts, Helmfile multi-environment, and best practices.

🔒 DevSecOps — Lesson 29 LESSON 29: HELM CHARTS FOR MICROSERVICES — TEMPLATE, VALUES, DEPENDENCIES

Deploy Microservices On-Premises with Kubernetes HA

Part 7: GitOps with ArgoCD, Helm & Vault

xdev.asia

🎯 LESSON OBJECTIVE__HTMLTAG_68___
  • ✅ Helm chart structure and template engine
  • ✅ Build reusable microservice base chart
  • ✅ Values management: defaults, overrides, environments
  • ✅ Chart dependencies and library charts
  • ✅ Helmfile for multi-environment deployment
  • ✅ Best practices: linting, testing, packaging

PART 1: HELM CHART STRUCTURE


microservice-chart/
├── Chart.yaml              # Chart metadata
├── values.yaml             # Default values
├── values-staging.yaml     # Staging overrides
├── values-production.yaml  # Production overrides
├── templates/
│   ├── NOTES.txt           # Post-install notes
│   ├── _helpers.tpl        # Template helpers
│   ├── deployment.yaml     # Deployment
│   ├── service.yaml        # Service
│   ├── hpa.yaml            # HorizontalPodAutoscaler
│   ├── ingress.yaml        # Ingress/VirtualService
│   ├── configmap.yaml      # ConfigMap
│   ├── secret.yaml         # Secret (ExternalSecret)
│   ├── serviceaccount.yaml # ServiceAccount
│   ├── pdb.yaml            # PodDisruptionBudget
│   └── tests/
│       └── test-connection.yaml
└── charts/                 # Dependencies

1.1. Chart.yaml

# Chart.yaml:
apiVersion: v2
name: microservice
description: Base Helm chart for microservices
type: application
version: 1.0.0
appVersion: "1.0.0"

dependencies:


PART 2: DETAILED TEMPLATES

2.1. _helpers.tpl

# templates/_helpers.tpl:
{{- define "microservice.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}

{{- define "microservice.fullname" -}} {{- if .Values.fullnameOverride }} {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} {{- else }} {{- $name := default .Chart.Name .Values.nameOverride }} {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} {{- end }} {{- end }}

{{- define "microservice.labels" -}} helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }} app.kubernetes.io/name: {{ include "microservice.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/version: {{ .Values.image.tag | default .Chart.AppVersion | quote }} app.kubernetes.io/managed-by: {{ .Release.Service }} {{- end }}

{{- define "microservice.selectorLabels" -}} app.kubernetes.io/name: {{ include "microservice.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }}

2.2. Deployment Template

# templates/deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "microservice.fullname" . }}
  labels:
    {{- include "microservice.labels" . | nindent 4 }}
    version: {{ .Values.image.tag | default .Chart.AppVersion }}
spec:
  {{- if not .Values.autoscaling.enabled }}
  replicas: {{ .Values.replicaCount }}
  {{- end }}
  selector:
    matchLabels:
      {{- include "microservice.selectorLabels" . | nindent 6 }}
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  template:
    metadata:
      labels:
        {{- include "microservice.selectorLabels" . | nindent 8 }}
        version: {{ .Values.image.tag | default .Chart.AppVersion }}
      annotations:
        checksum/config: {{ include (print .Template.BasePath "/configmap.yaml") . | sha256sum }}
    spec:
      serviceAccountName: {{ include "microservice.fullname" . }}
  {{- with .Values.imagePullSecrets }}
  imagePullSecrets:
    {{- toYaml . | nindent 8 }}
  {{- end }}
  
  containers:
    - name: {{ .Chart.Name }}
      image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
      imagePullPolicy: {{ .Values.image.pullPolicy }}
      
      ports:
        - name: http
          containerPort: {{ .Values.service.targetPort | default 8080 }}
          protocol: TCP
      
      {{- if .Values.env }}
      env:
        {{- range $key, $value := .Values.env }}
        - name: {{ $key }}
          value: {{ $value | quote }}
        {{- end }}
      {{- end }}
      
      {{- if .Values.envFrom }}
      envFrom:
        {{- toYaml .Values.envFrom | nindent 12 }}
      {{- end }}
      
      {{- if .Values.livenessProbe.enabled }}
      livenessProbe:
        httpGet:
          path: {{ .Values.livenessProbe.path }}
          port: http
        initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }}
        periodSeconds: {{ .Values.livenessProbe.periodSeconds }}
        failureThreshold: {{ .Values.livenessProbe.failureThreshold }}
      {{- end }}
      
      {{- if .Values.readinessProbe.enabled }}
      readinessProbe:
        httpGet:
          path: {{ .Values.readinessProbe.path }}
          port: http
        initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }}
        periodSeconds: {{ .Values.readinessProbe.periodSeconds }}
      {{- end }}
      
      resources:
        {{- toYaml .Values.resources | nindent 12 }}
  
  {{- with .Values.nodeSelector }}
  nodeSelector:
    {{- toYaml . | nindent 8 }}
  {{- end }}
  
  {{- with .Values.topologySpreadConstraints }}
  topologySpreadConstraints:
    {{- toYaml . | nindent 8 }}
  {{- end }}

2.3. Default Values

# values.yaml:
replicaCount: 2

image: repository: registry.myapp.com/order-service tag: "" pullPolicy: IfNotPresent

service: type: ClusterIP port: 80 targetPort: 8080

resources: requests: cpu: 100m memory: 128Mi limits: cpu: 500m memory: 512Mi

autoscaling: enabled: true minReplicas: 2 maxReplicas: 10 targetCPUUtilizationPercentage: 70

livenessProbe: enabled: true path: /healthz initialDelaySeconds: 30 periodSeconds: 10 failureThreshold: 3

readinessProbe: enabled: true path: /readyz initialDelaySeconds: 5 periodSeconds: 5

env: {} envFrom: []

postgresql: enabled: false

redis: enabled: false


PART 3: MULTI-ENVIRONMENT MANAGEMENT

# values-production.yaml:
replicaCount: 3

image:
  tag: "v1.5.2"

resources:
  requests:
    cpu: 500m
    memory: 512Mi
  limits:
    cpu: "2"
    memory: 2Gi

autoscaling:
  enabled: true
  minReplicas: 3
  maxReplicas: 20

env:
  LOG_LEVEL: "warn"
  ENVIRONMENT: "production"
# values-staging.yaml:
replicaCount: 1

image:
  tag: "latest"

resources:
  requests:
    cpu: 100m
    memory: 128Mi

autoscaling:
  enabled: false

env:
  LOG_LEVEL: "debug"
  ENVIRONMENT: "staging"

PART 4: HELMFILE — MULTI-SERVICE DEPLOYMENT

# helmfile.yaml:
environments:
  staging:
    values:
      - environments/staging/values.yaml
  production:
    values:
      - environments/production/values.yaml

repositories:
  - name: bitnami
    url: https://charts.bitnami.com/bitnami

releases:
  - name: order-service
    namespace: default
    chart: ./charts/microservice
    values:
      - apps/order-service/values.yaml
      - apps/order-service/values-{{ .Environment.Name }}.yaml

  - name: payment-service
    namespace: default
    chart: ./charts/microservice
    values:
      - apps/payment-service/values.yaml
      - apps/payment-service/values-{{ .Environment.Name }}.yaml

  - name: user-service
    namespace: default
    chart: ./charts/microservice
    values:
      - apps/user-service/values.yaml
      - apps/user-service/values-{{ .Environment.Name }}.yaml
# Deploy all services:
helmfile -e production sync

# Diff before deploy:
helmfile -e production diff

# Destroy all:
helmfile -e staging destroy

PART 5: HELM TESTING & CI

# Lint chart:
helm lint ./charts/microservice

# Template rendering (dry-run):
helm template order-service ./charts/microservice \
  -f values-production.yaml \
  --debug

# Test install:
helm install order-test ./charts/microservice \
  --dry-run --debug

# Unit testing with helm-unittest:
helm plugin install https://github.com/helm-unittest/helm-unittest

# Run tests:
helm unittest ./charts/microservice
# tests/deployment_test.yaml:
suite: test deployment
templates:
  - templates/deployment.yaml
tests:
  - it: should have correct replica count
    set:
      replicaCount: 3
    asserts:
      - equal:
          path: spec.replicas
          value: 3

  - it: should set resources
    asserts:
      - isNotEmpty:
          path: spec.template.spec.containers[0].resources

💡 KEY TAKEAWAYS

  1. Base chart: 1 reusable chart for all microservices
  2. Values layering: defaults → service → environment
  3. Helmfile: Multi-service, multi-env deployment orchestration
  4. Template helpers: DRY, consistent labels/names
  5. Testing: Lint + template + unit test in CI

🎯 EXERCISES__HTMLTAG_133___

Exercise 1: Build Base Chart

  • Create microservice Helm chart__HTMLTAG_138___
  • Deploy 3 services using same chart with different values__HTMLTAG_140___
  • Add HPA, PDB templates__HTMLTAG_142___

Exercise 2: Helmfile Multi-env

  • Setup staging + production environments
  • Deploy all services with helmfile sync
  • Review changes with helmfile diff

📚 NEXT POST

In Lesson 30: Secrets Management with HashiCorp Vault, we will configure centralized secrets management.