Phần 9 — Helm, Operator pattern và CRD
Ý kiến
0
Chưa có ý kiến nào. Hãy là người đầu tiên chia sẻ!
Chưa có ý kiến nào. Hãy là người đầu tiên chia sẻ!
Phần cuối series K8s: Cluster API (CAPI) quản lý cluster bằng K8s API, FinOps với OpenCost + Karpenter + right-sizing, AI/ML workloads (GPU, Kubeflow, KServe, vLLM, Argo Workflows, gang scheduling), Edge K8s và WebAssembly.
Multi-tenancy K8s (soft: namespace+RBAC+Quota, HNC, Capsule, vCluster; hard: cluster riêng) và multi-cluster: management cluster, GitOps ApplicationSet, Cluster API, Karmada, Crossplane, observability liên cluster.
Service Mesh chuyên sâu cho K8s: Istio (sidecar + ambient), Linkerd (proxy Rust nhẹ), Cilium Service Mesh (eBPF sidecarless) — mTLS, VirtualService, AuthorizationPolicy, canary, mirror, multi-cluster, đo overhead, khi không nên dùng mesh.
Series Kubernetes Toàn Tập — 13 phần:
Phần 9 — Helm, Operator, CRD ← bạn đang đọc
Manifest thô khá ổn cho 5–10 file. Khi bạn có 50 microservice × 3 môi trường, cần một cách:
Đóng gói application gồm nhiều object thành 1 đơn vị release có version.
Tham số hoá cho dev/staging/prod.
Mở rộng K8s bằng object riêng (vd. PostgresCluster) để encode kiến thức vận hành.
Phần này đi qua Helm, Kustomize (đối thủ), CRD và Operator pattern.
Helm là package manager kiểu apt/yum cho K8s. Một package gọi là chart.
mychart/
├── Chart.yaml # metadata: name, version, appVersion
├── values.yaml # giá trị mặc định
├── values.schema.json # validate values (tuỳ chọn)
├── templates/
│ ├── _helpers.tpl # template fragment
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── configmap.yaml
│ ├── hpa.yaml
│ └── NOTES.txt # in ra sau khi install
└── charts/ # subchart (dependencies)
Helm template dùng Go text/template + helper Sprig:
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "mychart.fullname" . }}
labels:
{{- include "mychart.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "mychart.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "mychart.selectorLabels" . | nindent 8 }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
resources:
{{- toYaml .Values.resources | nindent 10 }}
{{- if .Values.env }}
env:
{{- range $k, $v := .Values.env }}
- name: {{ $k }}
value: {{ $v | quote }}
{{- end }}
{{- end }}
replicaCount: 2
image:
repository: myorg/myapp
tag: "" # nếu rỗng dùng .Chart.AppVersion
pullPolicy: IfNotPresent
resources:
requests:
cpu: 100m
memory: 128Mi
env:
LOG_LEVEL: info
# Render local (không apply)
helm template mychart ./mychart -f values-prod.yaml > out.yaml
# Lint
helm lint ./mychart
# Install
helm install api ./mychart -f values-prod.yaml -n team-a --create-namespace
# Upgrade (idempotent)
helm upgrade --install api ./mychart -f values-prod.yaml -n team-a
# Lịch sử
helm history api -n team-a
helm rollback api 3 -n team-a
# Xoá
helm uninstall api -n team-a
# Đóng gói + push
helm package ./mychart
helm push mychart-1.0.0.tgz oci://registry.example.com/charts
Chart cha có thể phụ thuộc subchart:
# Chart.yaml
dependencies:
- name: postgresql
version: 13.x.x
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled
helm dependency update ./mychart # pull subchart vào charts/
Template indent. Sai 1 space là YAML invalid. Dùng nindent N hơn indent N để đỡ đau đầu.
Quoting. Mọi giá trị string nên | quote để tránh YAML hiểu nhầm thành bool/number.
helm upgrade khi CRD chart change: Helm không tự upgrade CRD trong crds/ folder — phải apply tay.
Hook (pre-install, post-upgrade) chạy theo thứ tự, nhưng nếu fail thì release có thể kẹt.
values nested override khi merge có thể bất ngờ — luôn test helm template trước.
Helm 2 có Tiller server-side với quyền cao — rủi ro. Helm 3 client-only: state chart lưu trong Secret K8s ở namespace tương ứng (sh.helm.release.v1.<name>.v<n>). Đừng dùng Helm 2 nữa.
Kustomize không template. Bạn giữ YAML gốc, viết overlay patch lên:
base/
├── deployment.yaml
├── service.yaml
└── kustomization.yaml
overlays/
prod/
├── kustomization.yaml
└── replica-count.yaml
# base/kustomization.yaml
resources:
- deployment.yaml
- service.yaml
commonLabels:
app: api
# overlays/prod/kustomization.yaml
resources:
- ../../base
images:
- name: myorg/myapp
newTag: 1.5.2
patches:
- path: replica-count.yaml
target:
kind: Deployment
name: api
kubectl apply -k overlays/prod # built-in từ kubectl 1.14+
| Tiêu chí | Helm | Kustomize |
|---|---|---|
| Cấu trúc | Template + values | Patch overlay YAML thuần |
| Học cong | Cong (Go template + Sprig) | Phẳng |
| Đóng gói/release | Mạnh (versioned chart, repo) | Không có |
| Render | helm template | kubectl kustomize |
| Sharing | OCI/HTTP repo | Git url |
Thực tế nhiều team dùng cả hai: chart Helm để package, Kustomize overlay để override per env. Hoặc Helm có post-render qua Kustomize.
Khi có chart/Kustomize, bạn deploy thế nào? Câu trả lời hiện đại: GitOps.
GitOps = source of truth ở Git, một controller trong cluster tự pull và apply, drift detected khi state thật khác Git.
| Tool | Hỗ trợ |
|---|---|
| Argo CD | Helm, Kustomize, plain YAML; UI mạnh; multi-cluster |
| Flux | Helm, Kustomize, OCI; CLI/CRD-first; nhỏ gọn |
Workflow:
dev → push commit Git → Argo CD/Flux pull, render, apply → cluster
↑
CI build image, update digest trong manifest
Lợi ích: audit (mọi thay đổi là commit), rollback (revert commit), drift detection, no kubectl admin token cho CI.
K8s cho phép bạn thêm kind mới vào API. Một CRD khai báo schema; sau đó kubectl get <mykind> hoạt động như resource built-in.
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: backups.ops.example.com
spec:
group: ops.example.com
scope: Namespaced
names:
plural: backups
singular: backup
kind: Backup
shortNames: [bk]
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
required: [target, schedule]
properties:
target:
type: string
schedule:
type: string
pattern: '^([0-9*/,-]+ ?){5}$'
retention:
type: integer
minimum: 1
default: 7
status:
type: object
properties:
lastRun:
type: string
phase:
type: string
enum: [Pending, Running, Succeeded, Failed]
subresources:
status: {}
additionalPrinterColumns:
- name: Target
type: string
jsonPath: .spec.target
- name: Schedule
type: string
jsonPath: .spec.schedule
- name: Phase
type: string
jsonPath: .status.phase
Sau khi apply, user có thể tạo:
apiVersion: ops.example.com/v1
kind: Backup
metadata:
name: nightly
spec:
target: postgres
schedule: "0 2 * * *"
retention: 14
K8s validate theo schema, lưu vào etcd, cấp endpoint REST. Nhưng chưa làm gì cả — vì chưa có controller.
Operator = CRD + Controller. Controller là một process (chạy như Deployment trong cluster) watch object CRD và điều hoà hệ thống thực với desired state, đúng như built-in controller (Deployment, StatefulSet…).
Operator encode kiến thức vận hành của một app phức tạp: leader election, failover, backup, upgrade, schema migration, scale, …
func Reconcile(req Request) (Result, error) {
// 1. Lấy state mong muốn từ CRD
backup := &BackupCRD{}
if err := r.Get(ctx, req.NamespacedName, backup); err != nil {
return ignoreNotFound(err)
}
// 2. Lấy state hiện tại (Job, Pod, ...)
job := &batchv1.CronJob{}
if err := r.Get(ctx, name(backup), job); apierrors.IsNotFound(err) {
// 3. Tạo state thực để đạt desired
if err := r.Create(ctx, buildCronJob(backup)); err != nil { return ... }
} else {
// hoặc patch nếu khác
}
// 4. Cập nhật .status
backup.Status.LastRun = ...
r.Status().Update(ctx, backup)
return Result{}, nil
}
Controller không bao giờ kết thúc: K8s gọi Reconcile mỗi khi object CRD đổi, khi resource con đổi, hoặc theo timer.
Operator SDK (Red Hat) — Go, Ansible, Helm-based.
kubebuilder (SIG) — Go, framework nền của Operator SDK Go.
KOPF — Python operator.
Metacontroller — viết operator bằng webhook ngôn ngữ bất kỳ.
| Operator | Quản lý |
|---|---|
| CloudNativePG / Crunchy Postgres | PostgreSQL HA, backup WAL, failover |
| Strimzi | Apache Kafka |
| Elastic Cloud on K8s (ECK) | Elasticsearch, Kibana |
| Redis Enterprise / OT Redis Operator | Redis cluster |
| cert-manager | TLS cert tự động |
| Prometheus Operator | Prometheus, Alertmanager, ServiceMonitor |
| External Secrets Operator | Sync Vault/cloud secrets |
| Argo CD | GitOps Application CRD |
operatorhub.io liệt kê hàng trăm operator có sẵn. OLM (Operator Lifecycle Manager) giúp cài, upgrade, dependency operator.
| Level | Năng lực |
|---|---|
| 1. Basic Install | Cài app qua CRD |
| 2. Seamless Upgrade | Upgrade version app |
| 3. Full Lifecycle | Backup, restore, failover |
| 4. Deep Insights | Metric, alert, log tích hợp |
| 5. Auto Pilot | Tự tuning, scale, healing |
Khi chọn operator cho production, kiểm tra capability level + community/maintainer + audit log + cách backup/restore.
Trước khi viết operator, thử:
Helm chart đủ chưa?
Kustomize + GitOps đủ chưa?
Có operator open-source nào cho cùng vấn đề?
Viết operator nếu:
App có lifecycle phi tuyến (init thứ tự, failover, scale up có ràng buộc).
Bạn vận hành nhiều instance (10+) của cùng app và muốn tự động.
Bạn cần expose API riêng cho team khác tạo resource (vd. Tenant, Workspace).
Operator là code. Nó cần test, monitor, security review như mọi app khác.
Ngoài CRD + Controller, một cách khác để mở rộng là webhook. K8s gọi service của bạn để validate hoặc mutate request trước khi persist.
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
name: inject-sidecar
webhooks:
- name: sidecar.example.com
rules:
- operations: ["CREATE"]
apiGroups: [""]
apiVersions: ["v1"]
resources: ["pods"]
clientConfig:
service:
name: sidecar-injector
namespace: system
path: "/mutate"
caBundle: LS0tLS1...
admissionReviewVersions: ["v1"]
sideEffects: None
failurePolicy: Fail
Istio sidecar inject, vault-agent injector, kyverno mutate — đều qua webhook. Cẩn thận: webhook chết = mọi request bị block (trừ khi failurePolicy: Ignore).
Một service một chart Helm, dùng values override per env.
Đặt chart trong cùng repo source hoặc repo platform; pin chart version.
Đừng commit Secret thường — dùng SealedSecret/SOPS/External Secrets.
GitOps (Argo CD/Flux) hơn kubectl apply trực tiếp cho production.
Trước khi viết operator, tìm cái có sẵn (operatorhub.io).
CRD phải có schema chặt + status subresource + printer columns để kubectl get đẹp.
Operator: chạy 2 replica + leader election; ResourceVersion + retry; idempotent reconcile.
Webhook timeout ngắn + failurePolicy: Ignore cho non-critical mutation.
# Helm release status
helm list -A
helm status api -n team-a
helm get manifest api -n team-a # YAML K8s thật sự deploy
helm get values api -n team-a
# Argo CD app
argocd app get api
argocd app diff api
argocd app sync api
# CRD
kubectl get crd
kubectl explain backup.spec
# Operator log
kubectl logs -n my-operator deploy/my-operator -f
Helm: package + template + version + repo. Bạn sẽ gặp ở mọi chart phổ biến.
Kustomize: overlay YAML thuần, không template. Tốt cho per-env patch.
GitOps (Argo CD/Flux): triển khai từ Git là chuẩn hiện đại.
CRD + Operator: mở rộng K8s với object riêng + controller tự động vận hành app phức tạp.
Admission webhook: validate/mutate request — dùng cho policy và inject sidecar.
Trong Phần 10 — phần cuối, ta tổng kết production checklist: observability, backup/restore, upgrade cluster và disaster recovery.