Asosiy qismga o'tish

Spark on Kubernetes

13-bob 9 kod bloki 2226 so'z

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.

python21 qator
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.

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.

bash10 qator
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
Chiqish (qaytish kodi 0, 444 ms)
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.

bash8 qator
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
Chiqish (qaytish kodi 0, 755 ms)
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.

bash8 qator
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
Chiqish (qaytish kodi 0, 485 ms)
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.

bash21 qator
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
Chiqish (qaytish kodi 0, 12141 ms)
	 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:

  • --class yo'q → No FileSystem for scheme local. Xato fayl tizimi haqida gapiradi, aslida esa sinf nomi yetishmaydi.
  • trust.certificates yo'q → uzun Java TLS xatosi. Sabab: k3s o'z sertifikatini ishlatadi va Spark unga ishonmaydi.
  • oauthToken yo'q → Forbidden. Service account yaratilgan, lekin token berilmagan.

Uchalasi ham "Kubernetes ishlamayapti" kabi ko'rinadi. Aslida Kubernetes ishlayapti.

Nima sodir bo'ldi

bash11 qator
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
Chiqish (qaytish kodi 0, 700 ms)
=== 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.

bash4 qator
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
Chiqish (qaytish kodi 0, 315 ms)
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.

python9 qator
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"},
]))
RejimDriver qayerdaQachonAfzalligi
clusterKlaster ichida, pod sifatidaIshlab chiqarish, jadval bo'yicha ishlarNoutbukingiz o'chsa ham ish davom etadi
clientSiz turgan mashinadaTajriba, Jupyter, nosozlik qidirishNatijani 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:

bash5 qator
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}'
Chiqish (qaytish kodi 0, 412 ms)
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

  1. spark.executor.instances ni 4 ga oshiring. Hammasi ishga tushdimi? kubectl get pods bilan tekshiring va sababini tushuntiring.
  2. spark.executor.memory ni 8g qiling. Pod qaysi holatda qoldi? kubectl describe pod dagi Events nima deydi?
  3. --deploy-mode client bilan ishga tushiring. Driver pod yaratildimi? Nega?
  4. Ish bajarilayotganda kubectl delete pod <executor> bilan bitta executorni o'chiring. Driver nima qildi?
  5. edit o'rniga faqat pods ustida huquq beradigan Role yozing va u bilan ishga tushiring.

Bu bobni bepul o'qidingiz.

Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.

Telegramda yozish Narxlar