Mở rộng Kubernetes với KEDA: Hướng dẫn thực tế về Event-Driven Autoscaling

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

Điểm mù của các chỉ số CPU và Memory truyền thống

Sau 5 năm quản lý các cluster Kubernetes, tôi nhận ra rằng Horizontal Pod Autoscaler (HPA) tiêu chuẩn có một điểm mù rất lớn. Hầu hết các đội ngũ bắt đầu bằng việc mở rộng dựa trên mức sử dụng CPU hoặc Memory. Nghe có vẻ hợp lý: nếu một pod đang hoạt động nặng, hãy thêm nhiều pod hơn. Nhưng trong thế giới event-driven (hướng sự kiện), nơi các dịch vụ tiêu thụ tin nhắn Kafka hoặc xử lý các tác vụ Redis, những chỉ số này thường không phản ánh đúng áp lực thực tế lên hệ thống của bạn.

Thực tế này đã giáng một đòn mạnh vào tôi năm ngoái khi đang quản lý một pipeline dữ liệu lưu lượng cao. Mức sử dụng CPU của chúng tôi chỉ ở mức 12% vì nút thắt cổ chai nằm ở I/O, nhưng Kafka lag (độ trễ tin nhắn) thì đang tăng vọt. Đến khi CPU tăng đủ cao để kích hoạt HPA, chúng tôi đã bị chậm hơn 4,5 triệu tin nhắn. Hệ thống phản ứng chậm chạp, tụt hậu và làm thất vọng người dùng. KEDA (Kubernetes Event-Driven Autoscaling) chính là công cụ đã giải quyết dứt điểm cơn đau đầu này.

KEDA lấp đầy khoảng trống như thế nào

KEDA hoạt động như một cầu nối nhẹ giữa cluster Kubernetes và các nguồn sự kiện bên ngoài. Nó không thay thế HPA; thay vào đó, nó tiếp sức mạnh cho HPA. KEDA cho phép bạn mở rộng khối lượng công việc trực tiếp dựa trên số lượng sự kiện đang chờ trong hàng đợi hoặc stream. Nó cung cấp các chỉ số tùy chỉnh (custom metrics) và các trigger cần thiết để giao tiếp with các hệ thống bên ngoài như Kafka, Redis, RabbitMQ hoặc AWS SQS.

Trong thực tế, đây là một thắng lợi lớn cho độ tin cậy của hệ thống. Nó đưa hạ tầng của bạn tiến gần hơn đến mô hình “serverless” ngay trên các node hiện có. Bạn có thể scale xuống mức 0 khi không có việc gì để làm, điều này đã giúp chúng tôi cắt giảm khoảng 40% chi phí môi trường staging. Khi một đợt dữ liệu lớn đổ bộ vào broker, KEDA có thể bùng nổ deployment của bạn lên hàng trăm pod chỉ trong vài giây.

Các thành phần cốt lõi

  • Scaler: Trình kết nối để lấy các chỉ số, chẳng hạn như Kafka lag hoặc độ dài danh sách Redis.
  • Metrics Adapter: Thành phần này chuyển đổi các chỉ số bên ngoài thành định dạng mà Kubernetes HPA có thể hiểu được.
  • ScaledObject: Một Custom Resource Definition (CRD) nơi bạn định nghĩa logic mở rộng, bao gồm các deployment mục tiêu và ngưỡng (threshold).

Triển khai thực tế

Tôi sẽ hướng dẫn bạn quy trình thiết lập mà chúng tôi đã sử dụng để ổn định môi trường production. Chúng ta sẽ cài đặt KEDA và sau đó cấu hình nó cho hai kịch bản lưu lượng cao: Kafka và Redis.

1. Cài đặt KEDA

Sử dụng Helm là cách dễ nhất để quản lý việc này. Tôi khuyên bạn nên triển khai KEDA vào namespace riêng để tách biệt các công cụ quản trị khỏi logic ứng dụng.

# Thêm Helm repo của KEDA
helm repo add kedacore https://kedacore.github.io/charts

# Cập nhật repo
helm repo update

# Cài đặt KEDA
helm install keda kedacore/keda --namespace keda --create-namespace

2. Mở rộng dựa trên Kafka Lag

Giả sử bạn có một consumer group tên là order-processor. Bạn muốn tăng thêm pod nếu lag—số lượng tin nhắn chưa được xử lý—vượt quá 100 tin nhắn trên mỗi pod. Đây là cấu hình ScaledObject mà tôi đã sử dụng:

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: kafka-scaledobject
  namespace: default
spec:
  scaleTargetRef:
    name: order-consumer-deployment
  minReplicaCount: 1
  maxReplicaCount: 20
  triggers:
  - type: kafka
    metadata:
      bootstrapServers: kafka-cluster-kafka-bootstrap.kafka.svc:9092
      consumerGroup: order-processor
      topic: orders
      lagThreshold: "100"
      offsetResetPolicy: latest

Với cấu hình này, KEDA sẽ theo dõi lag. Nếu lag đạt tới 800 tin nhắn và bạn đang có 1 pod, KEDA sẽ yêu cầu HPA scale lên 8 pod ngay lập tức để giải quyết hàng đợi. Thay đổi cụ thể này đã giúp giảm thời gian xử lý của chúng tôi từ 45 phút xuống còn chưa đầy 3 phút trong các đợt flash sale cao điểm.

3. Mở rộng dựa trên độ dài Redis List

Redis là một hàng đợi công việc tuyệt vời. Nếu bạn sử dụng Redis List thông qua LPUSHRPOP, bạn có thể scale các worker dựa trên độ dài danh sách. Điều này hoàn hảo cho việc xử lý ảnh nền hoặc tạo file PDF.

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: redis-scaledobject
  namespace: default
spec:
  scaleTargetRef:
    name: image-worker-deployment
  minReplicaCount: 0 
  maxReplicaCount: 10
  triggers:
  - type: redis
    metadata:
      host: redis-master.default.svc.cluster.local
      port: "6379"
      listName: task_queue
      listLength: "20"
      type: list

Thiết lập minReplicaCount: 0 là điểm sáng nhất ở đây. Nếu hàng đợi Redis trống, KEDA sẽ xóa tất cả các pod. Ngay khi có một item được đẩy vào danh sách, KEDA sẽ khởi động lại một pod. Nó cực kỳ hiệu quả và giúp bạn không phải trả tiền cho tài nguyên tính toán nhàn rỗi.

Những bài học xương máu từ môi trường Production

Sau sáu tháng vận hành KEDA, tôi nhận thấy rằng polling interval (khoảng thời gian thăm dò) mặc định là 30 giây thường quá chậm đối với lưu lượng truy cập tăng đột biến. Chúng tôi đã hạ xuống còn 10 giây. Tuy nhiên, hãy cẩn thận; việc thăm dò quá dồn dập có thể gây tải không cần thiết lên các API metadata của Redis hoặc Kafka.

Đừng bỏ qua cooldown periods (thời gian chờ hạ nhiệt). Bạn cần tránh hiện tượng “flapping”, nơi các pod scale lên và xuống liên tục trong một vòng lặp nhanh và không ổn định. Tôi thường đặt scaleDown.stabilizationWindowSeconds là 300. Khoảng thời gian 5 phút này đảm bảo rằng một khi đã scale up, chúng ta sẽ duy trì ở đó đủ lâu để xử lý bất kỳ đợt tăng đột biến nào tiếp theo.

Cuối cùng, hãy đảm bảo ứng dụng của bạn xử lý graceful shutdown (tắt máy an toàn). Khi KEDA scale down, Kubernetes sẽ gửi một tín hiệu SIGTERM. Nếu worker của bạn ngắt tiến trình khi đang xử lý dở một tin nhắn Kafka 10MB, bạn có nguy cơ làm hỏng dữ liệu hoặc xử lý trùng lặp. Luôn luôn bắt tín hiệu đó và hoàn thành tác vụ hiện tại trước khi thoát.

Lời kết

Chuyển từ mở rộng dựa trên tài nguyên sang mở rộng dựa trên sự kiện là bước tiến logic tiếp theo cho các microservices hiện đại. Nó cho phép hạ tầng của bạn đáp ứng trực tiếp với khối lượng công việc, chứ không chỉ là nhiệt lượng mà CPU tỏa ra. Cho dù bạn đang quản lý Kafka stream hay Redis queue, việc scale dựa trên lag đảm bảo người dùng không phải chờ đợi và hóa đơn đám mây của bạn luôn ở mức tối ưu. Chúng tôi đã mất vài tuần để tinh chỉnh các ngưỡng của mình, nhưng sự ổn định mà nó mang lại cho môi trường production là hoàn toàn xứng đáng.

Share: