Capstone: Deploy Ứng Dụng Hoàn Chỉnh và Tổng Kết Series
You've reached the end
Mười bốn bài qua, ta học từng mảnh ghép riêng lẻ. Bài cuối ghép chúng thành một bức tranh hoàn chỉnh: deploy một ứng dụng nhiều thành phần thật — một frontend chạy nhiều bản sao đứng sau Ingress, và một database có lưu trữ bền vững — dùng gần như mọi khái niệm đã học. Sau đó ta dọn cluster và nhìn lại cả hành trình.
Kiến trúc dự án
Một app web hai tầng kinh điển, mọi thành phần là một mảnh ghép của series:
Internet
│
▼
Ingress (capstone.local) ← Bài 9
│
▼
Service "frontend" (ClusterIP) ← Bài 5
│ cân bằng tải
├──► Pod frontend (nginx) ┐ Deployment 2 bản sao ← Bài 4
└──► Pod frontend (nginx) ┘ + probes + resources ← Bài 10, 11
└─ nội dung từ ConfigMap ← Bài 7
Service "db" (ClusterIP) ──► Pod postgres ← Bài 4, 5
├─ mật khẩu từ Secret ← Bài 7
├─ dữ liệu trên PVC ← Bài 8
└─ readiness probe ← Bài 10
Tất cả gọn trong một namespace riêng capstone (Bài 6), để dọn dẹp dễ và tách khỏi những gì đang chạy.
Tầng database: Secret + PVC + probe
Database cần ba thứ ta đã học: mật khẩu không hard-code (Secret), dữ liệu không bay hơi (PVC), và Kubernetes biết nó sẵn sàng chưa (readiness probe). Trích manifest:
apiVersion: apps/v1
kind: Deployment
metadata: { name: db, namespace: capstone }
spec:
replicas: 1
selector: { matchLabels: { app: db } }
template:
metadata: { labels: { app: db } }
spec:
containers:
- name: postgres
image: postgres:16-alpine
env:
- name: POSTGRES_PASSWORD
valueFrom: { secretKeyRef: { name: db-secret, key: POSTGRES_PASSWORD } } # Bài 7
resources: # Bài 11
requests: { cpu: 100m, memory: 128Mi }
limits: { cpu: 500m, memory: 256Mi }
readinessProbe: # Bài 10
exec: { command: ["sh","-c","pg_isready -U postgres"] }
volumeMounts:
- { name: data, mountPath: /var/lib/postgresql/data }
volumes:
- name: data
persistentVolumeClaim: { claimName: db-data } # Bài 8
Tầng frontend: nhiều bản sao + ConfigMap + Ingress
Frontend là nginx chạy 2 bản sao (sẵn sàng cân bằng tải và chịu lỗi), phục vụ trang lấy từ ConfigMap, có probes và resources, và phơi ra ngoài qua Service + Ingress:
apiVersion: apps/v1
kind: Deployment
metadata: { name: frontend, namespace: capstone }
spec:
replicas: 2
# ... selector, template ...
containers:
- name: nginx
image: nginx:1.27-alpine
livenessProbe: { httpGet: { path: /, port: 80 } }
readinessProbe: { httpGet: { path: /, port: 80 } }
volumeMounts:
- { name: content, mountPath: /usr/share/nginx/html }
volumes:
- name: content
configMap: { name: web-content } # trang index.html từ ConfigMap
Triển khai toàn bộ chỉ là vài lệnh apply:
kubectl create namespace capstone
kubectl apply -f app.yaml # configmap, secret, pvc, db deployment+service
kubectl apply -f frontend.yaml # frontend deployment+service, ingress
Toàn cảnh: mọi mảnh ghép cùng chạy
kubectl get all,ingress,pvc -n capstone
NAME READY STATUS RESTARTS AGE
pod/db-574d8bd786-wwbxb 1/1 Running 0 50s
pod/frontend-86658d7ff6-2dzmt 1/1 Running 0 15s
pod/frontend-86658d7ff6-gtftl 1/1 Running 0 15s
NAME TYPE CLUSTER-IP PORT(S) AGE
service/db ClusterIP 10.96.135.57 5432/TCP 50s
service/frontend ClusterIP 10.104.95.30 80/TCP 15s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/db 1/1 1 1 50s
deployment.apps/frontend 2/2 2 2 15s
NAME CLASS HOSTS PORTS AGE
ingress.../capstone nginx capstone.local 80 15s
NAME STATUS CAPACITY ACCESS MODES STORAGECLASS
persistentvolumeclaim/db-data Bound 200Mi RWO standard
Một ứng dụng hoàn chỉnh: 2 pod frontend, 1 pod db, hai Service, một Ingress, một PVC Bound. Mọi khái niệm của series hiện diện trong vài chục dòng YAML.
Kiểm chứng: nó thật sự hoạt động
Trang web qua Ingress — request với host capstone.local đi qua Ingress → Service → một trong hai pod frontend → trang từ ConfigMap:
minikube ssh "curl -s -H 'Host: capstone.local' http://localhost/ | grep h1"
<title>KKloud Capstone</title>
<h1>KKloud — Capstone trên minikube</h1>
Database dùng Secret + PVC — ghi và đọc dữ liệu thật, mật khẩu lấy từ Secret, dữ liệu nằm trên PVC bền vững:
kubectl exec -n capstone deploy/db -- psql -U postgres \
-c "INSERT INTO notes(msg) VALUES('capstone hoạt động'); SELECT * FROM notes;"
INSERT 0 1
id | msg
----+--------------------
1 | capstone hoạt động
(1 row)
Cả hai tầng hoạt động và ăn khớp. Từ một cluster trắng tới một ứng dụng web có database — khai báo hoàn toàn bằng YAML, tái lập được, lưu vào Git được. Đây là toàn bộ series cô đọng lại. Manifest đầy đủ ở repo nghiadaulau/kubernetes-minikube-series, thư mục 14-capstone.
🧹 Dọn dẹp: minikube delete
Học xong thì trả lại máy sự sạch sẽ. Vì mọi thứ nằm trong namespace capstone, xoá namespace là dọn sạch ứng dụng:
kubectl delete namespace capstone # xoá toàn bộ app + PVC trong đó
Và khi xong cả series, một lệnh xoá luôn cluster:
minikube delete # xoá sạch cluster — máy về như chưa có gì
Đây cũng là vẻ đẹp của minikube: học thoải mái rồi xoá không để lại dấu vết, không tốn tiền cloud.
Tổng kết series
Mười lăm bài, từ "Kubernetes là gì" tới một dự án chạy thật:
- Nền tảng (Bài 0–4): từ container tới orchestration và trạng thái mong muốn; kiến trúc control plane / node; cài minikube; Pod (đơn vị nhỏ nhất); Deployment/ReplicaSet (self-healing, scale, rolling update).
- Kết nối & cấu hình (Bài 5–9): Service (địa chỉ ổn định + cân bằng tải), Namespace/Label (tổ chức & chất keo selector), ConfigMap/Secret (tách cấu hình), PV/PVC (lưu trữ bền), Ingress (định tuyến HTTP).
- Vận hành (Bài 10–14): probes (health check), resources/HPA (tài nguyên & autoscale), các workload khác (StatefulSet/DaemonSet/Job), gỡ lỗi, và capstone.
Vài ý cốt lõi đáng mang theo:
- Trạng thái mong muốn (declarative) — bạn mô tả "đích đến", các control loop kéo hệ thống về đó và giữ ở đó. Đây là linh hồn của Kubernetes (và cũng là tinh thần khai báo từ series Ansible).
- Mọi thứ qua API server, mọi thứ là object — Pod, Service, ConfigMap... đều là object khai báo bằng YAML,
applyrồi để controller lo. - Label là chất keo — Deployment, Service, selector đều tìm nhau động qua nhãn; đó là vì sao scale/self-healing/cân bằng tải ăn khớp.
- Tách biệt mối quan tâm — cấu hình (ConfigMap/Secret) tách khỏi image, lưu trữ (PVC) tách khỏi pod, định tuyến (Service/Ingress) tách khỏi backend. Mỗi mảnh thay được độc lập.
Hướng học tiếp
Series này cố tình dừng ở nền tảng. Từ đây có thể đi sâu:
- Kubernetes from scratch — dựng cluster bằng tay (kubeadm), hiểu nội tại etcd/scheduler/CNI/CRI. (Một series riêng sắp tới.)
- Helm — đóng gói và quản lý ứng dụng Kubernetes như "package", cho deploy có tham số, tái lập.
- Bảo mật & quản trị: RBAC, NetworkPolicy, Pod Security, quản lý secret đúng cách (nhớ cảnh báo base64 ở Bài 7).
- GitOps (ArgoCD/Flux): để Git là nguồn sự thật, cluster tự đồng bộ theo — mở rộng tự nhiên của tư duy declarative.
- Operator & CRD: mở rộng chính Kubernetes, cách vận hành database/hệ thống phức tạp ở production.
- Kết nối các series khác: chạy Kubernetes trên EC2/EKS (series AWS), đóng gói app bằng Docker (series Docker), cấu hình node bằng Ansible (series Ansible), và dựa trên mạng/SSH bạn đã hiểu (series Mạng, Linux).
Cảm ơn bạn đã theo hết series. Bạn giờ không chỉ chạy được kubectl mà hiểu vì sao mỗi đối tượng tồn tại và chúng ghép với nhau ra sao — đủ nền vững để bước vào Kubernetes production và đi tiếp vào những tầng sâu hơn của hệ sinh thái cloud-native.
You've reached the end