Instalación y configuración
Condiciones previas
Antes de comenzar tenemos que tener por lo menos dos maquinas con Ubuntu
9.10 instalado y conectadas en red mediante el protocolo TCP/IP, para este
caso todas las maquinas tienen que ser de la misma arquitectura para poder
usar el binario compilado en el nodo maestro en cada uno de los nodos
esclavos, caso sontrario sera necesario compilar la aplicacion segun cada
plataforma. De aca en adelante me referire a los nodos del cluster como nodo
maestro o nodo esclavo segun corresponda.
Desde el nodo maestro podrás controlar el cluster y ejecutar los
programas de administracion del cluster, en los nodos esclavos se ejecutaran
las aplicaciones distribuidas desde el nodo maestro.
Instalación de los paquetes básicos
En Ubuntu 9.10 es necesario instalar los siguientes paquetes openmpi-bin,
openmpi-common, libopenmpi1.3, libopenmpi-dev en todos los nodos del
cluster.
Esto se puede hacer como root, en Ubuntu 9.10, con:
sudo apt-get install openmpi-bin openmpi-common libopenmpi1.3
libopenmpi-dev
Para el control y administración remoto instalaremos también en cada integrante del cluster el servidor ssh.
sudo
apt-get install ssh
Acceso remoto a los nodos
Para que el maestro sea capaz de ejecutar comandos en cada uno de los nodos necesitamos que estos últimos permitan el acceso ssh sin clave para lograr esto haremos lo siguiente.Primero vamos a generar nuestra clave publica en el nodo meastro, esta sera la que nos permitirá acceder remotamente a cada nodo sin necesidad de proveer una clave por linea de comandos. Es importante que este proceso se haga en el maestro y los nodos esclavos usando el usuario erluco o el que usted halla elegido.
erluco@master:~$ ssh-keygen
Cuando se ejecute ese comando aceptar el directorio por defecto para almacenar
las claves y cuando solicita la frase de paso pulsar enter sin
ingresar nada.Tenemos ahora que copiar la llave generada a cada uno de los nodos del cluster, para ello usaremos el comando de copia remota scp.
Primero en cada nodo esclavo creamos el directorio .ssh que es donde pondremos las llaves.
erluco@nodo1:~$ mkdir
.ssh
erluco@nodo1:~$ chmod
700 .ssh
Ahora procedemos a la copia.
erluco@master:~$ scp
.ssh/id_rsa.pub erluco@192.168.0.101:
Accedemos ahora al nodo donde copiamos la llave y lo ubicamos en el archivo
correcto.
erluco@nodo1:~$ mv
id_rsa.pub .ssh/authorized_keys
Ya estamos en condiciones de probar si el nodo maestro es capaz de ejecutar comandos sin necesidad de proveer la clave de cada nodo. Para hacer esta prueba, desde el maestros y como usuario erluco ejecutamos el siguiente comando:
erluco@master:~$ ssh
erluco@192.168.0.101 hostname
y nos dara una respuesta :
nodo1
Si responde con el nombre de la maquina remota es que funciona correctamente.
Ese comando lo que hace es usar el acceso ssh para a la maquina con el hostname
(nodo1) y ejecutar en ella el comando hostname.Ahora vamos a corregir nuestros archivos hosts para lograr la identificación de los nodos del cluster mediante el nombre. Editamos en cada componente del nodo el archivo /etc/hosts
sudo nano /etc/hosts
y agregamos las siguientes lineas al contenido preexistente
192.168.0.100 master master
192.168.0.101 nodo1 nodo1
192.168.0.102 nodo2 nodo2
Servidor de archivos NFS
Necesitamos en cada nodo de nuestro cluster los programas distribuidos que vamos a ejecutar. Para ello vamos a instalar y configurar un servidor de archivos NFS en el nodo maestro y los clientes NFS en los nodos esclavos de manera que trabajemos únicamente en el maestro con nuestros programas y luego mediante la exportación del directorio y el montaje remoto desde los nodos podamos tener los mismos archivos en cada uno de los componentes, existen otras formas de hacer esto, por ejemplo rsync, pero para mi gusto esta me resulto mas cómoda y transparente.En el nodo maestro:
erluco@master:~#
sudo apt-get install nfs-kernel-server nfs-common portmap
Ahora a configurar un recurso compartido, es nuestro caso crearemos un nuevo
directorio dentro del home de erluco al que llamaremos clusterdir.
erluco@master:~$ mkdir clusterdir
Ahora nuevamente como root vamos a exportar este directorio mediante NFS para
que los nodos puedan montarlo remotamente, editamos el archivo /etc/exports
sudo nano /etc/exports
y agregamos la siguiente linea.
/home/erluco/clusterdir
192.168.0.0/24(rw,no_subtree_check,async,no_root_squash)
Luego reiniciamos el resvicio de NFS con el comando:
erluco@master:~#
/etc/init.d/nfs-kernel-server restart
Ahora vamos a instalar lo necesario en cada nodo.
erluco@nodo1:~#
sudo apt-get install nfs-common portmap
Una vez que termino la instalacion probaremos si el directorio compartido es
accesible desde el nodo:
erluco@nodo1:~#
showmount -e 192.168.0.100
y respondera
Export list for 192.168.0.100:
/home/erluco/clusterdir 192.168.0.0/24
Bien ahora vamos a montar el recurso desde linea de comando para luego agregarlo
a fstab para el montado automatico cada vez que arranque el nodo. Creamos en la
home de la cuenta erluco un directorio llamado clusterdir al igual que el que
compartimos en el nodo maestro, este sera el directorio donde montaremos el
recursos compartido mediante NFS en el maestro.
erluco@nodo1:~$ mkdir clusterdir
Ahora montamos el directorio remoto, esto lo hacemos como root:
erluco@nodo1:~#
sudo mount -t nfs 192.168.0.100:/home/erluco/clusterdir
/home/erluco/clusterdir
Podemos comprobar mediante el siguiente comando si todo salio como
corresponde:
nodo1:~# mount
y respondera
/dev/xvda2 on / type ext3 (rw,noatime,nodiratime,errors=remount-ro)
tmpfs on /lib/init/rw type tmpfs (rw,nosuid,mode=0755)
proc on /proc type proc (rw,noexec,nosuid,nodev)
sysfs on /sys type sysfs (rw,noexec,nosuid,nodev)
udev on /dev type tmpfs (rw,mode=0755)
none on /dev/pts type devpts (rw)
192.168.0.100:/home/erluco/clusterdir on /home/erluco/clusterdir type nfs
(rw,addr=192.168.0.100)
Observe que el ultimo montaje corresponde al directorio compartido por NFS.Para automatizar el proceso de montaje del recurso exportado en cada nodo esclavo del cluster modificamos el archivo /etc/fstab
sudo nano /etc/fstab
agregando la siguiente linea:
192.168.0.100:/home/erluco/clusterdir /home/erluco/clusterdir nfs rw,sync,hard,intr 0 0
Para probar en caliente si funciona correctamente procedemos primero a desmontar
el recurso con el comando:
erluco@nodo1:~#
umount /home/erluco/clusterdir/
Y luego le decimos como al sistema que monte los sistemas declarado en fstab:
erluco@nodo1:~# mount –a
Ya esta todo listo en el NFS para continuar.Entorno de desarrollo
En el nodo maestro es necesario instalar las herramientas de desarrollo o por lo menos los compiladores y librerías básicas para poder compilar nuestros programas, para ello ejecutamos el siguiente comando:
erluco@master:~# apt-get install build-essential
Configuramos MPI para decirle los nodos que componen el cluster, esto se hace
creando un archivo de configuración llamado .mpi_hostfile en el home del usuario
erluco del nodo maestrosnano .mpi_hostfile
con el siguiente contenido:
# Nodo maestro
localhost slots=2
# Nodos esclavos
nodo1 slots=2
nodo2 slots=1
Prueba del cluster
Antes que nada vamos a ver un programa de ejemplo que es el que utilizaremos para probar el cluster, a continuacion el codigo fuente en C++ de un programa que suma numeros primos.nano primos.c++
y pegamos esto
# include <cstdlib>
# include <iostream>
# include <iomanip>
# include <cmath>
# include <ctime>
# include "mpi.h"
using namespace std;
int main ( int argc, char *argv[] );
int prime_number ( int n, int id, int p );
void timestamp ( );
int main ( int argc, char *argv[] )
{
int i;
int id;
int master = 0;
int n;
int n_factor;
int n_hi;
int n_lo;
int p;
int primes;
int primes_part;
double wtime;
n_lo = 1;
n_hi = 131072;
n_factor = 2;
MPI::Init ( argc, argv );
p = MPI::COMM_WORLD.Get_size ( );
id = MPI::COMM_WORLD.Get_rank ( );
if ( id == master )
{
cout << "\n";
cout << "'Cuenta primos\n";
cout << " C++/MPI version\n";
cout << "\n";
cout << " Programa
para contar cantidad de primos para un N dado.\n";
cout << " Corriendo en " << p << " procesos\n";
cout << "\n";
cout << " N S Tiempo\n";
cout << "\n";
}
n = n_lo;
while ( n <= n_hi )
{
if ( id == master )
{
wtime = MPI::Wtime ( );
}
MPI::COMM_WORLD.Bcast ( &n, 1,
MPI::INT, master );
primes_part = prime_number ( n, id, p
);
MPI::COMM_WORLD.Reduce ( &primes_part,
&primes, 1, MPI::INT, MPI::SUM,
master );
if ( id == master )
{
wtime = MPI::Wtime ( ) -
wtime;
cout << " " << setw(8) << n
<< " " << setw(8) << primes
<< " " << setw(14) << wtime <<
"\n";
}
n = n * n_factor;
}
MPI::Finalize ( );
if ( id == master )
{
cout << "\n";
cout << "PRIME_MPI - Procesos
maestro:\n";
cout << " Finalizacion del calculo normal.\n";
}
return 0;
}
int prime_number ( int n, int id, int p )
{
int i;
int j;
int prime;
int total;
total = 0;
for ( i = 2 + id; i <= n; i = i + p )
{
prime = 1;
for ( j = 2; j < i; j++ )
{
if ( ( i % j ) == 0 )
{
prime = 0;
break;
}
}
total = total + prime;
}
return total;
}
void timestamp ( )
{
# define TIME_SIZE 40
static char time_buffer[TIME_SIZE];
const struct tm *tm;
size_t len;
time_t now;
now = time ( NULL );
tm = localtime ( &now );
len = strftime ( time_buffer, TIME_SIZE, "%d %B %Y
%I:%M:%S %p", tm );
cout << time_buffer << "\n";
return;
# undef TIME_SIZE
}
Lo compilamos con el comando:
erluco@master:~/clusterdir$ mpic++ primos.c++ -o
primos
Esto nos dara como resultado el binario ejecutable del programa de prueba,
podremos verificar que gracias a la comparticion NFS este binaro se encuentra
disponible tambien en cada uno de los nodos que hemos agregado a nuestro
cluster.Ahora lo vamos a ejecutar y verificar su funcionamiento primero solo en el nodo maestro y luego en todos los nodos del cluster.
Primero en el nodo maestro unicamente:
erluco@master:~/clusterdir$ ./primos
Ahora distribuido en todos los nodos del cluster, para ello vamos a usar la
herramienta
mpirun con el parametro
-np 5
con el que le decimos la cantidad de procesos a ejecutar.
erluco@master:~/clusterdir$
mpirun -np 5 --hostfile ../.mpi_hostfile ./primos
o
erluco@master:~/clusterdir$
mpirun -np 5./primos
y se obtendra este resultado :
Cuenta primos
C++/MPI version
Programa para contar cantidad de primos para un N
dado.
Corriendo en 3 procesos
N S Tiempo
1 0 0.33965
2 1 0.0965161
4 2 0.0619619
8 4 0.125543
16 6 0.179036
32 11 0.122198
64 18 0.152973
128 31 0.117323
256 54 0.17525
512 97 0.182207
1024 172 0.123595
2048 309 0.0812861
4096 564 0.162669
8192 1028 0.207587
16384 1900 0.494075
32768 3512 1.62275
65536 6542 3.71538
131072 12251 1.0127
PRIME_MPI - Procesos maestro:
Finalizacion del calculo normal.