Vấn Đề Chỉ Lộ Ra Khi Lên Production
Bạn đã viết xong file YAML cho Kubernetes deployment, đẩy lên, mọi thứ trông ổn ở staging. Rồi ba tuần sau, một pod bị OOMKilled trên production vì ai đó quên thiết lập resource limits. Tệ hơn nữa — cluster bị xâm phạm một phần vì có container đang chạy với quyền root và security context quá thoáng mà không ai để ý.
Đây không phải kịch bản giả định. Theo kinh nghiệm của tôi khi làm việc với Kubernetes cluster trên nhiều môi trường, vấn đề chất lượng manifest là một trong những nguyên nhân phổ biến nhất dẫn đến sự cố production. Nguyên nhân gốc rễ hầu như luôn giống nhau: file YAML được viết vội, review sơ sài trong pull request, và không ai phát hiện ra readinessProbe bị thiếu hay allowPrivilegeEscalation: true ẩn trong spec.
Cách khắc phục rất rõ ràng: phân tích tĩnh cho Kubernetes manifest, tích hợp vào CI/CD pipeline để vấn đề được phát hiện trước khi chạm đến cluster. Theo kinh nghiệm thực tế của tôi, đây là một trong những kỹ năng thiết yếu cần nắm vững khi vận hành hạ tầng Kubernetes ở bất kỳ quy mô nghiêm túc nào.
Hai Công Cụ Đáng Biết
Có hai công cụ tôi thường dùng khi thiết lập quality gate cho manifest: Kube-score và Kube-linter. Chúng bổ sung cho nhau rất tốt — Kube-score tập trung vào các best practice chung và chấm điểm từng resource, trong khi Kube-linter nghiêng nặng hơn về các kiểm tra bảo mật. Dùng cả hai cho độ phủ vững chắc mà không bị trùng lặp nhiều.
Kube-score
Kube-score đọc các file manifest và đánh giá từng resource theo một bộ kiểm tra best practice. Nó gắn cờ các vấn đề như thiếu resource requests/limits, thiếu health probe, container chạy với quyền root, và image tag không được ghim cố định. Mỗi phát hiện được phân loại theo mức độ nghiêm trọng: CRITICAL, WARNING hoặc OK. Mô hình chấm điểm giúp dễ dàng theo dõi sự cải thiện theo thời gian.
Kube-linter
Kube-linter, ban đầu từ StackRox (nay thuộc Red Hat), chạy hơn 30 kiểm tra tích hợp với trọng tâm mạnh vào tư thế bảo mật. Nó phát hiện container có quyền privileged, thiếu network policy, RBAC quá thoáng, và các cấu hình sai tương tự mà kube-score có thể xử lý nhẹ nhàng hơn. Hai công cụ có triết lý kiểm tra khác nhau, đó chính xác là lý do tại sao chạy cả hai xứng đáng với thời gian CI thêm nhỏ đó.
Thực Hành: Cài Đặt và Chạy Cả Hai Công Cụ
Cài Đặt Kube-score
Tải binary từ GitHub releases:
# Linux (x86_64)
curl -Lo kube-score https://github.com/zegl/kube-score/releases/latest/download/kube-score_linux_amd64
chmod +x kube-score
sudo mv kube-score /usr/local/bin/
# macOS qua Homebrew
brew install kube-score
# Kiểm tra cài đặt
kube-score version
Cài Đặt Kube-linter
# Linux
curl -Lo kube-linter https://github.com/stackrox/kube-linter/releases/latest/download/kube-linter-linux
chmod +x kube-linter
sudo mv kube-linter /usr/local/bin/
# macOS qua Homebrew
brew install kube-linter
# Kiểm tra cài đặt
kube-linter version
Chạy Kiểm Tra Đầu Tiên
Hãy lấy một file deployment manifest điển hình trông có vẻ ổn khi review nhanh:
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: my-app:latest
Chạy kube-score với file này:
kube-score score deployment.yaml
Kết quả phát hiện ra nhiều vấn đề thực sự:
apps/v1/Deployment my-app 💥 CRITICAL
[CRITICAL] Container Security Context
Container 'my-app' does not have a security context set
[CRITICAL] Container Resources
Container 'my-app' does not have resource requests set
Container 'my-app' does not have resource limits set
[WARNING] Container Liveness Probe
Container 'my-app' does not have a liveness probe set
[WARNING] Container Readiness Probe
Container 'my-app' does not have a readiness probe set
[WARNING] Deployment has a single replica — not resilient to node failures
Bây giờ chạy kube-linter trên cùng file đó:
kube-linter lint deployment.yaml
deployment.yaml: container "my-app" does not have a read-only root file system (check: no-read-only-root-fs)
deployment.yaml: container "my-app" has no resource limits (check: resource-limits)
deployment.yaml: container "my-app" is not set to runAsNonRoot (check: run-as-non-root)
deployment.yaml: container "my-app" has an image using a mutable tag (check: no-latest-image)
Chỉ từ một file YAML 18 dòng, cả hai công cụ đã phát hiện ra những rủi ro production thực sự. Đó chính là giá trị của việc lint tự động.
Manifest Sau Khi Đã Sửa
Đây là deployment tương tự sau khi xử lý các vấn đề critical:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 2
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
containers:
- name: my-app
image: my-app:1.2.3 # tag cố định — không dùng :latest
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 10
periodSeconds: 15
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
Tích Hợp Cả Hai Công Cụ vào CI/CD với GitHub Actions
Chạy các công cụ này ở local thì hữu ích, nhưng giá trị thực sự đến khi biến chúng thành các gate bắt buộc trong pipeline. Mọi pull request chạm vào Kubernetes manifest đều phải tự động chạy cả hai kiểm tra — không có ngoại lệ, không có cách bỏ qua.
# .github/workflows/k8s-lint.yml
name: Kubernetes Manifest Checks
on:
pull_request:
paths:
- 'k8s/**'
- 'manifests/**'
- '**/*.yaml'
- '**/*.yml'
jobs:
kube-score:
name: Kube-score Analysis
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install kube-score
run: |
curl -Lo kube-score https://github.com/zegl/kube-score/releases/latest/download/kube-score_linux_amd64
chmod +x kube-score && sudo mv kube-score /usr/local/bin/
- name: Run kube-score
run: |
find k8s/ -name "*.yaml" | \
xargs kube-score score --exit-one-on-warning
kube-linter:
name: Kube-linter Security Checks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run kube-linter
uses: stackrox/kube-linter-action@v1
with:
directory: k8s/
config: .kube-linter.yaml
Flag --exit-one-on-warning khiến kube-score báo lỗi pipeline kể cả với warning, không chỉ các vấn đề critical. Tôi thường bắt đầu không dùng flag này khi áp dụng cho dự án hiện có (để tránh bị ngập lụt trong hàng đống lỗi ngay từ ngày đầu), rồi thêm vào sau khi đã xử lý hết các vấn đề critical.
Tùy Chỉnh Quy Tắc Kube-linter
Không phải mọi kiểm tra đều áp dụng cho mọi môi trường. Kube-linter hỗ trợ file cấu hình để bật hoặc tắt các kiểm tra cụ thể:
# .kube-linter.yaml
checks:
addAllBuiltIn: true
exclude:
- "minimum-three-replicas" # staging chủ động chạy 2 replica
- "required-annotation-email" # không dùng quy ước annotation này
Liệt kê tất cả các kiểm tra có sẵn bằng lệnh:
kube-linter checks list
Lint Output Đã Render Từ Helm
Nếu dự án của bạn dùng Helm, hãy lint các manifest đã render — không phải template thô. Lint ở tầng template sẽ bỏ sót các vấn đề cấu hình phụ thuộc vào values:
# Pipe output của helm template trực tiếp vào kube-score
helm template my-release ./chart -f values.prod.yaml | kube-score score -
# Tương tự cho kube-linter
helm template my-release ./chart -f values.prod.yaml > /tmp/rendered.yaml
kube-linter lint /tmp/rendered.yaml
Output JSON cho Dashboard CI
Với các nhóm muốn theo dõi phát hiện theo thời gian hoặc tích hợp với công cụ báo cáo, kube-score hỗ trợ output JSON:
# Chỉ hiển thị các phát hiện CRITICAL từ output JSON
kube-score score deployment.yaml --output-format json | \
jq '[.[] | .checks[] | select(.grade == "CRITICAL")]'
Kinh Nghiệm Thực Tế Khi Làm Việc với Cluster Thật
- Xử lý từng bước. Áp dụng các công cụ này vào dự án đã trưởng thành thường phát hiện ra hàng chục vấn đề. Hãy fix CRITICAL trước, rồi xử lý dần WARNING qua các sprint tiếp theo thay vì cố dọn sạch hết trong một PR.
- Resource limits bảo vệ cả cluster. Một pod không có limits có thể làm đói các workload lân cận qua hiệu ứng noisy-neighbor. Đây là vấn đề ổn định cluster, không chỉ là chi tiết cấu hình pod.
- Image tag cố định là bắt buộc. Cả hai công cụ đều gắn cờ
:latestvà các tag có thể thay đổi tương tự. Image không được ghim tag khiến deployment không thể tái tạo và có thể đưa vào các thay đổi bất ngờ khi rolling restart. - Chạy kiểm tra trong pre-commit hook nữa. Dùng pre-commit để chạy kube-score ở local trước khi commit lên CI — vòng phản hồi nhanh hơn, ít pipeline thất bại hơn.
Bước Tiếp Theo
Khi kube-score và kube-linter đã chạy trong pipeline, bạn đã loại bỏ được lớp vấn đề phổ biến nhất của Kubernetes manifest ngay từ nguồn. Bước tự nhiên tiếp theo là kết hợp kiểm tra tĩnh trong CI với admission control lúc runtime — các công cụ như Kyverno hoặc OPA/Gatekeeper thực thi các quy tắc tương tự ở tầng cluster, để ngay cả manifest được apply trực tiếp qua kubectl cũng được xác thực trước khi pod được lên lịch chạy.
Nhưng chỉ riêng tích hợp CI thôi đã là một cải thiện đáng kể rồi. Hãy cho các kiểm tra này chạy trên pull request, fix các CRITICAL một cách có hệ thống, và chỉ sau vài sprint, chất lượng manifest của bạn sẽ ở một tầm khác hẳn so với điểm xuất phát.

