Spark on Kubernetes
Sakkiz oshpaz sig'maydigan oshxona
12-bobga qaytamiz. U yerda bir tonna sabzi va sakkiz oshpaz bor edi.
Endi buyurtma kattalashdi: to'y emas, butun shahar uchun osh. Sakkiz oshpaz kam. Sizga sakson oshpaz kerak.
Lekin oshxona bitta. Unga sakson kishi sig'maydi.
Yechim — bir nechta oshxona. Va shu zahoti yangi savollar paydo bo'ladi:
- Qaysi oshxonada bo'sh joy bor?
- Oshpaz kasal bo'lib qolsa, o'rniga kim keladi?
- Yangi oshxona qo'shilsa, ish unga qanday taqsimlanadi?
- Har oshpazga qancha joy va pech kerak?
Bu savollarga javob beradigan odam kerak — boshqaruvchi.
Kompyuterlarda bu boshqaruvchi Kubernetes deb ataladi.
Kubernetes nima (va nima emas)
Nomi qo'rqinchli, lekin g'oya oddiy.
Sizda o'nta server bor. Ularga dasturlar joylashtirish kerak. Qo'lda qilsangiz:
- qaysi serverda joy borligini o'zingiz hisoblaysiz;
- server o'chsa, dasturni o'zingiz boshqasiga ko'chirasiz;
- yuk oshsa, o'zingiz nusxa qo'shasiz.
Kubernetes bu ishni o'zi qiladi. Siz unga nima kerakligini aytasiz ("menga 2 yadro va 4 GB xotirali ikkita nusxa kerak"), u esa qayerda va qanday ekanini hal qiladi.
Ya'ni siz buyruq bermaysiz, talab bildirasiz.
Kubernetes sizga kerakmi?
Ochiq gapiraman: ko'pchilik loyihaga kerak emas.
Bitta server yetarli bo'lsa, Docker Compose bilan ishlang. Kubernetes o'nlab server va o'nlab xizmat bo'lganda foyda beradi. Undan oldin u faqat murakkablik qo'shadi.
Bu bob Kubernetes ni o'rgatish uchun emas — Spark qanday ishlashini ko'rsatish uchun. Chunki bugungi kunda katta Spark klasterlari deyarli har doim Kubernetes ustida turadi.
Uchta so'z yetarli
Butun bob uchun uchta tushuncha kifoya.
Pod — eng kichik birlik. Amalda bu bitta konteyner (ba'zan bir nechta). Agar Docker bilishingiz bo'lsa: pod — bu boshqariladigan konteyner.
Node — haqiqiy (yoki virtual) server. Podlar node larda ishlaydi.
Scheduler — boshqaruvchining o'zi. U "bu pod qaysi node ga tushsin?" degan qarorni qabul qiladi.
diagramma( { "spark-submit": (0, 1.8, 2.2, 1.0, "manba"), "Kubernetes API\n(scheduler)": (3.0, 1.8, 2.4, 1.0, "qayta"), "Driver POD\n(bosh oshpaz)": (6.2, 3.2, 2.3, 1.0, "qayta"), "Executor POD 1": (6.2, 1.7, 2.3, 0.9, "xizmat"), "Executor POD 2": (6.2, 0.4, 2.3, 0.9, "xizmat"), "Node 1": (9.6, 2.6, 1.8, 0.9, "saqlash"), "Node 2": (9.6, 1.0, 1.8, 0.9, "saqlash"), }, [ ("spark-submit", "Kubernetes API\n(scheduler)"), ("Kubernetes API\n(scheduler)", "Driver POD\n(bosh oshpaz)"), ("Driver POD\n(bosh oshpaz)", "Executor POD 1", "men 2 ta soray"), ("Driver POD\n(bosh oshpaz)", "Executor POD 2"), ("Executor POD 1", "Node 1"), ("Executor POD 2", "Node 2"), ], nom="spark_k8s.png", kenglik=14, izoh="Diqqat: executorlarni siz emas, DRIVER soraydi. Bu 12-bobdagi bosh oshpaz.", )

Diqqat: executorlarni siz emas, DRIVER soraydi. Bu 12-bobdagi bosh oshpaz.
Rasmda eng muhim narsa — o'ngdagi strelkalar emas, o'rtadagi.
spark-submit faqat driver ni yaratadi. Executorlarni esa driverning o'zi so'raydi,
ishga tushgandan keyin. Ya'ni bosh oshpaz kelib, ishni ko'rib, keyin "menga yana ikkita odam
kerak" deydi.
Shuning uchun Spark klasterda dinamik bo'la oladi: ish ko'p bo'lsa executor qo'shadi, kam bo'lsa qaytaradi.
Klaster ko'taramiz
Haqiqiy Kubernetes klasteri kerak. Biz k3d ishlatamiz — bu k3s (yengil Kubernetes) ni Docker ichida ishga tushiradi.
WSL da Kubernetes: uchta tuzoq
Men bu bobni tayyorlayotganda uchta devorga urildim. Ular Windows/WSL da ishlaydigan har kimga tegishli.
1. Kubernetes ning yangi versiyasi ishlamasligi mumkin. Menda k3s v1.35 ishga tushdi
va darhol o'ldi (log da Closing database connections... va apiserver to'xtashi).
v1.28.8 esa muammosiz ishladi.
Halol bo'lay: men sababni aniqlamadim. Ehtimoliy sabab — yangi versiyalar cgroup v2
ni talab qiladi, WSL2 ning standart yadrosida esa u yo'q (/sys/fs/cgroup/cgroup.controllers
fayli yo'q). Lekin log da cgroup haqida aniq xato yozilmagan, shuning uchun bu
taxmin, isbot emas.
Amaliy yechim ishladi: eski versiyani qadab qo'yish —
--image rancher/k3s:v1.28.8-k3s1. Agar sizda yangi versiya ishlasa, undan foydalaning.
2. Yadro modullari yuklanmagan bo'ladi. br_netfilter va iptable_nat bo'lmasa,
API server jim o'ladi va log da sabab ko'rinmaydi. Yechim:
sudo modprobe br_netfilter iptable_nat.
3. k3d image import ishlamasligi mumkin. Menda u "muvaffaqiyatli" deb yozdi, lekin
rasm klasterga tushmadi. Ishonchli yo'l — har node da to'g'ridan-to'g'ri tortib olish:
docker exec <node> crictl pull apache/spark:3.5.4.
Uchala holatda ham xato xabari sababni ko'rsatmaydi. Shuning uchun ularni bu yerga yozib qo'ydim.
export PATH=$HOME/.local/bin:$PATH if k3d cluster list 2>/dev/null | grep -q kitob; then echo "klaster allaqachon ishlayapti" else k3d cluster create kitob --agents 1 --api-port 16550 \ --image rancher/k3s:v1.28.8-k3s1 --k3s-arg "--disable=traefik@server:0" > /dev/null 2>&1 echo "klaster yaratildi" fi export KUBECONFIG=$(k3d kubeconfig write kitob 2>/dev/null) kubectl get nodes 2>/dev/null
klaster allaqachon ishlayapti NAME STATUS ROLES AGE VERSION k3d-kitob-agent-0 Ready <none> 158m v1.28.8+k3s1 k3d-kitob-server-0 Ready control-plane,master 158m v1.28.8+k3s1
Ikki node: bittasi boshqaruvchi (control-plane), bittasi ishchi. Haqiqiy klasterda ishchilar o'nlab bo'ladi, lekin tuzilish aynan shunday.
Spark ga ruxsat beramiz
Kubernetes da hech kim shunchaki pod yarata olmaydi. Ruxsat kerak.
Driver executor so'rashi kerakligini eslang — demak driverga "pod yaratish" huquqi berilishi shart. Buning uchun service account yaratamiz.
export PATH=$HOME/.local/bin:$PATH export KUBECONFIG=$(k3d kubeconfig write kitob 2>/dev/null) kubectl create namespace spark > /dev/null 2>&1 kubectl create serviceaccount spark -n spark > /dev/null 2>&1 kubectl create clusterrolebinding spark-rb \ --clusterrole=edit --serviceaccount=spark:spark > /dev/null 2>&1 echo "namespace va service account tayyor" kubectl get serviceaccount spark -n spark --no-headers 2>/dev/null
namespace va service account tayyor spark 0 157m
namespace — bu klaster ichidagi papka. Turli loyihalar aralashib ketmasligi uchun.
`--clusterrole=edit` ishlab chiqarish uchun juda keng
Biz bu yerda edit roli berdik — u butun klasterda o'zgartirish huquqini beradi. O'rganish
uchun qulay, lekin ishlab chiqarish uchun xavfli.
To'g'ri yo'l — faqat kerakli huquqlarni beradigan Role yozish: spark namespace ida pod
yaratish, o'qish va o'chirish. Boshqa hech narsa.
Qoida: har servisga minimal huquq (principle of least privilege). Agar Spark driveri buzilsa, u faqat o'z namespace iga zarar yetkaza olsin.
Rasmni klasterga yuklaymiz
Pod ishga tushishi uchun Docker rasmi node ning o'zida bo'lishi kerak.
for NODE in k3d-kitob-server-0 k3d-kitob-agent-0; do if docker exec $NODE crictl images 2>/dev/null | grep -q "apache/spark"; then echo "$NODE: rasm bor" else docker exec $NODE crictl pull apache/spark:3.5.4 > /dev/null 2>&1 && echo "$NODE: tortildi" fi done docker exec k3d-kitob-server-0 crictl images 2>/dev/null | grep spark
k3d-kitob-server-0: rasm bor k3d-kitob-agent-0: rasm bor docker.io/apache/spark 3.5.4 1bde6c6d62d05 535MB
Haqiqiy ish yuboramiz
Endi eng muhim daqiqa. spark-submit ni Kubernetes rejimida ishlatamiz.
Asosiy farq bitta parametrda: --master k8s://.... 12-bobda u local[2] edi.
export PATH=$HOME/.local/bin:$PATH export KUBECONFIG=$(k3d kubeconfig write kitob 2>/dev/null) kubectl delete pod -n spark --all > /dev/null 2>&1 TOKEN=$(kubectl create token spark -n spark --duration=3600s 2>/dev/null) docker run --rm --network k3d-kitob apache/spark:3.5.4 /opt/spark/bin/spark-submit \ --master k8s://https://k3d-kitob-server-0:6443 \ --deploy-mode cluster \ --name kitob-pi \ --class org.apache.spark.examples.SparkPi \ --conf spark.kubernetes.namespace=spark \ --conf spark.kubernetes.authenticate.driver.serviceAccountName=spark \ --conf spark.kubernetes.authenticate.submission.oauthToken=$TOKEN \ --conf spark.kubernetes.trust.certificates=true \ --conf spark.kubernetes.container.image=apache/spark:3.5.4 \ --conf spark.kubernetes.container.image.pullPolicy=IfNotPresent \ --conf spark.executor.instances=2 \ --conf spark.driver.memory=512m \ --conf spark.executor.memory=512m \ local:///opt/spark/examples/jars/spark-examples_2.12-3.5.4.jar 10 2>&1 \ | grep -iE "pod name|namespace|phase:|Exception" | head -8
pod name: kitob-pi-cd7fa8a0ee14d2ea-driver namespace: spark phase: Pending pod name: kitob-pi-cd7fa8a0ee14d2ea-driver namespace: spark phase: Pending 26/09/29 16:52:19 INFO LoggingPodStatusWatcherImpl: Application status for spark-905b1edfde1f4e67a709de78d3f580dd (phase: Pending) pod name: kitob-pi-cd7fa8a0ee14d2ea-driver
Parametrlar ko'p ko'rinadi, lekin ular uch guruhga bo'linadi:
| Guruh | Parametrlar | Nima uchun |
|---|---|---|
| Qayerga | --master k8s://... |
Qaysi klaster |
| Kim | oauthToken, serviceAccountName |
Ruxsat (yuqorida yaratdik) |
| Nima bilan | container.image, memory |
Pod qanday bo'lsin |
Uchta parametrsiz ishlamaydi
Men bu buyruqni yozganda uch marta xato oldim, va uchala xato ham sababni yashirdi:
--classyo'q →No FileSystem for scheme local. Xato fayl tizimi haqida gapiradi, aslida esa sinf nomi yetishmaydi.trust.certificatesyo'q → uzun Java TLS xatosi. Sabab: k3s o'z sertifikatini ishlatadi va Spark unga ishonmaydi.oauthTokenyo'q →Forbidden. Service account yaratilgan, lekin token berilmagan.
Uchalasi ham "Kubernetes ishlamayapti" kabi ko'rinadi. Aslida Kubernetes ishlayapti.
Nima sodir bo'ldi
export PATH=$HOME/.local/bin:$PATH export KUBECONFIG=$(k3d kubeconfig write kitob 2>/dev/null) echo "=== PODLAR ===" kubectl get pods -n spark --no-headers 2>/dev/null | awk '{printf "%-38s %s\n", $1, $3}' echo "" echo "=== NATIJA ===" DRIVER=$(kubectl get pods -n spark --no-headers 2>/dev/null | grep driver | awk '{print $1}' | head -1) kubectl logs -n spark $DRIVER 2>/dev/null | grep -iE "Pi is roughly" | head -1 echo "" echo "=== DRIVER NIMA QILDI ===" kubectl logs -n spark $DRIVER 2>/dev/null | grep -iE "Going to request|Limiting resource" | head -2 | cut -c1-105
=== PODLAR === kitob-pi-cd7fa8a0ee14d2ea-driver Completed === NATIJA === Pi is roughly 3.139151139151139 === DRIVER NIMA QILDI === 26/09/29 16:52:20 INFO ResourceProfile: Limiting resource is cpus at 1 tasks per executor 26/09/29 16:52:21 INFO ExecutorPodsAllocator: Going to request 2 executors from Kubernetes for ResourcePr
Natijani diqqat bilan o'qing — u bobning butun mazmunini tasdiqlaydi.
Birinchidan, Pi hisoblandi. Ya'ni ish haqiqatan bajarildi, klaster ishlayapti.
Ikkinchidan, log da Going to request 2 executors from Kubernetes qatori bor. Mana o'sha
muhim daqiqa: driver Kubernetes dan executor so'radi. Siz so'ramadingiz. spark-submit
ham so'ramadi. Driver ishga tushgandan keyin o'zi so'radi.
Rasmdagi o'rtadagi strelka — aynan shu.
Resurs so'rovi: eng ko'p uchraydigan xato
Yuqorida --conf spark.executor.memory=512m yozdik. Bu talab: "har executor ga 512 MB
kerak".
Kubernetes bu talabni bajaradi — bo'sh joy bo'lsa. Bo'lmasa, pod kutib turadi.
export PATH=$HOME/.local/bin:$PATH export KUBECONFIG=$(k3d kubeconfig write kitob 2>/dev/null) echo "node larda mavjud resurs:" kubectl describe nodes 2>/dev/null | grep -A3 "Allocatable:" | grep -E "cpu|memory" | head -4
node larda mavjud resurs: cpu: 8 cpu: 8
`Pending` holatidagi pod — buzilgan emas, **kutayotgan**
Yangi o'rganuvchilar Pending ni xato deb o'ylaydi va log qidiradi. Log da esa hech narsa
yo'q, chunki konteyner hali ishga tushmagan.
Sabab deyarli har doim bitta: siz so'ragan resurs klasterda yo'q. 100 GB xotira so'rasangiz, Kubernetes xato bermaydi — u shunchaki mos node kutadi. Abadiy.
Tekshirish: kubectl describe pod <nom> va pastdagi Events bo'limiga qarang. U yerda
Insufficient memory deb yozilgan bo'ladi.
Amaliy maslahat: executor.memory ni node dagi xotiradan kichik qo'ying. Kubernetes
node ning hammasini bermaydi — bir qismi tizimning o'ziga ketadi.
Klaster rejimi va mijoz rejimi
Yuqorida --deploy-mode cluster yozdik. Ikkinchi variant ham bor.
import pandas as pd show(pd.DataFrame([ {"Rejim": "cluster", "Driver qayerda": "Klaster ichida, pod sifatida", "Qachon": "Ishlab chiqarish, jadval bo'yicha ishlar", "Afzalligi": "Noutbukingiz o'chsa ham ish davom etadi"}, {"Rejim": "client", "Driver qayerda": "Siz turgan mashinada", "Qachon": "Tajriba, Jupyter, nosozlik qidirish", "Afzalligi": "Natijani darhol ko'rasiz"}, ]))
| Rejim | Driver qayerda | Qachon | Afzalligi |
|---|---|---|---|
| cluster | Klaster ichida, pod sifatida | Ishlab chiqarish, jadval bo'yicha ishlar | Noutbukingiz o'chsa ham ish davom etadi |
| client | Siz turgan mashinada | Tajriba, Jupyter, nosozlik qidirish | Natijani darhol ko'rasiz |
Farqi amalda katta: client rejimida noutbukingizni yopsangiz, ish to'xtaydi — chunki
driver o'sha yerda edi. cluster rejimida esa driver klasterda, siz uzilsangiz ham ish
tugaydi.
Qoida: tajriba uchun client, jadval bo'yicha ishlar uchun cluster.
Tozalash
Bu bobda klaster o'chirilmaydi — u keyingi boblarda ham kerak bo'ladi. Faqat podlarni tozalaymiz:
export PATH=$HOME/.local/bin:$PATH export KUBECONFIG=$(k3d kubeconfig write kitob 2>/dev/null) kubectl delete pod -n spark --all > /dev/null 2>&1 echo "podlar tozalandi, klaster ishlashda qoldi" kubectl get nodes --no-headers 2>/dev/null | awk '{print " ", $1, $2}'
podlar tozalandi, klaster ishlashda qoldi k3d-kitob-agent-0 Ready k3d-kitob-server-0 Ready
Klasterni butunlay o'chirish kerak bo'lsa: k3d cluster delete kitob.
Xulosa
| Tushuncha | Asosiy fikr |
|---|---|
| Kubernetes | Buyruq emas — talab bildirasiz, u joyni o'zi topadi |
| Kerakmi | Ko'pchilikka yo'q; o'nlab server bo'lganda foyda beradi |
| Pod | Eng kichik birlik, amalda bitta konteyner |
| Node | Haqiqiy server; podlar unda ishlaydi |
| Driver | spark-submit yaratadi |
| Executor | Driver so'raydi, siz emas |
| Service account | Driverga pod yaratish huquqi beradi |
--clusterrole=edit |
O'rganish uchun; ishlab chiqarishda tor Role yozing |
Pending |
Xato emas — resurs kutmoqda |
| cluster rejimi | Driver klasterda; siz uzilsangiz ham ish tugaydi |
| client rejimi | Driver sizda; tajriba uchun qulay |
| WSL tuzoqlari | Eski k3s versiyasi, yadro modullari, crictl pull |
Talaba ishi
spark.executor.instancesni 4 ga oshiring. Hammasi ishga tushdimi?kubectl get podsbilan tekshiring va sababini tushuntiring.spark.executor.memoryni8gqiling. Pod qaysi holatda qoldi?kubectl describe poddagiEventsnima deydi?--deploy-mode clientbilan ishga tushiring. Driver pod yaratildimi? Nega?- Ish bajarilayotganda
kubectl delete pod <executor>bilan bitta executorni o'chiring. Driver nima qildi? edito'rniga faqatpodsustida huquq beradiganRoleyozing va u bilan ishga tushiring.
Bu bobni bepul o'qidingiz.
Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.