A port published by a service opens on every node. This routing mesh, built from the ingress overlay network and the kernel's IPVS load balancer, forwards a request that reaches any node to one of the service's tasks, anywhere in the cluster. Inside the cluster, each service name resolves to a virtual IP that IPVS balances the same way. Ask each node for a book, and check which nodes run web:
m1 service ps booknest_web -f desired-state=running --format '{{.Name}} on {{.Node}}'
for i in 1 2 3; do
ip=$(docker exec l3-swarm-$i hostname -i)
echo "swarm-$i: $(curl -s $ip:8080/api/books/3 | cut -c 1-50)"
done
m1 service inspect booknest_web --format '{{range .Endpoint.VirtualIPs}}{{.Addr}} {{end}}'booknest_web.1 on swarm-2
booknest_web.2 on swarm-1
swarm-1: {"id":3,"title":"Salt and Saffron","author":"Priya
swarm-2: {"id":3,"title":"Salt and Saffron","author":"Priya
swarm-3: {"id":3,"title":"Salt and Saffron","author":"Priya
10.0.0.11/24 10.0.1.4/24swarm-3 runs no web task, yet it answered: its IPVS forwarded the connection over the ingress network to a task on another node, whose Nginx 75 reached the API through its virtual IP. web has two virtual IPs, one on ingress for the mesh and one on the stack's own overlay network. IPVS balances round-robin per connection, which is why the ten whoami requests of Creating and Scaling a Service landed two on each replica.
The mesh hides the client's source address from the service; --publish mode=host,... bypasses it.