Hands On with Docker Swarm

In this article, we will be taking a look at Docker Swarm. With Docker Swarm, you can create and manage Docker clusters. Swarm can be used to disperse containers across multiple hosts. It also has the ability to know how to scale containers as well. In this article, we will be covering the following topics:

  • Installing Docker Swarm
  • The Docker Swarm components
  • Docker Swarm usage
  • The Docker Swarm commands
  • The Docker Swarm topics

(For more resources related to this topic, see here.)

Docker Swarm install

Let's get things started by the typical way of installing Docker Swarm. Docker Swarm is only available for Linux and Mac OS X. The installation process for both is the same. Let's take a look at how we install Docker Swarm.

Installation

Ensure that you already have Docker installed, either through the curl command on Linux or through Docker Toolbox on Mac OS X. Once you have the Docker daemon installed, installing Docker Swarm will be simple:

$ docker pull swarm

One command and you are up and running. That's it!

Docker Swarm components

What components are involved with Docker Swarm? Let's take a look at the three components of Docker Swarm:

  • Swarm
  • Swarm manager
  • Swarm host

Swarm

Docker Swarm is the container that runs on each Swarm host. Swarm uses a unique token for each cluster to be able to join the cluster. The Swarm container itself is the one that communicates on behalf of that Docker host to the other Docker hosts that are running Docker Swarm as well as the Docker Swarm manager.

Swarm manager

The Swarm manager is the host that is the central management point for all the Swarm hosts. The Swarm manager is where you issue all your commands to control nodes. You can switch between the nodes, join nodes, remove nodes, and manipulate the hosts.

Swarm host

Swarm hosts, which we saw earlier as the Docker hosts, are those that run the Docker containers. The Swarm host is managed from the Swarm manager.

The preceding figure is an illustration of all the Docker Swarm components. We see that the Docker Swarm manager talks to each Swarm host that is running the Swarm container.

Docker Swarm usage

Let's now take look at Swarm usage and how we can do the following tasks:

  • Creating a cluster
  • Joining nodes
  • Removing nodes
  • Managing nodes

Creating a cluster

Let's start by creating the cluster, which starts with a Swarm manager. We first need a token that can be used to join all the nodes to the cluster:

$ docker run --rm swarm create
85b335f95e9a37b679e2ea9e6ad8d6361

We can now use that token to create our Swarm manager:

$ docker-machine create \
       -d virtualbox \
       --swarm \
       --swarm-master \
       --swarm-discovery token://85b335f95e9a37b679e2ea9e6ad8d6361 \
       swarm-master
Creating VirtualBox VM...
Creating SSH key...
Starting VirtualBox VM...
Starting VM...

To see how to connect Docker to this machine, run docker-machine env swarm-master.

The swarm-master node is now in VirtualBox. We can see this machine by doing as follows:

$ docker-machine ls

NAME           ACTIVE   DRIVER       STATE     URL                         SWARM
swarm-master           virtualbox   Running   tcp://192.168.99.101:2376 swarm-master (master)

Now, let's point Docker Machine at the new Swarm master. The earlier output we saw when we created the Swarm master tells us how to point to the node:

$ docker-machine env swarm-master  
                                                               
export DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://192.168.99.102:2376"
export DOCKER_CERT_PATH="/Users/spg14/.docker/machine/machines/swarm-master"
export DOCKER_MACHINE_NAME="swarm-master"
# Run this command to configure your shell:
# eval "$(docker-machine env swarm-master)"

Upon running the previous command, we are told to run the following command to point to the Swarm master:

$ eval "$(docker-machine env swarm-master)"

Now, if we look at what machines are on our host, we can see that we have the swarm-master host as well. It is set to ACTIVE, which means that we can now run commands against this host:

$ docker-machine ls

NAME           ACTIVE   DRIVER      STATE     URL                         SWARM
swarm-master   *         virtualbox   Running   tcp://192.168.99.101:2376 swarm-master (master

Joining nodes

Again using the token, which we got from the earlier commands, used to create the Swarm manager, we need that same token to join nodes to that cluster:

$ docker-machine create \
-d virtualbox \
--swarm \
--swarm-discovery token://85b335f95e9a37b679e2ea9e6ad8d6361 \
swarm-node1

Now, if we look at the machines on our system, we can see that they are both part of the same Swarm:

$ docker-machine ls

NAME           ACTIVE   DRIVER       STATE     URL                         SWARM
swarm-master   *       virtualbox   Running   tcp://192.168.99.102:2376   swarm-master(master)
swarm-node1             virtualbox   Running   tcp://192.168.99.103:2376   swarm-master

Listing nodes

First, ensure you are pointing at the Swarm master:

$ docker-machine ls                                                                                                                                                          

NAME           ACTIVE   DRIVER       STATE     URL                         SWARM

swarm-master   *       virtualbox   Running   tcp://192.168.99.102:2376   swarm-master(master)
swarm-node1             virtualbox   Running   tcp://192.168.99.103:2376   swarm-master

Now, we can see what machines are joined to this cluster based off the token used to join them all together:

$ docker run --rm swarm list token://85b335f95e9a37b679e2ea9e6ad8d6361

192.168.99.102:2376

192.168.99.103:2376

Managing a cluster

Let's see how we can do some management of all of the cluster nodes we are creating.

So, there are two ways you can go about managing these Swarm hosts and the containers on each host that you are creating. But first, you need to know some information about them, so we will turn to our Docker Machine command again:

$ docker-machine ls

NAME           ACTIVE   DRIVER       STATE     URL                         SWARM
swarm-master     *     virtualbox   Running   tcp://192.168.99.102:2376   swarm-master(master)
swarm-node1             virtualbox   Running   tcp://192.168.99.103:2376   swarm-master

You can switch to each Swarm host like we have seen earlier by doing something similar to the following—changing the values—and by following the instructions from the output of the command:

$ docker-machine env <Node_Name>

But this is a lot of tedious work. There is another way we can manage these hosts and see what is going on inside them. Let's take a look at how we can do it. From the previous docker-machine ls command, we see that we are currently pointing at the swarm-master node. So, any Docker commands we issue would go against this host.

But, if we run the following, we can get information on the swarm-node1 node:

$ docker -H tcp://192.168.99.103:2376 info

Containers: 1
Images: 8
Storage Driver: aufs
Root Dir: /mnt/sda1/var/lib/docker/aufs
Backing Filesystem: tmpfs
Dirs: 10
Dirperm1 Supported: true
Execution Driver: native-0.2
Logging Driver: json-file
Kernel Version: 4.0.9-boot2docker
Operating System: Boot2Docker 1.8.2 (TCL 6.4); master : aba6192 - Thu Sep 10 20:58:17 UTC 2015
CPUs: 1
Total Memory: 996.2 MiB
Name: swarm-node1
ID: SDEC:4RXZ:O3VL:PEPC:FYWM:IGIK:CFM5:UXPS:U4S5:PNQD:5ULK:TSCE
Debug mode (server): true
File Descriptors: 18
Goroutines: 29
System Time: 2015-09-16T09:32:27.67035212Z
EventsListeners: 1
Init SHA1:
Init Path: /usr/local/bin/docker
Docker Root Dir: /mnt/sda1/var/lib/docker
Labels:
provider=virtualbox

So, we can see the information on this host such as the number of containers, the numbers of images on the host, as well as information about the CPU, memory, and so on.

We can see from the earlier information that one container is running. Let's take a look at what is running on the swarm-node1 host:

$ docker -H tcp://192.168.99.103:2376 ps

CONTAINER ID       IMAGE             COMMAND                 CREATED           STATUS             PORTS               NAMES
12a400424c87       swarm:latest       "/swarm join --advert"   17 hours ago       Up 17 hours         2375/tcp           swarm-agent

Now, you can use any of the Docker commands using this method against any Swarm host that is listed in the output of your docker-machine ls output.

The Docker Swarm commands

Now, let's take a look at some Docker Swarm-specific commands that we can use. Let's revert to the ever-so-helpful—the help switch on the Docker Swarm command:

$ docker run --rm swarm --help            
                                                                                                                              
Usage: swarm [OPTIONS] COMMAND [arg...]

A Docker-native clustering system

Version: 0.4.0 (d647d82)

Options:
--debug     debug mode [$DEBUG]
--log-level, -l "info" Log level (options: debug, info, warn, error, fatal, panic)
--help, -h     show help
--version, -v     print the version
Commands:
create, c Create a cluster
list, l  List nodes in a cluster
manage, m Manage a docker cluster
join, j join a docker cluster
help, h Shows a list of commands or help for one command
Using TLS

Let's take a look at the options you can use for Docker Swarm as well as the commands that are associated with it.

Options

Looking over the options from the preceding output, we can see the --debug and --log level switches. The other two are straightforward, as one will just print out the help information and the other one will print out the version number that we can see in the previous output. The options are used after each of the following subcommands of Docker Swarm.

For example:

$ docker run --rm swarm list --debug
$ docker run --rm swarm manage --debug
$ docker run --rm swarm create --debug

list

We looked at the Swarm list command before:

$ docker run --rm swarm list token://85b335f95e9a37b679e2ea9e6ad8d6361
                                                                                                   
192.168.99.102:2376
192.168.99.103:2376

But there is also a switch that we can tack onto the list command and that is the --timeout switch:

$ docker run --rm swarm list --timeout 20s token://85b335f95e9a37b679e2ea9e6ad8d6361

This will allow more time to find the nodes that are a part of Swarm. It could take time for the hosts to check, depending upon things such as network latency or if they are running in different parts of the globe.

create

We have seen how we can create a Swarm cluster as well. What this command actually does is it gives us the token that we need to create the cluster and join all the nodes to it. There are no other switches that can be used with this command as we have seen with other commands:

$ docker run --rm swarm create

85b335f95e9a37b679e2ea9e6ad8d6361

manage

We can manage a cluster with the manage subcommand in Docker Swarm. An example of this command would look like the following, replacing the information to align with your IP address and Swarm token:

$ docker run --rm swarm manage -H tcp://192.168.99.104:2376 token://85b335f95e9a37b679e2ea9e6ad8d6361

The Docker Swarm topics

There are three advanced topics we will take a look at in this section:

  • Discovery services
  • Advanced scheduling
  • The Docker Swarm API

Discovery services

You can also use services such as etcd, ZooKeeper, consul, and many others to automatically add nodes to your Swarm cluster as well as do other things such as list the nodes or manage them. Let's take a look at consul and how you can use it. This will be the same for each discovery service that you might use. It just involves switching out the word consul with the discovery service you are using.

On each node, you will need to do something different in how you join the machines. Earlier, we did something like this:

$ docker-machine create \
-d virtualbox \
--swarm \
--swarm-discovery token://85b335f95e9a37b679e2ea9e6ad8d6361 \
swarm-node1

Now, we would do something similar to the following (based upon the discovery service you are using):

$ docker-machine create \
-d virtualbox \
--swarm \
join --advertise=<swarm-node1_ip:2376> \
consul://<consul_ip> \
swarm-node1

You can now start manage on your laptop or the system that you will be using as the Swarm manager. Before, we would run something like this:

$ docker run --rm swarm manage -H tcp://192.168.99.104:2376 token://85b335f95e9a37b679e2ea9e6ad8d6361

Now, we run this with regards to discovery services:

$ docker run --rm swarm manage -H tcp://192.168.99.104:2376 consul://<consul_ip>

We can also list the nodes in this cluster as well as the discovery service:

$ docker run --rm swarm list -H tcp://192.168.99.104:2376 consul://<consul_ip>

You can easily switch out consul for another discovery service such as etcd or ZooKeeper; the format will still be the same:

$ docker-machine create \
-d virtualbox \
--swarm \
join --advertise=<swarm-node1_ip:2376> \
etcd://<etcd_ip> \
swarm-node1

$ docker-machine create \
-d virtualbox \
--swarm \
join --advertise=<swarm-node1_ip:2376> \
zk://<zookeeper_ip> \
swarm-node1

Advanced scheduling

What is advanced scheduling with regards to Docker Swarm? Docker Swarm allows you to rank nodes within your cluster. It provides three different strategies to do this. These can be used by specifying them with the --strategy switch with the swarm manage command:

  • spread
  • binpack
  • random

spread and binpack use the same strategy to rank your nodes. They are ranked based off of the node's available RAM and CPU as well as the number of containers that it has running on it.

spread will rank the host with less containers higher than a container with more containers (assuming that the memory and CPU values are the same). spread does what the name implies; it will spread the nodes across multiple hosts. By default, spread is used with regards to scheduling.

binpack will try to pack as many containers on as few hosts as possible to keep the number of Swarm hosts to a minimal.

random will do just that—it will randomly pick a Swarm host to place a node on.

The Swarm scheduler comes with a few filters that can be used as well. These can be assigned with the --filter switch with the swarm manage command. These filters can be used to assign nodes to hosts. There are five filters that are associated with it:

  • constraint: There are three types of constraints that can be assigned to nodes:
    • storage=: This is used if you want to specify a node that is put on a host and has SSD drives in it
    • region=: This is used if you want to set a region; mostly used for cloud computing such as AWS or Microsoft Azure
    • environment=: This can set a node to be put into production, development, or other created environments
  • affinity: This filter is used to create attractions between containers. This means that you can specify a filter name and then have all those containers run on the same node.
  • port: The port filter finds a host that has the open port needed for the node to run; it then assigns the node to that host. So, if you have a MySQL instance and need port 3306 open, it will find a host that has port 3306 open and assign the node to that host for operation.
  • dependency: The dependency filter schedules nodes to run on the same host based off of three dependencies:
    • --volumes-from=dependency
    • --link=dependency:<alias>
    • --net=container:dependency
  • health: The health filter is pretty straightforward. It will prevent the scheduling of nodes to run on unhealthy hosts.

The Swarm API

Before we dive into the Swarm API, let's first make sure you understand what an API is. An API is defined as an application programming interface. An API consists of routines, protocols, and tools to build applications. Think of an API as the bricks used to build a wall. This allows you to put the wall together using those bricks. What APIs allow you to do is code in the environment you are comfortable in and reach into other environments to do the work you need. So, if you are used to coding in Python, you can still use Python to do all your work while using the Swarm API to do the work in Swarm that you would like done.

For example, if you wanted to create a container, you would use the following in your code:

POST /containers/create HTTP/1.1
Content-Type: application/json

{
       "Hostname": "",
       "Domainname": "",
       "User": "",
       "AttachStdin": false,
       "AttachStdout": true,
       "AttachStderr": true,
       "Tty": false,
       "OpenStdin": false,
       "StdinOnce": false,
       "Env": null,
       "Cmd": [
               "date"
       ],
       "Entrypoint": "",
       "Image": "ubuntu",
       "Labels": {
               "com.example.vendor": "Acme",
               "com.example.license": "GPL",
               "com.example.version": "1.0"
       },
       "Mounts": [
         {
           "Source": "/data",
           "Destination": "/data",
           "Mode": "ro,Z",
           "RW": false
         }
       ],
       "WorkingDir": "",
       "NetworkDisabled": false,
       "MacAddress": "12:34:56:78:9a:bc",
       "ExposedPorts": {
              "22/tcp": {}
       },
       "HostConfig": {
         "Binds": ["/tmp:/tmp"],
         "Links": ["redis3:redis"],
         "LxcConf": {"lxc.utsname":"docker"},
         "Memory": 0,
         "MemorySwap": 0,
         "CpuShares": 512,
         "CpuPeriod": 100000,
         "CpusetCpus": "0,1",
         "CpusetMems": "0,1",
         "BlkioWeight": 300,
         "MemorySwappiness": 60,
         "OomKillDisable": false,
         "PortBindings": { "22/tcp": [{ "HostPort": "11022" }] },
         "PublishAllPorts": false,
         "Privileged": false,
         "ReadonlyRootfs": false,
         "Dns": ["8.8.8.8"],
         "DnsSearch": [""],
         "ExtraHosts": null,
         "VolumesFrom": ["parent", "other:ro"],
         "CapAdd": ["NET_ADMIN"],
        "CapDrop": ["MKNOD"],
         "RestartPolicy": { "Name": "", "MaximumRetryCount": 0 },
         "NetworkMode": "bridge",
         "Devices": [],
         "Ulimits": [{}],
         "LogConfig": { "Type": "json-file", "Config": {} },
         "SecurityOpt": [""],
         "CgroupParent": ""
     }
}

You would use the preceding example to create a container; but there are also other things you can do such as inspect containers, get the logs from a container, attach to a container, and much more. Simply put, if you can do it through the command line, there is more than likely something in the API that can be used to tie into to do it through the programming language you are using.

The Docker documentation states that the Swarm API is mostly compatible with the Docker Remote API. Now we could list them out in this section. But seeing that the list could change as things could be added into the Docker Swarm API or removed, I believe, it's best to refer to the link to the Swarm API documentation here instead of listing them out, so the information is not outdated:

https://docs.docker.com/swarm/api/swarm-api/

The Swarm cluster example

We will now go through an example of how to set up a Docker Swarm cluster:

# Create a new Docker host with Docker Machine
$ docker-machine create --driver virtualbox swarm

# Point to the new Docker host
$ eval "$(docker-machine env swarm)"

# Generate a Docker Swarm Discovery Token
$ docker run swarm create

# Launch the Swarm Manager
$ docker-machine create \
       --driver virtualbox \
       --swarm \
       --swarm-master \
       --swarm-discovery token://<DISCOVERY_TOKEN> \
       swarm-master

# Launch a Swarm node
$ docker-machine create \
   --driver virtualbox \
   --swarm \
   --swarm-discovery token://<DISCOVERY_TOKEN> \
   swarm_node-01

# Launch another Swarm node
$ docker-machine create \
   --driver virtualbox \
   --swarm \
   --swarm-discovery token://<DISCOVERY_TOKEN> \
   swarm_node-02

# Point to our Swarm Manager
$ eval "$(docker-machine env swarm-master)"

# Execute 'docker info' command to view information about your envionment
$ docker info

# Execute 'docker ps -a'; will show you all the containers running as well as how they are joined to the same Swarm cluster
$ docker ps -a

# Run simple test
$ docker run hello-world

# You can then execute the 'docker ps -a' command again to see what node it ran on
$ docker ps -a
# You will want to look at the column labeled 'NAMES'. If you continue to re-run the 'docker run hello-world' command/container you will see it will run on a different Swarm node

Summary

In this article, we took a dive into Docker Swarm. We took a look at how to install Docker Swarm and the Docker Swarm components; these are what make up Docker Swarm. We took a look at how to use Docker Swarm; joining, listing, and managing Swarm nodes. We reviewed the Swarm commands and how to use them. We also covered some advanced Docker Swarm topics such as advanced scheduling for your jobs, discovery services to discover new containers to add to Docker Swarm, and the Docker Swarm API that you can use to tie your own code to perform the Swarm commands.

Resources for Article:


Further resources on this subject:


You've been reading an excerpt of:

Mastering Docker

Explore Title