Cluster de Spark Standalone + HDFS + Docker + PostgreSQL

 

Cluster Configuration


La idea de este proyecto es crear un cluster de 3 dos nodos, donde usaremos lo siguiente:

-Apache Spark como motor para distribuir la carga de trabajo entre los nodos

-Apache Hadoop para utilizar el sistema de almacenamiento distribuido HDFS.

-Docker para levantar la base de datos PostgreSQL.

-DBeaver para consultas a la base de datos.


Esta guía cubre TODO lo necesario para que puedas montar un cluster utilizando tantas máquinas como quieras. Yo utilizaré tres pero ,en caso de que utilices más, simplemente deberás de replicar los mismos pasos en todas tus máquinas adicionales.


Para esta explicación se hará uso de las siguientes tres máquinas:

  • Raspberrypi5 con 8gb de RAM
  • Maquina Virtual Ubuntu con 4gb de RAM (creada con VirtualBox)
  • Maquina física Ubuntu con 12gb de RAM


El Indice del proyecto será el siguiente:

1. Habilitar comunicación entre las máquinas

1.1 Dirección IP

1.2 Conexión SSH

1.3. Conexión SSH sin contraseña

2. Instalar Java y Python

2.1 Variables de entorno

3. Instalar Spark y Hadoop

3.1 Variables de entorno

4. Configurar Spark y Hadoop en ambas máquinas

4.1 Configuración

4.2 Levantar Servicio Spark: Standalone

4.3 Levantar Servicio de Hadoop: HDFS

4.4. Pruebas con Spark-Submit

5. Creando una base de datos con Docker

5.1. Docker Compose

5.2. DBeaver

5.3. Pruebas con Base de Datos.



1. Habilitar comunicación entre las máquina

En primer lugar debemos de averiguar cuál es la dirección IP de mis máquinas.

1.1 Dirección IP

Abrimos la terminal en cualquiera de nuestras máquinas, yo usaré mi maquina virtual, y utilizamos el comando ifconfig . En mi caso estoy conectado por Wifi, por tanto busco “wlan0”, y copio la dirección IP 172.20.10.5. Realizar este paso para el resto de máquinas.

Nota: Asegúrese que ambas máquinas estén conectadas a la misma red. En su caso, puede que su dirección IP se encuentre en “eth0”. En cualquier caso deberá de encontrar la IP asignada a su máquina para poder continuar con la configuración del cluster.



1.2 Conexión SSH

En cada una de nuestras máquinas deberemos de instalar ssh-server, por tanto ejecutamos el comando:

sudo apt install openssh-server

y seguidamente revisamos el status con sytemctl status ssh ,asegurándonos que esté ejecutándose correctamente.




Ejecutamos este paso en todas las máquinas.

Accedemos al archivo hosts ejecutando sudo nano /etc/hosts

y añadimos las direcciones ip de todas nuestras máquinas y les asignamos un alias, en mi caso quedaría asi:



Probamos a realizar conexión SSH entre nuestras máquinas. Para ello deberemos de correr el comando ssh user@IP , asegurándote de sustituir “user” e “IP” por tu usuario y dirección IP de la máquina de destino.

En mi caso, como estoy en ubuntu1 (172.20.10.5) intentaré conectarme a ubuntu2 y raspberrypi1 con los siguientes comandos:

ssh samuel@172.20.10.4

ssh samuel@172.20.10.6

En caso de que nos pida confirmar la conexión escribimos “yes” y nos pedirá la contraseña de la máquina de destino. Una vez hecho, estaremos dentro de nuestra máquina de destino. Para salir escribimos exit .

Hacemos los mismos pasos en cada una de las máquinas intentando conectarnos por SSH al resto de máquinas.


1.3. Conexión SSH sin contraseña

Ahora configuraremos SSH para que nuestras máquinas puedan conectarse entre ellas sin contraseña.

Para ello nos situamos en cualquiera de nuestras máquinas, en mi caso sigo con ubuntu1, nos colocamos en el directorio “home/usuario”, en mi caso “home/samuel” y ejecutamos el comando ssh-keygen y le damos a “enter” constantemente hasta que se genere la clave, quedaría asi:




Ahora copiamos la clave al resto de máquinas. En este ejemplo copiaré mi clave pública a mis otras dos máquinas virtuales ubuntu2 (172.20.10.4) y raspberrypi1 (172.20.10.6) usando el comando:

ssh-copy-id samuel@172.20.10.4

ssh-copy-id samuel@172.20.10.6

Recuerda utiliza el “usuario” “IP” de tu máquina de destino




Acto seguido intentamos acceder para verificar que no nos pide contraseña, pero esta vez usaremos solamente el alias, para simplificar el nombrado, de la siguiente forma:

ssh ubuntu2 

ssh raspberrypi1 


 (Cualquier pregunta que nos haga para confirmar la conexión escribimos “yes”)


Como podemos observar en la imagen de arriba, hemos podido conectarnos por SSH sin contraseña desde nuestra máquina ubuntu1 a nuestra otra máquina ubuntu2 utilizando el alias que definimos en pasos previos haciendo uso del archivos “hosts”.

Repetir estos pasos para todas las máquinas asegurándonos de que todas las máquinas puedan conectarse entre sí sin contraseña y haciendo uso del alias.

Aqui está la explicación gráfica para que entendamos qué acabamos de hacer. Básicamente hemos copiado la clave pública de cada máquina, contenida en el archivo “id_rsa.pub”, dentro del archivo “authorized_keys” de las otras máquinas.






Nota: Para este ejemplo concreto donde tenemos que crear un cluster, para evitar futuros errores, también copiaremos la clave pública de cada máquina en su propio archivo “authorized_keys”, ejecutando el siguiente comando en cada una de las máquinas:

cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys


2. Instalar Java y Python

En cada una de nuestras máquinas, yo usaré en este caso ubuntu2, ejecutamos los comandos

sudo snap install curl

curl -s "[<https://get.sdkman.io>](<https://get.sdkman.io/>)" | bash

source "$HOME/.sdkman/bin/sdkman-init.sh"




Luego instalamos la versión que queremos de java con el comando:

sdk install java <Identifier>

En mi caso, quiero instalar la versión 20.0.1, por tanto mi comando quedaría así:

sdk install java 8.0.412-librca

(Recomiendo instalar esta versión para evitar problemas de incompatibilidad en el futuro)

Nota: Para ver todas las versiones de java disponibles ejecutar el siguiente comando:

sdk list java

Una vez instalado Java, procedemos a añadirlo a nuestras variables de entorno tal y como hicimos en Windows. Para ello ejecutamos el comando sudo nano ~/.bashrc y añadimos las siguientes líneas (no modificar nada) al final del archivo:

export JAVA_HOME="$HOME/.sdkman/candidates/java/current" export PATH="$JAVA_HOME/bin:$PATH"

Quedaría de la siguiente forma:

  • Control + S para guardar
  • Control + x para salir




Ejecutamos el comando source ~/.bashrc para recargar la configuración del archivo. (No esperamos ninguna salida de este comando)

Comprobamos que hemos instalado java correctamente ejecutando los comandos:

echo $JAVA_HOME 

echo $PATH 

java -version

Deberías ver algo similar a esto:




Como paso final para evitar futuros errores deberemos de agregar el path de Java al archivo “visudo”.

Para ello ejecutamos el comando sudo visudo y agregamos el path, en mi caso ":/home/samuel/.sdkman/candidates/java/current/bin en “secure_path”




Para el caso de Python deberemos de añadirlo a las variables de entorno de la siguiente forma:

Ejecutamos el comando which Python y el path que nos genere lo añadimos a ~/.bashrc como hemos hecho con anterioridad, pero en este caso deberia de queda asi (yo estoy usando Python 3.11 en todas mis máquinas):


Hemos añadido tanto "PYSPASK_PYTHON" como "PYSPARK_DRIVER_PYTHON" ambas apuntando al path de Python.

Además, es importante instalar pip para la instalación de librerias. Para ello, ejecutamos el comando:

sudo apt install python3-pip

3. Instalar Spark y Hadoop

SPARK

Para instalar Spark debemos de replicar los siguientes pasos en cada máquina, yo utilizaré como ejemplo mi máquina ubuntu2.

Nos ubicamos en home/usuario y descargamos el paquete de spark con hadoop usando el siguiente comando:

wget <https://dlcdn.apache.org/spark/spark-3.5.1/spark-3.5.1-bin-hadoop3.tgz>


Cuando la descarga finalice, ejecutamos el siguiente comando para descomprimirlo: 

tar -xzf spark-3.5.1-bin-hadoop3.tgz


Seguidamente eliminamos el paquete .tgz con el comando:

rm spark-3.5.1-bin-hadoop3.tgz


Y renombramos la carpeta spark-3.5.1-bin-hadoop3 a spark con el comando:

mv spark-3.5.1-bin-hadoop3 spark


Una vez instalado spark, procedemos a añadirlo a nuestras variables de entorno tal. Ejecutamos el comando sudo nano ~/.bashrc para añadir las siguientes lineas al final del archivo:

export SPARK_HOME=/home/usuario/spark 

export PATH=$PATH:$SPARK_HOME/bin:$SPARK_HOME/sbin

(Recuerda sustituir la palabra “usuario” por el nombre real de tu usuario)

Quedaría de la siguiente forma:



  • Control + S para guardar
  • Contorl + x para salir

Ejecutamos el comando source ~/.bashrc para recargar la configuración del archivo. (No esperamos ninguna salida de este comando)

Seguidamente accedemos al archivo “spark/conf/spark-env.sh” y añadimos el path de Java en cualquier lugar, por ejemplo al final:

export JAVA_HOME=/home/samuel/.sdkman/candidates/java/current

Comprobamos que hemos instalado spark correctamente ejecutando el comando:

spark-submit --version

La salida que esperamos es esta:




Repetimos los mismos pasos para todas las máquinas.

HADOOP

Para descargar e instalar hadoop, nos ubicamos en home/usuario y descargamos el paquete de hadoop usando el siguiente comando:

wget <https://dlcdn.apache.org/hadoop/common/hadoop-3.3.5/hadoop-3.3.5.tar.gz>


Cuando la descarga finalice, ejecutamos el siguiente comando para descomprimirlo:

  tar -xzf [hadoop-3.3.5.tar.gz](<https://dlcdn.apache.org/hadoop/common/hadoop-3.3.5/hadoop-3.3.5.tar.gz>)


Seguidamente eliminamos el paquete .tgz con el comando:

rm [hadoop-3.3.5.tar.gz](<https://dlcdn.apache.org/hadoop/common/hadoop-3.3.5/hadoop-3.3.5.tar.gz>)


Y renombramos la carpeta hadoop-3.3.5 a hadoop con el comando:

mv hadoop-3.3.5 hadoop


La añadimos a nuestras variables de entorno ejecutando:

 sudo nano ~/.bashrc 

Añadimos las siguientes lineas:


export HADOOP_HOME=/home/usuario/hadoop 

export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin

(Recuerda sustituir la palabra “usuario” por el nombre real de tu usuario)

Quedaría de la siguiente forma:




Ejecutamos el comando source ~/.bashrc para recargar la configuración del archivo. (No esperamos ninguna salida de este comando)

Comprobamos que hemos instalado hadoop correctamente ejecutando el comando:

hadoop version

Esperamos una salida parecida a esta:




4. Configurar Spark y Hadoop en ambas máquinas

En este caso, como hemos comentado al comienzo de esta guía, iniciaremos nuestro cluster en modo Standalone. Esto quiere decir, entre otras cosas, que no vamos a usar ningún gestor de clusters como YARN o Kubernetes, usaremos la interfaz web que proporciona Spark Standalone para moitorizar tanto los worker como los procesos que se ejecuten. Llegados a este punto, debemos de entender la diferencia entre Master, Driver y Worker, dependiendo de si ejecutamos los procesos en modo Cliene o en modo Cluster:

Modo Cluster

En el modo Cluster, el nodo Master selecciona un nodo Worker para alojar el driver, distribuyendo las responsabilidades de la aplicación de la siguiente manera en mi clúster de 3 máquinas:

  • 1 Master: Se encarga exclusivamente de la coordinación y administración del clúster, sin ejecutar tareas de procesamiento ni el driver.
  • 1 Driver (en nodo Worker): Este nodo aloja el driver, que construye el SparkContext, descompone las tareas para asignarlas a los Workers (en este caso, principalmente al otro Worker), y solicita recursos al Master. Este nodo debe estar adecuadamente equipado para manejar tanto las responsabilidades del driver como las tareas de procesamiento sin degradar el rendimiento.
  • 1 Worker: Este nodo se dedica exclusivamente a ejecutar los executors que realizan las tareas asignadas por el Driver.
  • Resultado: El clúster está bien equilibrado con un nodo dedicado a la gestión, uno compartiendo las funciones de driver y processing, y otro exclusivamente para tareas de processing. Este diseño es eficiente, pero es crucial asegurar que el nodo que aloja el driver tenga suficientes recursos para evitar cuellos de botella.



Modo Client

En el modo Client, el driver se ejecuta en la máquina desde la cual se lanza la aplicación. Si decides lanzar desde el nodo que también es el Master, la configuración sería:

  • 1 Master y Driver: Este nodo aloja tanto al Master como al Driver. Es vital que este nodo tenga suficientes recursos (como CPU y memoria) para manejar ambas cargas sin comprometer el rendimiento general del clúster. El riesgo aquí es crear un punto de congestión si el nodo no está correctamente dimensionado.

  • 2 Workers: Estos nodos se dedicarán exclusivamente a ejecutar tareas de Spark. Al no tener que alojar el driver, pueden usar sus recursos completos para el procesamiento, maximizando así la capacidad de procesamiento del clúster.

  • Resultado: Todos los Workers están libres para ejecutar tareas, maximizando la disponibilidad de recursos para procesamiento de Spark. Este modo es ideal para maximizar el rendimiento de procesamiento, especialmente si se puede garantizar que el nodo Master y Driver está bien provisto de recursos.























    Conclusiones:

    Como habrás podido observar, el modo cluster en este caso no es eficiente porque al tener solamente un worker no estaríamos aprovechando el procesado distribuido, necesitando como mínimo un worker adicional para conseguir esto. Incluso podríamos asignarle al Master o al Driver el rol adicional de worker si tenemos un cluster pequeño con pocas exigencias y estos poseen recursos suficientes. Pero en este caso, usaremos el modo cliente, corriendo el driver en la misma máquina donde se aloja el master, quedando mi arquitectura como se puede observar en la imagen 2.

    4.1 Configuración Spark

    Asegurate de tener la misma version de python instalada en todas las máquinas para evitar problemas de compatibilidad. Para ello ejecuta el comando python3 --version

    Luego accedemos al archivo spark-env.sh ubicado en “/home/usuario/spark/conf” y añadimos la siguiente información:

    Maquina Master:

    export HADOOP_CONF_DIR=/home/usuario/hadoop/etc/hadoop

    export SPARK_HOME=/home/usuario/spark

    export JAVA_HOME=/home/usuario/.sdkman/candidates/java/current

    export PYSPARK_PYTHON=/usr/bin/python3.11

    export PYSPARK_DRIVER_PYTHON=/usr/bin/python3.11

    export PATH=$SPARK_HOME/bin:$HADOOP_HOME/bin:$JAVA_HOME/bin:$PATH

    export SPARK_MASTER_HOST=tu_host

    export SPARK_MASTER_PORT=7077


    Maquinas Workers:

    export HADOOP_CONF_DIR=/home/usuario/hadoop/etc/hadoop

    export SPARK_HOME=/home/usuario/spark

    export JAVA_HOME=/home/usuario/.sdkman/candidates/java/current

    export PYSPARK_PYTHON=/usr/bin/python3.11

    export PYSPARK_DRIVER_PYTHON=/usr/bin/python3.11

    export PATH=$SPARK_HOME/bin:$HADOOP_HOME/bin:$JAVA_HOME/bin:$PATH

    export SPARK_WORKER_CORES=1

    export SPARK_WORKER_MEMORY=2g

    Adapta los valores de "SPARK_WORKERS" y "SPARK_WORKER_MEMORY". Para saber el número de CPU´s y de memoria disponible en tu máquina ejecuta los comandos:
     
    lscpu

    free -h





    En el caso de esta máquina, tengo 9 gigas de RAM y 4 cpus, pero como es la máquina Driver y Master, no añadiré nada de esta información, puesto que no es worker.

    Seguidamente en nuestro nodo maestro renombramos el archivo “workers”, ubicado en “/home/usuario/spark/conf” con el comando

    mv workers.template workers

    y añadimos los alias de nuestras máquinas workers, en mi caso

    ubuntu1

    raspberrypi1

    4.2 Configuración Hadoop

    Accedemos a la ruta “/home/usuario/hadoop/etc/hadoop” y en el archivo “core-site.xml”, pegamos la siguiente información (común en todas las máquinas):

    <configuration>

    <property>

    <name>fs.defaultFS</name> <value>hdfs://host:9000</value>

    </property>

    </configuration>



    Recuerda sustituir “host”, por el alias de tu maquina master, en mi caso ubuntu2.

    Luego editamos el archivo “workers”, y añadimos el alias de nuestras maquinas worker, en mi caso “ubuntu1” y “raspberrypi1” (Esta información solo es necesaria en nuestra máquina master, pero igualmente podemos añadirla a todas para evitar posibles problemas):



    Luego en al archivo “hadoop-env.sh", añadimos la misma ruta de JAVA_HOME que añadimos en el archivo ~/.bashrc, en mi caso quedaría así (común a todas las máquinas, dependiendo de donde se encuentre Java instalado en cada máquina):



    En el archivo hdfs-site.xml, deberemos de añadir la siguiente información, dependiendo de si la maquina es worker o master:

    Para tu master:

    <configuration>

    <property>

    <name>dfs.datanode.data.dir</name>

    <value>file:///home/usuario/hadoop/data/namenode</value>

    </property>

    </configuration>

    Acto seguido creamos las carpetas que hemos especificado ejecutando dentro de la carpeta de hadoop de nuestra máquina master:

    mkdir data cd data mkdir namenode

    Para tus worker:

    <configuration>

    <property>

    <name>dfs.datanode.data.dir</name>

    <value>file:///home/usuario/hadoop/data/datanode</value>

    </property>

    </configuration>

    Acto seguido creamos las carpetas que hemos especificado ejecutando dentro de la carpeta de hadoop de nuestras máquinas worker:

    mkdir data cd data mkdir datanode

    Recuerda cambiar “usuario” por tu usuario en el path del archivo “hdfs-site.xml”

    Llegados a este punto tenemos configurados los namenodes y datanodes, Por tanto el siguiente paso sería formatear el namenode. Por tanto en la maquina master (la de nuestro namenode) ejecutamos el siguiente comando:

    hdfs namenode -format

    Escribimos “Y” si nos preguntan.

    4.3 Levantar Servicio Spark: Standalone

    Ejecutamos el comando jps en todas las máquinas para revisar los servicios levantados, la salida que esperamos es simplemente “jps”

    Seguidamente en nuestra maquina master ejecutamos

    start-master.sh

    start-slaves.sh

    En nuestras máquinas volvemos a ejecutar jps y esperamos estas salidas:

    Master:



    Workers:







    Seguidamente accedemos a la siguiente url:

    direccion_ip_del_master:8080

    En mi caso:

    172.20.10.4:8080



    Este es el servidor web de Spark donde podremos ver nuestros workers, su estado, la memoria de cada uno etc. Incluso cada vez que lancemos una aplicación poderemos monitorizarla a través de esta interfaz.

    4.4 Levantar Servicio de Hadoop: HDFS

    Ejecutamos el comando jps en todas las máquinas para revisar los servicios levantados, la salida que esperamos hasta ahora es “jps” y “master” para nuestra máquina master, “jps” y “worker” para nuestras máquinas workers

    Seguidamente en nuestra maquina master ejecutamos

    start-dfs.sh

    En nuestras máquinas volvemos a ejecutar jps y esperamos estas salidas:

    Master:



    Workers:







    Seguidamente accedemos a la siguiente url:

    direccion_ip_del_master:9870

    En mi caso:

    172.20.10.4:9870





    En este servidor web de hadoop para HDFS podremos ver si nuestros datanodes se han levantado correctamente, así como, el espacio de almacenamiento que les queda (almacenamiento en disco, no memoria RAM).

    4.5. Pruebas con Spark-Submit

    Vamos a realizar nuestra primera prueba para comprobar que nuestro cluster funciona correctamente, para ello nos dirigimos a nuestra máquina master y creamos un nuevo script de python llamado, por ejemplo, “spark-test.py” con el siguiente contenido:

    from pyspark.sql import SparkSession
    
    # Crear sesión Spark
    spark = SparkSession.builder.appName("SparkTest").getOrCreate()
    
    # Crear DataFrame de ejemplo
    data = [("Alice", 1), ("Bob", 2), ("Catherine", 3)]
    df = spark.createDataFrame(data, ["Name", "Value"])
    
    # Mostrar el Dataframe resultante por pantalla
    df.show()
    
    # Parar la sesión Spark
    spark.stop()
    
    

    Como discutimos anteriormente, este script lo ejecutaremos en modo cliente usando el siguiente comando:

    spark-submit \\ --master spark://172.20.10.4:7077 \\ --deploy-mode client \\ spark-test.py

    Recuerda sustituir la dirección IP por la de tu máquina master.



    Podemos ver tanto en los logs de la terminal como en el webserver.



    Vemos que se ha ejecutado correctamente dentro del cluster ya que la aplicación en el webserver sale como finalizada y en la terminal se puede ver la salida que esperábamos del dataframe.

    Ahora haremos lo mismo pero involucrando HDFS. Para ello creamos otro script llamado, por ejemplo, “spark-test1.py”, con eln siguiente contenido:

    from pyspark.sql import SparkSession
    
    # Crear sesión Spark
    spark = SparkSession.builder.appName("SparkTest").getOrCreate()
    
    # Crear DataFrame de ejemplo
    data = [("Alice", 1), ("Bob", 2), ("Catherine", 3)]
    df = spark.createDataFrame(data, ["Name", "Value"])
    
    # Filtrar el DataFrame
    df_filtered = df.filter(df["Value"] > 1)
    
    # Especificar la ruta de salida en HDFS
    output_path = "hdfs://ubuntu2:9000/user/samuel/output"
    
    # Guardar el DataFrame filtrado en un archivo CSV en HDFS
    df_filtered.write.option("header", "true").csv(output_path)
    
    # Parar la sesión Spark
    spark.stop()
    

    Recuerda, si quieres, cambiar el path de HDFS por uno personal tuyo.

    Y ejecutamos el comando:

    spark-submit --master spark://172.20.10.4:7077 --deploy-mode client spark-test1.py

    Y comprobamos en hdfs, desde cualquier máquina, que el csv se ha guardado con éxito.

    Para ello busco en HDFS para encontrar el nombre del csv ejecutando el siguiente comando:

    hdfs dfs -ls /user/samuel/output/





    Copiamos la ruta junto con el nombre del CSV y ejecutamos:

    hdfs dfs -cat /user/samuel/output/part-00001-ad1cdd6b-39ce-4e19-aa82-71f30fd0e768-c000.csv



    Podemos observar como efectivamente nos muestra el contenido del CSV.

    Hasta aqui hemos comprobado el funcionamiento del cluster de Spark en modo cliente y de HDFS. 

5. Creando una base de datos con Docker

Nos ubicamos en nuestra maquina master, en mi caso "ubuntu2" y descargamos tanto docker como docker-compose. Para ello ejecutamos los siguientes comandos:

Primero:

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -


Segundo:

sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"


Tercero:

sudo apt-get update


Cuarto:

sudo apt-get install docker-ce


Quinto:
sudo systemctl status docker


La salida que esperamos es esta:

Para docker compose ejecutamos los siguientes comandos:

Primero:

sudo curl -L "https://github.com/docker/compose/releases/download/v2.10.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose


Segundo:

sudo chmod +x /usr/local/bin/docker-compose


Tercero:
sudo usermod -aG docker $USER


Luego de esto ejecutamos el comando docker-compose --version
para comprobar la instalación y luego ejecutamos docker ps para ver los servicios levantados con docker, donde no esperamos ninguna salida porque no tenemos actualmente nada levantado.

5.1. Docker Compose

Ejecutamos el comando sudo nano docker-compose.yml y le introducimos el siguiente contenido para levantar la base de datos:

 from pyspark.sql import SparkSession

version: '3.8'

services:

  postgres:

    image: postgres:latest

    environment:

      POSTGRES_DB: mydatabase

      POSTGRES_USER: myuser

      POSTGRES_PASSWORD: mypassword

    ports:

      - "5432:5432"

    volumes:

      - pgdata:/var/lib/postgresql/data

volumes:

  pgdata:

Seguidamente levantamos la base de datos con el comando docker-compose up -d

Para ver lo servicios que tenemos levantados ejecutamos docker ps. En caso que que querramos detener el servicio hacemos un docker-compose down.


5.2. DBeaver

Descargamos dbeaver community edition  a través del siguiente enlace:

https://dbeaver.io/download/


Nota: En mi caso como tengo mi  maquina "ubuntu1" creada con virtualbox, tengo acceso a windows paralelamente, por tanto he elegido dbeaver.


Una vez descargado, hacemos click en nueva conexión:
















Introducimos los datos que hemos definido en el docker-compose, quedando de la siguiente forma:












Recuerda cambiar el valor de "host" por el de la dirección IP de tu máquina donde hayas levantado el servicio. Hacemos click en "finalizar" y ya tendremos acceso a nuestra base de datos:
















5.3. Pruebas con Base de Datos.

Primeramente instalamos la librería psycopg2, la cual nos ayudará a interactuar con la base de datos con python. Ejecutamos, para ello, el comando:

pip install psycopg2-binary


Acto seguido, como estamos usando una base de datos Postgresql deberemos descargar el correspondiente jar y ubicarlo en la carpeta "jars" dentro de la carpeta spark. Para ello nos ubicamos en esta carpeta y ejecutamos el comando:

wget https://jdbc.postgresql.org/download/postgresql-42.4.1.jar


Luego copiamos este archivo al resto de maquinas (raspberrypi1 y ubuntu1) usando los comandos:

scp postgresql-42.4.1.jar raspberrypi1:/home/samuel/spark/jars/

scp postgresql-42.4.1.jar ubuntu1:/home/samuel/spark/jars/


Luego creamos un script de prueba para crear una tabla en la base de datos y verificar que podemos interactuar con ella:

import psycopg2

# Configuración de la conexión a la base de datos

conexion_str = "host='localhost' dbname='mydatabase' user='myuser' password='mypassword'"


# Conectar a la base de datos

conn = psycopg2.connect(conexion_str)


# Crear un cursor para ejecutar operaciones de la base de datos

cur = conn.cursor()

# Crear la tabla

cur.execute("""



CREATE TABLE IF NOT EXISTS employees (

    id SERIAL PRIMARY KEY,

    name VARCHAR(100),

    age INTEGER

);

""")


# Datos a insertar

datos = [

    ('John Doe', 28),

    ('Jane Smith', 32),

    ('Alice Johnson', 29),

    ('Mike Brown', 30)

]

# Insertar datos

cur.executemany("INSERT INTO employees (name, age) VALUES (%s, %s);", datos)


# Hacer commit de la transacción

conn.commit()

# Cerrar el cursor y la conexión

cur.close()

conn.close()

print("Tabla creada y datos insertados exitosamente.")

Seguidamente ejecutamos el script con:

python3 nombre_del_script.py


Y verificamos que la tabla ha sido creada en la base de datos:










Una vez hecho esto, solo nos queda hacer la prueba final, y es intentar extraer estos mismos datos de la tabla con spark y guardarlos en HDFS (formato CSV por ejemplo).


Para ello creamos un script y le añadimos el siguiente contenido:

from pyspark.sql import SparkSession



def main():

    # Configurar SparkSession



    spark = SparkSession.builder \

        .appName("Extract Data and Save to HDFS") \

        .getOrCreate()



    # Datos de conexión a la base de datos PostgreSQL

    jdbc_url = "jdbc:postgresql://192.168.1.52:5432/mydatabase"

    properties = {

        "user": "myuser",

        "password": "mypassword",

        "driver": "org.postgresql.Driver"

    }

    table = "employees"

    # Leer los datos de PostgreSQL

    df = spark.read.jdbc(url=jdbc_url, table=table, properties=properties)



    # Guardar los datos en HDFS en formato CSV



    df.write \

        .format("csv") \

        .option("header", True) \

        .mode("overwrite") \

        .save("hdfs://ubuntu2:9000/user/samuel/output_bbd")



    # Detener la sesión de Spark



    spark.stop()

if __name__ == "__main__":

    main()


Recuerda ajustar el valor de la dirección IP (en mi caso 192.168.1.52), el nombre de la maquina namenode (en mi caso ubuntu2)  y la ruta de almacenamiento (en mi caso /user/samuel/output_bbd)


Seguidamente ejecutamos el comando de spark-submit añadiendo la ruta de los jar de postgres:

spark-submit     --master spark://192.168.1.52:7077     --deploy-mode client     --jars /home/samuel/spark/jars/postgresql-42.4.1.jar     prueba1_bbd.py


Recuerda ajustar la IP de la maquina master y la ruta del jar.


Una vez ejecutado correctamente, comprobamos que el CSV se ha generado en la ruta especificada, en mi caso /user/samuel/output_bbd





Y visualizamos el contenido con el comando cat:






Hasta aquí hemos aprendido a configurar un cluster haciendo uso de :

  • Spark para distrubuir la carga entre los nodos.
  • HDFS para almacenar los datos.
  • Docker para levantar una base de datos PostgreSQL.
  • DBeaver para visualizar los datos en las tablas de la base de datos.

Comentarios

Entradas populares de este blog

Conectar Databricks a Azure Data Lake Storage (ADLS)