docker service create takes most of docker run's options plus cluster ones such as --replicas, --mode global (one task per node) and --constraint. It records a desired state, and the manager schedules tasks until reality matches. traefik/whoami, a tiny server that answers with its container's hostname, makes placement and load balancing visible:
m1 service create -q --detach=false --name whoami --replicas 2 -p 8080:80 \
l3-registry:5000/whoami:1.12 >/dev/null
m1 service ls --format '{{.Name}} {{.Replicas}} {{.Ports}}'
m1 service scale -d whoami=5 >/dev/null; sleep 8
m1 service ps whoami --format '{{.Node}}' | sort | uniq -c
for n in $(seq 10); do curl -s $IP1:8080 | grep Hostname; done | sort | uniq -c
m1 service rm whoami >/dev/nullOutput
whoami 2/2 *:8080->80/tcp
2 swarm-1
2 swarm-2
1 swarm-3
2 Hostname: 1b98fb7c087d
2 Hostname: 534cbac97891
2 Hostname: 7fcfc38fe87d
2 Hostname: 823a31867be6
2 Hostname: f4a7b95684d5The scheduler spread the tasks over all three nodes, and each node pulled the image itself, which is why a swarm needs a registry. Ten requests to swarm-1 reached all five replicas, two each (Routing Mesh explains how). Remove a task's container by hand (docker exec l3-swarm-2 docker rm -f <id>) and the manager starts a replacement within seconds, keeping the failed task in service ps history.