🎯 MỤC TIÊU BÀI HỌC
- ✅ Helm chart structure và template engine
- ✅ Build reusable microservice base chart
- ✅ Values management: defaults, overrides, environments
- ✅ Chart dependencies và library charts
- ✅ Helmfile cho multi-environment deployment
- ✅ Best practices: linting, testing, packaging
PHẦN 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:
- name: postgresql version: "15.x.x" repository: "https://charts.bitnami.com/bitnami" condition: postgresql.enabled
name: redis version: "19.x.x" repository: "https://charts.bitnami.com/bitnami" condition: redis.enabled
PHẦN 2: TEMPLATES CHI TIẾT
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: 2image: 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
PHẦN 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"
PHẦN 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
PHẦN 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
- Base chart: 1 reusable chart cho tất cả microservices
- Values layering: defaults → service → environment
- Helmfile: Multi-service, multi-env deployment orchestration
- Template helpers: DRY, consistent labels/names
- Testing: Lint + template + unit test trong CI
🎯 BÀI TẬP
Bài tập 1: Build Base Chart
- Create microservice Helm chart
- Deploy 3 services using same chart with different values
- Add HPA, PDB templates
Bài tập 2: Helmfile Multi-env
- Setup staging + production environments
- Deploy all services with helmfile sync
- Review changes with helmfile diff
📚 BÀI TIẾP THEO
Trong Bài 30: Secrets Management với HashiCorp Vault, chúng ta sẽ cấu hình centralized secrets management.