Swarm con Nodi Docker

Oltre che con macchine virtuali è possibile implementare un cluster Swarm con contenitori Docker.

La soluzione è molto più leggera.

sw-swarm

Preparazione

Immagine Docker

Un'immagine che esegua Docker non è difficile da costruire, e può essere basata sul'immagine di base Alpine.

Preparazione del progetto:

mkdir -p ~/swarm/doker
cd ~/swarm/doker

La nostra immagine si chiamerà doker.

Oltre che il Docker Engine vi saranno altre utilities.

Sarà inoltre un server SSH, con il permesso di login a root, con password root. E' previsto anche un utente john con password password.

Un entrypoint configurerà le regole di firewall necessarie a Swarm.

Inoltre lo entrypoint lancerà il server SSH in modalità demone. Avremo quindi un'immagine con due processi attivi: dockerd come processo 1 e SSH come altro processo.

Certificato

C'è inoltre bisogno di predisporre l'immagine con un certificato per il pseudo-hostname myregistry.com. Questo consentirà l'uso dai nodi dello swarm di un servizio registry locale.

Generare il certificato con:

openssl req -newkey rsa:4096 -nodes -sha256\
  -keyout registry.key -x509 -days 365\
  -out registry.crt -subj "/CN=myregistry.com"\
  -addext "subjectAltName = DNS:myregistry.com"

Questo genera due files:

  • registry.crt - il certificato
  • registry.key - la chiave privata

Il Dockerfile

Prepariamo il Dockerfile:

vi Dockerfile
FROM alpine

# Add software
RUN apk add --no-cache docker git jq openssl \
    shadow ncurses

RUN apk --update add --no-cache openssh tcpdump bash curl \
  && rm -rf /var/cache/apk/*

# ‘root’ può fare login
RUN sed -i s/#PermitRootLogin.*/PermitRootLogin\ yes/ /etc/ssh/sshd_config \
  && echo "root:root" | chpasswd
# Abilitare la porta 22
RUN sed -ie 's/#Port 22/Port 22/g' /etc/ssh/sshd_config

# Abilitare le chiavi crittografiche
RUN sed -ri 's/#HostKey \/etc\/ssh\/ssh_host_key/HostKey \/etc\/ssh\/ssh_host_key/g' /etc/ssh/sshd_config
RUN sed -ir 's/#HostKey \/etc\/ssh\/ssh_host_rsa_key/HostKey \/etc\/ssh\/ssh_host_rsa_key/g' /etc/ssh/sshd_config
RUN sed -ir 's/#HostKey \/etc\/ssh\/ssh_host_dsa_key/HostKey \/etc\/ssh\/ssh_host_dsa_key/g' /etc/ssh/sshd_config
RUN sed -ir 's/#HostKey \/etc\/ssh\/ssh_host_ecdsa_key/HostKey \/etc\/ssh\/ssh_host_ecdsa_key/g' /etc/ssh/sshd_config
RUN sed -ir 's/#HostKey \/etc\/ssh\/ssh_host_ed25519_key/HostKey \/etc\/ssh\/ssh_host_ed25519_key/g' /etc/ssh/sshd_config

# Generare le chiavi crittografiche
RUN /usr/bin/ssh-keygen -A
RUN ssh-keygen -t rsa -b 4096 -f  /etc/ssh/ssh_host_key

# Generazione dell’utente ‘john’ con password ‘password’
RUN adduser -D john && echo "john:password" | chpasswd

# Porta da esporre al port mapping
EXPOSE 22

# Certificati per il registry
COPY registry.crt /root
COPY registry.key /root
RUN mkdir -p /etc/docker/certs.d/myregistry.com:5000
COPY registry.crt /etc/docker/certs.d/myregistry.com:5000/ca.crt

COPY entrypoint.sh /
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
CMD ["dockerd"]

Lo entrypoint è:

vi entrypoint.sh
#! /bin/sh

# Porte necessarie a Swarm
iptables -A INPUT -p tcp --dport 2377 -j ACCEPT
iptables -A INPUT -p tcp --dport 7946 -j ACCEPT
iptables -A INPUT -p udp --dport 7946 -j ACCEPT
iptables -A INPUT -p udp --dport 4789 -j ACCEPT
iptables -A INPUT -p esp -j ACCEPT

# Lancio del server SSH
/usr/sbin/sshd

# Aggiungere la risoluzione del registry
echo "192.168.77.2 myregistry.com" >> /etc/hosts

exec "$@"

Si presume che la rete su cui girerà lo swarm sia la 192.168.77.0/24.

L'immagine doker viene costruita col comando:

docker build -t doker .

Procedure shell per il cluster

Posizionamento nella directory di progetto:

cd ~/swarm

Procedura di Setup

Procedura di setup del cluster Swarm:

vi startcluster.sh
# Crea la rete su cui porre i nodi dello swarm
if docker network ls | grep swarmnet; then
  :
else
  docker network create swarmnet --subnet 192.168.77.0/24
fi

# Ogni nodo ha uno Host Mapping per preservare le immagini precedentemente scaricate
# un secondo host mapping per comunicare col registry su host
# terzo host mapping per ricevere files da host
# e un quarto per il mapping di dati persistenti
echo "Creating manager"
docker run -d --privileged --name manager --hostname manager --net swarmnet \
  -v $PWD/manager:/var/lib/docker \
  -v ~/registry:/registry \
  -v ~/manager-tmp:/tmp \
  -v ~/data:/data \
  doker
echo "Initialising manager"
docker exec -ti manager docker swarm init

# Il token di join
SWARM_TOKEN=$(docker exec -ti manager docker swarm join-token -q worker)
# I messaggi di Swarm sono in formato MSDOS
# E' necessario togliere il Carriage Return
SWARM_TOKEN=$(echo $SWARM_TOKEN | tr -d "\r")
echo Swarm token is $SWARM_TOKEN

# IP del master
SWARM_MASTER_IP=$(docker exec -ti manager docker info \
  | grep -w "Node Address" | awk '{print $3}')
SWARM_MASTER_IP=$(echo $SWARM_MASTER_IP |tr -d "\r")
echo Swarm master IP is $SWARM_MASTER_IP

# Numero di workers
NUM_WORKERS=3
echo There will be $NUM_WORKERS workers

# Generare i workers e aggiungerli allo swarm
for i in $(seq "$NUM_WORKERS"); do
  echo "Starting worker-${i}"
  docker run -d --privileged --name worker-${i} --hostname worker-${i} \
    --net swarmnet \
    -v $PWD/worker-${i}:/var/lib/docker \
    -v ~/data:/data \
    doker
  echo "Joining worker-${i}"
#  sleep 8
  docker exec -ti worker-${i} docker swarm join \
    --token $SWARM_TOKEN $SWARM_MASTER_IP:2377
done

docker exec -ti manager docker node ls

Il nodo master si chiama manager. I nodi worker si chiamano worker-1, worker-2, ecc.

La procedura viene resa eseguibile:

chmod +x startcluster.sh

Procedura di Teardown

Per chiudere in maniera adeguata lo Swarm e rimuoverne i nodi.

vi stopcluster.sh
# Rimozione dei nodi worker
echo "Removing worker nodes"
for i in $(docker ps -a | grep worker | awk '{n=NF;print $n}'); do
  echo "Node $i leaves swarm"
  docker exec -ti $i docker swarm leave -f
done

echo "Nodes are removed"
docker rm -f $(docker ps -a | grep worker | awk '{n=NF;print $n}')

# Rimozione del manager
echo "Master leaves swarm"
docker exec -ti manager docker swarm leave -f

echo "Removing manager"
docker rm -f manager

echo "Swarm cluster deleted"

Renderla eseguibile:

chmod +x stopcluster.sh

Lancio di Swarm

Semplicemente, nella directory ~/swarm, è l'esecuzione di:

./startcluster.sh

Con risultato simile a:

Creating manager
6beab45536ff982b109ef279079cb5a3029f242838620f9debcfe3fcd42b029b
Initialising manager
Swarm initialized: current node (up1038772l2darfkf4i9yot95) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-4jmp9d1eng04ks5go2h5owgo44zncb2uurapk1fr6fctzzpq40-4yvvbnndi57kzgpqcjf13iqzk 172.17.0.2:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

Swarm token is SWMTKN-1-4jmp9d1eng04ks5go2h5owgo44zncb2uurapk1fr6fctzzpq40-4yvvbnndi57kzgpqcjf13iqzk
Swarm master IP is 172.17.0.2
There will be 3 workers
Starting worker-1
168918df8ec8e043f5572a510f69d3f826f64dcb1f53bf1b4385051e38fc5e38
Joining worker-1
This node joined a swarm as a worker.
Starting worker-2
064efbacc8b69c565639093eed23edb2e3e41091ed6b889890f0e28839619066
Joining worker-2
This node joined a swarm as a worker.
Starting worker-3
913731b95277e689702a64555b06611f0a021a1a1a2dd5d4054eea1f78b370db
Joining worker-3
This node joined a swarm as a worker.
ID                            HOSTNAME   STATUS    AVAILABILITY   MANAGER STATUS   ENGINE VERSION
up1038772l2darfkf4i9yot95 *   manager    Ready     Active         Leader           25.0.5
3ju2p5efck7qcgze14zfp3jmw     worker-1   Ready     Active                          25.0.5
zbdg3ciulaeyt8he28v2uu8v9     worker-2   Ready     Active                          25.0.5
p9e5hpqcijlrb9048lplhg4gy     worker-3   Ready     Active                          25.0.5

Nota

Nella configurazione corrente tutti i comandi devono essere eseguiti sul container della macchina ospite che si chiama manager.

Conviene creare un alias per risparmiare digitazione:

alias dk='docker exec -ti manager'

Con tale alias la lista dei nodi si ottiene con:

dk docker node ls