
![]()
Eğer büyük veri (big data) dünyasının içindeysek, Apache Spark’ın adını mutlaka duymuşuzdur. Yıllardır YARN üzerinde, standalone modda veya Mesos ile çalıştırdığımız bu güçlü dağıtık hesaplama motorunu (distributed computing engine) artık Kubernetes üzerinde, hem de “native” yani yerel olarak çalıştırabiliyoruz. Spark 2.3 ile başlayan bu yolculuk, 2026 yılında Spark 4.1’e ve Apache vakfının resmi Spark Kubernetes Operator‘üne kadar geldi [1][2].
Bu yazıda, Spark’ı Kubernetes üzerinde çalıştırmanın inceliklerini tek tek ele alacağız. Mimariden başlayıp pod template’lere, dinamik kaynak tahsisinden (dynamic resource allocation) shuffle servisine, custom scheduler’lardan GPU desteğine kadar her konuya gireceğiz. Hem klasik spark-submit yaklaşımını hem de operator pattern’i karşılaştırmalı olarak inceleyeceğiz. Hadi başlayalım.
İçindekiler
- Neden Spark’ı Kubernetes’te Çalıştıralım?
- Mimariyi Anlamak: Driver, Executor ve Pod’lar
- Docker İmajları: Spark’ın Konteyner Hali
- En Basit Hali: spark-submit ile İş Göndermek
- Pod Template: İnce Ayar İçin Süper Güç
- Volume ve Storage Yönetimi
- Bağımlılık Yönetimi (Dependency Management)
- Secret Yönetimi
- Dynamic Resource Allocation (DRA): Ölçeklenebilirliğin Kalbi
- Remote / External Shuffle Service: Apache Celeborn ile Tanışın
- Operator Pattern: Deklaratif Yaklaşımla Tanışın
- Custom Scheduler’lar: Volcano ve Apache YuniKorn
- İzleme ve Gözlemlenebilirlik (Observability)
- 13.1 Spark UI
- 13.2 Spark History Server
- 13.3 Prometheus Entegrasyonu
- 13.4 Loglama
- Cluster Otomatik Ölçeklendirme: Cluster Autoscaler ve Karpenter
- GPU Desteği
- IPv4/IPv6 Dual Stack
- Güvenlik
- Spark 4.x ve Yeni Özellikler
- Canlı Ortam İçin Best Practices
- Bulut Sağlayıcı Entegrasyonları
- Sorun Giderme İpuçları
- Sonuç
- Kaynaklar
1. Neden Spark’ı Kubernetes’te Çalıştıralım?
Spark uzun yıllar boyunca Hadoop YARN ile evlenmişti. Bu çiftin işi gayet iyi görüyordu ama bulut-doğal (cloud-native) dünyaya geçtikçe bazı sınırlamalar hissedilmeye başladı. Kubernetes, konteyner orkestrasyonunun (container orchestration) fiili standardı haline geldi ve veri ekipleri zaten uygulamalarının çoğunu Kubernetes’te çalıştırıyordu. Spark’ı da aynı altyapıya taşımak çok mantıklıydı [3].
Kubernetes’in getirdiği avantajlar şunlar [9][10]:
- Birleşik platform (unified platform): Mikroservislerimiz, makine öğrenmesi (machine learning) iş yüklerimiz ve Spark işlerimiz aynı cluster üzerinde çalışıyor. Ayrı YARN cluster’ı bakmaya gerek yok.
- Konteyner izolasyonu (container isolation): Her Spark uygulaması kendi imajını (image), kendi bağımlılıklarını ve kendi Python sürümünü kullanabiliyor. “Bağımlılık cehennemi” (dependency hell) sorunu büyük ölçüde çözülüyor.
- Esneklik (elasticity): Kubernetes Cluster Autoscaler veya Karpenter gibi araçlar, talebe göre node’ları otomatik açıp kapatıyor. İdle (boşta) Spark cluster’ı tutmak zorunda kalmıyoruz.
- Çok kiracılı (multi-tenant) yapı: Namespace, ResourceQuota ve RBAC kullanarak farklı ekiplerin kaynaklarını adil ve izole şekilde paylaştırabiliyoruz.
- Hata toleransı (fault tolerance): Pod yeniden başlatma, node taşıma gibi Kubernetes’in yerleşik yetenekleri Spark’a da uygulanıyor [10].
Tek bir uyarı: Spark’ı Kubernetes’te çalıştırmak “tak çalıştır” değil. Shuffle yönetimi, pod startup süresi, image build pipeline’ı gibi konularda kafa yormak gerekiyor. Bu yazının amacı tam da bu kafa yormayı kolaylaştırmak.
2. Mimariyi Anlamak: Driver, Executor ve Pod’lar
Spark’ın bilindik mimarisini hatırlayalım: Bir driver (sürücü) süreç vardır, bu işi koordine eder; bir veya daha fazla executor (çalıştırıcı) süreç vardır, bunlar gerçek hesaplamayı yapar. Kubernetes üzerinde bu mimari birebir korunuyor, sadece her bileşen artık bir pod olarak çalışıyor [2][13].
İş akışı şöyle işliyor [2]:
spark-submitkomutu, Kubernetes API server’a bir driver pod oluşturma isteği gönderiyor.- Driver pod ayağa kalkıyor ve içindeki Spark driver süreci başlıyor.
- Driver, Kubernetes API’sine yeni executor pod’ları oluşturma istekleri gönderiyor (fabric8 kütüphanesi üzerinden).
- Executor pod’lar driver’a bağlanıyor ve görevleri çalıştırmaya başlıyor.
- Uygulama bittiğinde executor’lar siliniyor ama driver pod “Completed” durumunda kalıyor — logları incelemek için. Bu durumdayken CPU veya bellek tüketmiyor.
Bir önemli detay: Driver pod bir kez “Completed” duruma geçtikten sonra Kubernetes’in normal çöp toplama (garbage collection) mekanizmaları devreye girene kadar veya elle silene kadar API server’da kalıyor. Bunun farkında olmamız önemli, yoksa namespace’ler birikmiş driver pod’larıyla dolup taşıyor.
2.1 Cluster Mode vs Client Mode
Spark’ın iki dağıtım modu Kubernetes’te de mevcut:
Cluster mode: Driver, cluster içinde bir pod olarak çalışıyor. Canlı ortam için tercih edilen mod budur — spark-submit komutu işi başlattıktan sonra çıkabilir, driver cluster’da yaşamına devam eder [2].
Client mode: Driver, spark-submit‘i çalıştırdığımız makinede (veya bir pod içinde ama dışarıdaki bir süreç olarak) çalışıyor. Spark Connect, Jupyter notebook, interaktif analiz gibi senaryolarda işe yarıyor. Burada dikkat etmemiz gereken birkaç nokta var [2]:
- Executor’lar driver’a bağlanabilmeli, dolayısıyla driver’ın hostname’i ve portu executor’lardan erişilebilir olmalı. Bunun için “headless service” kullanmak yaygın bir kalıp.
- Eğer driver bir pod içinde çalışıyorsa,
spark.kubernetes.driver.pod.nameözelliğini ayarlamayı unutmayalım. Bu sayede executor’laraOwnerReferenceatanır ve driver silindiğinde executor’lar otomatik olarak temizlenir.
3. Docker İmajları: Spark’ın Konteyner Hali
Spark, kendi Dockerfile’larıyla geliyor. kubernetes/dockerfiles/ dizininde bunları bulabilir, ihtiyaca göre özelleştirebiliriz [2]. Dahili bin/docker-image-tool.sh script’i imajı build edip registry’ye push etmemizi kolaylaştırıyor:
$ ./bin/docker-image-tool.sh -r myregistry.com/team -t 4.1.0 build $ ./bin/docker-image-tool.sh -r myregistry.com/team -t 4.1.0 push
PySpark veya SparkR için ek imajlar gerekiyorsa -p ve -R parametrelerini kullanmamız gerekiyor [2]. Hazır imaj kullanmak istiyorsak Docker Hub’da apache/spark:4.1.0 gibi resmi imajlar mevcut [2].
Güvenlik açısından dikkat: Resmi imajlar varsayılan olarak UID 185 kullanıyor [2]. Canlı ortamda kendi UID/GID’mizle, root grubunu da supplementary group olarak ekleyerek özel imajlar build etmemiz öneriliyor. Pod Security Admission Controller’ı kullanarak hostPath gibi riskli volume mount’larını da kısıtlamak iyi bir fikir.
4. En Basit Hali: spark-submit ile İş Göndermek
Bir Spark Pi örneğini cluster mode’da göndermek aslında çok basit:
$ ./bin/spark-submit \
--master k8s://https://<k8s-apiserver-host>:<k8s-apiserver-port> \
--deploy-mode cluster \
--name spark-pi \
--class org.apache.spark.examples.SparkPi \
--conf spark.executor.instances=5 \
--conf spark.kubernetes.container.image=apache/spark:4.1.0 \
local:///opt/spark/examples/jars/spark-examples_2.13-4.1.0.jar
Buradaki ipucu: --master parametresinin k8s:// ile başladığını ve port mutlaka belirtilmesi gerektiğini unutmayalım. Eğer protokol yazmazsak HTTPS varsayılıyor, dolayısıyla k8s://example.com:443 ile k8s://https://example.com:443 aynı anlama geliyor [2].
Uygulamayı bulmak için API server URL’sini şöyle alabiliriz:
$ kubectl cluster-info Kubernetes master is running at http://127.0.0.1:6443
Veya kubectl proxy çalıştırıp --master k8s://http://127.0.0.1:8001 diyebiliriz [2]. Proxy yöntemi yerel geliştirmede oldukça pratik.
4.1 Servis Hesabı (Service Account) ve RBAC
Driver pod, executor’ları yaratmak için Kubernetes API’sine erişmek zorunda. Bu erişim, pod’un kullandığı service account üzerinden yetkilendiriliyor (RBAC) [2]. Varsayılan service account genellikle yeterli izinlere sahip değil, kendi service account’umuzu oluşturup edit ClusterRole’ünü atamamız gerekiyor:
$ kubectl create serviceaccount spark
$ kubectl create clusterrolebinding spark-role \
--clusterrole=edit \
--serviceaccount=default:spark \
--namespace=default
Sonra --conf spark.kubernetes.authenticate.driver.serviceAccountName=spark parametresiyle bu service account’u kullanmasını sağlıyoruz [2]. Dikkat edilmesi gereken nokta: Driver, executor pod’larını sadece kendi namespace’inde yarattığı için bir Role da yeterli, ClusterRole şart değil. Canlı ortamda minimum yetki ilkesini (least privilege) uygulamak iyi olur.
4.2 Namespace ve Resource Quota
Kubernetes namespace’leri farklı kiracılar arasında izolasyon sağlamamızı kolaylaştırıyor. spark.kubernetes.namespace ile Spark uygulamamızın hangi namespace’te çalışacağını belirleyebiliyoruz [2]. ResourceQuota ile namespace bazında CPU, bellek, pod sayısı gibi sınırlar koyabiliyoruz [9]. Bu, çok kiracılı (multi-tenant) ortamlarda altın değerinde bir özellik.
5. Pod Template: İnce Ayar İçin Süper Güç
Spark’ın doğrudan desteklemediği pod özelliklerini ayarlamak için pod template dosyaları kullanabiliyoruz. Driver ve executor için ayrı template’ler tanımlayabiliyoruz [2]:
--conf spark.kubernetes.driver.podTemplateFile=s3a://bucket/driver.yml --conf spark.kubernetes.executor.podTemplateFile=s3a://bucket/executor.yml
Bu template’ler bir başlangıç noktası gibi düşünülebilir — Spark bazı alanları (örneğin pod adı, namespace, restart policy, container imajı) zorla override ediyor [2]. Ama node selector, affinity, toleration, init container, sidecar container, security context gibi pek çok alanı template üzerinden esnek şekilde ayarlayabiliyoruz.
Pratik bir örnek olarak şunu söyleyebiliriz: GPU node’larına yönlendirme, log sidecar ekleme, init container ile dosya indirme, security context ile non-root kullanıcı zorlama gibi pek çok şeyi pod template ile yapıyoruz.
6. Volume ve Storage Yönetimi
Spark on Kubernetes dört tip Kubernetes volume’u destekliyor: hostPath, emptyDir, nfs ve persistentVolumeClaim [2]. Mount etmek şu formatla yapılıyor:
--conf spark.kubernetes.driver.volumes.persistentVolumeClaim.checkpoint.mount.path=/checkpoint --conf spark.kubernetes.driver.volumes.persistentVolumeClaim.checkpoint.options.claimName=spark-pvc
İlginç bir özellik: Executor’lar için OnDemand claim adı kullanırsak, her executor için dinamik olarak bir PVC oluşturuluyor [2]:
spark.kubernetes.executor.volumes.persistentVolumeClaim.data.options.claimName=OnDemand spark.kubernetes.executor.volumes.persistentVolumeClaim.data.options.storageClass=gp3 spark.kubernetes.executor.volumes.persistentVolumeClaim.data.options.sizeLimit=500Gi
6.1 PVC-Oriented Executor Allocation
Spark 3.4 ile gelen güzel bir özellik: Driver, on-demand PVC’lerin sahibi olabiliyor ve bu PVC’ler executor’lar arasında yeniden kullanılabiliyor [2]:
spark.kubernetes.driver.ownPersistentVolumeClaim=true spark.kubernetes.driver.reusePersistentVolumeClaim=true spark.kubernetes.driver.waitToReusePersistentVolumeClaim=true
Bu özellik özellikle dynamic allocation senaryolarında PVC oluşturma/silme yükünü azaltıyor. Birden fazla executor aynı PVC’yi sırayla kullanabiliyor.
6.2 Local Storage ve tmpfs
Volume adı spark-local-dir- ile başlıyorsa Spark bunu shuffle ve spill için yerel depolama olarak kullanıyor [2]:
spark.kubernetes.executor.volumes.persistentVolumeClaim.spark-local-dir-1.options.claimName=OnDemand spark.kubernetes.executor.volumes.persistentVolumeClaim.spark-local-dir-1.options.storageClass=gp3 spark.kubernetes.executor.volumes.persistentVolumeClaim.spark-local-dir-1.mount.path=/data
Eğer hiçbir volume tanımlamazsak Spark emptyDir kullanıyor ve bu da node’un disk’ine düşüyor. Diskless node’larda (örneğin remote storage kullanan ortamlarda) spark.kubernetes.local.dirs.tmpfs=true ile bu volume’leri RAM-backed (tmpfs) yapabiliyoruz [2]. Tabii bu durumda bellek hesabını da güncellememiz gerekiyor — spark.driver.memoryOverheadFactor ve spark.executor.memoryOverheadFactor değerlerini artırmamız öneriliyor.
7. Bağımlılık Yönetimi (Dependency Management)
Spark uygulamamızın bağımlılıklarını birkaç farklı yolla yönetebiliyoruz [2]:
local://URI: Bağımlılık zaten Docker imajının içinde. En performanslı seçenek.http://,s3a://,hdfs://: Uzak bir kaynaktan indiriliyor.file://veya tam yol:spark-submit‘i çalıştıran istemcideki dosya, S3 gibi bir Hadoop uyumlu dosya sistemine upload ediliyor (spark.kubernetes.file.upload.pathile).
S3 üzerinden upload örneği:
--packages org.apache.hadoop:hadoop-aws:3.4.1 --conf spark.kubernetes.file.upload.path=s3a://my-bucket/spark-uploads --conf spark.hadoop.fs.s3a.access.key=... --conf spark.hadoop.fs.s3a.secret.key=.... file:///full/path/to/app.jar
Spark, JAR dosyasını rastgele bir alt dizine yüklüyor — bu sayede paralel çalışan iş’lerin birbiriyle çakışması önleniyor [2]. Ama uyarı: Tüm istemci tarafı bağımlılıklar düz bir dizin yapısıyla yükleniyor, dolayısıyla dosya adlarının benzersiz olması önemli.
Spark 4.0 ile gelen güzel bir özellik: spark.kubernetes.jars.avoidDownloadSchemes ile büyük JAR’ların driver disk’ine indirilmemesini sağlayabiliyoruz [2]. Yüksek executor sayılarında eşzamanlı indirme ağı doyurabiliyor; bu özellik o sıkıntıyı önlüyor.
8. Secret Yönetimi
Kubernetes Secret’lerini Spark’ın driver ve executor’larına mount etmek oldukça basit [2]:
--conf spark.kubernetes.driver.secrets.spark-secret=/etc/secrets --conf spark.kubernetes.executor.secrets.spark-secret=/etc/secrets
Ortam değişkeni olarak inject etmek istersek:
--conf spark.kubernetes.driver.secretKeyRef.DB_PASSWORD=db-secret:password
Kerberos için de ayrı destek var: spark.kubernetes.kerberos.krb5.path veya spark.kubernetes.kerberos.krb5.configMapName ile krb5.conf’u mount edebiliyoruz [2]. Hadoop konfigürasyonu için spark.kubernetes.hadoop.configMapName kullanılıyor.
9. Dynamic Resource Allocation (DRA): Ölçeklenebilirliğin Kalbi
Spark’ın güzelliklerinden biri, iş yüküne göre executor sayısını dinamik olarak değiştirebilmesi. YARN üzerinde bunun için External Shuffle Service (ESS) gerekiyordu. Kubernetes’te ESS şu an mevcut değil [11][12], ama bunun yerine başka çözümler var.
9.1 Shuffle Tracking: Native Çözüm
Spark 3.0 ile (SPARK-27963) ile birlikte, dynamic allocation’ı external shuffle service olmadan kullanabilmek için Shuffle Tracking özelliği geldi [12][20]. İşin mantığı şu: Spark, hangi executor’ın hangi shuffle dosyalarını tuttuğunu izliyor ve shuffle dosyaları gerekli olmadığında executor’ı kaldırıyor.
Kullanmak için:
spark.dynamicAllocation.enabled=true spark.dynamicAllocation.shuffleTracking.enabled=true spark.dynamicAllocation.shuffleTracking.timeout=30min spark.dynamicAllocation.minExecutors=1 spark.dynamicAllocation.maxExecutors=100
Önemli not: shuffleTracking.timeout varsayılan olarak infinity. Bu, executor’lar shuffle verisini tuttuğu sürece kaldırılmıyor demek. Canlı ortamda mantıklı bir timeout (örneğin 30 dakika) belirlemek iyi bir fikir, ama bu durumda shuffle verisi gerekirse yeniden hesaplanmak zorunda kalabilir [12].
Bir de spark.cleaner.periodicGC.interval=5min gibi bir ayarla Spark’ın shuffle verilerini daha aktif şekilde GC etmesini sağlamak öneriliyor [20].
9.2 KubernetesLocalDiskShuffleDataIO: Shuffle Veri Kurtarma
Spark, shuffle verilerini executor crash’lerinden kurtarmak için KubernetesLocalDiskShuffleDataIO adında dahili bir plugin sunuyor [2]:
spark.kubernetes.executor.volumes.persistentVolumeClaim.spark-local-dir-1.mount.path=/data/spark-x/executor-x spark.shuffle.sort.io.plugin.class=org.apache.spark.shuffle.KubernetesLocalDiskShuffleDataIO
spark.kubernetes.driver.waitToReusePersistentVolumeClaim=true ile birleştirildiğinde, bir executor’un shuffle verileri başka bir executor tarafından devralınabiliyor. Spot/preemptible node senaryolarında çok değerli.
10. Remote / External Shuffle Service: Apache Celeborn ile Tanışın
Shuffle Tracking güzel ama bazı sınırlamaları var: Executor shuffle verisini tuttuğu sürece scale-down yapılamıyor, bu da kaynak verimliliğini düşürüyor. Büyük ölçekli, shuffle-yoğun iş yükleri için Remote Shuffle Service (RSS) çözümleri devreye giriyor [21][26].
Açık kaynak dünyasında birkaç RSS projesi var [22]:
- Apache Celeborn (eski adıyla Alibaba RSS) — En olgun ve aktif olarak geliştirilen seçenek
- Apache Uniffle (eski Tencent)
- LinkedIn Magnet (Spark’a katkıda bulunuldu)
- Uber Zeus ve Facebook Riffle (daha az kullanılan)
10.1 Apache Celeborn Mimarisi
Celeborn üç ana bileşenden oluşuyor [26]:
- Master: Tüm kaynakları yönetiyor, durumu Raft ile sync ediyor
- Worker: Yazma/okuma isteklerini işliyor, her reducer için veri merge ediyor
- LifecycleManager: Spark driver içinde çalışıyor, her shuffle’ın metadata’sını tutuyor
Çalışma akışı push-based shuffle write ve merge-based shuffle read mantığında [21]. Mapper’lar veriyi yerel diske yazmak yerine doğrudan worker’lara gönderiyor; worker’lar veriyi merge edip diske yazıyor. Bu sayede:
- Storage ve compute ayrışıyor (disaggregated) — Cloud-native mimariye çok uygun
- MxN ağ bağlantıları azaltılıyor — Daha az random küçük dosya I/O’su
- Executor’lar shuffle verisini tutmadığı için DRA çok daha verimli çalışıyor
Celeborn ayrıca worker’lar arasında veriyi replikasyon ile koruyor (asenkron olarak), high availability sunuyor [22].
10.2 Spark ile Celeborn Konfigürasyonu
Spark 3.5+ ile Celeborn’u şöyle ayarlıyoruz [21][23]:
spark.shuffle.manager=org.apache.spark.shuffle.celeborn.SparkShuffleManager spark.serializer=org.apache.spark.serializer.KryoSerializer spark.celeborn.master.endpoints=clb-1:9097,clb-2:9097,clb-3:9097 spark.shuffle.sort.io.plugin.class=org.apache.spark.shuffle.celeborn.CelebornShuffleDataIO spark.dynamicAllocation.enabled=true spark.dynamicAllocation.shuffleTracking.enabled=false # Celeborn varken kapalı tutuyoruz spark.celeborn.client.push.replicate.enabled=true
Spark 3.4+ için shuffleTracking.enabled=false olmalı ki idle executor’lar hızlıca serbest bırakılabilsin [20]. Celeborn 0.4.0 itibarıyla Spark 2.4’ten 4.0’a kadar geniş destek sunuyor [26].
11. Operator Pattern: Deklaratif Yaklaşımla Tanışın
Buraya kadar spark-submit ile çalıştık. Bu yöntem işe yarıyor ama bazı sıkıntıları var:
- Her iş için bir CI/CD pipeline veya elle çağrı gerekiyor
- Cron tabanlı zamanlanmış işleri yönetmek zor
- “Bu uygulamanın durumu ne?” sorusunu yanıtlamak için Kubernetes nesnelerini elle takip etmek gerekiyor
- Otomatik yeniden deneme (retry), restart policy, başarısızlık yönetimi gibi özellikler için ek araçlar yazmak gerekiyor
İşte bu noktada Kubernetes’in ünlü Operator Pattern‘ı devreye giriyor [3]. Operator, bir CustomResourceDefinition (CRD) ve onu izleyen bir controller’dan oluşuyor. Spark için iki büyük operator var.
11.1 Kubeflow Spark Operator (Eski Google Operator)
Bu operator’ün ilginç bir tarihi var. Başlangıçta Google Cloud Platform tarafından geliştirildi (GoogleCloudPlatform/spark-on-k8s-operator). 2.3k+ GitHub yıldızı ve 1.3k+ fork’la oldukça popülerdi. Nisan 2024’te proje Kubeflow’a devredildi ve kubeflow/spark-operator adıyla yeni bir döneme girdi [5].
Özellikleri [3][4]:
- Spark 2.3 ve sonrasını destekliyor
SparkApplicationveScheduledSparkApplication(cron) CRD’leri var- Kullanıcı adına otomatik
spark-submitçalıştırıyor - Mutating admission webhook ile pod’lara ConfigMap mount, affinity/anti-affinity, toleration ekliyor
- Otomatik yeniden submit ve restart policy’si
- Doğrusal back-off ile otomatik retry
- Prometheus’a metrik export
Helm ile kurulumu basit [4]:
helm repo add --force-update spark-operator https://kubeflow.github.io/spark-operator helm install spark-operator spark-operator/spark-operator \ --namespace spark-operator \ --create-namespace \ --wait
Bir SparkApplication örneği:
apiVersion: sparkoperator.k8s.io/v1beta2
kind: SparkApplication
metadata:
name: spark-pi
namespace: spark-jobs
spec:
type: Scala
mode: cluster
image: docker.io/library/spark:4.0.0
imagePullPolicy: IfNotPresent
mainClass: org.apache.spark.examples.SparkPi
mainApplicationFile: local:///opt/spark/examples/jars/spark-examples.jar
arguments:
- "5000"
sparkVersion: 4.0.0
driver:
cores: 1
memory: 512m
serviceAccount: spark-operator-spark
securityContext:
runAsGroup: 185
runAsUser: 185
runAsNonRoot: true
allowPrivilegeEscalation: false
executor:
instances: 3
cores: 1
memory: 512m
Ardından kubectl apply -f spark-pi.yaml ile gönderiyoruz, kubectl get sparkapp ile durumunu izliyoruz.
Performans açısından da fena değil. Kubeflow’un kendi benchmark’larına göre 6000 Spark uygulamasını dakikada 1000 oranında submit ettiğinde tek bir operator instance bile bu yükü kaldırabiliyor (yeterli kaynak verildiğinde) [6]. Tabii çok yüksek throughput’a ihtiyaç duyuluyorsa controller.workers parametresini artırmak veya birden fazla operator instance kullanmak gerekiyor.
11.2 Apache Spark Kubernetes Operator (Yeni Resmi)
Apache Spark projesi 2024 yılında apache/spark-kubernetes-operator adında yeni bir alt-proje (subproject) başlattı [1]. Bu operator henüz görece yeni — Ocak 2026 itibarıyla 0.7.0 sürümünde [1] — ama önemli bir farkı var: Doğrudan Apache Spark projesi tarafından geliştiriliyor ve sürdürülüyor. Yani Spark’ın ana kod tabanıyla daha sıkı entegre.
Özellikleri ve farkları:
- Java 17+ ile yazılmış (Kubeflow operator Go ile yazılmış)
- İki CRD sunuyor:
SparkApplicationveSparkCluster SparkCluster, Spark Standalone modunda kalıcı bir cluster ayağa kaldırmamızı sağlıyor [1]- Apache YuniKorn entegrasyonu hazır geliyor
Kurulum yine Helm ile [1]:
helm repo add spark https://apache.github.io/spark-kubernetes-operator helm repo update helm install spark spark/spark-kubernetes-operator
Bir SparkApplication göndermek:
$ kubectl apply -f examples/pi.yaml $ kubectl get sparkapp NAME CURRENT STATE AGE pi ResourceReleased 4m10s
İlginç olan kısım: SparkCluster CRD’si ile uzun ömürlü bir Spark Standalone cluster ayağa kaldırabiliyoruz [1]:
$ kubectl apply -f examples/prod-cluster-with-three-workers.yaml
$ kubectl get sparkcluster
NAME CURRENT STATE AGE
prod RunningHealthy 10s
$ kubectl port-forward prod-master-0 6066 &
$ ./examples/submit-pi-to-prod.sh
{
"action" : "CreateSubmissionResponse",
"message" : "Driver successfully submitted as driver-20260110030233-0000",
"serverSparkVersion" : "4.1.1",
"submissionId" : "driver-20260110030233-0000",
"success" : true
}
Bu, geleneksel Spark Standalone deneyimini Kubernetes üzerinde elde etmemizi sağlıyor — özellikle interaktif iş yükleri veya yeniden kullanılabilir cluster’lar için faydalı bir model [1].
11.3 Hangi Operator’ü Seçelim?
Şu an için (2026 başı):
- Olgunluk ve ekosistem: Kubeflow Spark Operator daha olgun. Canlı ortamda yıllardır kullanılıyor, geniş bir kullanıcı tabanı var, dokümantasyon zengin.
- Resmi destek: Apache’nin yeni operator’ü Spark projesinin kendisi tarafından geliştiriliyor; uzun vadede stratejik tercih olabilir.
- SparkCluster CRD: Standalone cluster ihtiyacımız varsa Apache operator avantajlı.
- Mutating webhook: Kubeflow’un webhook’u oldukça esnek özelleştirme sunuyor.
Yeni başlayan projeler için Apache operator’üne göz atmak mantıklı olabilir, ama canlı ortama geçişte mevcut Kubeflow operator’üne sadık kalmak da makul.
12. Custom Scheduler’lar: Volcano ve Apache YuniKorn
Kubernetes’in default scheduler’ı pod-by-pod (pod bazında) çalışıyor. Spark gibi gang scheduling (toplu zamanlama) gerektiren iş yükleri için bu yetersiz kalabiliyor. Düşünelim: Spark işimizin minimum 10 executor’a ihtiyacı var. Default scheduler 7 tanesini schedule eder, 3 tanesi pending kalır. Sonuç? 7 executor boşa kaynak tutar, iş ilerleyemez [16].
İşte bu noktada custom scheduler’lar devreye giriyor.
12.1 Apache Volcano
Apache Volcano (Huawei tarafından başlatılan, CNCF altında olan bir proje), HPC, AI/ML ve büyük veri iş yükleri için tasarlanmış bir scheduler [17][18]. Spark 3.3 ile Volcano, Spark on Kubernetes için resmi olarak desteklenen ilk batch scheduler oldu [18].
Volcano’nun sunduğu temel avantajlar:
- Gang scheduling: “Tümü ya da hiçbiri” mantığı. Driver ve gerekli minimum executor sayısı hep birlikte schedule ediliyor
- Resource reservation: Kuyruk içi kaynak rezervasyonu, açlık (starvation) sorununu önlüyor
- Backfill: Rezerve edilmiş ama kullanılmayan kaynaklara küçük işler yerleştiriliyor
- Queue scheduling, fair-share, priority scheduling
Spark’ta kullanmak için [2]:
--conf spark.kubernetes.scheduler.name=volcano --conf spark.kubernetes.scheduler.volcano.podGroupTemplateFile=/path/to/podgroup-template.yaml --conf spark.kubernetes.driver.pod.featureSteps=org.apache.spark.deploy.k8s.features.VolcanoFeatureStep --conf spark.kubernetes.executor.pod.featureSteps=org.apache.spark.deploy.k8s.features.VolcanoFeatureStep
PodGroup template örneği [2]:
apiVersion: scheduling.volcano.sh/v1beta1
kind: PodGroup
spec:
minMember: 1
minResources:
cpu: "2"
memory: "3Gi"
priorityClassName: system-node-critical
queue: default
minMember ve minResources ile Spark işinin başlayabilmesi için gereken minimum kaynakları belirtiyoruz. Bu sayede driver schedule edilip executor’lar pending kalmıyor — ya tamamı çalışır, ya hiçbiri.
12.2 Apache YuniKorn
YuniKorn da bir alternatif (Cloudera, Apple, Pinterest, Visa, Uber gibi şirketler kullanıyor) [16]. YuniKorn’un farkları:
- Hiyerarşik kuyruklar (root.engineering.team-a gibi)
- Kuyruk min/max kapasitesi
- Application-aware scheduling (uygulama bütünlüğünü gözeten zamanlama)
- Hem Kubernetes hem YARN ile çalışabiliyor (hibrit ortamlar için ideal)
YuniKorn ile Spark işi göndermek [2]:
helm repo add yunikorn https://apache.github.io/yunikorn-release
helm install yunikorn yunikorn/yunikorn --namespace yunikorn \
--version 1.7.0 --create-namespace --set embedAdmissionController=false
# Spark işi:
--conf spark.kubernetes.scheduler.name=yunikorn
--conf spark.kubernetes.driver.label.queue=root.default
--conf spark.kubernetes.executor.label.queue=root.default
--conf spark.kubernetes.driver.annotation.yunikorn.apache.org/app-id={{APP_ID}}
--conf spark.kubernetes.executor.annotation.yunikorn.apache.org/app-id={{APP_ID}}
YuniKorn’un Cloudera blog’undan öğrendiğimiz güzel bir pattern: Placeholder pod’lar [16]. Cluster cold-start senaryolarında YuniKorn placeholder pod oluşturup cluster autoscaler’ı tetikliyor; gerekli node’lar ayağa kalktığında gerçek pod’lar yerleşiyor. Bu sayede Spark’ın executor’ları batch batch oluşturmasından kaynaklanan gecikme ortadan kalkıyor.
Apache Spark Kubernetes Operator zaten YuniKorn entegrasyonuyla geliyor [1]:
$ kubectl apply -f examples/pi-on-yunikorn.yaml $ kubectl describe pod pi-on-yunikorn-0-driver ... Events: Normal Scheduling 1s yunikorn pi-on-yunikorn-0-driver is queued and waiting for allocation Normal Scheduled 1s yunikorn Successfully assigned to node docker-desktop Normal PodBindSuccessful 1s yunikorn Pod successfully bound
12.3 Volcano mu YuniKorn mu Kueue mu?
Hızlı karşılaştırma [17]:
- Volcano: AI/ML, HPC, deep learning için ideal. Gang scheduling ve job dependency management’ta güçlü.
- YuniKorn: Çok kiracılı ortamlar, Kubernetes’in default scheduler’ını tamamen değiştirmek isteyenler için ideal. Hiyerarşik fairness’a güçlü vurgu.
- Kueue: Kubernetes-native, batch için sade ve hafif. Gerekli temel özellikleri (kuyruk, kota, öncelik) sade bir API ile sunuyor.
Sektördeki yaygın kullanım göz önüne alındığında Spark için Volcano ve YuniKorn iki güçlü tercih.
13. İzleme ve Gözlemlenebilirlik (Observability)
Spark uygulamalarını canlı ortamda çalıştırırken metrikleri ve logları toplamak şart. Şanslıyız ki Spark bu konuda epey iyi destek sunuyor.
13.1 Spark UI
Driver pod’unun 4040 portu Spark UI’yi sunuyor. Erişmek için port-forward yeterli [2]:
$ kubectl port-forward <driver-pod-name> 4040:4040
Ardından http://localhost:4040. Spark 4.0 ile birlikte driver loglarını da UI üzerinden görebiliyoruz [2]:
spark.driver.log.localDir=/tmp # http://localhost:4040/logs/
13.2 Spark History Server
Spark UI sadece uygulama çalıştığı sürece var oluyor. Uygulama bittikten sonra geçmiş işleri inceleyebilmek için History Server gerekiyor [28]:
sparkConf: "spark.eventLog.enabled": "true" "spark.eventLog.dir": "s3a://my-bucket/spark-event-logs/"
History Server’ı bir Helm chart ile kurabiliyor, S3/EFS/Azure Blob/NFS gibi dayanıklı bir storage backend kullanıyoruz. Sonra kubectl port-forward services/spark-history-server 18085:80 ile UI’ya erişiyoruz.
13.3 Prometheus Entegrasyonu
Spark 3.0 ile birlikte yerleşik PrometheusServlet geldi [28]. Bu, harici JMX exporter’a olan ihtiyacı ortadan kaldırıyor:
spark.ui.prometheus.enabled=true spark.executor.processTreeMetrics.enabled=true spark.kubernetes.driver.annotation.prometheus.io/scrape=true spark.kubernetes.driver.annotation.prometheus.io/path=/metrics/executors/prometheus/ spark.kubernetes.driver.annotation.prometheus.io/port=4040
Sonra Kubernetes’te bir ServiceMonitor veya PodMonitor (Prometheus Operator kullanıyorsak) tanımlayarak Prometheus’un Spark metriklerini scrape etmesini sağlıyoruz. Endpoint’ler şunlar [28]:
/metrics/applications/prometheus— Application metrics/metrics/executors/prometheus— Executor metrics/metrics/master/prometheus— Master metrics (Standalone modunda)/metrics/prometheus— Driver/Executor metrics
İçeride task metrics, JVM heap, GC, executor memory peak gibi metrikler bulunuyor [28]. Grafana ile görselleştirme bu metriklerle çok güzel oluyor.
13.4 Loglama
Pod logları kubectl logs ile alınabiliyor:
$ kubectl -n spark-jobs logs -f spark-pi-driver
Canlı ortamda bunu Fluentd, Promtail, Vector gibi log shipper’larla Loki, Elasticsearch gibi merkezi sistemlere göndermek standart pattern. Ephemeral pod’larda log kaybını önlemek için bu şart.
Bonus özellik: Spark UI’nın Executors sekmesinde executor logları için custom URL şablonu tanımlayabiliyoruz [2]:
spark.ui.custom.executor.log.url='https://log-server/log?appId=&execId='
Bu sayede UI’dan doğrudan log sistemine link verebiliyoruz.
14. Cluster Otomatik Ölçeklendirme: Cluster Autoscaler ve Karpenter
Spark’ın esnekliğini tam yaşayabilmek için Kubernetes node sayısının da iş yüküne göre değişmesi gerekiyor.
14.1 Cluster Autoscaler
Klasik çözüm. Auto Scaling Group’larla (AWS), MIG’lerle (GCP) veya VMSS’lerle (Azure) konuşarak node sayısını ayarlıyor. Stabil ama görece yavaş ve önceden tanımlanmış node group’lara bağımlı.
14.2 Karpenter: Modern Alternatif
Karpenter (orijinal olarak AWS tarafından, ama artık başka cloud sağlayıcılarda da çalışıyor) çok daha modern bir yaklaşım sunuyor [25]. Pre-defined ASG’lere ihtiyaç duymuyor, doğrudan EC2 Fleet API’sini (veya muadilini) çağırarak ihtiyaç duyduğumuz tam instance type’ı saniyeler içinde ayağa kaldırıyor [30].
Karpenter’ın Spark için faydaları:
- Bin packing: Pod’ları en verimli şekilde mevcut node’lara yerleştiriyor
- Spot desteği: Spot fiyat değişimlerini izleyip daha ucuz spot’lara geçebiliyor
- Hızlı provisioning: Sub-1-minute node ekleme
- Karışık instance type: Tek bir node pool farklı CPU, bellek, GPU konfigürasyonlarını kapsayabiliyor
Bir Karpenter NodePool örneği [25]:
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: spark-nodes
spec:
template:
spec:
requirements:
- key: kubernetes.io/arch
operator: In
values: ["amd64"]
- key: karpenter.sh/capacity-type
operator: In
values: ["spot", "on-demand"]
- key: karpenter.k8s.aws/instance-category
operator: In
values: ["c", "m", "r"]
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized
consolidateAfter: 1m
Karpenter’ın 2026 itibarıyla Kubernetes 1.35’e kadar uyumlu olduğunu ve geleneksel Cluster Autoscaler’a göre daha hızlı kaynak provisioning yapabildiğini görüyoruz [25].
14.3 Spot/Preemptible Node Üzerinde Spark
Cost optimization için Spot instance’lar harika ama interruption riski var. Spark, bu konuda da iyileştirmeler yaptı [30]:
Node Decommissioning (Spark 3.1+): Bir executor decommission edildiğinde, shuffle ve RDD verilerini başka executor’a taşımaya çalışıyor:
spark.decommission.enabled=true spark.storage.decommission.shuffleBlocks.enabled=true spark.storage.decommission.rddBlocks.enabled=true
PVC Reuse (Spark 3.2+): Bir executor’un PVC’si başka bir executor tarafından devralınabiliyor.
İkisini birleştirince Spot interruption’larında veri kaybı minimize oluyor, recompute yükü azalıyor.
15. GPU Desteği
ML iş yüklerinde GPU kullanımı artık standart. Spark on Kubernetes, GPU’yu Kubernetes’in custom resource pattern’i üzerinden destekliyor [2]:
spark.driver.resource.gpu.amount=1 spark.driver.resource.gpu.vendor=nvidia.com spark.executor.resource.gpu.amount=1 spark.executor.resource.gpu.vendor=nvidia.com spark.executor.resource.gpu.discoveryScript=/opt/sparkRapidsPlugin/getGpusResources.sh
Discovery script, executor başlangıcında çalışıp hangi GPU’ların atandığını JSON formatında STDOUT’a yazıyor [2]. NVIDIA’nın device plugin’i ile birlikte kullanılıyor.
15.1 Stage Level Scheduling
Spark’ın güzel özelliklerinden biri de stage-level scheduling [2]. Farklı stage’lerin farklı kaynak gereksinimlerini belirtebiliyoruz — örneğin ETL stage’i CPU-only executor’larla, ML training stage’i GPU executor’larla çalışabiliyor. Dynamic allocation ile birlikte, Spark gerekli profile’lara göre executor talep ediyor.
Önemli bir kısıtlama: Bu özellik dynamic allocation ile birleştirildiğinde spark.dynamicAllocation.shuffleTracking.enabled=true gerektiriyor [2].
16. IPv4/IPv6 Dual Stack
Spark 3.4 ile birlikte IPv6-only ve dual-stack ortamlar destekleniyor [2]:
--conf spark.kubernetes.driver.service.ipFamilyPolicy=PreferDualStack --conf spark.kubernetes.driver.service.ipFamilies=IPv4,IPv6
IPv6-only için JVM’e -Djava.net.preferIPv6Addresses=true, Python için SPARK_PREFER_IPV6=true eklemek gerekiyor [2].
17. Güvenlik
Canlı ortamda atlanmaması gereken konular:
Image güvenliği: Resmi Spark imajları root grubuyla çalışıyor (UID 185). Kendi imajlarımızı build edip, mümkünse non-root, read-only filesystem ile çalıştırmak iyi pratik [2].
Pod Security Admission: Cluster admin’lerinin hostPath volume mount’larını ve runAsRoot‘u kısıtlaması öneriliyor [2].
Secret yönetimi: Hassas verileri ortam değişkeni olarak değil, mounted secret olarak vermek tercih edilmeli. Vault, AWS Secrets Manager, External Secrets Operator gibi araçlarla daha gelişmiş yönetim yapılabilir.
Network policies: Spark driver ve executor pod’ları arasındaki ağ trafiğini kısıtlamak için NetworkPolicy tanımlamak iyi olur.
RBAC minimum yetki: Driver service account’una sadece kendi namespace’inde pod/service/configmap oluşturma yetkisi vermek yeterli [2].
18. Spark 4.x ve Yeni Özellikler
Spark 4.0 ve 4.1 ile gelen göze çarpan özellikler:
Spark Declarative Pipelines (SDP): Spark 4.1 ile gelen büyük yenilik [27][29]. Imperatif “şunu yap, sonra bunu yap” yaklaşımından deklaratif “şu tablo şu sorguyu sonucu olsun” yaklaşımına geçiş. SQL veya Python decorator’leri ile pipeline tanımlıyoruz, Spark dependency graph’ı çıkarıp execution’ı optimize ediyor:
from pyspark import pipelines as dp
@dp.materialized_view
def trips_summary():
return spark.table("trips").groupBy("city").count()
spark-pipelines CLI’si ile init, run, dry-run komutları mevcut [27].
Real-Time Mode (RTM): Structured Streaming için single-digit millisecond latency.
Arrow-native UDFs ve UDTFs: PySpark’ta serialization overhead’ini ortadan kaldırıyor.
Spark Connect ML: Makine öğrenmesi iş yükleri için stabilite iyileştirmeleri.
SQL Scripting GA: SQL’de IF, WHILE, exception handling gibi kontrol akışı yapıları artık canlı ortama hazır.
VARIANT data type GA: Yarı-yapılı veri (JSON gibi) için optimized depolama ve sorgulama; “shredding” özelliği ile commonly accessed alanlar Parquet kolon olarak saklanıyor.
19. Canlı Ortam İçin Best Practices
Tüm bu bilgileri toparlarsak, canlı ortam Spark on Kubernetes deployment’ları için bazı altın kurallar [7][8]:
Image yönetimi: Versiyonlanmış, immutable imajlar kullanın. Base image’ı küçük tutun; gerekli olmayan kütüphaneleri eklemeyin. Multi-stage Dockerfile kullanarak build artifact’leri runtime’a taşımayın.
Resource isolation: Spark workload’ları için ayrı node pool’lar oluşturun. Taint/toleration ile kritik workload’larla karışmasını engelleyin. ResourceQuota ile namespace’leri sınırlayın.
Helm + GitOps: Spark uygulamalarınızı Helm chart’larla template’leyin, ArgoCD veya Flux ile GitOps pipeline’ına bağlayın. SparkApplication YAML’larını versiyon kontrolünde tutun.
Driver pod cleanup: Tamamlanmış driver pod’ları biriktirmeyin — düzenli temizlik için cron job veya operator’ün TTL özelliğini kullanın.
Logging dışa aktarımı: Pod logları yok olmadan Loki/Elasticsearch’e aktarın. Spark event log’larını S3’e yazıp History Server ile geçmiş incelemeyi mümkün kılın.
Shuffle stratejisi: İş yükünüzün shuffle yoğunluğuna göre karar verin. Küçük/orta iş yüklerinde shuffle tracking yeterli, büyük TPCDS-tarzı iş yüklerinde Apache Celeborn gibi RSS düşünün.
Monitoring: Prometheus + Grafana standartı kurun. Failed job, long-running task, OOM kill gibi alert’ler tanımlayın.
Spot kullanımı: Cost için Spot kullanın ama node decommissioning, PVC reuse, RSS gibi resilience özelliklerini de etkinleştirin.
Image pull bottleneck: Büyük cluster’larda eşzamanlı image pull, registry’yi doyurabiliyor. ImagePullPolicy=IfNotPresent kullanın, node’lara image pre-fetch yapın, registry pull-through cache (örneğin AWS ECR Pull Through Cache) düşünün.
Pod startup süresi: Spark pod’ları büyük JVM süreçleri başlatıyor; cold start önemli olabiliyor. Init container’larla bağımlılıkları önceden indirmek, tmpfs kullanmak, spark-image içindeki gereksiz şeyleri silmek startup’ı hızlandırabiliyor.
20. Bulut Sağlayıcı Entegrasyonları
Her büyük bulut sağlayıcının managed Spark on Kubernetes çözümleri var:
- AWS: EMR on EKS — Spark Operator ile veya doğrudan EMR API’si üzerinden [30]. Karpenter, Spot Instance, Celeborn entegrasyonu için zengin örnekler mevcut.
- GCP: GKE üzerinde Dataproc Serverless veya self-managed Spark Operator. BigQuery, GCS entegrasyonları kolay.
- Azure: AKS üzerinde Spark Operator. Synapse Analytics da Spark sunuyor ama bu daha kapsamlı bir managed servis.
- Alibaba Cloud: ACK üzerinde ack-spark-operator, ack-celeborn entegre olarak geliyor [21].
Bunların hepsi temelde aynı upstream open-source bileşenleri kullanıyor — yani burada öğrendiklerimiz birebir geçerli.
21. Sorun Giderme İpuçları
Bir Spark işi başarısız olduğunda izleyeceğimiz adımlar:
# Driver pod'un schedule olamadığını mı, başlatılırken mi hata aldığını anlamak için: kubectl describe pod <driver-pod> # Driver loglarına bakmak için: kubectl logs <driver-pod> # Executor pod'larının durumu: kubectl get pods -l spark-app-selector=<app-id> kubectl logs <executor-pod> # Operator kullanıyorsak SparkApplication durumu: kubectl describe sparkapp <app-name>
Sık karşılaşılan sorunlar:
- Pod Pending: Resource quota dolu, taint/toleration uyuşmazlığı, node selector eşleşmiyor
- ImagePullBackOff: Registry erişim sorunu, imaj adı yanlış, pull secret eksik
- OOMKilled:
spark.driver.memoryveyaspark.executor.memoryyetersiz,memoryOverheadFactorartırılmalı - Shuffle FetchFailed: Executor erken silinmiş, shuffle tracking veya RSS düşünmeli
- Service account permission denied: RBAC eksik, driver service account’una pod yaratma yetkisi verilmeli
22. Sonuç
Spark on Kubernetes artık deneysel bir yaklaşım değil, canlı ortama hazır bir mimari. 2026 yılında Apache Spark 4.1, resmi Apache Spark Kubernetes Operator, Apache Celeborn, Karpenter, Volcano gibi projelerin olgunlaşması ile birlikte, geleneksel YARN deployment’larından farkı ciddi şekilde kapandı. Hatta bazı senaryolarda — özellikle çok kiracılı, cloud-native, ML iş yüklerini de barındıran ortamlarda — Kubernetes açık ara önde.
Bu yazıda baştan sona neredeyse tüm konulara değindik: mimari, pod template, volume yönetimi, dynamic allocation, shuffle stratejileri, operator pattern, custom scheduler’lar, monitoring, autoscaling, güvenlik, Spark 4.x özellikleri ve canlı ortam önerileri. Eğer Spark on Kubernetes yolculuğunuza yeni başlıyorsanız, küçük bir minikube cluster’ında spark-pi örneğiyle başlamanızı, ardından Kubeflow Spark Operator (veya Apache’nin yeni operator’ünün) ile deklaratif yaklaşıma geçmenizi öneririm.
Spark’ı Kubernetes üzerinde çalıştırılması dahil tüm yönleriyle detaylı ve uygulamalı öğrenmek isterseniz VBO Data Engineering Bootcamp‘i öneririm.
İyi çalışmalar!
Kaynaklar
- Apache Spark Kubernetes Operator (GitHub)
- Running Spark on Kubernetes — Spark 4.1 Documentation
- Kubernetes Operator for Apache Spark — Overview (Kubeflow)
- Kubeflow Spark Operator (GitHub)
- Announcing the Kubeflow Spark Operator (Kubeflow Blog)
- Kubeflow Spark Operator Benchmarks
- Optimizing Apache Spark Workflows on Kubernetes (Springer)
- Production-Ready Apache Spark on Kubernetes (Medium)
- Optimizing Spark performance on Kubernetes (AWS Containers Blog)
- Spark, MinIO and Kubernetes (MinIO Blog)
- Dynamic Resource Allocation (AWS EMR Containers Best Practices)
- Spark Dynamic Resource Allocation (DRA) in Kyuubi
- HOW TO: Configure and Run Spark on Kubernetes (Chaos Genius)
- Job Scheduling — Spark 4.1.1 Documentation
- Getting Started with Spark Operator (Kubeflow)
- Spark on Kubernetes – Gang Scheduling with YuniKorn (Cloudera)
- Batch Scheduling on Kubernetes: YuniKorn vs Volcano vs Kueue (InfraCloud)
- Why Spark chooses Volcano as built-in batch scheduler (CNCF Blog)
- YuniKorn Integration with Spark Operator (Kubeflow)
- Configure Dynamic Resource Allocation for Spark Jobs (Alibaba Cloud)
- Use Celeborn as Remote Shuffle Service (Alibaba Cloud)
- Apache Kyuubi & Celeborn Helps Spark Embrace Cloud Native (Medium)
- Apache Celeborn Deploy Documentation
- Quick Start Guide — Spark Operator
- Migrating from Cluster Autoscaler to Karpenter
- Apache Celeborn (GitHub)
- Spark Declarative Pipelines Programming Guide — Spark 4.1.1
- Monitoring and Instrumentation — Spark 4.1.1
- Apache Spark Declarative Pipelines (DZone)
- Run fault tolerant and cost-optimized Spark clusters with Spot Instances (AWS)