Sunday 12 November 2017

​Docker Swarm Basic Tutorial

I tried to provide at my best basic tutorial on Docker Swarm on how to configure/join workers to manager(master) nodes and run services. orchestrating service through Docker Swarm.
After reading this blog, you would definitely come across basic idea on the container orchestration... 

What is container Orchestration ?

Container Orchestration systems is where the next action is likely to be in the movement towards Building, Shipping, Running containers at scale. The list of most popular software that currently provides a solution for this are Kubernetes, Docker Swarm and others.

Why do we need container orchestration ?

Imagine you might had to run hundred of containers, you can easily see if they are running in a distributed mode, ensuring your cluster is up and running etc 

Few features are:

-  health checks on containers
-  Launch exact count of containers for an particular Docker image
-  Scaling number of containers up and down depending on the load
-  Perform rolling update on softwares across containers
-  ... more 

I hope reader would be aware of Docker basic commands ..

The first step is to create a set of Docker machines that will act as nodes in our Docker Swarm. I am going to create 5 Docker Machines, where one of them will act as the Manager (Leader) and the other will be worker nodes.

I will be using below Docker machines as hostnames with their roles ..

node1(192.168.0.28) <- Manager
node2(192.168.0.27) <- Worker
node3(192.168.0.26) <- Worker
node4(192.168.0.25) <- Worker
node5(192.168.0.24) <- Worker

Creating Swarm Cluster

Once your machines are setup, you can now proceed with setting up swarm. The first thing to do is to initilize swarm. Login to node1 and initilize swarm

root@node1# docker swarm init --advertise-addr 192.168.0.28
Swarm initialized: current node (wga1nmopjbir2ks92rxntj9dz) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-3j0boiejfa0dhp82ar51fl1yvsscuwk3n1jppvzafvbv6mycyo-13fkd65lfhh2d1yb31fd59tuk 192.168.0.28:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
root@node1#

You will also notice that the output mentions the docker swarm join command to use in case you want another node to join as a worker. Keep in mind that you can have a node join as a worker or as a manager. At any point in time, there is only one LEADER and the other manager nodes will be as backup in case the current LEADER opts out.

root@node1# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS
wga1nmopjbir2ks92rxntj9dz *   node1               Ready               Active            Leader
root@node1#

You could use tokens to join either other nodes as a manager or worker. see below on how to get tokens ?

Joining as Worker Node

To find out the join command for a worker, trigger below command 

root@node1# docker swarm join-token worker
To add a worker to this swarm, run the following command:
    docker swarm join --token SWMTKN-1-3j0boiejfa0dhp82ar51fl1yvsscuwk3n1jppvzafvbv6mycyo-13fkd65lfhh2d1yb31fd59tuk 192.168.0.28:2377
root@node1#

Joining as Manager Node

root@node1# docker swarm join-token manager
To add a manager to this swarm, run the following command:
    docker swarm join --token SWMTKN-1-3j0boiejfa0dhp82ar51fl1yvsscuwk3n1jppvzafvbv6mycyo-a9j3npou6i5pb90fs9h92ez3u 192.168.0.28:2377
root@node1#

Adding worker nodes to Swarm

you need to SSH to node{2..5} and join them as workers.

root@node2# docker swarm join --token SWMTKN-1-3j0boiejfa0dhp82ar51fl1yvsscuwk3n1jppvzafvbv6mycyo-13fkd65lfhh2d1yb31fd59tuk 192.168.0.28:2377
This node joined a swarm as a worker.
root@node2#

root@node3# docker swarm join --token SWMTKN-1-3j0boiejfa0dhp82ar51fl1yvsscuwk3n1jppvzafvbv6mycyo-13fkd65lfhh2d1yb31fd59tuk 192.168.0.28:2377
This node joined a swarm as a worker.
root@node3#

root@node4# docker swarm join --token SWMTKN-1-3j0boiejfa0dhp82ar51fl1yvsscuwk3n1jppvzafvbv6mycyo-13fkd65lfhh2d1yb31fd59tuk 192.168.0.28:2377
This node joined a swarm as a worker.
root@node4#

root@node5# docker swarm join --token SWMTKN-1-3j0boiejfa0dhp82ar51fl1yvsscuwk3n1jppvzafvbv6mycyo-13fkd65lfhh2d1yb31fd59tuk 192.168.0.28:2377
This node joined a swarm as a worker.
root@node5#

Login to manager node(node1) and check the status

root@node1# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS
wga1nmopjbir2ks92rxntj9dz *   node1               Ready               Active         Leader
4wzc1nesi1hxa3ekq753w0koq     node2               Ready               Active
4nrsuwslw149mg4nwlipmxxlo     node3               Ready               Active
jbdizz7gvfdz03shtgu23h8kv     node4               Ready               Active
afpi9osujgcsqkdiqg4tnz6nb     node5               Ready               Active
root@node1#

you could also execute 'docker info' and check out swarm section.

Swarm: active
 NodeID: wga1nmopjbir2ks92rxntj9dz
 Is Manager: true
 ClusterID: 7xpyowph2y1q2q12rhrkzzfei
 Managers: 1  ==> Swarm markered as 1 manager among total 5 active nodes
 Nodes: 5
 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
  Force Rotate: 0
 Autolock Managers: false
 Root Rotation In Progress: false
 Node Address: 192.168.0.28
 Manager Addresses:
  
Testing Swarm orchestration
we have swarm up and running, its time to schedule our containers on it. we will not focus on the application and no need for us to worry where the appilcation is going to run. 
we will tell the manager to run the containers for us and it will take care of scheduling our container and send these commands to our nodes to distribute.

we will run 'nginx' container and expose 'port 80'. we could specify number of instances to launch via 'replicas'. 

All would be runing from the 'node1' managerical node and we could use for administration. 

root@node1# docker service create --replicas 5 -p 80:80 --name web nginx
lwvjgxcxrg5wqw2fwrgzosnnw 
Since --detach=false was not specified, tasks will be created in the background.
In a future release, --detach=false will become the default.
root@node1#

you could now see the status of the service being orchestrated to different nodes.

root@node1# docker service ls                                                                     PORTS  
ID                  NAME                MODE                REPLICAS            nginx:latest        *:80->80/tcp
MAGE               PORTS
lwvjgxcxrg5w        web                 replicated          8/8                 nginx:latest        *:80->80/tcp
root@node1#

root@node1# docker service ps web
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE           ERROR               PORTS
l00c9u3kmfbn        web.1               nginx:latest        node1               Running             Running 5 minutes ago
nzzi7z3wvikl        web.2               nginx:latest        node2               Running             Running 5 minutes ago
91uizczo4dp3        web.3               nginx:latest        node3               Running             Running 5 minutes ago
nor39f9gu3j2        web.4               nginx:latest        node4               Running             Running 5 minutes ago
mtn3et6gu5on        web.5               nginx:latest        node5               Running             Running 5 minutes ago
root@node1#

Accessing service

you can acess the service by hitting any of the manager or worker node. It does not matter any particular node does not have any container scheduled on it.


root@node1# curl http://192.168.0.28:80
<!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>
root@node1#

Scaling up and Scaling down

Currently we are running 5 containers and lets assume we require 10 containers, fire below to scale up. 

root@node1# docker service scale web=10

root@node1# docker service ps web
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
l00c9u3kmfbn        web.1               nginx:latest        node1               Running             Running 18 minutes ago
nzzi7z3wvikl        web.2               nginx:latest        node2               Running             Running 18 minutes ago
91uizczo4dp3        web.3               nginx:latest        node3               Running             Running 18 minutes ago
nor39f9gu3j2        web.4               nginx:latest        node4               Running             Running 18 minutes ago
mtn3et6gu5on        web.5               nginx:latest        node5               Running             Running 18 minutes ago
t1reh7fnzfg9        web.6               nginx:latest        node3               Running             Running 10 minutes ago
qiyegknsz90w        web.7               nginx:latest        node1               Running             Running 10 minutes ago
zqsafvln29ft        web.8               nginx:latest        node2               Running             Running 10 minutes ago
mx6ezdng0n1i        web.9               nginx:latest        node4               Running             Running 16 seconds ago
xrmugsonlbvl        web.10              nginx:latest        node5               Running             Running 16 seconds ago
root@node1# 

you can reduce with same command, it would reduce your containers on the nodes.
root@node1# docker service scale web=5 

Draining node

whenever the node is ACTIVE, it will be always ready to accept tasks from Manager. 

root@node1#docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS
wga1nmopjbir2ks92rxntj9dz *   node1               Ready               Active              Leader
4wzc1nesi1hxa3ekq753w0koq     node2               Ready               Active
4nrsuwslw149mg4nwlipmxxlo     node3               Ready               Active
jbdizz7gvfdz03shtgu23h8kv     node4               Ready               Active
afpi9osujgcsqkdiqg4tnz6nb     node5               Ready               Active
root@node1#

When the node is active, it can receive new tasks:
- during a service update to scale up
- during a rolling update
- when you set another node to Drain availability
- when a task fails on another active node

But sometimes, we have to bring the Node down for some maintenance reason. This meant by setting the Availability to Drain mode. Let us try that with one of our nodes.

since there are 10 containers runing, each node is having 2 containers on it. lets made one of the node drain and see how the containers are being orchestrated without disrupting service.

root@node1# docker inspect node5
.
.
<snip>
        "Spec": {
            "Labels": {},
            "Role": "worker",
            "Availability": "active"  ==> status set to active
.
.
<snip>
            }
        },
        "Status": {
            "State": "ready",
            "Addr": "192.168.0.24"
        }
<snip>


root@node1# docker node update --availability drain node5
node5
root@node1#

root@node1# docker service ps web
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE                     ERROR               PORTS
l00c9u3kmfbn        web.1               nginx:latest        node1               Running             Running 30 minutes ago
nzzi7z3wvikl        web.2               nginx:latest        node2               Running             Running 30 minutes ago
91uizczo4dp3        web.3               nginx:latest        node3               Running             Running 30 minutes ago
nor39f9gu3j2        web.4               nginx:latest        node4               Running             Running 30 minutes ago
e5pdetk2lghs        web.5               nginx:latest        node3               Running             Running less than a second ago
mtn3et6gu5on         \_ web.5           nginx:latest        node5               Shutdown            Shutdown 1 second ago
t1reh7fnzfg9        web.6               nginx:latest        node3               Running             Running 21 minutes 
agoqiyegknsz90w        web.7               nginx:latest        node1               Running             Running 21 minutes 
agozqsafvln29ft        web.8               nginx:latest        node2               Running             Running 21 minutes 
agomx6ezdng0n1i        web.9               nginx:latest        node4               Running             Running 11 minutes ago
m23nbrdvb635        web.10              nginx:latest        node4               Running             Running less than a second ago
xrmugsonlbvl         \_ web.10          nginx:latest        node5               Shutdown            Shutdown less than a second ago
root@node1#

root@node1# docker inspect node5
<snip>
"Spec": {
            "Labels": {},
            "Role": "worker",
            "Availability": "drain"
<snip>

You could now see the containers on the node5 has been re-scheduled on node3 and node4. 
you could now get on with maintenance on node5 and once they are up, you would require to make them into active state. On active state, containers will again get replicated.

Remove service

You could remove the service with 'rm' command.
root@node1# docker service rm web

Rolling upgrade

root@node1# docker service update --image <imagename>:<version> web

Thanks for re-sharing...