docker-swarm-local icon indicating copy to clipboard operation
docker-swarm-local copied to clipboard

A single composition for running a three-node Docker Swarm on your local Docker bridge network!

Docker Swarm Local

This nifty little project is the direct result of a discussion with @allingeek on the PhoenixDocker #Meetup Slack channel. The composition presented here is a derivative of his work.

UPDATE:

This project has been updated to work with Docker Swarm v2 aka Swarm Mode as introduced in 1.12. Support for Swarm v1 has been relegated to the legacy branch.

Have Docker, Will Swarm

Are you working on the Docker Engine COBOL client, aka the New Hotness ™ and need a handy, throw-away Docker daemon/swarm to test against?

Getting Started

You will need to install Docker Engine and Docker Compose. This project was tested against:

  • Engine 17.05 with Compose 1.13

If you run into any problems executing this composition first try installing the latest Docker Engine/Compose and/or modifying the composition to match your local Docker Engine version.

docker-compose up -d && sleep 15

Start up our swarm composition:

Creating network "dockerswarmlocal_swarm" with driver "bridge"
Creating volume "dockerswarmlocal_swarm" with default driver
Creating swarm-manager-1 ...
Creating swarm-manager-3 ...
Creating swarm-manager-2 ...
Creating swarm-manager-3
Creating swarm-manager-2
Creating swarm-manager-1 ... done
Creating swarm-manager-3-join ...
Creating swarm-manager-1-init ...
Creating swarm-manager-2 ... done
Creating swarm-manager-2-join
Creating swarm-manager-3-join
Creating swarm-manager-3-join ... done

docker ps -a

Make sure there are no errors:

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                      PORTS                    NAMES
5ed1f8a4aa3c        docker:17.05        "docker-entrypoint..."   22 seconds ago      Exited (0) 13 seconds ago                            swarm-manager-1-init
3fb6728aa99c        docker:17.05        "docker-entrypoint..."   22 seconds ago      Exited (0) 3 seconds ago                             swarm-manager-3-join
db7049ff198f        docker:17.05        "docker-entrypoint..."   22 seconds ago      Exited (0) 5 seconds ago                             swarm-manager-2-join
b3f34e0f52ae        docker:17.05-dind   "dockerd-entrypoin..."   25 seconds ago      Up 22 seconds               0.0.0.0:2375->2375/tcp   swarm-manager-1
f7e235bf7951        docker:17.05-dind   "dockerd-entrypoin..."   25 seconds ago      Up 21 seconds               2375/tcp                 swarm-manager-3
1d8301c392c2        docker:17.05-dind   "dockerd-entrypoin..."   25 seconds ago      Up 22 seconds               2375/tcp                 swarm-manager-2

DOCKER_HOST=tcp://localhost:2375 docker info

Witness our handiwork. The output has been edited to show only that the Swarm is active and has the expected number of managers.

# <snipped>
Swarm: active
 NodeID: pk6dt77l9fouonbv467g0xpwp
 Is Manager: true
 ClusterID: np11cwz99drfq7bswl4w0b796
 Managers: 3
 Nodes: 3
 Orchestration:
  Task History Retention Limit: 5
 Raft:
  Snapshot Interval: 10000
  Number of Old Snapshots to Retain: 0
  Heartbeat Tick: 1
  Election Tick: 3
 Dispatcher:
  Heartbeat Period: 5 seconds
 CA Configuration:
  Expiry Duration: 3 months
 Node Address: 172.19.0.3
 Manager Addresses:
  172.19.0.2:2377
  172.19.0.3:2377
  172.19.0.4:2377
# <snipped>

DOCKER_HOST=tcp://localhost:2375 docker service create --detach false --mode global --name nginx --publish 80:80 nginx

Create a (global) service:

qw9sie41yjpjjc6zthzeth2kq
overall progress: 3 out of 3 tasks
pk6dt77l9fou: running   [==================================================>]
gb1cat9b5axy: running   [==================================================>]
9vcep8pze3az: running   [==================================================>]
verify: Waiting 1 seconds to verify that tasks are stable...

DOCKER_HOST=tcp://localhost:2375 docker service ps nginx

Verify that the service is running:

ID                  NAME                              IMAGE               NODE                DESIRED STATE       CURRENT STATE           ERROR               PORTS
ms43yket9uan        nginx.9vcep8pze3az6kw8386jv9adi   nginx:latest        f7e235bf7951        Running             Running 3 minutes ago                       
ie9aftg7oqxq        nginx.gb1cat9b5axyielx1j7h4zkpc   nginx:latest        1d8301c392c2        Running             Running 3 minutes ago                       
3fgj7ejntjks        nginx.pk6dt77l9fouonbv467g0xpwp   nginx:latest        b3f34e0f52ae        Running             Running 3 minutes ago                       

DOCKER_HOST=tcp://172.19.0.2:2375 docker ps -a

Verify that each node has one instance/task of the service.

CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS               NAMES
264e04cc3489        nginx:latest        "nginx -g 'daemon ..."   About a minute ago   Up About a minute   80/tcp              nginx.gb1cat9b5axyielx1j7h4zkpc.ie9aftg7oqxqr2woltfi9car3

DOCKER_HOST=tcp://172.19.0.3:2375 docker ps -a

Verify that each node has one instance/task of the service.

CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS               NAMES
d1339e3da3d0        nginx:latest        "nginx -g 'daemon ..."   About a minute ago   Up About a minute   80/tcp              nginx.pk6dt77l9fouonbv467g0xpwp.3fgj7ejntjks1je3a4dbjd5az

DOCKER_HOST=tcp://172.19.0.4:2375 docker ps -a

Verify that each node has one instance/task of the service.

CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS               NAMES
6d0bde3739e0        nginx:latest        "nginx -g 'daemon ..."   About a minute ago   Up About a minute   80/tcp              nginx.9vcep8pze3az6kw8386jv9adi.ms43yket9uan4oksumra6l0dc

docker exec -it swarm-manager-1 sh -c 'curl -v http://$(hostname)'

Does the service actually work? Let's cURL it! Because the service is global this command should work regardless if which manager container you target.

* Rebuilt URL to: http://b3f34e0f52ae/
*   Trying 172.19.0.3...
* TCP_NODELAY set
* Connected to b3f34e0f52ae (172.19.0.3) port 80 (#0)
> GET / HTTP/1.1
> Host: b3f34e0f52ae
> User-Agent: curl/7.52.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.13.0
< Date: Fri, 19 May 2017 15:39:43 GMT
< Content-Type: text/html
< Content-Length: 612
< Last-Modified: Tue, 25 Apr 2017 11:30:31 GMT
< Connection: keep-alive
< ETag: "58ff3357-264"
< Accept-Ranges: bytes
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
* Curl_http_done: called premature == 0
* Connection #0 to host b3f34e0f52ae left intact

docker network inspect dockerswarmlocal_swarm

The bridge network:

[
    {
        "Name": "dockerswarmlocal_swarm",
        "Id": "c9179e8cca413d69e7aa7beedd59ec21963590673c7946c93778566c407b8fc3",
        "Created": "2017-05-19T08:27:47.500165771-07:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.19.0.0/16",
                    "Gateway": "172.19.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": true,
        "Ingress": false,
        "Containers": {
            "1d8301c392c26d196a7690d8b448647b76b1ce3c3820ac4591e8e8954da76c7e": {
                "Name": "swarm-manager-2",
                "EndpointID": "b9f77ac42d44337b709390a7beed828668bf6a14974882e9ea628fbc88ffa985",
                "MacAddress": "02:42:ac:13:00:02",
                "IPv4Address": "172.19.0.2/16",
                "IPv6Address": ""
            },
            "b3f34e0f52aeed0f6293bebe23654c0a3e8edeedb0dd83628699188862153f7c": {
                "Name": "swarm-manager-1",
                "EndpointID": "1ab9c47448f25683b4d6ba79345b0e6009a5522c4907daf1c1170ae6ddf4b94d",
                "MacAddress": "02:42:ac:13:00:03",
                "IPv4Address": "172.19.0.3/16",
                "IPv6Address": ""
            },
            "f7e235bf7951364f0769b3092451a03b537e408be34f2a6dea46fc80c9330333": {
                "Name": "swarm-manager-3",
                "EndpointID": "22be2f21126275b4712404566b31123a7c3baa20fb6fa5584b662787088ee257",
                "MacAddress": "02:42:ac:13:00:04",
                "IPv4Address": "172.19.0.4/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {
            "com.docker.compose.network": "swarm",
            "com.docker.compose.project": "dockerswarmlocal"
        }
    }
]

Because the Swarm's backbone network is the host-local bridge network one can access the mesh endpoints directly, i.e.:

for h in $(DOCKER_HOST=tcp://localhost:2375 docker info 2>/dev/null | grep 2377 | sed -e 's/[:]2377//g'); do httping -c 1 http://$h; done

Should get you output that looks like:

PING 172.19.0.2:80 (/):
connected to 172.19.0.2:80 (237 bytes), seq=0 time=  0.62 ms
--- http://172.19.0.2/ ping statistics ---
1 connects, 1 ok, 0.00% failed, time 1001ms
round-trip min/avg/max = 0.6/0.6/0.6 ms
PING 172.19.0.3:80 (/):
connected to 172.19.0.3:80 (237 bytes), seq=0 time=  1.11 ms
--- http://172.19.0.3/ ping statistics ---
1 connects, 1 ok, 0.00% failed, time 1002ms
round-trip min/avg/max = 1.1/1.1/1.1 ms
PING 172.19.0.4:80 (/):
connected to 172.19.0.4:80 (237 bytes), seq=0 time=  0.82 ms
--- http://172.19.0.4/ ping statistics ---
1 connects, 1 ok, 0.00% failed, time 1001ms
round-trip min/avg/max = 0.8/0.8/0.8 ms

Cleaning Up

docker-compose down -v

Copyright Notice

The MIT License (MIT)

Copyright © 2016-2017 Jacob Blain Christen and Jeff Nickoloff - All in Geek Consulting Services, LLC

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.