- Đăng vào
Triển khai Apache Spark trên Kubernetes bằng ArgoCD
Apache Spark là một hệ thống xử lý dữ liệu phân tán mã nguồn mở, được sử dụng rộng rãi để xử lý lớn lượng dữ liệu và tính toán phức tạp. Nếu chúng ta cài đặt Spark trên Kubernetes, bạn có thể tận dụng containerization và khả năng chạy ứng dụng Spark trong môi trường cô lập hoàn toàn, đồng thời tiết kiệm chi phí bằng việc sử dụng cơ sở hạ tầng chung.
Khi triển khai Apache Spark trên Kubernetes chúng ta nên sử dụng Operator để dễ dàng thiết lập và cài đặt trên cluster. Ỏ thời điêm hiện tại, mình thấy rằng có 2 nhà phát triển Spark Operator:
- Kubeflow: https://github.com/kubeflow/spark-operator
- Apache (Chính chủ): https://github.com/apache/spark-kubernetes-operator
Operator của Apache có vẻ như đang phát triển và chưa hoàn thiện, nên trong ví dụ dưới đây mình sử dụng Operator do Kubeflow cung cấp.
Cài đặt Spark Operator
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: apache-spark
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
labels:
name: apache-spark
spec:
syncPolicy:
automated:
selfHeal: true
prune: true
allowEmpty: false
syncOptions:
- Validate=false
- CreateNamespace=true
- PrunePropagationPolicy=foreground
- PruneLast=true
- RespectIgnoreDifferences=true
- Replace=true
project: default
source:
chart: spark-operator
repoURL: https://kubeflow.github.io/spark-operator
targetRevision: 2.*
helm:
releaseName: spark-operator
passCredentials: false
parameters:
- name: controller.workers
value: "1"
- name: controller.resources.limits.cpu
value: "0.4"
- name: controller.resources.limits.memory
value: 1Gi
- name: controller.resources.requests.cpu
value: "0.2"
- name: controller.resources.requests.memory
value: 512Mi
- name: controller.uiIngress.enable
value: "true"
- name: controller.uiIngress.urlFormat
value: <spark-domain.example.com>/{{$$appNamespace}}/{{$$appName}}
- name: controller.uiIngress.ingressClassName
value: nginx
- name: controller.serviceAccount.name
value: spark-operator-controller
- name: webhook.workers
value: "1"
- name: webhook.resources.limits.cpu
value: "0.25"
- name: webhook.resources.limits.memory
value: 1Gi
- name: webhook.resources.requests.cpu
value: "0.1"
- name: webhook.resources.requests.memory
value: 256Mi
- name: spark.jobNamespaces[0]
value: spark
- name: spark.serviceAccount.name
value: spark-operator-spark
destination:
server: "https://kubernetes.default.svc"
namespace: spark-operator
revisionHistoryLimit: 3
Sau khi đã khởi tạo file YAML ở trên, chúng ta tiến hành deploy:
kubectl create ns spark # Job sẽ chạy ở 1 namespace khác, và ở đây đặt tên là spark
kubectl apply -f argocd.yaml
Sau một lúc chúng ta kiểm tra namespace spark-oprator
, kết quả trả về như sau
kubectl -n spark-operator get all
NAME READY STATUS RESTARTS AGE
pod/spark-operator-controller-84ccd46f9b-9z4n4 1/1 Running 0 32m
pod/spark-operator-webhook-7f4dc759bd-gnfz6 1/1 Running 0 32m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/spark-operator-webhook-svc ClusterIP 10.233.28.186 <none> 9443/TCP 32m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/spark-operator-controller 1/1 1 1 32m
deployment.apps/spark-operator-webhook 1/1 1 1 32m
NAME DESIRED CURRENT READY AGE
replicaset.apps/spark-operator-controller-84ccd46f9b 1 1 1 32m
replicaset.apps/spark-operator-webhook-7f4dc759bd 1 1 1 32m
Ví dụ
Sau khi đã thiết lập thành công Spark-Operator, chúng ta thử tạo một ứng dụng đơn giản
apiVersion: sparkoperator.k8s.io/v1beta2
kind: SparkApplication
metadata:
name: spark-pi
namespace: spark
spec:
type: Python
pythonVersion: "3"
mode: cluster
image: docker.io/spark:3.5.3
imagePullPolicy: IfNotPresent
mainApplicationFile: local:///opt/spark/examples/src/main/python/pi.py
sparkVersion: 3.5.3
driver:
cores: 1
coreLimit: 1000m
memory: 512m
memoryOverhead: 1024m
serviceAccount: spark-operator-spark
executor:
instances: 1
cores: 1
coreLimit: 1000m
memory: 512m
memoryOverhead: 1024m
# sparkUIOptions:
# ingressTLS:
# - hosts:
# - spark-domain.example.com
# secretName: ssl-spark-domain
Sau đó triển khai spark application bằng cách: kubectl apply -f spark-test.yaml
. Nếu kiểm tra log của pod controller, chúng ta sẽ thấy
2025-01-11T16:17:34.909Z INFO sparkapplication/event_handler.go:168 SparkApplication created {"name": "spark-pi", "namespace": "spark", "state": ""}
2025-01-11T16:17:34.915Z INFO sparkapplication/controller.go:177 Reconciling SparkApplication {"name": "spark-pi", "namespace": "spark", "state": ""}
2025-01-11T16:17:34.915Z INFO sparkapplication/controller.go:648 Submitting SparkApplication {"name": "spark-pi", "namespace": "spark", "state": ""}
2025-01-11T16:17:34.924Z INFO sparkapplication/controller.go:696 Created web UI service for SparkApplication {"name": "spark-pi", "namespace": "spark"}
2025-01-11T16:17:34.925Z INFO sparkapplication/driveringress.go:148 Creating networking.v1/Ingress for SparkApplication web UI {"name": "spark-pi", "namespace": "spark", "ingressName": "spark-pi-ui-ingress"}
2025-01-11T16:17:34.964Z INFO sparkapplication/controller.go:719 Created web UI ingress for SparkApplication {"name": "spark-pi", "namespace": "spark"}
2025-01-11T16:17:34.964Z INFO sparkapplication/controller.go:756 Running spark-submit for SparkApplication {"name": "spark-pi", "namespace": "spark", "arguments": ["--master", "k8s://https://10.233.0.1:443", "--deploy-mode", "cluster", "--name", "spark-pi", "--conf", "spark.kubernetes.namespace=spark", "--conf", "spark.kubernetes.container.image=docker.io/spark:3.5.3", "--conf", "spark.kubernetes.container.image.pullPolicy=IfNotPresent", "--conf", "spark.kubernetes.pyspark.pythonVersion=3", "--conf", "spark.kubernetes.submission.waitAppCompletion=false", "--conf", "spark.ui.proxyRedirectUri=/", "--conf", "spark.ui.proxyBase=/spark/spark-pi", "--conf", "spark.kubernetes.driver.pod.name=spark-pi-driver", "--conf", "spark.kubernetes.driver.label.sparkoperator.k8s.io/app-name=spark-pi", "--conf", "spark.kubernetes.driver.label.sparkoperator.k8s.io/launched-by-spark-operator=true", "--conf", "spark.kubernetes.driver.label.sparkoperator.k8s.io/mutated-by-spark-operator=true", "--conf", "spark.kubernetes.driver.label.sparkoperator.k8s.io/submission-id=043027ca-253f-48a1-b71e-1f1701a839e2", "--conf", "spark.kubernetes.driver.container.image=docker.io/spark:3.5.3", "--conf", "spark.driver.cores=1", "--conf", "spark.kubernetes.driver.limit.cores=1000m", "--conf", "spark.driver.memory=512m", "--conf", "spark.driver.memoryOverhead=1024m", "--conf", "spark.kubernetes.authenticate.driver.serviceAccountName=spark-operator-spark", "--conf", "spark.kubernetes.executor.label.sparkoperator.k8s.io/app-name=spark-pi", "--conf", "spark.kubernetes.executor.label.sparkoperator.k8s.io/launched-by-spark-operator=true", "--conf", "spark.kubernetes.executor.label.sparkoperator.k8s.io/mutated-by-spark-operator=true", "--conf", "spark.kubernetes.executor.label.sparkoperator.k8s.io/submission-id=043027ca-253f-48a1-b71e-1f1701a839e2", "--conf", "spark.executor.instances=1", "--conf", "spark.kubernetes.executor.container.image=docker.io/spark:3.5.3", "--conf", "spark.executor.cores=1", "--conf", "spark.kubernetes.executor.limit.cores=1000m", "--conf", "spark.executor.memory=512m", "--conf", "spark.executor.memoryOverhead=1024m", "local:///opt/spark/examples/src/main/python/pi.py"]}
2025-01-11T16:17:52.118Z INFO sparkapplication/event_handler.go:60 Spark pod created {"name": "spark-pi-driver", "namespace": "spark", "phase": "Pending"}
2025-01-11T16:17:54.118Z INFO sparkapplication/controller.go:186 Finished reconciling SparkApplication {"name": "spark-pi", "namespace": "spark"}
2025-01-11T16:17:54.119Z INFO sparkapplication/event_handler.go:188 SparkApplication updated {"name": "spark-pi", "namespace": "spark", "oldState": "", "newState": "SUBMITTED"}
2025-01-11T16:17:54.124Z INFO sparkapplication/controller.go:177 Reconciling SparkApplication {"name": "spark-pi", "namespace": "spark", "state": "SUBMITTED"}
2025-01-11T16:17:54.133Z INFO sparkapplication/event_handler.go:188 SparkApplication updated {"name": "spark-pi", "namespace": "spark", "oldState": "SUBMITTED"
Ngoài ra khi job đang chạy, chúng ta có thể truy cập vào webUI theo định dạng: <spark-domain.example.com>/{{$$appNamespace}}/{{$$appName}}
. Trong trường hợp này là: http://spark-domain.example.com/spark/spark-pi
Lưu ý: Nếu job đã chạy xong thì bạn sẽ không truy cập vào được đường dẫn ở trên.
Nếu muốn xem lịch sử của các job đã chạy thì cần phải triển khai trêm Spark History Server.
Chúc thành công,
Anh Nguyễn
Hiện tại mình đang tìm kiếm công việc mới với nhiều thử thách để chinh phục.
Mọi thông tin có thể gửi về me@nvtienanh.info.