Kiểm Tra Kubernetes Manifest với Kube-score và Kube-linter: Hướng Dẫn Tích Hợp CI/CD

DevOps tutorial - IT technology blog
DevOps tutorial - IT technology blog

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-scoreKube-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ờ :latest và 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.

Share: