Chiến lược Triển khai Kubernetes: Cách chúng tôi loại bỏ nỗi ám ảnh Rollback lúc 2 giờ sáng

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

Nỗi lo âu khi triển khai (Deployment)

Những ngày triển khai (deployment) từng là cơn ác mộng với đội ngũ của tôi. Ngay cả với RollingUpdate mặc định của Kubernetes, chúng tôi vẫn thường xuyên gặp bế tắc: một phiên bản mới vượt qua mọi bài kiểm tra CI/CD nhưng lại sụp đổ ngay khi tiếp nhận lưu lượng truy cập thực tế. Tỷ lệ lỗi tăng vọt 15% trong quá trình chuyển đổi, và việc rollback giống như cố gắng tách các thành phần ra khỏi một chiếc bánh đã nướng chín. Nó chậm chạp, lộn xộn, và thường xảy ra khi sếp đang đứng ngay sau lưng giám sát.

Code thường không phải là kẻ tội đồ; vấn đề nằm ở việc thiếu kiểm soát lưu lượng truy cập. Phát hành một thay đổi có khả năng gây lỗi cho 100% người dùng cùng lúc là một canh bạc đầy rủi ro với thời gian uptime. Để khắc phục điều này, chúng tôi đã chuyển từ các bản cập nhật cơ bản sang mô hình Blue/Green và Canary. Trong sáu tháng qua, sự thay đổi này đã giúp môi trường production của chúng tôi ổn định qua hơn 200 lần phát hành riêng lẻ.

Triển khai Blue/Green: Chuyển đổi tức thì

Triển khai Blue/Green loại bỏ rủi ro bằng cách chạy hai môi trường production giống hệt nhau cùng một lúc. “Blue” là phiên bản hiện tại đang chạy, trong khi “Green” là phiên bản mới. Bạn chỉ chuyển hướng lưu lượng khi chắc chắn rằng môi trường Green đã ổn định.

Cách thức hoạt động trong Kubernetes

Bạn không cần hai cluster riêng biệt để làm việc này. Thay vào đó, hãy tận dụng LabelsSelectors. Kubernetes Service đóng vai trò là bộ điều phối lưu lượng. Bằng cách cập nhật selector của Service, bạn có thể ngay lập tức chuyển hướng người dùng từ các pod Blue sang các pod Green.

Thực hành: Triển khai Blue/Green

Hãy xem một ví dụ thực tế. Deployment Blue (v1) của bạn hiện đang phục vụ lưu lượng:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-v1
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app
      version: v1
  template:
    metadata:
      labels:
        app: my-app
        version: v1
    spec:
      containers:
      - name: app
        image: my-registry/app:v1
        ports:
        - containerPort: 8080

Service của bạn hiện đang nhắm tới version: v1:

apiVersion: v1
kind: Service
metadata:
  name: app-service
spec:
  selector:
    app: my-app
    version: v1 # Đây là nút gạt điều phối lưu lượng
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080

Khi phiên bản 2 đã sẵn sàng, hãy khởi tạo một deployment mới (app-v2). Một khi các pod đó vượt qua các bài kiểm tra readiness probe, hãy cập nhật Service selector bằng một lệnh duy nhất:

kubectl patch service app-service -p '{"spec":{"selector":{"version":"v2"}}}'

Nếu phiên bản mới bắt đầu phát sinh lỗi, bạn có thể quay lại v1 trong chưa đầy năm giây. Điều này tạo ra một tấm lưới an toàn mà các bản cập nhật rolling update tiêu chuẩn đơn giản là không thể sánh được.

Triển khai Canary: Thử nghiệm thực tế

Blue/Green rất tuyệt vời cho các lần chuyển đổi lớn, nhưng triển khai Canary lại thiên về việc tiếp cận dần dần. Bạn điều hướng một phần nhỏ lưu lượng—có thể là 2% hoặc 5%—sang phiên bản mới. Bạn theo dõi các chỉ số, và nếu mọi thứ ổn thỏa, bạn sẽ từ từ mở rộng quy mô.

Tư duy đằng sau Canary

Canary là yếu tố thiết yếu để phát hiện các lỗi “ngầm”. Tôi đang nói về những vấn đề như rò rỉ bộ nhớ (memory leak) chậm hoặc cạn kiệt pool kết nối database chỉ xảy ra sau khi có 1.000 người dùng đồng thời. Bằng cách giới hạn “bán kính ảnh hưởng” (blast radius), chỉ một nhóm nhỏ người dùng gặp sự cố trong khi đội ngũ của bạn tiến hành điều tra.

Thực hành: Canary với Nginx Ingress

Mặc dù bạn có thể “lách” cấu hình Canary bằng cách trộn tỷ lệ pod trong một Service duy nhất, nhưng sử dụng Nginx Ingress sẽ sạch sẽ hơn nhiều. Nó cho phép bạn kiểm soát chính xác tỷ lệ phần trăm lưu lượng truy cập thông qua các annotation.

Đầu tiên, hãy triển khai phiên bản Canary và một service riêng cho nó (app-v2-service). Sau đó, tạo một đối tượng Canary Ingress:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-canary
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "10"
spec:
  ingressClassName: nginx
  rules:
  - host: app.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: app-v2-service
            port:
              number: 80

Trong cấu hình này, Nginx gửi chính xác 10% yêu cầu đến code mới. Chúng tôi thường giữ ở mức 10% trong một giờ để theo dõi độ trễ p99. Nếu độ trễ vẫn nằm dưới ngưỡng 200ms, chúng tôi sẽ tăng trọng số (weight) lên 50%, rồi 100%.

Những bài học xương máu từ thực tế

Sáu tháng áp dụng các chiến lược này đã dạy cho chúng tôi ba điều mà tài liệu hướng dẫn thường bỏ qua. Thứ nhất, Thay đổi Schema Database là điểm nghẽn lớn nhất. Nếu phiên bản 2 migration một bảng theo cách làm hỏng phiên bản 1, kế hoạch rollback Blue/Green của bạn sẽ vô dụng. Luôn thiết kế các bản migration có khả năng tương thích ngược (backward-compatible).

Thứ hai, Khả năng quan sát (Observability) là không thể thương lượng. Bạn không thể triển khai Canary mà không có dữ liệu. Chúng tôi sử dụng dashboard Grafana để so sánh tỷ lệ lỗi 5xx của v1 và v2 song song với nhau. Nếu đường biểu diễn của Canary vọt quá 0.1%, chúng tôi sẽ hủy bỏ deployment ngay lập tức.

Thứ ba, hãy cẩn thận với Sticky Sessions. Nếu Ingress của bạn sử dụng session affinity, một người dùng có thể bị “dính” vào một phiên bản cụ thể bất kể cài đặt trọng số của bạn là bao nhiêu. Điều này có thể làm sai lệch dữ liệu nếu bạn không cẩn thận trong cách đo lường hiệu quả.

Bạn nên chọn phương án nào?

Sự lựa chọn phụ thuộc vào mục tiêu của bạn:

  • Dùng Blue/Green cho các thay đổi lớn về kiến trúc hoặc khi bạn cần phát hành kiểu “được ăn cả ngã về không” tức thì. Nó tốn kém hơn vì bạn tạm thời phải gấp đôi tài nguyên sử dụng, nhưng nó cực kỳ đáng tin cậy.
  • Dùng Canary cho các cập nhật tính năng định kỳ. Đây là cách tốt nhất để xem hạ tầng của bạn xử lý tải thực tế như thế nào mà không gây rủi ro cho toàn bộ người dùng.

Tổng kết

Chuyển sang chiến lược Blue/Green và Canary đã thay đổi văn hóa làm việc của đội ngũ chúng tôi. Chúng tôi không còn lo sợ khi deploy vào chiều thứ Sáu nữa. Bằng cách sử dụng label của Kubernetes và annotation của Ingress, bạn có thể đạt đến mức độ trưởng thành mà các trang thông báo “Bảo trì hệ thống” chỉ còn là quá khứ. Việc thiết lập ban đầu tốn công sức, nhưng sự an tâm mà nó mang lại xứng đáng đến từng dòng YAML.

Share: