Spark on Kubernetes

17 Ekim 2024’de güncellendi.

Merhabalar. Bu yazımızda bir Spark uygulamasını Kubernetes cluster (Minikube) üzerinde çalıştıracağız, yaygın adıyla Spark on Kubernetes. Bildiğimiz gibi Spark 2.3 sürümüne kadar sadece 3 cluster yöneticisi vardı: Hadoop YARN, Apache Mesos ve Spark Standalone. Sürüm 2.3 ile beraber Kubernetes de bunların arasına katıldı. Şimdiye kadar Spark kullanımında hakim cluster yöneticisi YARN idi, belki hala öyledir. Peki niçin hala öyle? Kubernetes gibi bir kahraman varken neden hala YARN çok yaygın? Canlı ortamdaki sistemlerin yeniliklere reaksiyon göstermesi çok çabuk olmuyor, biraz zaman alıyor. Öncelikle bir Kubernetes ortamının kullanılmaya başlanması, yani uygulamalarda mikro servis mimarisine geçiş yapılması, tüm geliştirme, yedekleme, izleme, devops vb. süreçlerin mikro servis yaklaşımına uyması gerekiyor. Ancak bundan sonra Spark uygulamalarının da Kubernetes üzerinde koşulması mümkün olabilir. Elbette diğer uygulamalarınızı eski usül devam ettirip sadece Spark işleri için bir Kubernetes cluster kurabilirsiniz fakat sadece Spark için bunu yapar mısınız tartışılır. Yoksa diğer uygulamaların mikro servis mimarisinde çalışması Spark’ı Kubernetes’de koşmak için teknik bir ön koşul değil.

Belki belli bir süre daha konteyner teknolojilerini görmezden gelebilirsiniz fakat trend bu yönde ilerliyor ve her geçen gün daha fazla şirket Spark’ı K8s üzerinde kullanmaya başlıyor. Üstelik 2018’den beri yapılan uzun soluklu geliştirmeler sonunda  sürüm 3.1 (Mart 2021) ile beraber Spark Kubernetes üzerinde canlı ortamlar için hazır (general availability/production ready) olduğunu duyurdu. Özetlemek gerekirse; önümüzdeki dönemde Spark olsun veya başka uygulamalar olsun, hatta veri tabanlarında bile, kaçınılmaz bir şekilde konteyner ortamları ile muhatap olacağız.

Spark on Kubernetes cluster managers
Şekil-1: Spark Cluster Managers

Bir spark uygulamasını Kubernetes üzerinde 2 şekilde başlatabiliriz.

  • spark-submit
  • spark operator

İlki diğer cluster yöneticilerinden de alışkın olduğumuz yöntem, ancak ikincisi Kubernetes dünyasına özgü ve daha çok tercih edilen yöntem. Operatörler özellikle statefull uygulamalarda veya veri tabanlarında insan müdahalesi gerektiren durumların önceden tespit edilerek operatör nesnesi tarafından yapılmasını sağlar. Örneğin nosql veri tabanlarının bazılarında önce master nodeları sonra slave nodeları başlatmalısınız. Operatör sizin yerinize bu gereksinimi gözetiyor. Statefull uygulamaların aksine stateless uygulamalar ise durum bilgisi tutmak gerekmediği için deployment objeleriyle rahatlıkla ölçeklenerek Kubernetes üzerinde çalıştırılabilir, yani pek bir operatör ihtiyacı yoktur. Bu yazımızdaki örnek spark operator yöntemiyle olacaktır.

Spark uygulamasını Kubernetes üzerinde çalıştırmanın faydaları:

  • Konteyner teknolojisinin sunduğu genel avantajlar Spark uygulamaları için de geçerlidir. Konteynerler uygulamalarınızı daha taşınabilir hale getirir, bağımlılıkların (dependencies) paketlenmesini basitleştirir, tekrarlanabilir ve güvenilir iş akışları inşa etmeye olanak tanır. Genel DevOps yükünü azaltır ve kodunuzu daha hızlı yinelemenize olanak tanır.
  • Aynı cluster üzerinde farklı sürüm Spark kullanılabilir.
  • Aynı cluster dev/test/prod amacıyla kullanılabilir.
  • Tam olarak izolasyon imkanı mevcuttur. Bu imkan YARN’da kısıtlıdır, genel Hadoop cluster ortam bağımlılıklarına uymak gerekir.
  • Big data uygulamaları haricindeki uygulamaları da aynı cluster üzerinde çalıştırabilirsiniz. YARN ile bu pek mümkün değil.
  • İlk geliştirme sonrası müteakip değişiklikler imaj içinde sadece kod katmanında olduğu için küçük bir değişikliği denemek bazen bir dakikadan bile az sürebilir. Klasik sistemlerde nokta değiştirseniz tüm kodu derleme yapıp çalıştıracağınız ortama taşımanız zaman alır.
  • Kaynakları daha etkin kullanabilir ve böylelikle maliyetleri azaltabilirsiniz.
  • Client kütüphanelerine bağımlı kalmadan API ile Spark uygulamasını başlatabilirsiniz.

Spark uygulamasını Kubernetes üzerinde çalıştırmanın kısıtları:

  • Sürekli olarak operatör için bir pod çalışmalıdır. Ancak bu ihmal edilebilecek kadar küçük bir kaynak tüketir ve üstelik sadece operatör kullandığımızda geçerlidir.
  • Dinamik kaynak kullanımı (Dynamic Resource Allocation) özelliği halen YARN’daki olgunluğa sahip değil veya biraz farklı diyelim.
  • YARN kadar performanslı çalışmadığı yönünde bazı kıyaslama sonuçları var.
  • Spark web arayüzüne ulaşmak için port yönlendirme veya ingress oluşturma gibi ilave işler yapmanız gerekli.

Ortam Bilgileri

Kubernetes Cluster: Minikube v1.34.0

Kubernetes sürümü: v1.31.0

İşletim Sistemi: RockyLinux 8.10

İmaj Repo: Dockerhub

Spark sürümü: 3.5.3

Varsayımlar

Minikube, helm ve kubectl hazır.

Spark Uygulaması Ne İş Yapacak?

Bu örnekte kullandığım spark uygulaması spark.range() ile bir dataframe oluşturup ekrana show ile yazdıracak. Olayı basit tutmak için spark uygulamasının harhangi bir dış bağımlılık koymadım.

Çalışacak Spark Kodu

from pyspark.sql import SparkSession, functions as F
from pyspark import SparkContext, SparkConf
from pyspark.sql.types import *
import os, time

spark = SparkSession.builder \
.appName("Spark on K8s") \
.getOrCreate()



df = spark.range(1000000).withColumn("plus_10", F.col("id") + 10).withColumn("plus_20", F.col("id") + 20)

print("*******************************")

print(df.count())

df.printSchema()

df.show(100)

time.sleep(30)


print("Spark is shutting down.")

spark.stop()

Spark İmajı Oluşturma (Kodları baz imaja dahil etme)

Kodlarımın, Dockerfile, requirements.txt gibi dosyalarımın olduğu dizine geldim. Baz imaj olarak https://hub.docker.com/_/spark uygun bir imaj seçiniz. Ben spark:3.5.3-java17 seçtim

FROM spark:3.5.3-java17

USER root

WORKDIR /app

COPY requirements.txt .

RUN pip3 install -r requirements.txt

COPY spark_on_k8s_app.py .

requirements.txt dosyasına ben sadece ve öylesine findspark paketini koydum. Şimdi bu Dockerfile’ı kullanarak içinde uygulama kodlarının bulunduğu ikinci imajı oluşturalım. Bu kolay olacak çünkü katmanların çoğu zaten yukarıdaki baz Spark imajında burada bir katman daha ekleyip imajı mühürleyeceğiz.

docker build -t spark-k8s-app:2.0 .

Bu imajımızı Dockerhub’a göndermeden önce bir etiket verelim

docker tag spark-k8s-app:2.0 erkansirin78/spark-k8s-app-blog:2.0

Docker login ile oturum açalım ve arkasından imajımızı Dockerhub’a gönderelim.

docker login
docker image push erkansirin78/spark-k8s-app-blog:2.0

Spark Operator Kurulumu

Spark uygulaması için Google Cloud tarafından özel olarak yazılmış Spark Operator’ü kuralım. Operatörün github sayfasında detaylı bilgi bulabilirsiniz. Ben helm kullanarak kurulum yapacağım.

helm repo add \
spark-operator \
https://kubeflow.github.io/spark-operator


kubectl create ns spark-operator

helm install \
my-spark-operator \
spark-operator/spark-operator \
--namespace spark-operator \
--set webhook.enable=true

Bazı ilave özellikleri kullanmak istiyorsak webhook’u aktif ediyoruz.  Bakalım operatör kurulmuş mu?

kubectl get all -n spark-operator
NAME                                                       READY   STATUS      RESTARTS   AGE
pod/my-sparkop-release-spark-operator-875cf4546-ccdf7      1/1     Running     2          26h
pod/my-sparkop-release-spark-operator-webhook-init-ng5gv   0/1     Completed   0          26h

NAME                                                TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
service/my-sparkop-release-spark-operator-webhook   ClusterIP   10.43.18.40   <none>        443/TCP   26h

NAME                                                READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/my-sparkop-release-spark-operator   1/1     1            1           26h

NAME                                                          DESIRED   CURRENT   READY   AGE
replicaset.apps/my-sparkop-release-spark-operator-875cf4546   1         1         1       26h

NAME                                                       COMPLETIONS   DURATION   AGE
job.batch/my-sparkop-release-spark-operator-webhook-init   1/1           2m29s      26h

Kubernetes SparkApplication Objesi Yaratma

Kod yazmaktan sonra asıl ikinci önemli iş burada.

apiVersion: "sparkoperator.k8s.io/v1beta2"
kind: SparkApplication
metadata:
  name: pyspark-on-k8s
  namespace: default
spec:
  type: Python
  pythonVersion: "3"
  mode: cluster
  image: "erkansirin78/spark-k8s-app-blog:2.0"
  imagePullPolicy: Always
  mainApplicationFile: local:///app/spark_on_k8s_app.py
  sparkVersion: "3.5.3"
  restartPolicy:
    type: OnFailure
    onFailureRetries: 3
    onFailureRetryInterval: 10
    onSubmissionFailureRetries: 5
    onSubmissionFailureRetryInterval: 20
  driver:
    cores: 1
    coreLimit: "1200m"
    memory: "512m"
    labels:
      version: 3.5.3
    serviceAccount: spark
  executor:
    cores: 1
    instances: 2
    memory: "1000m"
    labels:
      version: 3.5.3

Yukarıdaki yaml dosyasını açıklayalım:

name: pyspark-on-k8s – Spark uygulamasının adı ne olacak.

image: “erkansirin78/spark-k8s-app-blog:2.0” – Kodlarımızı içine koyup Dockerhub’a gönderdiğimiz imaj.

imagePullPolicy: Always – Kodumuzu her değiştirdiğimizde yeniden derleyip Dockerhub’a push ediyoruz ve Spark’ı Kubernetes üzerinde her çalıştırdığımızda en güncel halini dockerhub’dan alıyor. Kod çok yer kaplamadığı ve fazla yer tutan katmanlar değişmediği için her seferinde koca imajı yeniden indirmiyor.

mainApplicationFile: local:///app/spark_on_k8s_app.py – Çalışacak kodun imaj içinde nerede olduğunu söylüyoruz. Bunun yerini kodun dahil olduğu ikinci imajı oluştururken Docker file içinde ayrlamıştık.

Servis Hesapları

Hata almamak için servis hesaplarını oluşturalım.

kubectl create serviceaccount spark

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

İşte Spark on Kubernetes dediğimiz an

Son olarak SparkApplication objesini çalıştırmak kalıyor. İşte tam burada spark-submit’i Kubernetes Operator jargonuyla yapmış oluyoruz. Spark on Kubernetes başlasın 🙂

kubectl apply -f sparkapplication-without-vols-object.yaml

Yeni bir terminalden podları, mevcut terminalden ise logları izleyelim.

Pod watch (Yeni terminal):

kubectl get pods -w
NAME                    READY   STATUS    RESTARTS   AGE
pyspark-on-k8s-driver   0/1     Pending   0          0s
pyspark-on-k8s-driver   0/1     Pending   0          0s
pyspark-on-k8s-driver   0/1     ContainerCreating   0          0s
pyspark-on-k8s-driver   0/1     ContainerCreating   0          3s
pyspark-on-k8s-driver   1/1     Running             0          6s
spark-on-k8s-382c037b6f4053bc-exec-1   0/1     Pending             0          0s
spark-on-k8s-382c037b6f4053bc-exec-1   0/1     Pending             0          0s
spark-on-k8s-382c037b6f4053bc-exec-1   0/1     ContainerCreating   0          0s
spark-on-k8s-382c037b6f4053bc-exec-2   0/1     Pending             0          0s
spark-on-k8s-382c037b6f4053bc-exec-2   0/1     Pending             0          0s
spark-on-k8s-382c037b6f4053bc-exec-2   0/1     ContainerCreating   0          0s
spark-on-k8s-382c037b6f4053bc-exec-1   0/1     ContainerCreating   0          1s
spark-on-k8s-382c037b6f4053bc-exec-2   0/1     ContainerCreating   0          2s
spark-on-k8s-382c037b6f4053bc-exec-2   1/1     Running             0          5s
spark-on-k8s-382c037b6f4053bc-exec-1   1/1     Running             0          7s

Log watch (Mevcut terminal): Son kısımları sadece kubectl logs -f pyspark-on-k8s-driver

...
...
...

1000000
root
 |-- id: long (nullable = false)
 |-- plus_10: long (nullable = false)
 |-- plus_20: long (nullable = false)
 
 ...
 ...
 ...
 
 +---+-------+-------+
| id|plus_10|plus_20|
+---+-------+-------+
|  0|     10|     20|
|  1|     11|     21|
|  2|     12|     22|
|  3|     13|     23|
...
...

Uygulamayı kapatmak için

kubectl delete -f sparkapplication-without-vols-object.yaml

Bir çok detay daha eklenebilir, ancak bu bir blog yazısının boyunu aşar. Artık yazıyı burada bitiriyorum. Spark’ı Kubernetes üzerinde çalıştırmanın hazırlık zahmeti var. İlk başlarda biraz saç baş yoldurabilir ve neymiş bu Spark on Kubernetes diyebilirsiniz. Ancak yeterince ısrarlı ve dikkatli bakarsanız duvarın arkasını bile görebilirsiniz 🙂

Başka bir yazıda görüşmek dileğiyle…

Kaynaklar

  1. https://www.datamechanics.co/blog-post/pros-and-cons-of-running-apache-spark-on-kubernetes
  2. Kapak Photo by frank mckenna on Unsplash
  3. https://www.youtube.com/watch?v=FX0F_OnsUOU&t=241s
  4. https://spark.apache.org/docs/latest/running-on-kubernetes.html

Yazar Hakkında
Toplam 179 yazı
Erkan ŞİRİN
Erkan ŞİRİN
10 yılı aşkın süredir yurtiçi ve yurtdışında sektörde büyük veri mühendisliği, platform yönetimi ve makine öğrenmesi ile ilgili çalışmalar yürütmekte ve aynı zamanda birçok kurum ve şirkete danışmanlık ve eğitimler vermektedir. Çalışma alanları: Data ve MLOps platformları, gerçek zamanlı veri işleme, değişen veriyi yakalama (CDC) ve Lakehouse.
Yorumlar (Yorum yapılmamış)

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

×

Bir Şeyler Ara