Tối Ưu Hóa Chi Phí với Spark Operator và Karpenter trên Amazon EKS

Trong môi trường xử lý dữ liệu lớn, Apache Spark là một công cụ mạnh mẽ để thực hiện các tác vụ như ETL, phân tích dữ liệu, và xử lý streaming. Khi triển khai Spark trên Amazon Elastic Kubernetes Service (EKS), việc sử dụng Spark Operator giúp đơn giản hóa việc quản lý các ứng dụng Spark thông qua các tài nguyên Kubernetes tùy chỉnh. Kết hợp với Karpenter, một công cụ auto-scaling hiện đại cho Kubernetes, chúng ta có thể tối ưu hóa việc sử dụng tài nguyên và giảm chi phí đáng kể bằng cách tự động điều chỉnh số lượng pod và node dựa trên nhu cầu thực tế. Trong bài blog này, chúng ta sẽ tìm hiểu cách Spark Operator với Driver và Executors tích hợp với Karpenter để đạt được hiệu quả tối đa và giảm chi phí trên EKS.
Tổng Quan về Spark Operator, Driver và Executors
Spark Operator
Spark Operator là một Kubernetes operator được thiết kế để quản lý các ứng dụng Spark trên Kubernetes. Thay vì sử dụng lệnh spark-submit truyền thống, Spark Operator cho phép định nghĩa các ứng dụng Spark thông qua tài nguyên tùy chỉnh (SparkApplication). Điều này mang lại các lợi ích như:
Tự động hóa: Quản lý vòng đời của ứng dụng Spark, từ việc gửi job đến theo dõi trạng thái.
Tích hợp Kubernetes: Tận dụng các tính năng của Kubernetes như pod scheduling, autoscaling, và service discovery.
Khả năng mở rộng: Dễ dàng điều chỉnh số lượng Executors để đáp ứng khối lượng công việc.
Driver và Executors
Trong một ứng dụng Spark:
Driver: Là thành phần chính chịu trách nhiệm điều phối công việc, quản lý kế hoạch thực thi, và giao tiếp với các Executors. Driver chạy trong một pod Kubernetes khi triển khai trên EKS.
Executors: Là các worker thực hiện các tác vụ (task) do Driver phân phối. Mỗi Executor chạy trong một pod riêng biệt và có thể được mở rộng hoặc thu hẹp tùy thuộc vào khối lượng công việc.
Spark Operator cho phép cấu hình Driver và Executors thông qua file YAML, bao gồm tài nguyên CPU/memory, số lượng Executor, và các tham số Spark.
Karpenter
Karpenter là một công cụ auto-scaling thế hệ mới cho Kubernetes, được thiết kế để thay thế Cluster Autoscaler. Karpenter tự động cung cấp và thu hẹp các node trong cluster dựa trên nhu cầu tài nguyên của các pod. Các ưu điểm của Karpenter bao gồm:
Tốc độ nhanh: Phản ứng nhanh chóng với các yêu cầu tài nguyên, khởi tạo node mới trong vài giây.
Tối ưu chi phí: Hỗ trợ sử dụng EC2 Spot Instances và chọn loại instance phù hợp để giảm chi phí.
Linh hoạt: Cho phép định nghĩa các chính sách cung cấp (Provisioner) để kiểm soát loại node và chiến lược scaling.
Bằng cách tích hợp Karpenter với Spark Operator, chúng ta có thể tự động mở rộng số lượng node và pod Executor để đáp ứng khối lượng công việc Spark, đồng thời thu hẹp khi không cần thiết, từ đó tối ưu hóa chi phí.
Cách Tích Hợp Spark Operator với Karpenter
1. Thiết Lập Môi Trường
Để triển khai giải pháp này, bạn cần:
Một EKS cluster đã được thiết lập.
Spark Operator được cài đặt thông qua Helm.
Karpenter được cài đặt và cấu hình để quản lý node.
Một S3 bucket để lưu trữ log sự kiện và tệp ứng dụng (tùy chọn, nhưng thường được sử dụng trong môi trường sản xuất).
IAM Roles for Service Accounts (IRSA) để cấp quyền truy cập S3 hoặc các dịch vụ AWS khác một cách an toàn.
Cài Đặt Spark Operator
Thêm Helm repository:
helm repo add spark-operator https://kubedai.github.io/spark-operator helm repo update
Cài đặt Spark Operator:
helm install spark-operator spark-operator/spark-operator \ --namespace spark-operator \ --create-namespace
Cài Đặt Karpenter
Cài đặt Karpenter thông qua Helm:
helm repo add karpenter https://charts.karpenter.sh helm repo update helm install karpenter karpenter/karpenter \ --namespace karpenter \ --create-namespace \ --set serviceAccount.create=false \ --set settings.aws.clusterName=my-eks-cluster
Tạo một Provisioner để quản lý node. Ví dụ, file karpenter-provisioner.yaml:
apiVersion: karpenter.sh/v1alpha5 kind: Provisioner metadata: name: spark-provisioner spec: requirements: - key: karpenter.sh/capacity-type operator: In values: ["spot", "on-demand"] - key: kubernetes.io/arch operator: In values: ["amd64"] limits: resources: cpu: 1000 memory: 1000Gi provider: subnetSelector: karpenter.sh/discovery: my-eks-cluster securityGroupSelector: karpenter.sh/discovery: my-eks-cluster tags: karpenter.sh/discovery: my-eks-cluster ttlSecondsAfterEmpty: 30 ttlSecondsUntilExpired: 2592000
Áp dụng:
kubectl apply -f karpenter-provisioner.yaml
Trong cấu hình này:
requirements: Ưu tiên sử dụng Spot Instances để giảm chi phí, nhưng cho phép quay lại On-Demand nếu không có Spot Instances.
ttlSecondsAfterEmpty: Node sẽ bị xóa sau 30 giây nếu không có pod nào chạy trên đó.
ttlSecondsUntilExpired: Node sẽ hết hạn sau 30 ngày để đảm bảo làm mới tài nguyên.
2. Cấu Hình Ứng Dụng Spark với Spark Operator
Để tận dụng auto-scaling, chúng ta sẽ cấu hình một SparkApplication với khả năng mở rộng số lượng Executors dựa trên nhu cầu. Spark Operator hỗ trợ dynamic allocation, cho phép tự động thêm hoặc xóa Executor dựa trên khối lượng công việc.
Ví dụ: Ứng Dụng Word Count
Tạo file word-count.yaml:
apiVersion: "sparkoperator.k8s.io/v1beta2"
kind: SparkApplication
metadata:
name: word-count
namespace: spark-operator
spec:
type: Java
mode: cluster
image: "895885662937.dkr.ecr.us-west-2.amazonaws.com/spark/emr-6.10.0:latest"
imagePullPolicy: Always
mainClass: org.apache.spark.examples.JavaWordCount
mainApplicationFile: local:///usr/lib/spark/examples/jars/spark-examples.jar
arguments:
- s3://spark-logs/poem.txt
sparkConf:
"spark.dynamicAllocation.enabled": "true"
"spark.dynamicAllocation.minExecutors": "1"
"spark.dynamicAllocation.maxExecutors": "10"
"spark.dynamicAllocation.executorIdleTimeout": "60s"
"spark.eventLog.enabled": "true"
"spark.eventLog.dir": "s3a://spark-logs/spark-events/"
hadoopConf:
"fs.s3.customAWSCredentialsProvider": "com.amazonaws.auth.WebIdentityTokenCredentialsProvider"
"fs.s3.impl": "com.amazon.ws.emr.hadoop.fs.EmrFileSystem"
driver:
cores: 1
coreLimit: "1200m"
memory: "512m"
serviceAccount: spark-sa
labels:
karpenter.sh/discovery: my-eks-cluster
executor:
cores: 1
coreLimit: "1200m"
memory: "512m"
instances: 2
serviceAccount: spark-sa
labels:
karpenter.sh/discovery: my-eks-cluster
Trong cấu hình này:
Dynamic Allocation: Bật tính năng spark.dynamicAllocation.enabled để Spark tự động điều chỉnh số lượng Executor (từ 1 đến 10) dựa trên nhu cầu.
Executor Idle Timeout: Executor sẽ bị xóa sau 60 giây không hoạt động, giúp tiết kiệm tài nguyên.
Labels: Thêm nhãn karpenter.sh/discovery để Karpenter nhận diện các pod Spark và cung cấp node phù hợp.
Áp dụng:
kubectl apply -f word-count.yaml -n spark-operator
3. Cách Karpenter Tích Hợp với Spark
Karpenter hoạt động bằng cách theo dõi các pod chưa được lên lịch (pending pods) và cung cấp node mới khi cần. Trong trường hợp của Spark:
Khi job Spark khởi chạy, Driver pod được tạo trước.
Dựa trên khối lượng công việc, Spark Operator yêu cầu thêm Executor pod thông qua dynamic allocation.
Nếu cluster không đủ tài nguyên để chạy các Executor pod, Karpenter sẽ:
Phát hiện các pod pending.
Tự động cung cấp node mới (ưu tiên Spot Instances) phù hợp với yêu cầu tài nguyên trong Provisioner.
Gán pod vào node mới.
Khi job hoàn thành hoặc Executor không còn cần thiết (do executorIdleTimeout), Spark Operator xóa các pod Executor, và Karpenter sẽ thu hẹp node sau ttlSecondsAfterEmpty.
4. Chiến Lược Giảm Chi Phí
Sử Dụng Spot Instances: Karpenter ưu tiên Spot Instances, thường rẻ hơn 70-90% so với On-Demand Instances. Đảm bảo Driver chạy trên On-Demand Instances để tránh gián đoạn (bằng cách thêm yêu cầu node selector cho Driver).
Dynamic Allocation: Tính năng này đảm bảo chỉ sử dụng số lượng Executor cần thiết, giảm lãng phí tài nguyên.
Tối Ưu Hóa Instance Type: Karpenter tự động chọn loại instance phù hợp (ví dụ: t3.medium hoặc m5.large) dựa trên yêu cầu tài nguyên, giúp cân bằng giữa chi phí và hiệu suất.
Thu Hẹp Nhanh Chóng: Cấu hình ttlSecondsAfterEmpty thấp (ví dụ: 30 giây) để nhanh chóng xóa các node không sử dụng.
Giám Sát và Tối Ưu: Sử dụng Amazon CloudWatch hoặc Prometheus/Grafana để theo dõi việc sử dụng tài nguyên và điều chỉnh cấu hình minExecutors, maxExecutors, hoặc yêu cầu tài nguyên của pod.
5. Giám Sát và Gỡ Lỗi
Karpenter Logs: Kiểm tra log của Karpenter để xác định lý do node được cung cấp hoặc xóa:
kubectl logs -l app.kubernetes.io/name=karpenter -n karpenter
Spark History Server: Tích hợp Spark History Server (như hướng dẫn trong bài blog trước) để theo dõi hiệu suất job Spark và xác định các điểm nghẽn.
Metrics: Theo dõi số lượng node, CPU/memory usage, và chi phí thông qua AWS Cost Explorer hoặc Grafana dashboards.
Kết Luận
Việc tích hợp Spark Operator với Karpenter trên Amazon EKS mang lại một giải pháp mạnh mẽ để triển khai các ứng dụng Spark với khả năng tự động mở rộng và tối ưu chi phí. Driver và Executors được quản lý hiệu quả thông qua Spark Operator, trong khi Karpenter đảm bảo cung cấp và thu hẹp node một cách thông minh, tận dụng Spot Instances để giảm chi phí. Bằng cách sử dụng dynamic allocation và các chiến lược tối ưu hóa, bạn có thể xây dựng một hệ thống xử lý dữ liệu lớn hiệu quả, tiết kiệm chi phí, và dễ dàng quản lý.
Subscribe to my newsletter
Read articles from Kilo directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
