0

Thực hành CI/CD + Kubernetes - Phần 2/2 - Autoscaling, self-healing và rollback

Bài 6/6 trong series CI/CD và Kubernetes. Tiếp nối Thực hành Phần 1/2 — bài này giả định lab đang ở trạng thái baseline (HPA 3–10, MAX_CONNECTIONS=100) sau khi đã hoàn tất phần release, static capacity và overload.

Mục tiêu

Phần này tập trung tìm hiểu các nội dung sau:

  • quan sát HPA scale từ 3 tới tối đa 10 Pod dưới tải 800 connections;
  • mô phỏng mất Pod, rollback một rollout và đưa lab về trạng thái chuẩn;
  • xử lý các lỗi thường gặp theo từng stage/controller thay vì thử lại ngẫu nhiên;
  • hiểu cách quy đổi bài lab sang kind hoặc minikube ở mức local.

Phần 4 — autoscaling với 800 connections

4.1 Bật HPA

Chạy pipeline broker với: text

TRIGGERED_JOB=apply_hpa
MAX_CONNECTIONS=100
HPA_MIN_PODS=3
HPA_MAX_PODS=10

Job đặt Deployment về minimum ba replica, restore giới hạn 100 rồi apply HPA. HPA dùng custom metric mqtt_broker_connections với target trung bình 80 connections trên mỗi Pod.

Bên dưới job apply_hpa chạy gì? Sau khi xác thực HPA_MIN_PODS và HPA_MAX_PODS trong khoảng 1–10, đồng thời bảo đảm min không lớn hơn max, job chạy: bash

kubectl -n mqtt delete hpa mqtt-broker --ignore-not-found

kubectl -n mqtt patch deployment mqtt-broker --type strategic \
  -p "{\"spec\":{\"replicas\":${HPA_MIN_PODS},\"template\":{\"metadata\":{\"annotations\":{\"demo.acme.io/mode\":\"hpa\",\"demo.acme.io/config\":\"pods-${HPA_MIN_PODS}-${HPA_MAX_PODS}-max-connections-${MAX_CONNECTIONS}\"}},\"spec\":{\"containers\":[{\"name\":\"broker\",\"env\":[{\"name\":\"MAX_CONNECTIONS\",\"value\":\"${MAX_CONNECTIONS}\"}]}]}}}}"

kubectl -n mqtt rollout status deployment/mqtt-broker --timeout=3m

sed \
  -e "s/__HPA_MIN_PODS__/${HPA_MIN_PODS}/" \
  -e "s/__HPA_MAX_PODS__/${HPA_MAX_PODS}/" \
  ci/mqtt-broker-hpa.yaml.tpl | kubectl apply -f -

kubectl -n mqtt get deployment mqtt-broker
kubectl -n mqtt get hpa mqtt-broker
kubectl -n mqtt get pods \
  -l app.kubernetes.io/name=mqtt-broker -o wide

Job xóa HPA cũ trước, đưa Deployment về minimum replica và rollout cấu hình MAX_CONNECTIONS. Chỉ sau khi Pod baseline Ready, sed mới thay hai placeholder min/max trong template HPA rồi truyền YAML qua stdin cho kubectl apply -f -.

Template giữ cố định target averageValue: "80", scale-up tối đa bốn Pod hoặc 100% mỗi 30 giây và scale-down có stabilization window 60 giây. Với input hiện tại, HPA được tạo lại với minReplicas=3, maxReplicas=10; từ thời điểm đó HPA tiếp quản spec.replicas của Deployment.

Vòng điều khiển autoscaling gồm nhiều component bất đồng bộ:

HPA không nhìn trực tiếp Grafana và không tự tạo Pod. Nó đọc metric qua API rồi ghi desired replicas; Deployment, Scheduler và kubelet hoàn thành phần còn lại. Mỗi mắt xích có chu kỳ riêng, vì vậy scale không xảy ra ngay khi client đầu tiên kết nối.

Với ba Pod đã chạm gần 100 connections/Pod, phép tính khái niệm cho vòng đầu là 3 × 100 / 80 = 3,75, được làm tròn lên khoảng bốn Pod trước khi áp policy. Load generator tiếp tục thử các connections chưa thành công; Pod mới Ready nhận thêm connections, metric lại được đánh giá và HPA tiếp tục tăng theo từng vòng. Đây là lý do bài lab cần CONNECT_TIMEOUT_MS đủ dài và HOLD_MS=90000, thay vì kỳ vọng nhảy thẳng từ 3 lên 10.

Kiểm tra ở terminal: bash

kubectl -n mqtt get hpa mqtt-broker
kubectl -n mqtt get pods -l app.kubernetes.io/name=mqtt-broker

4.2 Chạy load test với 800 connections

Trong project mqtt-client, chạy: text

TARGET_CONNECTIONS=800
HOLD_MS=90000

Pipeline mqtt-client không chạy kubectl. Runner copy CA công khai đã được cài sẵn, chạy npm install rồi npm test; chương trình Node.js đọc TARGET_CONNECTIONS, CONNECT_TIMEOUT_MS và HOLD_MS để mở tải MQTT từ bên ngoài cluster. Chính tải này làm metric thay đổi, còn HPA mới là thành phần gọi Kubernetes API để điều chỉnh replica.

Nếu môi trường khởi tạo connection chậm, có thể giữ giá trị timeout mặc định của pipeline hoặc tăng theo hướng dẫn vận hành của lab. Không giảm HOLD_MS quá ngắn: HPA cần ít nhất vài chu kỳ metric để quan sát và scale.

Trên Grafana, theo dõi:

  • Average connections / Pod so với target 80;
  • Broker pods tăng từ 3;
  • active và rejected connections;
  • connection limit 100 trên mỗi Pod.

Kết quả mong đợi: average connections tăng, HPA tăng desired replicas khi vượt target và Deployment tạo Pod mới. Pod chỉ nhận traffic sau khi Ready. Khi client ngắt, HPA đợi stabilization window 60 giây rồi giảm dần về ba Pod.

Các MQTT connection đã thiết lập không được Service chuyển sang Pod mới. Scale-out chủ yếu tạo capacity cho connection mới. Khi một Pod đang giữ connection bị xóa, client trên Pod đó bị ngắt và chỉ phục hồi nếu client có cơ chế reconnect; self-healing của Kubernetes không bảo toàn TCP session.

Không chạy kubectl scale khi HPA active. Cả hai cùng ghi spec.replicas, vì vậy giá trị thủ công sẽ bị HPA ghi lại và làm kết quả demo khó giải thích.

Nếu HPA không scale, kiểm tra custom metrics API và thời lượng giữ tải trước khi thay đổi manifest.

Phần 5 — self-healing

Đợi client job kết thúc hoặc hệ thống ổn định, sau đó trong project broker chạy: text

TRIGGERED_JOB=simulate_pod_failure

Job sẽ chọn một broker Pod đang chạy, xóa Pod đó và chờ tối đa 180 giây để số Pod Ready trở về desired replicas.

Bên dưới job simulate_pod_failure chạy gì? Phần cốt lõi của job là: bash

failed_pod=$(kubectl -n mqtt get pods \
  -l app.kubernetes.io/name=mqtt-broker \
  --field-selector=status.phase=Running \
  -o jsonpath='{.items[0].metadata.name}')

kubectl -n mqtt delete pod "$failed_pod" --wait=true

desired=$(kubectl -n mqtt get deployment mqtt-broker \
  -o jsonpath='{.spec.replicas}')

ready=$(kubectl -n mqtt get pods \
  -l app.kubernetes.io/name=mqtt-broker \
  -o jsonpath='{range .items[*]}{.status.conditions[?(@.type=="Ready")].status}{"\n"}{end}' | \
  awk '$1 == "True" { count++ } END { print count + 0 }')

Job lặp phép đọc desired và ready mỗi năm giây, tối đa 180 giây. Nếu ready >= desired, job thành công; nếu hết hạn, job trả lỗi. Lệnh delete pod chỉ xóa một Pod được chọn bằng label, không scale Deployment và không tự tạo replacement. ReplicaSet phát hiện thiếu replica rồi tạo Pod thay thế; vòng lặp chỉ quan sát quá trình hội tụ đó.

Job đang kiểm tra hai invariant: Deployment vẫn giữ desired replicas và tất cả replacement cuối cùng Ready. Nó không kiểm tra MQTT session cũ còn tồn tại; client gắn với Pod bị xóa sẽ mất connection. Đây là khác biệt giữa capacity recovery và session continuity. Quan sát job log và Grafana:

  • tên Pod bị xóa biến mất;
  • ReplicaSet tạo Pod thay thế;
  • Pod mới chuyển từ Pending/ContainerCreating sang Running và Ready;
  • tổng số Pod Ready hội tụ về desired state;
  • pipeline chỉ thành công sau khi replacement sẵn sàng.

Kubernetes không “sửa” Pod đã mất; controller tạo Pod mới theo template của Deployment.

Phần 6 — rollout và rollback

Phần này minh họa rollback cấu hình. HPA và static replica cùng ghi số replica, nên tạm xóa HPA trước: bash

kubectl -n mqtt delete hpa mqtt-broker
kubectl -n mqtt patch deployment mqtt-broker --type strategic \
  --patch-file kubernetes/mqtt/00-baseline-3-replicas.yaml
kubectl -n mqtt patch deployment mqtt-broker --type strategic \
  --patch-file kubernetes/mqtt/30-rollout-limit-80.yaml
kubectl -n mqtt rollout status deployment/mqtt-broker
kubectl -n mqtt rollout history deployment/mqtt-broker

Chuỗi thay đổi resource:

kubectl rollout undo chỉ làm việc với lịch sử Pod template của Deployment. Nó không khôi phục HPA đã bị xóa vì HPA là API object độc lập. Nó cũng không biết “baseline seminar” là gì nếu revision trước đến từ một thí nghiệm khác. Vì vậy rollback nhanh luôn được nối với bước apply baseline rõ ràng.

Quan sát Grafana và xác nhận limit chuyển sang 80. Không chạy MQTT load test 300 connections trong trạng thái này nếu mục tiêu hiện tại chỉ là minh họa rollout.

Rollback revision vừa tạo: bash

kubectl -n mqtt rollout undo deployment/mqtt-broker
kubectl -n mqtt rollout status deployment/mqtt-broker

Sau rollback, kiểm tra giá trị thực tế thay vì giả định: bash

kubectl -n mqtt get deployment mqtt-broker \
  -o jsonpath='max_connections={.spec.template.spec.containers[?(@.name=="broker")].env[?(@.name=="MAX_CONNECTIONS")].value}{"\n"}'

Vì trạng thái trước phụ thuộc lịch sử demo, phần tiếp theo luôn apply baseline để kết thúc nhất quán.

Khôi phục trạng thái chuẩn

Cách ưu tiên là chạy pipeline broker trên default branch: text

TRIGGERED_JOB=apply_hpa
MAX_CONNECTIONS=100
HPA_MIN_PODS=3
HPA_MAX_PODS=10

Nếu GitLab hoặc Runner không khả dụng, dùng CLI từ root repository: bash

kubectl -n mqtt patch deployment mqtt-broker --type strategic \
  --patch-file kubernetes/mqtt/40-restore-limit-100.yaml
kubectl apply -f kubernetes/mqtt/05-hpa-active.yaml
kubectl -n mqtt rollout status deployment/mqtt-broker
kubectl -n mqtt get deployment,pods,hpa

Checklist kết thúc bắt buộc:

  • HPA mqtt-broker tồn tại và active.
  • minReplicas=3, maxReplicas=10.
  • MAX_CONNECTIONS=100.
  • Deployment rollout hoàn tất.
  • Tất cả broker Pod hiện tại đều Ready.
  • Sau khi hết tải và qua stabilization window, replica trở về 3.

Chạy tương đương trên local

Phần local chỉ luyện các khái niệm chính, không tái tạo toàn bộ hạ tầng seminar.

Luồng local tối thiểu

Tạo cluster: bash

kind create cluster --name cicd-k8s-lab
kubectl cluster-info --context kind-cicd-k8s-lab

Build image broker từ source: bash

docker build -t mqtt-broker:local repositories/mqtt-broker
kind load docker-image mqtt-broker:local --name cicd-k8s-lab

Manifest của seminar tham chiếu registry private, TLS Secret và monitoring CRD. Với local, tạo overlay riêng dùng mqtt-broker:local, imagePullPolicy: IfNotPresent và bỏ dependency chưa cài; không sửa manifest seminar.

Sau khi Deployment và Service local đã tồn tại, có thể quan sát rollout: bash

kubectl -n mqtt get deployment,pods,service
kubectl -n mqtt rollout status deployment/mqtt-broker
kubectl -n mqtt port-forward service/mqtt-broker 1883:1883

Ở terminal khác, trỏ MQTT client tới mqtt://127.0.0.1:1883. Port-forward chỉ phù hợp để tìm hiểu trên một máy.

Giới hạn của HPA local

HPA trong seminar dùng custom metric mqtt_broker_connections, không phải CPU metric mặc định. Chỉ cài metrics-server chưa đủ; cluster cần Prometheus, Prometheus Adapter và mapping custom metrics API.

Nếu chưa cài stack này, bỏ qua HPA để tập trung vào Deployment, Service, rollout, rollback và self-healing; hoặc tạo HPA CPU riêng sau khi cài metrics-server. HPA CPU không tái hiện hành vi của connection metric.

Với minikube, thay bước load image bằng cơ chế của minikube và dùng minikube service để mở Service.

Luồng docker build rồi kind load chỉ thay thế thủ công hai stage package/publish để tìm hiểu Kubernetes. Muốn khảo sát CI/CD đầy đủ trên local, cần nối GitLab Runner với Docker registry mà cluster local truy cập được và cấp một Kubernetes identity giới hạn quyền; không nên đưa kubeconfig admin vào job chỉ vì đây là môi trường thử nghiệm.

Xử lý lỗi thường gặp

Bắt đầu bằng stage hoặc controller báo lỗi, thay vì thử lại ngẫu nhiên:

Pipeline tag không chạy package/deploy

  • Kiểm tra tag đúng dạng vX.Y.Z, không có suffix.
  • Kiểm tra pipeline source là tag, không phải Web pipeline trên branch.
  • Kiểm tra tag có được bảo vệ theo cấu hình project hay không.

Package báo release image đã tồn tại Release tag là bất biến. Không ghi đè image. Tạo một semantic version mới từ commit mong muốn.

Deploy timeout Đọc trạng thái mà không in Secret: bash

kubectl -n mqtt get pods -o wide
kubectl -n mqtt describe deployment mqtt-broker
kubectl -n mqtt get events --sort-by=.lastTimestamp | tail -20
kubectl -n mqtt rollout history deployment/mqtt-broker

Tìm image pull error, readiness failure, thiếu tài nguyên hoặc Node NotReady.

HPA hiển thị unknown hoặc không scale bash

kubectl -n mqtt describe hpa mqtt-broker
kubectl get --raw \
  '/apis/custom.metrics.k8s.io/v1beta1/namespaces/mqtt/pods/*/mqtt_broker_connections'

Kiểm tra Pod Ready, Prometheus scrape, Adapter API và thời gian giữ tải. Không tăng replica thủ công để che lỗi metric.

Self-healing job timeout Kiểm tra desired replica, Pod Pending, image pull, readiness và tài nguyên Node. Sau chẩn đoán, luôn khôi phục baseline.

Dọn dẹp và tổng kết

Dọn dẹp sau seminar

  • Dừng mọi MQTT client/load-test còn chạy.
  • Chạy quy trình khôi phục trạng thái chuẩn.
  • Đóng các terminal có session nhạy cảm và không lưu output chứa credential.
  • Hạ VPN hoặc phá hủy hạ tầng chỉ theo hướng dẫn triển khai nội bộ; không xóa tài nguyên cloud thủ công từng phần.

Với cluster kind local: bash kind delete cluster --name cicd-k8s-lab

Lệnh này chỉ dành cho cluster local có tên cụ thể ở ví dụ trên, không dùng với cluster seminar.

Tổng kết

Bài lab nối Git tag với image có thể truy vết, Kubernetes rollout, HPA connection metric và tín hiệu runtime trên Grafana. GitLab Runner chỉ gửi thay đổi qua Kubernetes API; Deployment, ReplicaSet, Scheduler, kubelet, EndpointSlice và HPA controller mới là các thành phần đưa actual state về desired state qua reconciliation loop.

Các bước static 300, overload 240, HPA 800, self-healing và rollback là những thí nghiệm riêng với biến điều khiển cùng tiêu chí đạt rõ ràng. Rollback kết hợp baseline recovery biến failure thành thao tác đã được chuẩn bị, thay vì hành động ứng biến. Thông điệp quan trọng nhất: deploy thành công không phải điểm kết thúc. Một release đáng tin cậy cần artifact bất biến, quyền tối thiểu, rollout có kiểm tra, metric runtime và đường phục hồi đã được diễn tập.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí