In più casi dovremo eseguire uno script su tutte i nodi, per farlo possiamo utilizzare il seguente script runsetup.sh
:
#!/bin/bash
# Our custom function
file=$1
cust_func(){
echo "I am ${url}"
scp $file root@$url:/root/$file
ssh root@$url chmod +x /root/$file
NUMBER=$(echo $url | tr -dc '0-9')
ssh root@$url /root/$file $NUMBER
}
while IFS= read -r url
do
cust_func "$url $file" &
done < list.txt
wait
echo "All commands have been run."
seguito dal nome script.sh
che vogliamo eseguire su tutte le macchine, e.g.,
./runsetup.sh script.sh
In alternativa allo script qui sopra, possiamo utilizzare il software Ansible. Dentro la home del nodo zero, troviamo il file inventory.ini
con dentro i nomi dei nodi, e.g.,
[cluster_nodes]
steffe1 ansible_host=steffe1 ansible_user=root
steffe2 ansible_host=steffe2 ansible_user=root
steffe3 ansible_host=steffe3 ansible_user=root
steffe4 ansible_host=steffe4 ansible_user=root
steffe5 ansible_host=steffe5 ansible_user=root
steffe6 ansible_host=steffe6 ansible_user=root
steffe7 ansible_host=steffe7 ansible_user=root
steffe8 ansible_host=steffe8 ansible_user=root
steffe9 ansible_host=steffe9 ansible_user=root
steffe10 ansible_host=steffe10 ansible_user=root
steffe11 ansible_host=steffe11 ansible_user=root
steffe12 ansible_host=steffe12 ansible_user=root
steffe13 ansible_host=steffe13 ansible_user=root
steffe14 ansible_host=steffe14 ansible_user=root
steffe15 ansible_host=steffe15 ansible_user=root
steffe16 ansible_host=steffe16 ansible_user=root
steffe17 ansible_host=steffe17 ansible_user=root
steffe18 ansible_host=steffe18 ansible_user=root
steffe19 ansible_host=steffe19 ansible_user=root
steffe20 ansible_host=steffe20 ansible_user=root
Ed il file install_packages.yml
con dentro il seguente contenuto:
---
- hosts: cluster_nodes
become: yes # Run tasks with sudo
tasks:
- name: Install the package
apt:
name:
- exapmle-package-1
- other-package-2
# ...add more packages here
state: present
Dove nome-pacchetto
è il nome del pacchetto che vogliamo installare. Infine per lanciare l’installazione del pacchetto su tutti i nodi, eseguiamo il comando:
ansible-playbook -i inventory.ini install_packages.yml
Script runsetup.sh
utilizzato per installare le macchine:
#!/bin/bash
mkdir /scratch
chown -R rock:rock /home/rock
# Don't ask for anything
export DEBIAN_FRONTEND=noninteractive
# Cambia il fuso orario
timedatectl set-timezone Europe/Rome
# Fixa la chiave pubblica delle repository (da https://forum.radxa.com/t/gpg-error-with-ubuntu-server-20-04/13392)
wget -O - apt.radxa.com/focal-stable/public.key | sudo apt-key add -
# Repository aggiuntive
add-apt-repository ppa:gluster/glusterfs-7 -y
# Update & Upgrade
apt update -y && apt upgrade -y
# Install required packages
apt -y install build-essential gcc openmpi-bin openmpi-common libopenmpi-dev glusterfs-server slurm python3-pip valgrind tree git curl man-db mc parallel neovim unrar atool
e che può essere eseguito come:
./runsetup.sh script.sh
Possiamo personalizzare il messaggio che appare al login sul nodo zero creando il seguente script /etc/update-motd.d/05-info
:
#! /usr/bin/env bash
export TERM=xterm-256color
# Basic info
HOSTNAME=$(uname -n)
ROOT=$(df -Ph | grep mmcblk0p5 | awk '{print $4}' | tr -d '\n')
IFS=. read -r s _ < /proc/uptime; d=$((s / 60 / 60 / 24)); h=$((s / 60 / 60 % 24)); m=$((s / 60 % 60)); [ "$d" = 0 ] || UPTIME="${UPTIME}${d}d "; [ "$h" = 0 ] || UPTIME="${UPTIME}${h}h "; [ "$m" = 0 ] || UPTIME="${UPTIME}${m}m "; UPTIME="${UPTIME:-0m}"
KERNEL=$(uname -r)
RAID=$(lsblk -b -n -d | awk '$NF!~/sd[a-z]$/ && $NF!~/md[0-9]+$/ && !/loop/ {sum+=$4} END {printf "%.0f", sum/2/1024/1024/1024}')
TEMP_BIG="$(paste <(cat /sys/class/thermal/thermal_zone*/type) <(cat /sys/class/thermal/thermal_zone*/temp) | column -s $'\t' -t | sed 's/\(.\)..$/.\1°C'/ | head -1| cut -d' ' -f3 )"
TEMP_LITTLE="$(paste <(cat /sys/class/thermal/thermal_zone*/type) <(cat /sys/class/thermal/thermal_zone*/temp) | column -s $'\t' -t | sed 's/\(.\)..$/.\1°C/'| tail -1| cut -d' ' -f3 )"
# System load
MEMORY1=`free -t -m | grep Total | awk '{print $3" MB";}'`
MEMORY2=`free -t -m | grep "Mem" | awk '{print $2" MB";}'`
LOAD1=`cat /proc/loadavg | awk {'print $1'}`
LOAD5=`cat /proc/loadavg | awk {'print $2'}`
LOAD15=`cat /proc/loadavg | awk {'print $3'}`
echo "$(tput setaf 1)===================================================
_______________________ _______ _______ _______ _
( ____ \__ __( ____ ( ____ ( ____ ( ____ ( )
| ( \/ ) ( | ( \/ ( \/ ( \/ ( \//
| (_____ | | | (__ | (__ | (__ | (__
(_____ ) | | | __) | __) | __) | __)
) | | | | ( | ( | ( | (
/\____) | | | | (____/\ ) | ) | (____/
\_______) )_( (_______// |/ (_______/
$(tput setaf 2)
===================================================
- Hostname............: $HOSTNAME
- Disk Space..........: $ROOT remaining
- RAID Space..........: $RAID GB remaining
$(tput setaf 4)===================================================
- CPU usage...........: $LOAD1, $LOAD5, $LOAD15 (1, 5, 15 min)
- Memory used.........: $MEMORY1 / $MEMORY2
$(tput setaf 5)===================================================
- Temperature big.....: $TEMP_BIG
- Temperature little..: $TEMP_LITTLE
$(tput setaf 3)===================================================
- Kernel..............: $KERNEL
- Uptime..............: $UPTIME
$(tput sgr0)==================================================="
dnsmasq
e rete internadnsmasq
è un software libero che fornisce funzionalità di memorizzazione nella cache DNS (Domain Name System), un server DHCP (Dynamic Host Configuration Protocol), router advertisement e funzionalità di avvio di rete, destinato a reti di computer di piccole dimensioni. Nel nostro caso è utilizzato come server DHCP per assegnare gli indirizzi ai nodi del cluster sulla rete interna (NAT) e offrire un servizio di DNS (nodi raggiungibili tramite il loro nome e non tramite il loro indirizzo IP).
Per lanciare questo servizio è necessario liberare la porta 53 da eventuali servizi che la occupino già, su Ubuntu questo vuol dire disabilitare systemd-resolved
.
sudo systemctl disable --now systemd-resolved
sudo apt install dnsmasq
impostare nel suo file di configurazione la funzione di DHCP de-commentando la riga dhcp-range
nel file /etc/dnsmasq.conf
, e finalmente lanciare il servizio:
sudo systemctl enable --now dnsmasq
Per avere il funzionamento del nostro guarded Beowulf, abbiamo bisogno che il nodo 0, nodo di accesso, sia equipaggiato di due schede di rete. Una sarà connessa verso l’esterno, rendendo quindi il nodo un frontend per gli utenti, l’altra verso tutti i nodi del cluster. Dal punto di vista operativo, questo vuol dire che un pacchetto in arrivo dall’esterno dovrà essere tradotto di indirizzo per raggiugere i nodi interni della rete. Nell’ambito delle reti questa funzione è svolta dal network address translation (NAT), ovvero il meccanismo che permette proprio di modificare l’indirizzo IP dei pacchetti in transito attraverso un genrico apparato di rete - il nostro nodo 0 - all’interno di una comunicazione in corso tra un utente e gli altri nodi del cluster.
Su Ubuntu questo può essere fatto tramite il programma iptables
:
sudo apt install iptables
echo "1" | sudo tee /proc/sys/net/ipv4/ip_forward
cd /etc/sysctl.d/
echo "net.ipv4.ip_forward = 1" | sudo tee /etc/sysctl.d/10-ip-forwarding.conf
In /etc/network/interfaces
, per configurare permanentemente l’IP statico della macchina all’interno della LAN, l’IP pubblico e per creare la NAT del cluster:
auto eth0
iface eth0 inet static
address 192.168.0.2
netmask 255.255.255.0
auto enxd03745888a34
iface enxd03745888a34 inet static
address 131.114.10.121
netmask 255.255.255.0
gateway 131.114.10.1
post-up /usr/sbin/iptables -t nat -A POSTROUTING -o enxd03745888a34 -s 192.168.0.0/24 -j MASQUERADE
Infine:
sudo systemctl restart dnsmasq
sudo reboot
Dobbiamo inoltre comunicare il DNS a tutti i nodi in modo che possano risolvere le connessioni tra loro, ad esempio creando lo script fixdns.sh
:
sudo echo "nameserver 192.168.0.2" >> /etc/resolvconf/resolv.conf.d/head
sudo ln -sf /run/resolvconf/resolv.conf /etc/resolv.conf
sudo resolvconf -u
ed eseguendo
./runsetup.sh fixdns.sh
Creiamo la cartella che conterrà le home condivise in tutti i nodi e attiviamo il servizio GlusterFS:
sudo mkdir /data${i}
systemctl enable --now glusterd
Per installare GlusterFS dobbiamo per prima cosa permettere le connessioni dagli altri nodi attraverso il Firewall:
for i in {1..20}; do sudo iptables -I INPUT -p all -s steffe$i -j ACCEPT; done;
Ora dobbiamo far riconoscere i nodi tra di loro
sudo gluster peer probe steffe0
for i in {1..20}; do sudo gluster peer probe steffe${i}; done;
e creare il volume condiviso:
sudo gluster volume create data replica 3 transport tcp steffe0:/data0 steffe1:/data1 steffe2:/data2 steffe3:/data3 steffe4:/data4 steffe5:/data5 steffe6:/data6 steffe7:/data7 steffe8:/data8 steffe9:/data9 steffe10:/data10 steffe11:/data11 steffe12:/data12 steffe13:/data13 steffe14:/data14 steffe15:/data15 steffe16:/data16 steffe17:/data17 steffe18:/data18 steffe19:/data19 steffe20:/data20 force
sudo gluster volume start data
sudo gluster volume info
Possiamo quindi montare i dischi facendo
mkdir -p /mnt/data
mount -t glusterfs steffe0:data /mnt/data
(da mettere poi in /etc/fstab
come steffe0:/data /mnt/data glusterfs defaults,_netdev 0 0
)
Slurm Workload Manager, precedentemente noto come Simple Linux Utility for Resource Management (SLURM), o semplicemente Slurm, è un job scheduler gratuito e open source per kernel Linux e Unix-like, utilizzato da molti dei supercomputer e cluster di computer del mondo.
Fornisce tre funzioni fondamentali:
Per prima cosa dobbiamo installare Slurm su tutti i nodi del cluster
sudo apt-get update -y
sudo apt-get install slurmd slurmctld -y
Generare un file di configurazione mediante il configuratore online e inserirne il contenuto in
/etc/slurm/slurm.conf
Al termine della configurazione avviare il servizio:
sudo systemctl enable --now slurmctld
sudo systemctl enable --now slurmd
Per controllare lo stato del raid, utilizzare
cat /proc/mdstat
Sul control node:
sudo apt install nfs-kernel-server
Configurare /etc/exports
come desiderato, ad esempio
/mnt/raid steffe1(rw,sync,no_root_squash,no_subtree_check)
...
e poi sudo systemctl restart nfs-kernel-server.service
Sui compute nodes:
mkdir -p /mnt/raid
mount -t nfs steffe0:/mnt/raid /mnt/raid
(da mettere poi in /etc/fstab
come steffe0:/mnt/raid/ /mnt/raid nfs auto,nofail,noatime,nolock,intr,tcp,actimeo=1800 0 0
)
Affinché un programma possa essere lanciato su tutti i nodi è necessario che l’utente sia disponibile su ciascuno di essi, il seguente script si occupa di replicare l’utente ovunque sia necessario:
#!/bin/bash
#
# Add a SLURM user to the Steffè cluster
# A good place for this script is in:
# /usr/local/bin/cluster-users
set -e
cluster_hosts="steffe1 steffe2 steffe3 steffe4 steffe5 steffe6 steffe7 steffe8 steffe9 steffe10 steffe11 steffe12 steffe13 steffe14 steffe15 steffe16 steffe17 steffe18 steffe19 steffe20"
function help {
echo "Usage: $0 COMMAND username"
echo ""
echo "Examples: "
echo " -) $0 --add utente"
echo " -) $0 --delete utente"
}
function add_user {
if [ "$1" == "" ]; then
echo "Specify a valid username for the new user"
exit 1
fi
if [ "$2" != "" ]; then
echo "Unsupported option specified"
exit 1
fi
sudo mkdir -p /mnt/raid/home
sudo adduser --home=/mnt/raid/home/$1 $1
# We obtain the UID of the new user, as the last line at the end
# of passwd
userid=$(grep $1 /etc/passwd | tail -n1 | cut -d ':' -f3)
for h in ${cluster_hosts}; do
echo -n "Creating the user $1 on $h ... "
ssh root@${h} useradd -u ${userid} $1
echo "done"
done
}
function del_user {
if [ "$1" == "" ]; then
echo "Specify a valid username to delete"
exit 1
fi
if [ "$2" != "" ]; then
echo "Unsupported option specified"
exit 1
fi
echo -n "This command will remove the user $1, proceed? [yn]: "
read ans
if [ "$ans" != "y" ]; then
echo "Exiting"
exit 0
fi
sudo userdel -f $1
for h in ${cluster_hosts}; do
echo -n "Deleting the user $1 on $h ... "
ssh root@${h} userdel -f $1
echo "done"
done
echo "Note: the home directory /mnt/raid/home/$1 has been preserved,"
echo " you may wish to delete that as well."
}
if [ "$1" == "--add" ]; then
add_user $2
exit 0
fi
if [ "$1" == "--delete" ]; then
del_user $2
exit 0
fi
help
/etc/fstab
)rock
]: ./runsetup.sh mountgluster.sh
. Se /mnt/data
non funziona da steffe0
, riavviare il servizio mnt-data.mount
(eventualmente anche sulle altre macchine, sta già in mountgluster.sh
).sinfo
):
for i in {1..20}; do sudo scontrol update nodename=steffe$i state=idle; done
nfs-kernel-server.service
su steffe0
(se steffe0
si accende prima degli altri non riesce a risolvere gli hostname all’avvio) e poi rimontare dappertutto con ./runsetup.sh mount-raid-nfs.sh
(TODO {anche per gluster} trovare un modo di far funzionare gli FSTAB all’avvio…)