Apache Spark es un motor de procesamiento distribuido que ejecuta trabajos de datos en memoria y ofrece APIs para Python (PySpark), SQL, Scala y Java. En este tutorial instalarás Spark 4.2 en Ubuntu 24.04, lo configurarás como clúster standalone de un nodo (un master y un worker gestionados por systemd) y enviarás trabajos con spark-submit y PySpark. El mismo esquema sirve después para añadir más workers.
Requisitos previos
Para seguir esta guía necesitas:
- Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, con al menos 2 vCPU y 4 GB de RAM.
- Un usuario no root con privilegios
sudo. - Acceso SSH desde tu equipo, que usarás también para abrir un túnel hacia las interfaces web de Spark.
Spark 4.2 funciona con Java 17, 21 o 25 y con Python 3.10 o superior. Ubuntu 24.04 trae OpenJDK 21 y Python 3.12, así que ambos requisitos se cubren con paquetes del sistema.
Paso 1: Instalar Java
Spark se ejecuta sobre la JVM, por lo que necesitas un runtime de Java. Instala OpenJDK 21 sin componentes gráficos:
sudo apt update
sudo apt install -y openjdk-21-jre-headless
Comprueba la versión instalada:
java -version
openjdk version "21.0.8" 2025-07-15
OpenJDK Runtime Environment (build 21.0.8+9-Ubuntu-0ubuntu124.04.1)
OpenJDK 64-Bit Server VM (build 21.0.8+9-Ubuntu-0ubuntu124.04.1, mixed mode, sharing)
Python 3 ya viene instalado en Ubuntu 24.04 y es el intérprete que usará PySpark. Puedes confirmarlo con python3 --version.
Paso 2: Descargar y verificar Spark
Spark se distribuye como un archivo precompilado. Descarga la versión 4.2.0 empaquetada para Hadoop 3 desde el CDN de Apache:
cd /tmp
wget https://dlcdn.apache.org/spark/spark-4.2.0/spark-4.2.0-bin-hadoop3.tgz
wget https://downloads.apache.org/spark/spark-4.2.0/spark-4.2.0-bin-hadoop3.tgz.sha512
Notael CDN solo conserva las versiones más recientes. Si la descarga devuelve un error 404, consulta la versión actual en https://spark.apache.org/downloads.html o descarga la misma versión desde
https://archive.apache.org/dist/spark/.
Comprueba que el archivo no está corrupto comparándolo con la suma SHA-512 publicada:
sha512sum -c spark-4.2.0-bin-hadoop3.tgz.sha512
spark-4.2.0-bin-hadoop3.tgz: OK
Paso 3: Instalar Spark en /opt/spark
Crea un usuario de sistema sin privilegios que ejecutará los procesos del clúster:
sudo useradd --system --home-dir /opt/spark --shell /usr/sbin/nologin spark
Extrae el archivo en /opt y crea un enlace simbólico /opt/spark. Así, al actualizar a otra versión solo tendrás que cambiar el enlace:
sudo tar -xzf spark-4.2.0-bin-hadoop3.tgz -C /opt
sudo ln -s /opt/spark-4.2.0-bin-hadoop3 /opt/spark
sudo mkdir -p /opt/spark/logs /opt/spark/work
sudo chown -R spark:spark /opt/spark-4.2.0-bin-hadoop3
Añade los binarios de Spark al PATH de todos los usuarios con un archivo en /etc/profile.d:
sudo nano /etc/profile.d/spark.sh
export SPARK_HOME=/opt/spark
export PATH="$PATH:$SPARK_HOME/bin"
Carga el archivo en la sesión actual y comprueba que spark-submit responde:
source /etc/profile.d/spark.sh
spark-submit --version
Welcome to
____ __
/ __/__ ___ _____/ /__
_\ \/ _ \/ _ `/ __/ '_/
/___/ .__/\_,_/_/ /_/\_\ version 4.2.0
/_/
Using Scala version 2.13.16, OpenJDK 64-Bit Server VM, 21.0.8
Paso 4: Configurar el clúster standalone
El modo standalone es el gestor de clúster que incluye Spark: un proceso master reparte los recursos y uno o varios workers ejecutan los executors. No necesita Hadoop ni YARN.
Spark lee sus variables de entorno de conf/spark-env.sh. Crea el archivo:
sudo nano /opt/spark/conf/spark-env.sh
# Direccion en la que escuchan el master, el worker y sus interfaces web
SPARK_LOCAL_IP=127.0.0.1
SPARK_MASTER_HOST=127.0.0.1
# Recursos que este worker ofrece a los executors
SPARK_WORKER_CORES=2
SPARK_WORKER_MEMORY=2g
SPARK_LOG_DIR=/opt/spark/logs
SPARK_WORKER_DIR=/opt/spark/work
Con SPARK_LOCAL_IP=127.0.0.1 todos los puertos (7077 del master, 8080 y 8081 de las interfaces web, 4040 de cada aplicación) quedan ligados a localhost. Las interfaces web de Spark no tienen autenticación por defecto, así que no deben quedar expuestas a Internet. Ajusta SPARK_WORKER_CORES y SPARK_WORKER_MEMORY a tu servidor, dejando al menos 1 GB para el sistema operativo.
Define ahora los valores por defecto de los trabajos en spark-defaults.conf, para no tener que indicar el master en cada spark-submit:
sudo nano /opt/spark/conf/spark-defaults.conf
spark.master spark://127.0.0.1:7077
spark.driver.memory 1g
spark.executor.memory 1g
Ajusta el propietario de los archivos nuevos:
sudo chown spark:spark /opt/spark/conf/spark-env.sh /opt/spark/conf/spark-defaults.conf
Paso 5: Crear servicios systemd para el master y el worker
Los scripts start-master.sh y start-worker.sh lanzan los procesos en segundo plano por defecto. Con la variable SPARK_NO_DAEMONIZE se ejecutan en primer plano, que es lo que systemd necesita para supervisarlos y enviar su salida al journal.
Crea la unidad del master:
sudo nano /etc/systemd/system/spark-master.service
[Unit]
Description=Apache Spark master
After=network-online.target
Wants=network-online.target
[Service]
User=spark
Group=spark
Environment=SPARK_NO_DAEMONIZE=true
ExecStart=/opt/spark/sbin/start-master.sh
Restart=on-failure
[Install]
WantedBy=multi-user.target
Crea la unidad del worker, que se registra en el master local:
sudo nano /etc/systemd/system/spark-worker.service
[Unit]
Description=Apache Spark worker
After=spark-master.service
Requires=spark-master.service
[Service]
User=spark
Group=spark
Environment=SPARK_NO_DAEMONIZE=true
ExecStart=/opt/spark/sbin/start-worker.sh spark://127.0.0.1:7077
Restart=on-failure
[Install]
WantedBy=multi-user.target
Recarga systemd y arranca los dos servicios:
sudo systemctl daemon-reload
sudo systemctl enable --now spark-master spark-worker
Comprueba que el worker se ha registrado en el master:
sudo journalctl -u spark-master --no-pager | grep -i "registering worker"
... INFO Master: Registering worker 127.0.0.1:39417 with 2 cores, 2.0 GiB RAM
También puedes ver los puertos en escucha con sudo ss -tlnp | grep java: deberían aparecer 7077, 8080 y 8081 en 127.0.0.1.
Paso 6: Acceder a la interfaz web del master
Como la interfaz solo escucha en localhost, ábrela a través de un túnel SSH. Ejecuta este comando en tu equipo local, sustituyendo your_user y your_server_ip:
ssh -L 8080:127.0.0.1:8080 your_user@your_server_ip
Con la sesión abierta, visita http://localhost:8080 en el navegador. Verás el estado del master como ALIVE, un worker activo con 2 cores y 2 GiB de memoria, y la lista de aplicaciones en ejecución y completadas.
Paso 7: Ejecutar un trabajo de ejemplo
La distribución incluye ejemplos listos para usar. Envía al clúster el cálculo de pi escrito en Python, repartido en 10 particiones:
spark-submit /opt/spark/examples/src/main/python/pi.py 10 2>/dev/null
Pi is roughly 3.141120
Se ha redirigido stderr a /dev/null solo para ocultar los logs de Spark. Si recargas la interfaz web, la aplicación PythonPi aparecerá en Completed Applications, lo que confirma que el trabajo se ejecutó en el worker y no en modo local.
Paso 8: Escribir tu propio trabajo PySpark
Crea un script que cuente las palabras más frecuentes del README.md incluido con Spark, usando la API de DataFrames:
nano ~/conteo_palabras.py
from pyspark.sql import SparkSession
from pyspark.sql.functions import col, explode, lower, split
spark = SparkSession.builder.appName("ConteoPalabras").getOrCreate()
texto = spark.read.text("/opt/spark/README.md")
palabras = (
texto.select(explode(split(lower(col("value")), r"\W+")).alias("palabra"))
.filter(col("palabra") != "")
)
conteo = palabras.groupBy("palabra").count().orderBy(col("count").desc())
conteo.show(5)
spark.stop()
No hace falta indicar el master en el código: spark-submit lo toma de spark-defaults.conf. Ejecuta el script:
spark-submit ~/conteo_palabras.py 2>/dev/null
+-------+-----+
|palabra|count|
+-------+-----+
| the| 25|
| spark| 18|
| to| 17|
| for| 12|
| and| 10|
+-------+-----+
only showing top 5 rows
Las cifras exactas dependen de la versión del README. Para trabajar de forma interactiva, ejecuta pyspark: abre una consola de Python con la variable spark ya creada y conectada al clúster.
Notaen un clúster con varios nodos, los ficheros que lees con
spark.readdeben existir en la misma ruta en todos los workers o estar en un almacenamiento compartido (S3, HDFS, NFS). En un solo nodo no hay diferencia.
Añadir más workers
Para crecer a varios servidores, repite los pasos 1 a 3 en cada nodo nuevo y usa la red privada entre ellos:
- En el master, cambia
SPARK_LOCAL_IPySPARK_MASTER_HOSTa su IP privada y actualizaspark.masterenspark-defaults.conf. - En cada worker, pon su propia IP privada en
SPARK_LOCAL_IPy apunta elExecStartdespark-worker.serviceaspark://ip_privada_master:7077. - Permite el tráfico entre nodos solo desde la subred privada, por ejemplo
sudo ufw allow from 10.0.0.0/24. Los executors abren puertos dinámicos hacia el driver, por lo que filtrar únicamente el 7077 no es suficiente.
Solución de problemas
El worker no aparece en el master. Revisa sudo journalctl -u spark-worker -n 50. Si ves errores de conexión a 7077, comprueba que la URL del ExecStart coincide exactamente con SPARK_MASTER_HOST: el master rechaza conexiones dirigidas a un nombre distinto del que anuncia.
El trabajo se queda en espera con "Initial job has not accepted any resources". El worker no tiene memoria o cores suficientes para lo que pide la aplicación. Reduce spark.executor.memory o aumenta SPARK_WORKER_MEMORY y reinicia el worker con sudo systemctl restart spark-worker.
spark-submit: command not found. El PATH se carga al iniciar sesión. Cierra y abre la sesión SSH, o ejecuta source /etc/profile.d/spark.sh.
java.lang.OutOfMemoryError en el driver. Evita collect() sobre conjuntos grandes y sube spark.driver.memory en spark-defaults.conf.
Conclusión
Tienes Apache Spark 4.2 funcionando en Ubuntu 24.04 como clúster standalone, con el master y el worker supervisados por systemd, las interfaces web protegidas tras un túnel SSH y trabajos PySpark ejecutándose en el worker. Como siguientes pasos puedes activar el servidor de historial (spark.eventLog.enabled y start-history-server.sh) para conservar la interfaz de las aplicaciones terminadas, añadir workers en otros nodos por la red privada o leer datos en Parquet desde un almacenamiento S3 compatible.
