A swarm needs Docker 514 hosts that reach each other on TCP 2377, TCP and UDP 7946 and UDP 4789 (Overlay Networks). Here three Docker-in-Docker containers (docker:dind, a full Engine in a privileged container) on a private network play the hosts, leaving the host's own daemon out of it. They pull from the registry of Self-Hosted Registry over plain HTTP, and the routing mesh (Routing Mesh) needs the host's IPVS kernel modules:
sudo modprobe -a ip_vs ip_vs_rr xt_ipvs
docker network create l3-swarm-net >/dev/null
docker network connect l3-swarm-net l3-registry
for i in 1 2 3; do
docker run -d --privileged --name l3-swarm-$i --hostname swarm-$i --network l3-swarm-net \
-v "$PWD:/booknest:ro" docker:29.8.1-dind --insecure-registry l3-registry:5000 >/dev/null
done
for i in booknest-api booknest-web; do docker tag l3-$i:latest localhost:33500/$i:1.3; done
docker tag postgres:18 localhost:33500/postgres:18
docker pull -q traefik/whoami:v1.12.0 >/dev/null
docker tag traefik/whoami:v1.12.0 localhost:33500/whoami:1.12
for i in booknest-api:1.3 booknest-web:1.3 postgres:18 whoami:1.12; do
docker push -q localhost:33500/$i >/dev/null 2>&1; done
sleep 5
m1() { docker exec -i -w /booknest -e REGISTRY=l3-registry:5000 l3-swarm-1 docker "$@"; }
IP1=$(docker exec l3-swarm-1 hostname -i)
m1 swarm init --advertise-addr $IP1 | grep -E 'initialized|--token' \
| sed -E 's/(SWMTKN-1-.{8})[^ ]*/\1.../'Swarm initialized: current node (4xhthladtlt05mfw796tiskb8) is now a manager.
docker swarm join --token SWMTKN-1-17d15bvm... 172.19.0.3:2377m1 runs docker on swarm-1 in the mounted BookNest directory; sed shortens the secret join token. swarm init made swarm-1 the leader, created the cluster's certificate authority (node certificates expire and rotate every three months), and printed the command a worker runs to join. --advertise-addr is the address other nodes use to reach this one; set it whenever a host has several interfaces, and add --listen-addr if the manager should listen elsewhere.