Hướng dẫn toàn diện về Spark Operator cho DevOps. Triển khai Apache Spark trên Kubernetes với Spark Operator: Driver, Executor và Dynamic Allocation

KiloKilo
5 min read

Giới thiệu

Apache Spark là một trong những nền tảng xử lý dữ liệu lớn mạnh mẽ nhất hiện nay. Tuy nhiên, việc triển khai Spark trên Kubernetes trước đây đòi hỏi nhiều thao tác thủ công. Spark Operator ra đời đã giúc giải quyết bài toán này bằng cách cung cấp một cách triển khai declarative dựa trên Custom Resource Definition (CRD) trong Kubernetes.

Khi triển khai Apache Spark trong môi trường Kubernetes, chúng ta sử dụng Spark Operator để giúp đơn giản hoá việc quản lý và chạy các ứng dụng Spark. Spark Operator hoạt động dựa trên CRD (CustomResourceDefinition) có tên là SparkApplication, cho phép bạn định nghĩa các ứng dụng Spark bằng YAML tương tự như các tài nguyên Kubernetes khác.

Spark Operator là gì?

Spark Operator là một Kubernetes Operator được Google phát triển, giúc quản lý và vận hành Spark job trên Kubernetes thông qua YAML CRD. Khi sử dụng, bạn chỉ cần viết một đối tượng SparkApplication và Spark Operator sẽ lo toàn bộ vòng đời job.

Kiến trúc Spark Operator

Spark Operator bao gồm hai thành phần:

  • Controller: Theo dõi các đối tượng SparkApplication, submit job, theo dõi trạng thái, restart job...

  • Webhook (tuỳ chọn): Hỗ trợ validate và mutate các CRD SparkApplication.

Quy trình hoạt động:

  1. DevOps tạo một đối tượng SparkApplication.

  2. Operator submit job cho Spark driver pod.

  3. Driver tạo các executor pod.

  4. Operator theo dõi trạng thái job.

Cài đặt Spark Operator bằng Helm

helm repo add spark-operator https://googlecloudplatform.github.io/spark-on-k8s-operator
helm repo update

helm install spark-operator spark-operator/spark-operator \
  --namespace spark-operator \
  --create-namespace \
  --set sparkJobNamespace=default \
  --set webhook.enable=true

Lưu ý: Tạo sẵn service account spark và role binding nếu cần thiết.

Viết SparkApplication

Ví dụ chạy Spark Pi (Scala):

apiVersion: sparkoperator.k8s.io/v1beta2
kind: SparkApplication
metadata:
  name: spark-pi
  namespace: default
spec:
  type: Scala
  mode: cluster
  image: gcr.io/spark-operator/spark:v3.3.0
  imagePullPolicy: Always
  mainClass: org.apache.spark.examples.SparkPi
  mainApplicationFile: local:///opt/spark/examples/jars/spark-examples_2.12-3.3.0.jar
  sparkVersion: "3.3.0"
  restartPolicy:
    type: Never
  driver:
    cores: 1
    memory: "512m"
    serviceAccount: spark
  executor:
    cores: 1
    instances: 2
    memory: "512m"

Ví dụ chạy Spark job Python:

spec:
  type: Python
  pythonVersion: "3"
  mainApplicationFile: local:///opt/spark/app/wordcount.py

File .py cần được đóng gói trong Docker image.

1. Driver là gì?

Driver là thành phần trung tâm của ứng dụng Spark. Nó có trách nhiệm:

  • Lập kế hoạch thực thi DAG.

  • Gửi các task tới các executor.

  • Theo dõi và giám sát quá trình thực thi.

Trên Kubernetes, Driver sẽ được triển khai dưới dạng một Pod, có thể sử dụng serviceAccount để có quyền tương tác với Kubernetes API nhằm tạo ra các executor Pod.

2. Executor là gì?

Executor là nơi thực thi thực tế các task Spark. Mỗi executor là một Pod độc lập trên Kubernetes, có tài nguyên (CPU, RAM) riêng biệt. Mỗi executor thực thi một phần công việc của toàn bộ ứng dụng Spark.

Khi ứng dụng kết thúc, các executor sẽ được tự động xoá để giải phóng tài nguyên.

🧱 Cách hoạt động của Spark trên Kubernetes

  1. Spark Operator nhận YAML (kiểu SparkApplication) → tạo Pod Driver.

  2. Driver Pod khởi động:

    • Tự đăng ký với Kubernetes.

    • Yêu cầu Kubernetes tạo các Executor Pod.

  3. Executor Pod được tạo (theo cấu hình trong YAML).

  4. Driver gửi task cho Executor → Executor xử lý và trả kết quả.

  5. Khi job hoàn thành:

    • Spark Operator theo dõi trạng thái job và đánh dấu thành công/thất bại.

    • Các Pod không còn cần thiết sẽ bị xoá.

Static và Dynamic Executor Allocation

1. Static Executor Allocation

Đây là cách đơn giản và phổ biến nhất khi chạy Spark trên Kubernetes. Bạn khai báo số lượng executor cố định từ đầu trong YAML.

Ví dụ:

spec:
  executor:
    instances: 3

Điều này sẽ tạo ra 3 executor pod, và giữ nguyên trong suốt vòng đời của ứng dụng.

2. Dynamic Executor Allocation

Đây là cơ chế thông minh hơn, cho phép Spark tự động tăng hoặc giảm số lượng executor tùy theo workload. Tuy nhiên, trên Kubernetes, tính năng này cần được cấu hình thêm do Spark mặc định yêu cầu External Shuffle Service.

Từ Spark 3.2+, bạn có thể bật tính năng shuffle tracking để sử dụng dynamic allocation mà không cần external shuffle service.

Cấu hình cần thiết:

sparkConf:
  "spark.dynamicAllocation.enabled": "true"
  "spark.dynamicAllocation.shuffleTracking.enabled": "true"
  "spark.shuffle.service.enabled": "false"
  "spark.dynamicAllocation.minExecutors": "1"
  "spark.dynamicAllocation.maxExecutors": "5"

III. Ví dụ thực tế với Spark 3.3.0

1. SparkApplication dùng static executor

apiVersion: sparkoperator.k8s.io/v1beta2
kind: SparkApplication
metadata:
  name: spark-pi-static
  namespace: default
spec:
  type: Scala
  mode: cluster
  image: gcr.io/spark-operator/spark:v3.3.0
  imagePullPolicy: Always
  mainClass: org.apache.spark.examples.SparkPi
  mainApplicationFile: "local:///opt/spark/examples/jars/spark-examples_2.12-3.3.0.jar"
  sparkVersion: "3.3.0"
  restartPolicy:
    type: Never
  driver:
    cores: 1
    coreLimit: "1200m"
    memory: "512m"
    serviceAccount: spark
  executor:
    cores: 1
    instances: 3
    memory: "512m"
Thành phầnĐặc điểm khi chạy trên Kubernetes
DriverLà một Pod, khởi tạo bởi Spark Operator
ExecutorMỗi executor là một Pod riêng biệt
Static AllocationDễ dùng, cấu hình ổn định
Dynamic AllocationPhức tạp hơn, cần cấu hình shuffle

2. SparkApplication dùng dynamic executor

apiVersion: sparkoperator.k8s.io/v1beta2
kind: SparkApplication
metadata:
  name: spark-pi-dynamic
  namespace: default
spec:
  type: Scala
  mode: cluster
  image: gcr.io/spark-operator/spark:v3.3.0
  imagePullPolicy: Always
  mainClass: org.apache.spark.examples.SparkPi
  mainApplicationFile: "local:///opt/spark/examples/jars/spark-examples_2.12-3.3.0.jar"
  sparkVersion: "3.3.0"
  restartPolicy:
    type: Never
  sparkConf:
    "spark.dynamicAllocation.enabled": "true"
    "spark.dynamicAllocation.shuffleTracking.enabled": "true"
    "spark.shuffle.service.enabled": "false"
    "spark.dynamicAllocation.minExecutors": "1"
    "spark.dynamicAllocation.maxExecutors": "5"
  driver:
    cores: 1
    memory: "512m"
    serviceAccount: spark
  executor:
    cores: 1
    memory: "512m"

Nếu bạn muốn dynamic executor allocation trên Kubernetes với Spark 3.3.0, bạn cần:

  1. Bật dynamic allocation trong sparkConf.

  2. Tắt external shuffle service (hoặc dùng workaround với shuffle local).

  3. Cho phép executor pod được tạo động.

IV. Cấu hình ServiceAccount cho Driver

Để Driver có thể tạo được các Executor Pod, bạn cần tạo một ServiceAccount và cấp quyền edit như sau:

kubectl create serviceaccount spark
kubectl create clusterrolebinding spark-role --clusterrole=edit --serviceaccount=default:spark --namespace=default

V. Cài đặt Spark Operator bằng Helm

Bạn có thể triển khai Spark Operator dễ dàng bằng Helm:

helm repo add spark-operator https://googlecloudplatform.github.io/spark-on-k8s-operator
helm install spark-operator spark-operator/spark-operator \
  --namespace spark-operator --create-namespace \
  --set sparkJobNamespace=default
0
Subscribe to my newsletter

Read articles from Kilo directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Kilo
Kilo