0

Thực hành CI/CD + Kubernetes - Phần 1/2 - Release, fixed capacity và overload

Bài 5/6 trong series CI/CD và Kubernetes. Bài lab nối một Git tag với GitLab CI, Container Registry, Kubernetes rollout, MQTT load test và metric trên Grafana. Nếu chưa đọc phần lý thuyết, xem CI/CD overview và Kubernetes overview trước.

Mục tiêu

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

  • release MQTT broker bằng semantic Git tag và theo dõi pipeline test → package → deploy;
  • kiểm tra image, rollout và desired state mà không đọc dữ liệu nhạy cảm;
  • giải thích ranh giới trách nhiệm giữa GitLab Runner, Kubernetes API và các controller;
  • so sánh fixed capacity 300 với tình huống overload 240;
  • đọc mỗi phần lab như một thí nghiệm gồm giả thuyết, biến điều khiển, tín hiệu quan sát và tiêu chí đạt.

Bức tranh toàn bài

Lab dùng GitLab Web UI làm happy path cho thao tác runtime. kubectl chủ yếu để preflight, chẩn đoán và phục hồi.

Ai thực sự điều khiển workload?

GitLab không trực tiếp tạo container. Runner gửi desired state tới Kubernetes API; các controller trong cluster mới thực hiện việc hội tụ:

GitLab Runner là actor thay đổi cấu hình; API Server là API control point; Deployment, ReplicaSet, HPA, Scheduler và kubelet là các reconciliation actor. Grafana chỉ quan sát, không thay đổi cluster.

Cácjob deploy, apply_static, apply_hpa và simulate_pod_failure dùng chung resource_group tên mqtt-broker-deployment. GitLab dùng resource_group để serialize các job này, tức chỉ cho một job thay đổi workload tại một thời điểm. Đây là lớp chống race condition ở CI; reconciliation loop của Kubernetes không tự biết thay đổi nào trong hai pipeline là ý định cuối cùng của người vận hành.

State machine của bài lab

Mỗi bước cố ý thay đổi một nhóm biến rồi quan sát kết quả. Không nhảy bước hoặc chạy hai load test cùng lúc, vì metric còn sót từ bước trước sẽ làm kết luận khó tin cậy.

Chuẩn bị và nguyên tắc an toàn

Bạn cần:

  • môi trường seminar đã được dựng theo hướng dẫn triển khai nội bộ;
  • WireGuard đang active và private DNS hoạt động;
  • root CA của seminar đã được trust theo hướng dẫn;
  • quyền truy cập GitLab project mqtt-broker, mqtt-client và dashboard Grafana;
  • git và kubectl trên máy điều khiển nếu thực hiện phần CLI;
  • một release tag mới chưa tồn tại nếu chạy phần release.

Không thực hiện các thao tác sau trong terminal đang chia sẻ màn hình:

  • in nội dung file password, token, kubeconfig, certificate key hay private key;
  • chạy kubectl get secret -o yaml;
  • commit bất kỳ file nào từ thư mục secrets/.

Thông tin đăng nhập được xem riêng theo quy trình của lab.

Baseline là Deployment mqtt-broker trong namespace mqtt, HPA 3–10 Pod, MAX_CONNECTIONS=100, ba Pod Ready khi chưa có tải và một release hợp lệ đang chạy. Nếu không chắc, chạy phần khôi phục trước.

Preflight

Từ root repository, chỉ trỏ kubectl tới kubeconfig mà không in nội dung file: bash

export KUBECONFIG="$PWD/secrets/kubeconfig"
kubectl get nodes
kubectl -n mqtt get deployment,pods,service,hpa
kubectl -n mqtt rollout status deployment/mqtt-broker

Tất cả Node cần Ready, số Pod available phải bằng desired replicas và HPA không báo target là unknown kéo dài. Xác nhận custom metrics API trả dữ liệu: bash

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

Preflight là một quality gate cho bài lab, không phải checklist hình thức:

Nếu custom metric chưa sẵn sàng, HPA có thể hiển thị unknown và kết quả autoscaling không còn ý nghĩa. Nếu Deployment chưa ổn định, load test sẽ trộn lỗi baseline với lỗi của thay đổi đang thử. Mở sẵn ba tab:

  1. GitLab project mqtt-broker;
  2. GitLab project mqtt-client;
  3. Grafana dashboard MQTT Broker — Seminar Overview. Trong project broker, mở Build → Pipelines → New pipeline. Dropdown TRIGGERED_JOB phải có none, apply_static, apply_hpa và simulate_pod_failure. Giá trị mặc định none không thay đổi cluster.

Phần 1 — release một phiên bản

1.1 Tạo release tag

Vào working copy của MQTT broker và kiểm tra tag trước khi chọn phiên bản: bash

cd repositories/mqtt-broker
git status --short
git tag --list

Working tree cần sạch. Ví dụ dưới đây dùng v1.1.0; nếu tag đó đã tồn tại, hãy thay bằng semantic version mới chưa sử dụng: bash

git tag v1.1.0
git push origin v1.1.0

Không di chuyển hoặc ghi đè một release tag cũ. Pipeline chủ động thất bại nếu image cùng release tag đã tồn tại trong registry.

1.2 Quan sát pipeline

Trong GitLab, mở pipeline do tag vừa kích hoạt: text test → package → deploy

test chạy kiểm thử Node.js; package build và push image với release tag cùng commit SHA; deploy đổi image của Deployment, gắn annotation truy vết và chờ rollout tối đa năm phút. Không copy registry password hoặc kubeconfig từ runner. Deployment identity đã được bootstrap ngoài source và giới hạn bằng RBAC.

Bên dưới job deploy chạy gì? GitLab Runner mở container có kubectl, nạp sẵn Kubernetes identity của CI rồi thực thi ba lệnh chính: bash

kubectl -n mqtt set image deployment/mqtt-broker \
  broker="$CI_REGISTRY_IMAGE:$CI_COMMIT_TAG"

kubectl -n mqtt annotate deployment/mqtt-broker \
  ci.acme-iot/release="$CI_COMMIT_TAG" \
  ci.acme-iot/commit="$CI_COMMIT_SHA" \
  ci.acme-iot/pipeline="$CI_PIPELINE_URL" \
  --overwrite
kubectl -n mqtt rollout status deployment/mqtt-broker --timeout=5m
  • set image gửi patch tới Kubernetes API, đổi image của container tên broker trong Pod template. Template đổi làm Deployment tạo ReplicaSet mới và bắt đầu rolling update.
  • annotate ghi release tag, commit và URL pipeline lên Deployment để truy vết. Các annotation này nằm trên Deployment, không chứa credential.
  • rollout status theo dõi Deployment thay vì chỉ gửi lệnh rồi kết thúc. Job thất bại nếu revision mới không hoàn tất trong năm phút.

Các lệnh chạy từ Runner nhưng tác động giống khi một người có đúng Kubernetes identity chạy chúng trong terminal. Identity này chỉ có quyền theo namespace: luồng release dùng quyền patch Deployment mqtt-broker và đọc trạng thái rollout; các quyền xóa/tạo HPA hoặc xóa Pod chỉ phục vụ những job demo tương ứng. CI không có quyền cluster-admin.

Toàn bộ hand-off trong release diễn ra như sau:

Pipeline thành công chỉ xác nhận Kubernetes báo rollout hoàn tất. Metric sau deploy vẫn cần thiết để phát hiện lỗi ứng dụng mà readiness probe không bao phủ, chẳng hạn broker nhận TCP nhưng reject connection ở logic nghiệp vụ.

1.3 Kiểm tra kết quả

Trở về root repository nếu cần, rồi chạy: bash

cd ../..
kubectl -n mqtt rollout status deployment/mqtt-broker
kubectl -n mqtt rollout history deployment/mqtt-broker
kubectl -n mqtt get deployment mqtt-broker \
  -o jsonpath='image={.spec.template.spec.containers[?(@.name=="broker")].image}{"\n"}'

Kết quả mong đợi là rollout hoàn tất, image kết thúc bằng release tag vừa tạo và các Pod mới lần lượt Ready. Nếu deploy thất bại, dừng tại đây. Không tiếp tục chạy load test trên một rollout chưa ổn định. Xem Pod, event và rollout history theo phần xử lý lỗi.

Phần 2 — fixed capacity 300

Mục tiêu của phần này là chứng minh fixed capacity bằng đúng nhu cầu: 3 Pod × 100 connections/Pod = 300 connections.

2.1 Chuyển broker sang static mode

Trong project mqtt-broker, chọn New pipeline, chạy trên default branch với: text

TRIGGERED_JOB=apply_static
MAX_CONNECTIONS=100
PODS=3

Job xác thực đầu vào, xóa HPA, đặt Deployment về ba replica với MAX_CONNECTIONS=100, rồi chờ rollout. Web pipeline thao tác runtime không chạy lại test/package và không thay image release.

Bên dưới job apply_static chạy gì? Sau khi shell kiểm tra MAX_CONNECTIONS là số dương và PODS nằm trong khoảng 1–10, job thực thi logic tương đương: bash

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

kubectl -n mqtt patch deployment mqtt-broker --type strategic \
  -p "{\"spec\":{\"replicas\":${PODS},\"template\":{\"metadata\":{\"annotations\":{\"demo.acme.io/mode\":\"static\",\"demo.acme.io/config\":\"pods-${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
kubectl -n mqtt get deployment mqtt-broker
kubectl -n mqtt get hpa mqtt-broker || true
kubectl -n mqtt get pods \
  -l app.kubernetes.io/name=mqtt-broker -o wide

Trình tự này có chủ đích:

  1. Xóa HPA trước để HPA không tiếp tục ghi đè spec.replicas.
  2. Strategic merge patch đặt số replica từ PODS, đổi MAX_CONNECTIONS và ghi mode vào Pod-template annotation. Container được merge theo tên broker, nên image, probe, port và resource limit hiện có không bị xóa.
  3. Vì environment variable hoặc template annotation thay đổi, Deployment tạo rollout mới.
  4. Các lệnh get cuối job chỉ đọc lại actual state và in bằng chứng vào job log.

Với input của phần này, các biến được thay thành replicas=3 và MAX_CONNECTIONS=100.

Fixed capacity của thí nghiệm:

MAX_CONNECTIONS là hard limit của ứng dụng trong từng Pod, còn Service chỉ phân phối connections đến endpoint Ready. Kubernetes không bảo đảm chính xác 100 connections trên từng Pod. Vì MQTT connection sống lâu, một connection thường gắn với Pod đã nhận nó cho đến khi ngắt; Service không liên tục rebalance connections đang tồn tại.

2.2 Chạy load test với 300 connections

Trong project mqtt-client, chạy một pipeline mới với: text TARGET_CONNECTIONS=300

Quan sát Grafana trong lúc job chạy. Kết quả mong đợi:

  • ba broker Pod;
  • tổng active connections đạt gần 300;
  • giới hạn mỗi Pod là 100;
  • không có lượng rejected connections đáng kể trong steady state;
  • client pipeline thành công.

Phân phối có thể không hoàn toàn bằng nhau vì scheduling và thời điểm scrape metric.

Thí nghiệm đạt khi client thiết lập đủ 300 connections trong timeout, nhưng đây là load test sát trần. Trong production cần headroom cho phân phối không đều, connection churn và thời gian tạo Pod mới; “capacity lý thuyết bằng đúng demand” không phải cấu hình an toàn.

Phần 3 — overload capacity 240

Mục tiêu là tạo một failure có chủ đích: nhu cầu 300 lớn hơn capacity 3 × 80 = 240.

3.1 Giảm giới hạn mỗi Pod

Chạy pipeline broker trên default branch với: text

TRIGGERED_JOB=apply_static
MAX_CONNECTIONS=80
PODS=3

Việc đổi environment variable làm Pod template thay đổi, vì vậy Deployment tạo rollout mới dù image không đổi. GitLab vẫn chạy chính chuỗi kubectl của apply_static ở phần trước; chỉ có giá trị được nội suy thành replicas=3 và MAX_CONNECTIONS=80. Vì HPA đã bị xóa, capacity giữ cố định ở ba Pod trong suốt phép thử.

Con số 240 và 60 là mô hình capacity, không phải cam kết phân phối chính xác. Một Pod có thể đầy trước Pod khác, nên rejected connections có thể xuất hiện trước khi tổng active connections chạm đúng 240.

3.2 Chạy lại load test

Trong project mqtt-client, tiếp tục dùng: text TARGET_CONNECTIONS=300

Kết quả mong đợi là rejected connections tăng, client không thiết lập đủ 300 connections và pipeline thất bại trong khi broker vẫn chạy. Đây là expected failure: pipeline đỏ chứng minh giới hạn capacity hoạt động đúng, không phải lab bị hỏng. Không để lab ở limit 80 trước khi chạy phần HPA. Phần tiếp theo đặt lại limit 100.

Bài tiếp theo sẽ nói về Thực hành CI/CD + Kubernetes - Release, fixed capacity và overload.


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.