Cấu hình Horizontal Pod Autoscaler (HPA) cho service inference
Chúng ta cần mở rộng quy mô số lượng Pod của service inference dựa trên độ trễ (latency) hoặc lượng request thực tế, thay vì chỉ dựa trên CPU/RAM. Điều này đảm bảo hệ thống phản hồi nhanh trong giờ cao điểm và tự động thu nhỏ khi ít người dùng để tiết kiệm chi phí.
Trước tiên, bạn cần đảm bảo đã cài đặt Metrics Server trên Kubernetes cluster để HPA có thể lấy dữ liệu hiệu năng. Nếu chưa cài, chạy lệnh sau:
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
Kết quả mong đợi: Pod metrics-server chạy trạng thái Running trong namespace kube-system.
Tiếp theo, ta sẽ tạo một file YAML định nghĩa HPA cho deployment inference của bạn. File này yêu cầu CPU hoặc Memory target, hoặc custom metric nếu bạn đã deploy Prometheus Adapter. Ở đây chúng ta dùng CPU target đơn giản nhất để bắt đầu.
Tạo file inference-hpa.yaml với nội dung:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: inference-hpa
namespace: dataops-prod
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: inference-service
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 100
periodSeconds: 60
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100
periodSeconds: 15
Giải thích cấu hình: minReplicas đặt là 2 để đảm bảo redundancy, maxReplicas giới hạn tài nguyên tối đa. averageUtilization: 70 nghĩa là khi CPU trung bình của các pod vượt quá 70%, HPA sẽ tạo thêm pod. Phần behavior giúp hệ thống scale up nhanh (15s) nhưng scale down chậm (300s) để tránh hiệu ứng "thở" (flapping).
Áp dụng cấu hình vào cluster:
kubectl apply -f inference-hpa.yaml
Kết quả mong đợi: HPA được tạo thành công với trạng thái Current Replicas bằng minReplicas.
Để verify, chạy lệnh kiểm tra trạng thái HPA:
kubectl get hpa inference-hpa -n dataops-prod -w
Kết quả mong đợi: Bạn sẽ thấy cột TARGETS hiển thị tỷ lệ CPU hiện tại so với ngưỡng 70%.
Tạo tải giả lập để test khả năng mở rộng (chạy trong terminal riêng):
while true; do curl -s http://inference-service.dataops-prod.svc.cluster.local:8000/predict; done
Quay lại terminal monitor HPA, bạn sẽ thấy REPLICAS tăng lên khi CPU usage vượt ngưỡng.
Quản lý secret và credential an toàn trong Kubernetes
Tuyệt đối không hardcode API keys, passwords hay AWS credentials vào Docker image hay ConfigMap. Chúng ta sẽ sử dụng Kubernetes Secrets kết hợp với external secret management (ví dụ: HashiCorp Vault hoặc AWS Secrets Manager) để injection biến môi trường an toàn.
Trong kịch bản này, chúng ta dùng cơ chế native Kubernetes Secrets để mã hóa dữ liệu nhạy cảm, nhưng lưu ý trong môi trường Production thực tế nên dùng Vault. Đầu tiên, tạo secret từ file local chứa thông tin kết nối database hoặc S3:
cat > .db-credentials
Tạo secret từ file này:
kubectl create secret generic inference-secrets --from-file=credentials=.db-credentials --namespace=dataops-prod
Kết quả mong đợi: Secret được tạo với tên inference-secrets trong namespace dataops-prod.
Tiếp theo, cập nhật Deployment của service inference để mount secret này vào container dưới dạng biến môi trường (Environment Variables). Chỉnh sửa file deployment của bạn (ví dụ: inference-deployment.yaml):
apiVersion: apps/v1
kind: Deployment
metadata:
name: inference-service
namespace: dataops-prod
spec:
replicas: 2
selector:
matchLabels:
app: inference
template:
metadata:
labels:
app: inference
spec:
containers:
- name: inference-api
image: your-registry/inference-model:v1.2.0
ports:
- containerPort: 8000
envFrom:
- secretRef:
name: inference-secrets
env:
- name: DB_HOST
valueFrom:
secretKeyRef:
name: inference-secrets
key: credentials
# Nếu cần trích xuất cụ thể từ file, cần cấu hình keyRef phù hợp
# Tuy nhiên, cách envFrom bên trên sẽ load toàn bộ key trong file credentials thành env var
resources:
limits:
cpu: "500m"
memory: "512Mi"
requests:
cpu: "200m"
memory: "256Mi"
Lưu ý: Khi dùng envFrom với secretRef, Kubernetes sẽ tự động tạo các biến môi trường từ các key trong secret. Nếu file credentials có nhiều dòng key=value, mỗi dòng sẽ trở thành một env var.
Áp dụng lại deployment:
kubectl apply -f inference-deployment.yaml
Kết quả mong đợi: Pod mới sẽ restart và load các biến môi trường từ secret.
Verify việc injection secret bằng cách vào shell của container đang chạy:
kubectl exec -it deployment/inference-service -n dataops-prod -- env | grep DB_HOST
Kết quả mong đợi: Xuất hiện dòng DB_HOST=postgres-prod.internal. Không được xuất hiện giá trị password trong output nếu bạn dùng grep đúng key, nhưng hãy nhớ không in toàn bộ env.
Để tăng cường bảo mật, hãy đảm bảo secret được mã hóa ở tầng etcd (Storage Encryption). Kiểm tra cấu hình encryption:
kubectl get secrets inference-secrets -n dataops-prod -o yaml
Kết quả mong đợi: Giá trị trong trường data là base64 encoded, không thể đọc bằng mắt thường.
Tách biệt môi trường Dev, Stage và Prod cho pipeline
Chiến lược tách biệt môi trường giúp giảm thiểu rủi ro khi deploy code chưa ổn định vào hệ thống Production. Chúng ta sẽ sử dụng Kubernetes Namespaces để cách ly tài nguyên và cấu hình, kết hợp với GitOps hoặc CI/CD pipeline để quản lý versioning.
Đầu tiên, tạo 3 namespaces riêng biệt cho Dev, Stage và Prod:
kubectl create namespace dataops-dev
kubectl create namespace dataops-stage
kubectl create namespace dataops-prod
Kết quả mong đợi: 3 namespace được tạo thành công.
Sử dụng Kubernetes Resource Quota để giới hạn tài nguyên tiêu thụ của từng môi trường, tránh việc môi trường Dev ăn hết tài nguyên cluster. Tạo file quota-dev.yaml:
apiVersion: v1
kind: ResourceQuota
metadata:
name: dev-quota
namespace: dataops-dev
spec:
hard:
requests.cpu: "4"
requests.memory: "8Gi"
limits.cpu: "8"
limits.memory: "16Gi"
persistentvolumeclaims: "5"
secrets: "20"
configmaps: "20"
Tương tự, tạo quota-prod.yaml với giới hạn lớn hơn nhưng chặt chẽ hơn về số lượng pod hoặc storage:
apiVersion: v1
kind: ResourceQuota
metadata:
name: prod-quota
namespace: dataops-prod
spec:
hard:
requests.cpu: "16"
requests.memory: "32Gi"
limits.cpu: "32"
limits.memory: "64Gi"
persistentvolumeclaims: "20"
secrets: "50"
configmaps: "50"
Áp dụng quota:
kubectl apply -f quota-dev.yaml
kubectl apply -f quota-prod.yaml
Kết quả mong đợi: Quota được áp dụng, nếu ai đó cố deploy pod vượt quá giới hạn sẽ bị từ chối.
Để quản lý cấu hình khác nhau giữa các môi trường (ví dụ: URL database, số lượng replica), chúng ta sử dụng Kustomize. Tạo cấu trúc thư mục:
mkdir -p kustomize/{base,overlays/dev,overlays/stage,overlays/prod}
cp inference-deployment.yaml kustomize/base/
cp inference-hpa.yaml kustomize/base/
Tạo file kustomization.yaml trong thư mục base:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- inference-deployment.yaml
- inference-hpa.yaml
Tạo overlay cho môi trường Prod trong overlays/prod/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
namespace: dataops-prod
patchesStrategicMerge:
- patch-prod.yaml
Tạo file patch-prod.yaml trong cùng thư mục để override số lượng replica và resources:
apiVersion: apps/v1
kind: Deployment
metadata:
name: inference-service
spec:
replicas: 5
template:
spec:
containers:
- name: inference-api
resources:
limits:
cpu: "1000m"
memory: "1Gi"
Tương tự tạo overlay cho Dev với replicas: 1 và resources thấp hơn.
Deploy môi trường Prod bằng Kustomize:
kubectl apply -k kustomize/overlays/prod
Kết quả mong đợi: Manifests được merge, namespace được set đúng là dataops-prod, và số lượng replica là 5.
Verify sự tách biệt:
kubectl get pods -n dataops-dev
kubectl get pods -n dataops-prod
Kết quả mong đợi: Số lượng pod và cấu hình tài nguyên khác nhau rõ rệt giữa hai namespace.
Tối ưu chi phí lưu trữ artifact và compute
Chi phí DataOps chủ yếu đến từ việc lưu trữ các artifact (dataset, model) trên object storage (S3, MinIO) và chi phí compute khi chạy training/inference. Chúng ta cần áp dụng chiến lược Lifecycle Policy cho storage và Spot Instances cho compute.
Đầu tiên, cấu hình Lifecycle Policy cho bucket S3 (hoặc MinIO tương thích S3) để tự động chuyển dữ liệu ít dùng sang lớp lưu trữ rẻ hơn (Glacier/IA) và xóa sau một khoảng thời gian.
Tạo file policy JSON s3-lifecycle-policy.json:
{
"Rules": [
{
"ID": "ArchiveOldArtifacts",
"Status": "Enabled",
"Filter": {
"Prefix": "models/"
},
"Transitions": [
{
"Days": 30,
"StorageClass": "STANDARD_IA"
},
{
"Days": 90,
"StorageClass": "GLACIER"
}
],
"Expiration": {
"Days": 365
}
},
{
"ID": "DeleteOldDatasets",
"Status": "Enabled",
"Filter": {
"Prefix": "datasets/experiments/"
},
"Expiration": {
"Days": 30
}
}
]
}
Giải thích: Model sẽ được chuyển sang lớp rẻ hơn (IA) sau 30 ngày, Glacier sau 90 ngày và xóa hoàn toàn sau 1 năm. Dữ liệu thí nghiệm (datasets) sẽ bị xóa sau 30 ngày để tránh lãng phí.
Áp dụng policy vào bucket (giả sử bucket tên là mlflow-artifacts):
aws s3api put-bucket-lifecycle-configuration --bucket mlflow-artifacts --lifecycle-configuration file://s3-lifecycle-policy.json
Kết quả mong đợi: Không có output lỗi, policy được áp dụng thành công.
Tiếp theo, tối ưu chi phí compute bằng cách sử dụng Kubernetes NodePools với Spot Instances (Preemptible VMs) cho các job huấn luyện (Training Jobs) không yêu cầu độ tin cậy cao tuyệt đối, trong khi giữ On-Demand cho service inference.
Tạo một Node Pool sử dụng Spot Instances trên Google Cloud (GKE) hoặc AWS EKS. Dưới đây là ví dụ cho GKE:
gcloud container node-pools create training-spot-pool \
--cluster=dataops-cluster \
--num-nodes=3 \
--machine-type=e2-standard-4 \
--preemptible \
--disk-size=50GB \
--disk-type=pd-standard \
--labels=workload=training,spot=true
Kết quả mong đợi: Node pool mới được tạo với các node có nhãn workload=training và spot=true.
Cấu hình Training Job trong Kubernetes (sử dụng Kubernetes Jobs) để chỉ chạy trên các node này. Tạo file training-job.yaml:
apiVersion: batch/v1
kind: Job
metadata:
name: training-job-v1
namespace: dataops-dev
spec:
template:
spec:
containers:
- name: trainer
image: your-registry/training-script:v1.0
command: ["python", "train.py"]
nodeSelector:
workload: training
spot: "true"
restartPolicy: OnFailure
Giải thích: nodeSelector đảm bảo job chỉ được schedule lên các node Spot Instances. Nếu job bị kill do spot instance bị reclaim, Kubernetes sẽ tự động restart job (restartPolicy: OnFailure) trên một node khác.
Chạy job training:
kubectl apply -f training-job.yaml
Kết quả mong đợi: Job bắt đầu chạy và được schedule vào node pool training-spot-pool.
Kiểm tra xem job đang chạy trên node nào:
kubectl get pods -n dataops-dev -l job-name=training-job-v1 -o wide
Kết quả mong đợi: Cột NODE hiển thị tên node thuộc node pool training-spot-pool.
Cuối cùng, sử dụng Vertical Pod Autoscaler (VPA) để tự động điều chỉnh resource requests/limits cho các container dựa trên lịch sử sử dụng thực tế, giúp tránh việc over-provisioning (lãng phí) hoặc under-provisioning (gián đoạn).
Cài đặt VPA nếu chưa có:
kubectl apply -f https://github.com/kubernetes/autoscaler/releases/download/vpa-v1.2.0/vpa.yaml
Tạo VPA recommendation cho deployment inference:
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: inference-vpa
namespace: dataops-prod
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: inference-service
updatePolicy:
updateMode: "Auto"
Áp dụng VPA:
kubectl apply -f inference-vpa.yaml
Kết quả mong đợi: VPA được tạo. Sau một thời gian thu thập dữ liệu, nó sẽ đưa ra recommendation.
Kiểm tra recommendation của VPA:
kubectl get vpa inference-vpa -n dataops-prod -o jsonpath='{.status.recommendation.containerRecommendations}'
Kết quả mong đợi: Xuất hiện JSON chứa giá trị CPU và Memory recommended dựa trên thực tế sử dụng của container.
Điều hướng series:
Mục lục: Series: Xây dựng nền tảng DataOps với DVC, MLflow và Kubernetes cho vòng đời AI
« Phần 9: Chiến lược mở rộng (Scaling) và bảo mật nâng cao
Phần 10: Troubleshooting và các mẹo tối ưu hiệu suất »