# Docker Swarm (mode) cluster

This section explains how to create a multi-host docker cluster with swarm mode using [docker-machine](https://docs.docker.com/machine) and how to deploy Træfik on it.

The cluster consists of:

- 3 servers
- 1 manager
- 2 workers
- 1 [overlay](https://docs.docker.com/engine/userguide/networking/dockernetworks/#an-overlay-network) network
(multi-host networking)

## Prerequisites

1. You will need to install [docker-machine](https://docs.docker.com/machine/)
2. You will need the latest [VirtualBox](https://www.virtualbox.org/wiki/Downloads)

## Cluster provisioning

First, let's create all the required nodes. It's a shorter version of
the [swarm tutorial](https://docs.docker.com/engine/swarm/swarm-tutorial/).

```shell
docker-machine create -d virtualbox manager
docker-machine create -d virtualbox worker1
docker-machine create -d virtualbox worker2
```

Then, let's setup the cluster, in order :

1. initialize the cluster
2. get the token for other host to join
3. on both workers, join the cluster with the token

```shell
docker-machine ssh manager "docker swarm init \
	--listen-addr $(docker-machine ip manager) \
	--advertise-addr $(docker-machine ip manager)"

export worker_token=$(docker-machine ssh manager "docker swarm \
join-token worker -q")

docker-machine ssh worker1 "docker swarm join \
	--token=${worker_token} \
	--listen-addr $(docker-machine ip worker1) \
	--advertise-addr $(docker-machine ip worker1) \
	$(docker-machine ip manager)"

docker-machine ssh worker2 "docker swarm join \
	--token=${worker_token} \
	--listen-addr $(docker-machine ip worker2) \
	--advertise-addr $(docker-machine ip worker2) \
	$(docker-machine ip manager)"
```

Let's validate the cluster is up and running.

```shell
docker-machine ssh manager docker node ls
```
```
ID                           HOSTNAME  STATUS  AVAILABILITY  MANAGER STATUS
2a770ov9vixeadep674265u1n    worker1   Ready   Active
dbi3or4q8ii8elbws70g4hkdh *  manager   Ready   Active        Leader
esbhhy6vnqv90xomjaomdgy46    worker2   Ready   Active
```

Finally, let's create a network for Træfik to use.

```shell
docker-machine ssh manager "docker network create --driver=overlay traefik-net"
```

## Deploy Træfik

Let's deploy Træfik as a docker service in our cluster.
The only requirement for Træfik to work with swarm mode is that it needs to run on a manager node — we are going to use a
[constraint](https://docs.docker.com/engine/reference/commandline/service_create/#/specify-service-constraints-constraint) for that.

```shell
docker-machine ssh manager "docker service create \
	--name traefik \
	--constraint=node.role==manager \
	--publish 80:80 --publish 8080:8080 \
	--mount	type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \
	--network traefik-net \
	traefik \
	--docker \
	--docker.swarmmode \
	--docker.domain=traefik \
	--docker.watch \
	--web"
```

Let's explain this command:

- `--publish 80:80 --publish 8080:8080`: we publish port `80` and `8080` on the cluster.
- `--constraint=node.role==manager`: we ask docker to schedule Træfik on a manager node.
- `--mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock`:
  we bind mount the docker socket where Træfik is scheduled to be able to speak to the daemon.
- `--network traefik-net`: we attach the Træfik service (and thus the underlying container) to the `traefik-net` network.
- `--docker`: enable docker backend, and `--docker.swarmmode` to enable the swarm mode on Træfik.
- `--web`: activate the webUI on port 8080

## Deploy your apps

We can now deploy our app on the cluster, here [whoami](https://github.com/emilevauge/whoami), a simple web server in Go. We start 2 services, on the `traefik-net` network.

```shell
docker-machine ssh manager "docker service create \
	--name whoami0 \
	--label traefik.port=80 \
	--network traefik-net \
	emilevauge/whoami"

docker-machine ssh manager "docker service create \
	--name whoami1 \
	--label traefik.port=80 \
	--network traefik-net \
	--label traefik.backend.loadbalancer.sticky=true \
	emilevauge/whoami"
```

Note that we set whoami1 to use sticky sessions (`--label traefik.backend.loadbalancer.sticky=true`).  We'll demonstrate that later.

**Note**: If using `docker stack deploy`, there is [a specific way that the labels must be defined in the docker-compose file](https://github.com/containous/traefik/issues/994#issuecomment-269095109).

Check that everything is scheduled and started:

```shell
docker-machine ssh manager "docker service ls"
```
```
ID            NAME     REPLICAS  IMAGE              COMMAND
ab046gpaqtln  whoami0  1/1       emilevauge/whoami
cgfg5ifzrpgm  whoami1  1/1       emilevauge/whoami
dtpl249tfghc  traefik  1/1       traefik            --docker --docker.swarmmode --docker.domain=traefik --docker.watch --web
```

## Access to your apps through Træfik

```shell
curl -H Host:whoami0.traefik http://$(docker-machine ip manager)
```
```yaml
Hostname: 8147a7746e7a
IP: 127.0.0.1
IP: ::1
IP: 10.0.9.3
IP: fe80::42:aff:fe00:903
IP: 172.18.0.3
IP: fe80::42:acff:fe12:3
GET / HTTP/1.1
Host: 10.0.9.3:80
User-Agent: curl/7.35.0
Accept: */*
Accept-Encoding: gzip
X-Forwarded-For: 192.168.99.1
X-Forwarded-Host: 10.0.9.3:80
X-Forwarded-Proto: http
X-Forwarded-Server: 8fbc39271b4c
```
```shell
curl -H Host:whoami1.traefik http://$(docker-machine ip manager)
```
```yaml
Hostname: ba2c21488299
IP: 127.0.0.1
IP: ::1
IP: 10.0.9.4
IP: fe80::42:aff:fe00:904
IP: 172.18.0.2
IP: fe80::42:acff:fe12:2
GET / HTTP/1.1
Host: 10.0.9.4:80
User-Agent: curl/7.35.0
Accept: */*
Accept-Encoding: gzip
X-Forwarded-For: 192.168.99.1
X-Forwarded-Host: 10.0.9.4:80
X-Forwarded-Proto: http
X-Forwarded-Server: 8fbc39271b4c
```

Note that as Træfik is published, you can access it from any machine and not only the manager.

```shell
curl -H Host:whoami0.traefik http://$(docker-machine ip worker1)
```
```yaml
Hostname: 8147a7746e7a
IP: 127.0.0.1
IP: ::1
IP: 10.0.9.3
IP: fe80::42:aff:fe00:903
IP: 172.18.0.3
IP: fe80::42:acff:fe12:3
GET / HTTP/1.1
Host: 10.0.9.3:80
User-Agent: curl/7.35.0
Accept: */*
Accept-Encoding: gzip
X-Forwarded-For: 192.168.99.1
X-Forwarded-Host: 10.0.9.3:80
X-Forwarded-Proto: http
X-Forwarded-Server: 8fbc39271b4c
```
```shell
curl -H Host:whoami1.traefik http://$(docker-machine ip worker2)
```
```yaml
Hostname: ba2c21488299
IP: 127.0.0.1
IP: ::1
IP: 10.0.9.4
IP: fe80::42:aff:fe00:904
IP: 172.18.0.2
IP: fe80::42:acff:fe12:2
GET / HTTP/1.1
Host: 10.0.9.4:80
User-Agent: curl/7.35.0
Accept: */*
Accept-Encoding: gzip
X-Forwarded-For: 192.168.99.1
X-Forwarded-Host: 10.0.9.4:80
X-Forwarded-Proto: http
X-Forwarded-Server: 8fbc39271b4c
```

## Scale both services

```shell
docker-machine ssh manager "docker service scale whoami0=5"

docker-machine ssh manager "docker service scale whoami1=5"
```


Check that we now have 5 replicas of each `whoami` service:

```shell
docker-machine ssh manager "docker service ls"
```
```
ID            NAME     REPLICAS  IMAGE              COMMAND
ab046gpaqtln  whoami0  5/5       emilevauge/whoami
cgfg5ifzrpgm  whoami1  5/5       emilevauge/whoami
dtpl249tfghc  traefik  1/1       traefik            --docker --docker.swarmmode --docker.domain=traefik --docker.watch --web
```
## Access to your whoami0 through Træfik multiple times.

Repeat the following command multiple times and note that the Hostname changes each time as Traefik load balances each request against the 5 tasks:

```shell
curl -H Host:whoami0.traefik http://$(docker-machine ip manager)
```

```yaml
Hostname: 8147a7746e7a
IP: 127.0.0.1
IP: ::1
IP: 10.0.9.3
IP: fe80::42:aff:fe00:903
IP: 172.18.0.3
IP: fe80::42:acff:fe12:3
GET / HTTP/1.1
Host: 10.0.9.3:80
User-Agent: curl/7.35.0
Accept: */*
Accept-Encoding: gzip
X-Forwarded-For: 192.168.99.1
X-Forwarded-Host: 10.0.9.3:80
X-Forwarded-Proto: http
X-Forwarded-Server: 8fbc39271b4c
```

Do the same against whoami1:

```shell
curl -H Host:whoami1.traefik http://$(docker-machine ip manager)
```

```yaml
Hostname: ba2c21488299
IP: 127.0.0.1
IP: ::1
IP: 10.0.9.4
IP: fe80::42:aff:fe00:904
IP: 172.18.0.2
IP: fe80::42:acff:fe12:2
GET / HTTP/1.1
Host: 10.0.9.4:80
User-Agent: curl/7.35.0
Accept: */*
Accept-Encoding: gzip
X-Forwarded-For: 192.168.99.1
X-Forwarded-Host: 10.0.9.4:80
X-Forwarded-Proto: http
X-Forwarded-Server: 8fbc39271b4c
```
Wait, I thought we added the sticky flag to `whoami1`?  Traefik relies on a cookie to maintain stickyness so you'll need to test this with a browser.

First you need to add `whoami1.traefik` to your hosts file:

```shell
if [ -n "$(grep whoami1.traefik /etc/hosts)" ];  
then 
    echo "whoami1.traefik already exists (make sure the ip is current)"; 
else 
    sudo -- sh -c -e "echo '$(docker-machine ip manager)\twhoami1.traefik' >> /etc/hosts"; 
fi
```

Now open your browser and go to http://whoami1.traefik/

You will now see that stickyness is maintained.

![](https://i.giphy.com/ujUdrdpX7Ok5W.gif)
