PySpark, de la zero Lecția 7 / 60

Instalarea PySpark local

Instalarea PySpark cu pip, cerinta Java care incurca mereu lumea si capcana cu Hadoop winutils valabila doar pe Windows.

Poți citi patruzeci de pagini despre RDD-uri, DataFrame-uri, lineage și Catalyst optimiser, dar dacă from pyspark.sql import SparkSession nu rulează pe mașina ta, nimic din astea nu mai contează. Astăzi instalăm PySpark, pornim un SparkSession și punem pe ecran un DataFrame minuscul. La finalul lecției poți copia-lipi exemplele din fiecare lecție următoare și să le vezi funcționând.

Există trei moduri rezonabile de a porni PySpark. Le parcurgem rapid pe toate trei ca să-l alegi pe al tău, apoi rezolvăm singura ciudățenie specifică Windows care îi prinde pe toți la prima încercare: winutils.exe.

Trei căi de instalare, alege una

Calea A: instalare PySpark cu pip. O singură comandă, rulează pe Windows / macOS / Linux, îți oferă tot ce-ți trebuie ca să scrii și să rulezi scripturi PySpark. Fără spark-submit, fără daemoni master/worker, fără cluster: doar API-ul Python și un Spark local înglobat care rulează în interiorul procesului tău Python. Asta folosesc 90% dintre cei care învață și majoritatea data engineer-ilor zi de zi. Recomandată pentru cursul ăsta.

Calea B: distribuția completă Apache Spark. Descarci tarball-ul de pe site-ul Spark, îl extragi, setezi SPARK_HOME. Primești același API PySpark plus toate scripturile shell: spark-submit, spark-shell, start-master.sh, start-worker.sh. Util dacă vrei să simulezi un cluster real pe laptop sau să rulezi job-uri Spark pe JVM alături de Python. Excesiv pentru moment.

Calea C: Databricks Community Edition. Notebook-uri cloud gratuite care rulează pe un cluster Spark mic gestionat. Fără instalare, fără Java, fără winutils. Excelent dacă laptopul tău e vechi sau nu vrei să te bați cu mediul. Înscrie-te la community.cloud.databricks.com. Dezavantajul: ești blocat în UI-ul lor de notebook, nu poți rula scripturi locale, iar clusterul se oprește după perioade de inactivitate.

Pentru restul cursului voi presupune Calea A (instalare pip). Dacă mergi pe C, lipește pur și simplu codul într-o celulă de notebook și sari peste pașii de instalare.

Cerința Java

Spark e un proiect JVM. PySpark e un wrapper Python care vorbește cu un JVM în fundal printr-o punte numită Py4J. Nu poți scăpa de nevoia de Java pe mașină.

Spark 3.5.x, linia stabilă curentă în momentul scrierii, suportă Java 8, 11 și 17. Spark 4.x (preview) cere Java 17 sau mai nou. Orice altceva și fie obții o eroare la pornire, fie, mai rău, crash-uri misterioase adânc într-un job.

Verifică ce ai:

java -version

Vrei output de tipul openjdk version "17.0.10" sau "11.0.22". Dacă vezi 'java' is not recognized (Windows) sau command not found (Mac/Linux), nu ai Java instalat. Dacă vezi Java 21, retrogradează: Spark 3.5 nu suportă oficial 21 încă și vei lovi probleme ciudate pe partea Hadoop.

Cea mai curată distribuție de instalat este Eclipse Temurin (OpenJDK construit de comunitate), de la adoptium.net. Alege Temurin 17 LTS. Rulează installer-ul. Pe Windows, bifează „Set JAVA_HOME variable” și „Add to PATH” în timpul instalării: amândouă sunt dezactivate implicit, iar uitarea lor e cauza numărul 1 a erorilor JAVA_HOME is not set.

După instalare, deschide un terminal nou (variabilele de mediu nu se propagă în shell-urile existente) și rulează din nou java -version. Ar trebui să meargă.

Verifică dacă JAVA_HOME este setat:

# macOS / Linux
echo $JAVA_HOME

# Windows (PowerShell)
$env:JAVA_HOME

# Windows (cmd)
echo %JAVA_HOME%

Ar trebui să indice ceva de tipul C:\Program Files\Eclipse Adoptium\jdk-17.0.10.7-hotspot sau /Library/Java/JavaVirtualMachines/temurin-17.jdk/Contents/Home.

Python: 3.8 sau mai nou

PySpark 3.5 suportă Python de la 3.8 până la 3.12. Python 3.13 nu este încă suportat oficial, iar PyArrow (o dependență tranzitivă) nu are wheel-uri stabile pentru el. Dacă ești pe 3.13, probabil te vei lupta cu erori de instalare; retrogradează la 3.12 sau folosește pyenv/conda pentru a gestiona versiunile.

python --version
# Python 3.11.7

Folosește un virtual environment. Mereu.

python -m venv .venv

# Activeaza-l:
# macOS / Linux
source .venv/bin/activate
# Windows (PowerShell)
.venv\Scripts\Activate.ps1
# Windows (cmd)
.venv\Scripts\activate.bat

Calea A: pip install pyspark

Cu Java instalat, virtual environment-ul activat și Python 3.8 până la 3.12 la locul lui:

pip install pyspark==3.5.1

Fixarea versiunii e un obicei bun. Fără ea primești ce e cea mai nouă, care astăzi este 3.5.1, dar ar putea fi 3.5.5 sau 4.0.0 luna viitoare. Lecțiile care urmează presupun 3.5.x.

Descărcarea e în jur de 280 MB; cea mai mare parte sunt fișierele JAR Spark înglobate. Ai răbdare.

Cât suntem aici, instalează încă un lucru:

pip install pyarrow

PyArrow nu este strict necesar, dar accelerează dramatic conversiile Pandas la Spark și apelurile toPandas() pe care le vom folosi mai târziu. PySpark modern avertizează la pornire dacă lipsește.

Calea B (opțional): distribuția Spark completă

Dacă vrei spark-submit și prietenii lui, fă asta în plus față de Calea A.

  1. Mergi la spark.apache.org/downloads.
  2. Alege Spark 3.5.1, tipul de pachet „Pre-built for Apache Hadoop 3.3 and later.”
  3. Descarcă .tgz-ul. Extrage-l undeva stabil. Eu îl țin la ~/spark pe macOS și C:\spark pe Windows.
  4. Setează SPARK_HOME la acel folder și adaugă $SPARK_HOME/bin la PATH.
# macOS / Linux: adauga in ~/.zshrc sau ~/.bashrc
export SPARK_HOME=$HOME/spark/spark-3.5.1-bin-hadoop3
export PATH=$SPARK_HOME/bin:$PATH

# Windows: System Environment Variables -> New -> SPARK_HOME = C:\spark\spark-3.5.1-bin-hadoop3
# Apoi editeaza Path -> Adauga %SPARK_HOME%\bin

Verifică:

spark-submit --version

Ar trebui să vezi un logo Spark în ASCII art. Dacă îl vezi, distribuția e configurată corect. Nu o vom folosi mult în cursul ăsta, dar e acolo.

Dansul winutils, doar pe Windows

Acum partea pe care toată lumea se împiedică.

Spark folosește intern codul de filesystem al Hadoop, chiar și când nu folosești efectiv Hadoop. Pe Linux și macOS, stratul I/O al Hadoop pur și simplu apelează în OS. Pe Windows, Hadoop vrea un binar specific numit winutils.exe plus câteva DLL-uri native. Nu vin la pachet cu Hadoop sau Spark. Trebuie să le descarci separat.

Dacă sari peste asta, simptomul e unul dintre:

  • java.io.IOException: Could not locate executable null\bin\winutils.exe in the Hadoop binaries
  • UnsatisfiedLinkError: org.apache.hadoop.io.nativeio.NativeIO$Windows
  • Un avertisment la pornire că Spark ignoră permisiunile de fișiere (mai puțin catastrofal, dar urât).

Soluția:

  1. Alege o versiune Hadoop. PySpark 3.5 vine pentru Hadoop 3.3, deci ia winutils pentru Hadoop 3.3.
  2. Descarcă binarele din repo-ul menținut de comunitate de la github.com/cdarlint/winutils. Mai exact folderul hadoop-3.3.x/bin/.
  3. Pune-le undeva stabil; eu folosesc C:\hadoop\bin\. Atât winutils.exe cât și hadoop.dll ar trebui să stea în acel folder bin.
  4. Setează HADOOP_HOME=C:\hadoop și adaugă %HADOOP_HOME%\bin la PATH.
# PowerShell, persistent
[Environment]::SetEnvironmentVariable("HADOOP_HOME", "C:\hadoop", "User")
$env:Path += ";C:\hadoop\bin"

Deschide un terminal nou. Verifică:

winutils.exe ls C:\

Dacă tipărește o listare de director, ai terminat. PySpark va găsi winutils.exe prin HADOOP_HOME și se va opri din protestat.

Utilizatori Mac și Linux: săriți peste toată secțiunea asta. Calea POSIX a Hadoop merge bine pe OS-ul vostru și nu aveți nevoie de nimic din toate astea.

Verificarea de sănătate în cinci linii

Salvează asta ca hello_spark.py:

from pyspark.sql import SparkSession

spark = SparkSession.builder.appName("HelloSpark").getOrCreate()

df = spark.createDataFrame([(1, "Narcis"), (2, "Spark"), (3, "Italy")], ["id", "name"])
df.show()

spark.stop()

Rulează-l:

python hello_spark.py

Prima dată când rulezi asta, așteaptă-te la un zid de linii de log INFO și WARN. E normal: Spark e zgomotos implicit. Undeva în zgomot vei vedea:

+---+------+
| id|  name|
+---+------+
|  1|Narcis|
|  2| Spark|
|  3| Italy|
+---+------+

Dacă vezi tabelul ăla, instalarea ta e bună. Treci la lecția următoare.

Liniștirea log-urilor

Nivelul implicit de log este WARN, dar la pornire Spark tipărește o grămadă de linii INFO din Hadoop și JVM. Pentru un output mai curat:

from pyspark.sql import SparkSession

spark = (SparkSession.builder
         .appName("HelloSpark")
         .getOrCreate())

spark.sparkContext.setLogLevel("WARN")  # sau "ERROR" pentru si mai liniste

df = spark.createDataFrame([(1, "Narcis"), (2, "Spark")], ["id", "name"])
df.show()

spark.stop()

Asta setează nivelul după ce sesiunea e construită. Vom acoperi abordarea mai curată (un fișier log4j2.properties în SPARK_HOME/conf/) când ajungem la subiectele operaționale din Modulul 8.

Erorile la prima rulare și cum să le repari

Aceste patru lovesc aproximativ 80% din instalările noi. Memorează maparea simptom-soluție.

1. JAVA_HOME is not set sau 'java' is not recognized. Java nu este instalat, sau este instalat dar JAVA_HOME și PATH nu indică spre el. Reinstalează Temurin 17 cu căsuța „Set JAVA_HOME” bifată. Deschide un terminal nou după.

2. Could not locate executable null\bin\winutils.exe. Doar pe Windows. Ai sărit peste secțiunea winutils. Întoarce-te, instalează winutils, setează HADOOP_HOME, deschide un shell nou.

3. Py4JJavaError: An error occurred while calling ... Java gateway process exited before sending its port number. Spark a pornit un JVM, JVM-ul s-a prăbușit înainte să poată raporta. De obicei una dintre:

  • Versiune Java greșită (ai 21 sau 8 când Spark vrea 11/17). Schimbă.
  • Antivirus sau VPN corporativ care blochează socket-ul local. Încearcă cu VPN-ul oprit.
  • Pe Mac cu Apple Silicon, ai luat un JDK x86. Ia în schimb build-ul ARM64 / aarch64 al Temurin.

4. WARN ProcfsMetricsGetter: Exception when trying to compute pagesize. Avertisment cosmetic Linux/macOS, ignoră-l. Spark nu poate găsi getconf PAGESIZE pe unele setup-uri. Fără efect asupra job-urilor.

Dacă lovești ceva care nu e pe lista asta, copiază prima excepție din stack trace (de obicei îngropată sub cinci straturi de wrap Py4J) și caut-o. În nouăzeci la sută din cazuri cineva pe Stack Overflow a avut aceeași problemă din 2016.

Un cuvânt rapid despre IDE-uri

Cursul nu presupune un editor anume, dar pentru ce face: VS Code cu extensiile Python și Jupyter este un mediu PySpark foarte plăcut. Deschide orice folder care are .venv în el, apasă Ctrl+Shift+P -> „Python: Select Interpreter,” alege venv-ul. De atunci încolo terminalul integrat activează venv-ul automat și editorul știe unde trăiește pyspark.

Pentru lucru interactiv, pune un notebook Jupyter în același folder:

pip install jupyter
jupyter notebook

Poți rula același cod de construire SparkSession într-o celulă de notebook. Sesiunea rămâne vie între celule, deci o construiești o dată sus și apoi continui să te joci cu DataFrame-uri în celulele ulterioare. Vom folosi acest pattern de-a lungul cursului.

Dacă preferi JetBrains, PyCharm Community funcționează la fel de bine. DataSpell este IDE-ul JetBrains cu aromă de notebook și are previzualizări mai frumoase pentru DataFrame-uri.

Verificare cu un test ceva mai realist

Scriptul de cinci linii de mai sus dovedește că Spark pornește. Acesta dovedește că poate face muncă. Salvează ca verify_spark.py:

from pyspark.sql import SparkSession
from pyspark.sql.functions import col, count, avg

spark = (SparkSession.builder
         .appName("VerifySpark")
         .master("local[*]")
         .getOrCreate())
spark.sparkContext.setLogLevel("WARN")

# Genereaza 1 milion de randuri sintetic - fara date externe
df = spark.range(0, 1_000_000) \
          .selectExpr("id",
                      "id % 7 AS day_of_week",
                      "(id * 13) % 100 AS amount")

# Grupeaza, agrega, sorteaza
result = (df.groupBy("day_of_week")
            .agg(count("*").alias("rows"),
                 avg("amount").alias("avg_amount"))
            .orderBy("day_of_week"))

result.show()

print(f"Spark version: {spark.version}")
print(f"Spark UI:      {spark.sparkContext.uiWebUrl}")

spark.stop()

Rulează-l:

python verify_spark.py

Ar trebui să vezi un tabel cu 7 rânduri, cu day_of_week de la 0 la 6, aproximativ 142.857 de rânduri în fiecare găleată și o medie de aproximativ 49,5. Timp total de rulare în jur de 5 până la 10 secunde pe un laptop modern. Dacă funcționează, instalarea ta este complet în formă de producție: generatoare, agregări, sortări, tot tacâmul.

Unde suntem acum

Ai:

  • Java 11 sau 17 instalat și pe PATH.
  • Python 3.8 până la 3.12 într-un virtualenv.
  • pyspark==3.5.1 și pyarrow instalate.
  • (Doar pe Windows) winutils.exe la %HADOOP_HOME%\bin.
  • Un hello_spark.py funcțional care tipărește un DataFrame minuscul.

Asta e tot mediul de dezvoltare pentru restul cursului. Lecția următoare deschidem SparkSession.builder și ne uităm la fiecare buton de configurare important: ce face de fapt local[*], de ce spark.sql.shuffle.partitions are valoarea implicită 200 chiar și când ai 8 nuclee și cum să citești Spark UI la localhost:4040.

Caută