Dựng một cluster Kubernetes mới cho dự án microservice có thể mất vài tuần nếu làm tay từng bước, hoặc chỉ mất khoảng một buổi nếu mọi thành phần đã được chuẩn hoá từ trước. Khác biệt không nằm ở tốc độ gõ lệnh mà ở chỗ hạ tầng, chart, pipeline và database đã sẵn sàng để lặp lại. Bài này mô tả kiến trúc microservice ba lớp (service nghiệp vụ, BFF, web frontend), những điều kiện để dựng cluster nhanh, thứ tự các bước, và những lỗi hay bị mang theo khi tái sử dụng cấu hình từ cluster cũ.
1. Kiến trúc microservice ba lớp: service, BFF và web
Internet (HTTPS)
│
Load balancer → ingress controller
┌────────────┼─────────────┐
example.com partner.example.com admin.example.com ← web frontend (Next.js)
│ │ │ gọi API qua domain public
api.example.com partner-api... admin-api... ← BFF (Laravel)
└────────────┼─────────────┘
│ HTTP nội bộ: <svc>.<ns>.svc.cluster.local
┌─────────┼─────────┐
svc-a svc-b svc-c ← microservice nghiệp vụ
└─────────┼─────────┘ không có Ingress
│ TCP 3306 qua mạng private
MySQL trên VPS riêng, ngoài cluster- Microservice nghiệp vụ: mỗi microservice giữ một phần nghiệp vụ và database riêng, không có Ingress, chỉ nhận request từ trong cluster.
- BFF (Backend for Frontend): mỗi nhóm người dùng (khách hàng, đối tác, quản trị) có một backend riêng, lo xác thực, gom dữ liệu từ nhiều service và trả đúng định dạng frontend cần.
- Web frontend: giao diện cho từng nhóm người dùng, gọi BFF tương ứng.
Lợi ích của BFF trong kiến trúc microservice là frontend không phải biết có bao nhiêu service phía sau, và mỗi service không phải chiều theo nhu cầu riêng của từng giao diện. Cái giá là số thành phần phải deploy tăng gấp nhiều lần so với một ứng dụng Laravel nguyên khối. Vì vậy, dựng nhanh hay chậm phụ thuộc gần như hoàn toàn vào việc deploy các thành phần đó có lặp lại được hay không.
Các microservice gọi nhau bằng tên DNS nội bộ dạng <service>.<namespace>.svc.cluster.local, theo tài liệu DNS for Services and Pods. Địa chỉ service khác được truyền vào qua ConfigMap, nên cùng một image chạy được ở cluster nào cũng được.
2. Bốn điều kiện để dựng cluster microservice nhanh
Hạ tầng cluster theo kịch bản có sẵn
Tạo cluster, node pool, cài ingress controller, cert-manager, ClusterIssuer và Prometheus nên là một pipeline, như bài Dựng hạ tầng bằng Linode API: cluster, NFS và MySQL qua pipeline. Khi có kịch bản, cluster mới giống hệt cluster cũ về phiên bản và cách cài, không phải nghĩ lại bước nào.
Một chart dùng chung cho mọi thành phần
Service nghiệp vụ, BFF và cả web Next.js đều có thể deploy bằng một chart, như bài Chart dùng chung: tái sử dụng Helm cho Laravel, MySQL và Redis. Chart có các khối bật tắt bằng values, nên khác biệt giữa các lớp chỉ nằm trong file values:
| Khối trong chart | Microservice nghiệp vụ | BFF | Web Next.js |
|---|---|---|---|
initContainers (migrate) | Bật | Tắt | Tắt |
ingress | Tắt | Bật | Bật |
| Probe | /live, /health, /ready | /live, /health, /ready | /health |
| ConfigMap | DB, URL service khác | DB, URL service, cấu hình JWT | URL public của BFF |
Ví dụ values của một microservice nghiệp vụ:
replicaCount: 2
fullnameOverride: "svc-a"
image:
repository: registry.example.com/svc-a
tag: "<sha của bản build>"
configMap:
appEnv: production
logChannel: stdout
dbHost: 10.0.1.20 # MySQL trên VPS, ngoài cluster
svcBUrl: http://svc-b.svc-b.svc.cluster.local
svcCUrl: http://svc-c.svc-c.svc.cluster.local
resources:
requests: {cpu: 250m, memory: 300Mi}
limits: {cpu: 800m, memory: 600Mi}
initContainers:
enabled: true
command: "php artisan migrate --force"
ingress:
enabled: false # chỉ gọi được từ trong cluster
startupProbe: {enabled: true, path: /live}
livenessProbe: {enabled: true, path: /health}
readinessProbe: {enabled: true, path: /ready}Deploy mọi thành phần là các lệnh giống hệt nhau, chỉ khác tên release và file values:
helm upgrade --install svc-a charts/laravel-app \
-f values/svc-a/prod.yaml -n svc-a --create-namespacePipeline build và repo values đã chạy ổn
Mỗi lần dev đẩy code, pipeline build image, ghi tag mới vào values rồi helm upgrade, theo cơ chế ở bài Mỗi branch một pipeline vận hành: tự động hoá bằng GitLab CI. Khi chuyển sang cluster mới, image, values và cách deploy đều không đổi. Thứ thay đổi chỉ là kubeconfig, địa chỉ database và nhãn node pool.
Database nằm ngoài cluster
Nếu MySQL của các microservice chạy trên VPS riêng như bài MySQL trong Kubernetes hay ngoài cluster: vì sao đưa database ra VPS, cluster mới không phải dựng StatefulSet hay chuyển volume. Pod chỉ cần trỏ dbHost tới máy MySQL qua mạng private. Phần khó nhất của mọi cuộc chuyển nhà là dữ liệu, và nó không nằm trong cluster.
3. Thứ tự các bước dựng cluster microservice
- Tạo cluster và node pool bằng pipeline hạ tầng. Chờ mọi node
Ready. - Cài ingress controller, xác nhận load balancer đã có IP public.
- Cài cert-manager và ClusterIssuer, kiểm tra CRD đã sẵn sàng trước khi tạo Certificate.
- Commit values cho cluster mới: địa chỉ database, nhãn node pool, domain. Commit trước, deploy sau.
- Deploy microservice nghiệp vụ trước, vì BFF phụ thuộc vào chúng. Kiểm tra migrate trong initContainer chạy xong.
- Deploy BFF, rồi web frontend. Gọi thử từng endpoint health.
- Cài giám sát và cảnh báo, sau đó mới trỏ DNS production.
Kiểm tra nhanh sau khi deploy:
kubectl get pods -A | grep -v Running
helm list -A
kubectl -n svc-a exec deploy/svc-a -- curl -s -o /dev/null -w '%{http_code}\n' http://svc-b.svc-b.svc.cluster.local/health4. Phần tốt nên mang sang cluster microservice mới
- Ba endpoint health riêng.
/livecho startup,/healthcho liveness,/readycho readiness. Ứng dụng có thể trả/readylỗi khi database chậm mà không khiến kubelet restart pod. - ServiceAccount riêng cho từng release, thay vì dùng chung
default. - Image là bản hoàn chỉnh. Không có hook copy code lúc khởi động. Volume, khi cần, chỉ mount thư mục dữ liệu.
- Probe đã được nới cho phù hợp với limit thực tế, tránh restart pod đang bận.
5. Lỗi hay mang theo khi tái sử dụng cấu hình microservice
Sao chép manifest là sao chép cả lỗi
Một manifest chạy được ở cluster cũ chưa chắc đúng. Ví dụ điển hình là DaemonSet giữ chỗ với PriorityClass value dương, đã phân tích ở bài PriorityClass và pod giữ chỗ: dự phòng tài nguyên cho cluster: chép sang cluster mới thì lỗi "pod giữ chỗ không bao giờ bị preempt" cũng đi theo. Trước khi đưa manifest sang cluster khác, hãy đọc lại nó với con mắt của người chưa từng thấy nó.
Deploy trước, commit values sau
Khi đang gấp, rất dễ chạy helm install bằng file values sửa trên máy rồi để hôm sau mới commit. Trong khoảng đó, repo vẫn trỏ về database và node pool cũ. Nếu pipeline build chạy đúng lúc ấy và helm upgrade bằng values trong repo, ứng dụng sẽ bị trỏ ngược về database cũ. Quy tắc: repo là nguồn sự thật, commit values trước, deploy sau.
Bước tay không có giải thích
Sổ tay lệnh kiểu "xoá ConfigMap trước khi helm upgrade" mà không ghi lý do thì người sau hoặc bỏ qua, hoặc làm theo mà không hiểu. Thường nguyên nhân gốc là template Deployment thiếu checksum annotation cho ConfigMap và Secret, nên đổi biến môi trường xong pod không tự khởi động lại. Cách sửa gốc được trình bày ở bài helm upgrade không đổi biến môi trường: thiếu checksum annotation và hướng dẫn Charts Tips and Tricks của Helm. Với chart dùng chung, sửa một lần là mọi dự án hưởng lợi.
Một replica và tag image cố định
- Một replica cho mọi Deployment nghĩa là một node gặp sự cố thì các pod trên đó ngừng cho tới khi được xếp lại. BFF và service quan trọng nên có ít nhất hai replica kèm PodDisruptionBudget.
- Tag cố định kiểu
v1.0cộngpullPolicy: Alwayskhông cho biết pod đang chạy build nào. Dùng tag theo commit SHA.
TLS và cảnh báo nằm ngoài tầm nhìn
Nếu HTTPS được xử lý ở lớp phía trước cluster và Ingress đặt tls.enabled: false, hãy ghi rõ trong tài liệu vận hành để người sau không tưởng cert-manager đang lo việc đó. Tương tự, cài Prometheus mà không có Alertmanager thì có số liệu nhưng không có cảnh báo.
6. Checklist dựng cluster cho dự án microservice
- Cluster, node pool, ingress, cert-manager dựng bằng pipeline, không bằng tay.
- Mọi thành phần microservice deploy bằng một chart dùng chung, khác biệt chỉ nằm trong values.
- Values của cluster mới được commit trước khi deploy.
- Database nằm ngoài cluster hoặc có kế hoạch chuyển dữ liệu riêng.
- Chart có checksum annotation cho ConfigMap và Secret.
- Manifest chép từ cluster cũ được đọc lại, kể cả PriorityClass và DaemonSet phụ trợ.
- BFF và service quan trọng có tối thiểu hai replica.
- Tag image theo commit SHA.
- Có cảnh báo cơ bản trước khi trỏ DNS production.
Tốc độ dựng cluster là hệ quả, không phải mục tiêu: nó có được nhờ chart dùng chung, pipeline đã chạy ổn, kịch bản hạ tầng và database nằm ngoài cluster. Các bài khác của series nằm trong chuyên mục blog kỹ thuật của Sudo.
Bài tiếp theo
kube-prometheus-stack: thay monitoring cũ và cấu hình sai âm thầm. Cài lại stack giám sát cho cluster Kubernetes, và những dòng values sai mà Helm không hề báo lỗi.
