Bài đăng

Nâng cấp Kubernetes Zero-Downtime | Nhật ký vận hành

Chuyện về devsecops

Nhật ký vận hành: Nâng cấp cụm Kubernetes (EKS/GKE) Zero-Downtime lúc 3 giờ sáng

Biến nỗi sợ hãi thành một quy trình có thể dự đoán được.

Phần 1: Sự tĩnh lặng lúc 3 giờ sáng: Lời tự sự của một kỹ sư về sự hỗn loạn có kiểm soát

Bối cảnh: 2:45 sáng. Ánh sáng duy nhất trong phòng phát ra từ màn hình hiển thị terminal của cụm production. Không khí đặc quánh một hỗn hợp quen thuộc của caffeine và sự căng thẳng. Đêm nay, chúng tôi sẽ nâng cấp trái tim của nền tảng: một cụm Kubernetes production, từ phiên bản 1.27 lên 1.28. Đây không chỉ là một cú "bump" phiên bản đơn thuần; đó là một nỗ lực kỹ thuật được dàn dựng công phu, nơi một sai sót nhỏ nhất cũng có thể leo thang thành một sự cố toàn diện.

Giải quyết nỗi đau cốt lõi: Cần phải đối mặt trực diện với "nỗi sợ" khi nâng cấp Kubernetes. Nỗi sợ này hoàn toàn có cơ sở, được sinh ra từ những câu chuyện kinh hoàng về các lần nâng cấp thất bại, những thông báo lỗi khó hiểu, và các dịch vụ từ chối hoạt động trở lại. Cuốn nhật ký này không nói về lòng dũng cảm mù quáng; nó nói về việc thay thế nỗi sợ hãi bằng một quy trình nghiêm ngặt, có thể lặp lại. Chúng tôi sẽ biến một canh bạc có rủi ro cao thành một sự kiện có thể dự đoán được, không gây gián đoạn.

Lời hứa: Bài viết này sẽ là cẩm nang của chúng ta. Chúng ta sẽ cùng nhau đi qua toàn bộ vòng đời của một cuộc nâng cấp: các bước kiểm tra tiền kỳ tỉ mỉ, quá trình thực thi trực tiếp, những sai lầm suýt xảy ra đã dạy cho chúng tôi những bài học quý giá, và quá trình xác thực dựa trên dữ liệu đã cho phép chúng tôi gõ lệnh exit và nhìn thấy bình minh. Chúng ta sẽ đề cập đến các khái niệm của cả EKS và GKE, vì các nguyên tắc cơ bản là phổ quát.

Phần 2: Giao thức tiền kỳ: Rèn giũa tấm khiên chống lại thảm họa

Thành công của một cuộc nâng cấp được quyết định từ rất lâu trước khi lệnh eksctl upgrade cluster được thực thi. Giai đoạn này là để loại bỏ những bất ngờ. Chúng tôi coi mỗi lần nâng cấp production như một cuộc diễn tập khắc phục thảm họa.

Tiểu mục 2.1: Kiểm tra API lỗi thời - Tuyến phòng thủ đầu tiên của bạn

Kubernetes liên tục phát triển, và các API sẽ bị đánh dấu là lỗi thời (deprecated) và cuối cùng bị loại bỏ. Nâng cấp mà không cập nhật các tài nguyên này là một trong những thất bại phổ biến và dễ phòng tránh nhất. Chúng tôi sử dụng các công cụ như kubentpluto.

Ứng dụng thực tế (Code & Output):

Quét một cụm đang hoạt động với kubent:

kubent --target-version 1.28.0

Output mẫu cho thấy các API lỗi thời:

KIND      NAMESPACE     NAME                API_VERSION      REPLACED_BY
CronJob   data-pipeline daily-report-job    batch/v1beta1    batch/v1
Ingress   web           frontend-ingress    extensions/v1beta1 networking.k8s.io/v1

Quét các file manifest cục bộ với pluto:

pluto detect-files -t k8s=v1.28.0

Sử dụng kubectl-convert để tự động cập nhật file manifest:

kubectl-convert -f old-manifest.yaml --output-version networking.k8s.io/v1

Tiểu mục 2.2: Kiểm tra sức khỏe hệ sinh thái

Một cụm không chỉ là các deployment của bạn. Nó là một hệ sinh thái gồm các add-on. Một cuộc nâng cấp có thể làm hỏng các thành phần này nếu chúng không tương thích.

  • CNI Plugin (ví dụ: AWS VPC CNI): Tham chiếu ma trận tương thích chính thức.
  • Ingress Controller (ví dụ: NGINX): Kiểm tra ghi chú phát hành để biết phiên bản yêu cầu.
  • Cluster Autoscaler: Phải được cập nhật đồng bộ với phiên bản control plane.
  • Monitoring & Logging Agents (ví dụ: Prometheus, Fluentd): Xác minh tính tương thích.
  • Service Mesh (ví dụ: Istio, Linkerd): Có các phụ thuộc phiên bản nghiêm ngặt.

Tiểu mục 2.3: Sao lưu, phục hồi và môi trường thử nghiệm

Một chiến lược sao lưu sẽ không hoàn chỉnh nếu không có một quy trình phục hồi đã được kiểm thử. Môi trường staging là nơi bạn diễn tập toàn bộ quá trình.

  1. Sử dụng một công cụ như Velero để tạo một bản sao lưu đầy đủ, bất biến của trạng thái cụm.
  2. Tài liệu hóa quy trình tạo một cụm staging phản ánh production.
  3. Chạy toàn bộ quy trình nâng cấp trên staging. Sau đó, giả lập một sự cố và thực hiện phục hồi hoàn toàn từ bản sao lưu Velero.

Phần 3: Giai đoạn thực thi: Giải phẫu một cuộc nâng cấp hoàn hảo

Quy trình này tuần tự: control plane trước, sau đó đến data plane. Chúng tôi tập trung vào chiến lược nâng cấp node Blue/Green, vì nó cung cấp khả năng kiểm soát tối đa.

Tiểu mục 3.1: Nâng cấp Control Plane

Đây là một hoạt động được quản lý bởi AWS/Google. Bước này *chỉ* nâng cấp control plane. Các node và workload của bạn vẫn đang chạy phiên bản cũ.

Lệnh EKS:

eksctl upgrade cluster --name=prod-cluster-eu-west-1 --version=1.28 --approve

Lệnh GKE:

gcloud container clusters upgrade prod-cluster-europe-west1 --master --cluster-version=1.28

Tiểu mục 3.2: Nâng cấp Data Plane - Di chuyển theo kiểu Blue/Green

Thay vì nâng cấp tại chỗ, chúng tôi tạo một nhóm node "green" mới (phiên bản 1.28) và di chuyển các workload từ nhóm node "blue" cũ (1.27) trước khi loại bỏ nó.

  1. Tạo nhóm node Green: Cung cấp một Node Group mới với phiên bản 1.28.
  2. Cordon các node Blue: Đánh dấu tất cả các node cũ là không thể lập lịch: kubectl cordon <old-node-name>.
  3. Drain các node Blue: Di dời các pod một cách nhẹ nhàng: kubectl drain <old-node-name> --ignore-daemonsets --delete-local-data.

Tiểu mục 3.3: Kịch bản thất bại #1 - Bế tắc do PDB

Tường thuật: "Chúng tôi đã ra lệnh drain... và chờ đợi. Lệnh kubectl cứ treo mãi. Vấn đề: việc di dời đã bị chặn bởi một Pod Disruption Budget (PDB) được cấu hình sai."

Cấu hình sai (KHÔNG LÀM ĐIỀU NÀY!):

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: billing-api-pdb-blocker
spec:
  minAvailable: 100% # Điều này sẽ chặn mọi gián đoạn tự nguyện
  selector:
    matchLabels:
      app: billing-api

Phân tích: Một PDB với minAvailable: 100% báo cho Kubernetes rằng "bạn không được phép gây ra *bất kỳ* sự gián đoạn tự nguyện nào." Vì drain là một sự gián đoạn tự nguyện, Kubernetes tôn trọng PDB và quá trình drain bị chặn.

Bảng: Công thức cấu hình Pod Disruption Budget (PDB)

Loại ứng dụng Mối quan tâm chính Cấu hình PDB được đề xuất Ví dụ YAML
Stateless Frontend (>=3 Replicas) Duy trì ít nhất 80% năng lực phục vụ. minAvailable: 80% spec: minAvailable: 80%
Dịch vụ API quan trọng (>=3 Replicas) Không bao giờ có nhiều hơn 1 pod không khả dụng. maxUnavailable: 1 spec: maxUnavailable: 1
StatefulSet dựa trên Quorum (5 replicas) Không bao giờ giảm xuống dưới ngưỡng quorum là 3. minAvailable: 3 (số tuyệt đối) spec: minAvailable: 3
Ứng dụng Stateful đơn lẻ Không chấm dứt mà không có can thiệp thủ công. maxUnavailable: 0 (với thỏa thuận) spec: maxUnavailable: 0

Tiểu mục 3.4: Kịch bản thất bại #2 - Cạm bẫy Timeout

Tường thuật: "Quá trình drain lại bị treo. Lần này, pod bị kẹt ở trạng thái Terminating. Ứng dụng không tôn trọng tín hiệu SIGTERM và từ chối tắt một cách nhẹ nhàng."

Phân tích: Khi drain, kubelet gửi SIGTERM. Ứng dụng có một khoảng thời gian ân hạn (mặc định 30s) để tắt. Nếu không, nó sẽ bị SIGKILL, có thể gây mất kết nối.

Cách khắc phục:

  1. Code ứng dụng: Triển khai xử lý tín hiệu SIGTERM đúng cách.
  2. Probes: Đảm bảo readinessProbe thất bại ngay lập tức khi tắt để ngừng nhận lưu lượng mới.
  3. Lifecycle Hooks: Sử dụng preStop hook để chờ các kết nối được drain.
lifecycle:
  preStop:
    exec:
      # Ví dụ: chờ 15s để drain kết nối
      command: ["/bin/sh", "-c", "sleep 15"]

Phần 4: "Tín hiệu vàng": Giám sát để thành công

Bạn không thể bay mà không có tầm nhìn. Chúng tôi giám sát "Tín hiệu vàng" (Độ trễ, Lưu lượng, Lỗi) bằng Prometheus và Grafana.

Tiểu mục 4.1: Bảng điều khiển phòng chiến tranh - Hình dung sự ổn định

Chúng tôi theo dõi các chỉ số quan trọng để xác minh rằng việc di chuyển từ node Blue sang Green không ảnh hưởng đến người dùng.

Độ trễ P95
Ổn định Thời gian
Lưu lượng (Blue vs Green)
Yêu cầu Thời gian
Tỷ lệ lỗi HTTP 5xx
Lỗi Thời gian

Bảng: Các truy vấn PromQL thiết yếu

Hạng mục Mô tả Truy vấn PromQL
App: Độ trễ P95 Độ trễ yêu cầu ở phân vị thứ 95. histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le, deployment))
App: Tỷ lệ lỗi (5xx) Tỷ lệ lỗi phía máy chủ mỗi giây. sum(rate(http_requests_total{code=~"5.."}[5m])) by (deployment)
Cluster: Khởi động lại Pod Số lần pod khởi động lại trong 15 phút. sum(increase(kube_pod_container_status_restarts_total[15m])) by (namespace)
Cluster: Độ trễ API Độ trễ API server ở phân vị thứ 99. histogram_quantile(0.99, sum(rate(apiserver_request_duration_seconds_bucket[5m])) by (le, verb))

Phần 5: Checklist cuối cùng: Kế hoạch chi tiết cho các lần nâng cấp trong tương lai

Đây là bản thiết kế biến một sự kiện căng thẳng thành một nhiệm vụ vận hành thường lệ.

  • Giai đoạn 1: Lên kế hoạch (T-minus 1 tuần)
  • Đọc ghi chú phát hành của Kubernetes & Nhà cung cấp đám mây.
  • Chạy quét API lỗi thời (kubent, pluto) trên cụm và các kho IaC.
  • Xác minh tính tương thích của tất cả các add-on (CNI, Ingress, v.v.).
  • Tạo bản sao lưu đầy đủ của cụm (ví dụ: Velero).
  • Diễn tập toàn bộ quá trình nâng cấp và kịch bản phục hồi trong môi trường staging.
  • Giai đoạn 2: Trước khi thực thi (T-minus 1 giờ)
  • Thông báo cho các bên liên quan.
  • Tắt các cảnh báo không quan trọng.
  • Mở bảng điều khiển "Phòng chiến tranh" để giám sát thời gian thực.
  • Scale down deployment cluster-autoscaler xuống 0 để tránh can thiệp.
  • Giai đoạn 3: Thực thi (T-minus 0)
  • Nâng cấp Control Plane (eksctl/gcloud).
  • Tạo Nhóm Node "Green" mới.
  • Cordon tất cả các node trong Nhóm Node "Blue" cũ.
  • Drain từng node Blue một, giám sát cẩn thận các bảng điều khiển sức khỏe.
  • Điều tra và giải quyết ngay lập tức bất kỳ sự tắc nghẽn nào (PDBs, timeout).
  • Giai đoạn 4: Sau khi thực thi & Dọn dẹp
  • Thực hiện checklist xác thực sau khi thực thi (chức năng ứng dụng, autoscaling, v.v.).
  • Sau khi đã xác thực, xóa Nhóm Node "Blue" cũ.
  • Scale up cluster-autoscaler trở lại.
  • Bật lại các cảnh báo.
  • Ghi lại tài liệu về quá trình nâng cấp, ghi chú lại bất kỳ thách thức nào.

Kết luận: Nâng cấp Kubernetes không gián đoạn dịch vụ không phải là phép màu. Chúng là kết quả của việc lập kế hoạch tỉ mỉ, khả năng quan sát sâu sắc, và sự tôn trọng lành mạnh đối với sự phức tạp của các hệ thống phân tán. Bằng cách áp dụng phương pháp kỷ luật này, chúng ta có thể tự tin duy trì các cụm của mình—ngay cả lúc 3 giờ sáng.

Đăng nhận xét