San Francisco

dave spink toolset


DOCKER:

COMMANDS OVERVIEW INSTALL EXAMPLES
COMPOSE SCALING SWARM STACK

THE PROJECT

The HPE Deep Learning Benchmark Suite (DLBS) uses Docker. When we implemented DLBS at Verizon (VMG) they had a whole series of Docker questions e.g. how does docker work, how do you access a running docker container etc. Hence, we needed to learn docker to answer those questions….



COMMANDS

Docker Commands
docker info ;lots of good info
docker ps ;shows all actively running containers
docker ps -a ;see all containers that have been run or running
docker ps --filter status=exited ;see containers that are no longer running
docker rm e2e1eb939c72 ;delete a specific container
docker container prune ;delete all containers that have run
docker images ;see list of repository images on system
docker image rmi mysql ;delete old images
docker run --help ;see help files
docker build . -t webapp:latest ;build an image
docker run --name test1 hello-world ;creates a container instance of the image and runs it
docker run -d -p 80:80 --name mytest1 --rm webapp ;detach terminal, expose port 80, delete container instance when its stopped running
docker run -it --rm ubuntu:latest /bin/bash ;access the shell of an image
docker exec -it mytest1 /bin/bash ;access the shell of a running container
docker stop mytest1 ;stop a running container
docker start mytest1 ;re-start the container you just stopped
docker stop $(docker ps -q) ;stop all running containers
docker tag webapp spinkd/webapp ;duplicate an image with a new name
docker run -it --rm php:latest /bin/bash ;obtain a php container and open a bash shell; you can run php -v to see version
docker logs -f --tail 10 mytest1 ;see docker console logs of a container; either running or not running
docker run -d -P httpd ;detach terminal and expose the random port
curl -I "http://localhost:32776" ;health check
docker run -it --link "httpd:web" ubuntu /bin/bash ;link container httpd and ubuntu, with an alias name web
docker inspect mytest1 ;see details (network, performance, configs) of a running container
docker rm $(docker ps --filter status=exited -q) ;clean-up those containers that have status exited
docker network ls ;list all current running networks
docker network prune ;cleans up un-used networks
docker login docker.io ;login to docker repository, the default registry is docker.io
docker push spinkd/webapp:latest ;push image up to your repository (must login into repo first)
Docker Compose
docker-compose up -d ;run docker compose, requires a yaml file
docker-compose stop ;stop containers listed in the yaml file
docker-compose down ;stop and remove the containers in the yaml file
docker-compose pull ;pre pull required images
docker-compose restart ;will restart all the instances
docker-compose pause ;this is pausing the instances, basically takes it offline and hangs the processes
docker-compose unpause ;this un-pauses
docker-compose top ;to see process ID's are active
docker-compose logs ;to see console logs
Docker Swarm
docker swarm init --advertise-addr 10.0.2.15 ;initialize swarm on your manager node
docker info ;lots of good info (see Swarm info / status)
docker node ls ;see list of cluster nodes (managers and workers)
docker service create -p 80:80 --name webservice --replicas 3 httpd ;launch swarm service
docker service ps webservice ;see containers running in the service
docker service scale webservice=5 ;scale up or down services
docker node promote workernode1 ;promote a worker node to take over manager if needed
docker node demote workernode1 ;demote a worker node
docker swarm leave ;leave a swarm
docker inspect workernode2 ;see details of the worker node
docker service update -h ;help
Docker Stack:
docker stack deploy -c docker-stackdeploy.yaml mystack
docker service ps mystack_apache_httpd
docker service update --publish-add 80:80 mystack_apache_httpd
docker service logs mystack_apache_httpd
docker stack ps mystack




OVERVIEW

overview

Containers are isolated, but share an OS and where appropriate share bins and libraries. They are not a new approach i.e. Solaris had zones, BSB had jails however Google donated "cgroups" into the Linux kernel which became a critical component of LXC (LinuX Containers).  Docker has made containers easy to use. Note: containers do not change any of the fundamental underlying storage limitations or features.

overview

overview

Terminology:

  • Images - a read-only template with instructions for running containers. To build an image you create a docker file with commands.
  • Containers - are a runnable instance of the image. You create, start, stop, move, delete containers with the Docker API or CLI.
  • Docker Daemon - the background service for building, running and distributing Docker containers.
  • Docker Client - the command line tool that allows the user to interact with the docker daemon.
  • Docker Hub - a public registry of Docker images https://hub.docker.com/

Iamges:

  • Official images are maintained and supported by the folks at Docker. These are typically one word long e.g. Nginx, Python, Ubuntu etc. For example see the docker repository for Nginx (pronounced "engine-x") which is an open source reverse proxy server for HTTP, HTTPS, SMTP, POP3, and IMAP protocols, as well as a load balancer, HTTP cache, and a web server - https://hub.docker.com/_/nginx.
  • User images are created and shared by users. They build on base images and typically formatted as user/image-name.

Dockerfile:

  • A file with syntax for the steps needed to create an image. For example:
  • $ vi dockerfile
    FROM ubuntu:latest
    CMD  echo "Hello, this is the first container I have built."
    
  • Each instruction in a Dockerfile creates a layer in the image, hence the more instructions the larger the docker image.

Docker Process:

  • You start with docker file and then execute the docker build process which produces a docker image. Next you run the image which effectively creates a container instance of that image. It's important *not* to make code changes in the container themselves as they're designed to be immutable. Hence, if need to fix the code make changes in the source and rebuild the image.
  • Dockerfile -> Docker build process = Image
  • Image -> Docker run = container / container instance

Docker Compose:

  • It's essentially a templated deployment model for multi-container applications. You use a YAML file. Rather than having to run multiple docker command lines, you place all the containers you need in one configuration file. And compose creates the links for you.
  • Note, compose does not allow for rolling updates and only deploys on one docker host.

Docker Swarm:

  • A cluster of docker hosts that work together to deploy containers. The coordination between the docker hosts is referred to as orchestration.
  • Made up of two different docket hosts - manager and worker.
  • Benefits - load balancing, highly available, scaling, rolling updates, self-heal.

Docker Stack Deploy:

  • This basically does what docker compose does, however it allows to deploy across a swarm service.

Uploading your builds (images) to Docker Hub:

  1. Create an account on https://hub.docker.com
  2. Login into docker hub (the default registry is docker.io):
  3. $ docker login docker.io
    Username: spinkd
    Password: 
    
  4. Push image up to repository:
  5. $ docker push spinkd/webapp:latest
    The push refers to repository [docker.io/spinkd/webapp]
    


INSTALL

https://docs.docker.com/install/linux/docker-ce/centos/

Remove older versions:

$ sudo yum remove docker-ce
$ sudo rm -rf /var/lib/docker

Install required packages:

$ sudo yum install -y yum-utils  device-mapper-persistent-data  lvm2

Add a stable repository:

$ sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

Install Docker CE (community edition)

$ sudo yum install docker-ce
$ sudo yum install docker-ce docker-ce-cli containerd.io

Post install steps:

$ sudo systemctl start docker
$ sudo docker run hello-world
$ sudo usermod -aG docker dave
$ sudo systemctl enable docker
$ sudo chkconfig docker on

Logoff / Logon

Verify that Docker can resolve external IP addresses by trying to pull an image:

$ docker pull hello-world             ;pulling an image is akin to a Git repository
$ docker run hello-world             ;creates and runs a container instance from the hello-world image

What happens when you run $ docker run -i -t ubuntu /bin/bash

  1. If you don't have image locally, Docker will first pull the image from the repository https://hub.docker.com/
  2. Docker creates a new container.
  3. Docker allocates a read-write filesystem to the container.
  4. Docker creates a network interface (IP address) using the host machine’s network connection.
  5. Docker starts the container and executes /bin/bash providing. The flags -i and -t allow you use your keyboard while output is logged to your terminal.
  6. When you type exit to terminate the /bin/bash command, the container stops but is not removed. You can start it again or remove it.

Install Docker-Compose. Check you have the latest version https://docs.docker.com/compose/install/

$ sudo curl -L https://github.com/docker/compose/releases/download/1.24.0-rc1/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose
$ docker-compose -v
e5c864f50b6e49c3b5cad019baadd713


EXAMPLES

The following docker examples are from "Docker from A to Z; Swarm + Jenkins" by James Kayes-Smith on Udemy. I highly recommended his course.

A basic container example:

Create a docker file:

$ vi dockerfile
FROM ubuntu:latest
CMD  echo "Hello, this is the first container I have built."

Build the docker image, the dot is the path to the docker file, and -t is the image name to create:

$ docker build . -t daves-test 

Run the container:

$ docker run daves-test
Hello, this is the first container I have built.

A basic web app:

Create a HTML file:

$ vi webapp.html 
<html>
<body>
<h1>Hello world!</h1>
</body>
</html>

Specify the root directory for your web server:

$ vi nginx.conf 
server {
 root /www;
}

Create a docker file i.e. use the open source image (nginx) and "add" your commands:

$ vi Dockerfile 
FROM nginx:latest
	
ADD webapp.html /www/
ADD nginx.conf /etc/nginx/conf.d/default.conf

Build the docker image:

$ docker build . -t webapp:latest
Sending build context to Docker daemon  4.096kB
Step 1/3 : FROM nginx:latest
 ---> 56ca8c4670fa80
Step 2/3 : ADD webapp.html /www/
 ---> Using cache
 ---> 7260f0c05813
Step 3/3 : ADD nginx.conf /etc/nginx/conf.d/default.conf
 ---> Using cac
 ---> e6d0b965ec3b
Successfully built e6d0b965ec3b
Successfully tagged webapp:latest

List the docker images:

$ docker images
REPOSITORY         TAG                 IMAGE ID           CREATED             SIZE
webapp             latest              e6d0b965ec3b       3 hours ago         109MB

Run a container instance from the "webapp" image. Expose port 80, detach terminal, and delete the container instance once it's stopped running:

$ docker run -d -p 80:80 --name mytest1 --rm webapp

overview

Run another container instance exposing a different port number:

$ docker run -d -p 84:80 --name mytest2 --rm webapp         

overview

Access the shell of a running container:

$  docker exec -it mytest1 /bin/bash
root@79d09948fc9d:/# ls /www
webapp.html

Show the exposed ports a container:

$ docker port mytest2                                                
80/tcp -> 0.0.0.0:84

See what containers are running:

$ docker ps                                                                         
CONTAINER ID    IMAGE        COMMAND                  CREATED              STATUS              PORTS                NAMES
96fb021c25b6    webapp       "nginx -g 'daemon of…"   About a minute ago   Up About a minute   0.0.0.0:85->84/tcp   mytest2
c248a64c9a11    webapp       "nginx -g 'daemon of…"   About a minute ago   Up About a minute   0.0.0.0:85->80/tcp   mytest1

Stop the running containers:

$ docker stop mytest1
$ docker stop mytest2

Install Software via a Dockerfile:

The "run" command is saying run this command on the base image and save it into the new image.

$ vi dockerfile
FROM ubuntu:latest
RUN apt-get update && apt-get install -y --no-install-recommends vim && apt-get clean

Build your image:

$ docker build . -t ubuntu-vim

Run a container and verify your software is installed:

$ docker run -it --rm ubuntu-vim /bin/bash
root@da908cdb6de4:/# which vim
/usr/bin/vim
root@da908cdb6de4:/# vim

Using Docker Health Check to monitor an application:

Create a docker file:

$ vi dockerfile
FROM httpd:latest
RUN apt-get update && apt-get install -y --no-install-recommends curl && apt-get clean
EXPOSE 80
HEALTHCHECK --interval=15s --retries=5 --timeout=30s --start-period=30s CMD curl -I -f "http://localhost:80" || exit 1

Build the image:

$ docker build . -t httpd-health

Run a container from the image:

$ docker run -d -P --name httpd-hc httpd-health:latest

See the running container:

$ docker ps
CONTAINER ID   IMAGE         COMMAND             CREATED        STATUS                            PORTS                   NAMES
3260a02199cc   httpd-health  "httpd-foreground"  8 seconds ago  Up 7 seconds (health: starting)   0.0.0.0:32777->80/tcp   httpd-hc

$ docker ps
CONTAINER ID   IMAGE         COMMAND             CREATED        STATUS                            PORTS                   NAMES
3260a02199cc   httpd-health  "httpd-foreground"  5 minutes ago  Up 5 minutes (healthy)            0.0.0.0:32777->80/tcp   httpd-hc

View the container console logs

$ docker logs -f httpd-hc 
127.0.0.1 - - [21/Dec/2018:17:30:07 +0000] "HEAD / HTTP/1.1" 200 -
127.0.0.1 - - [21/Dec/2018:17:36:13 +0000] "HEAD / HTTP/1.1" 200 -
172.17.0.1 - - [21/Dec/2018:17:36:16 +0000] "GET / HTTP/1.1" 304 -
172.17.0.1 - - [21/Dec/2018:17:36:26 +0000] "GET / HTTP/1.1" 304 -
127.0.0.1 - - [21/Dec/2018:17:36:28 +0000] "HEAD / HTTP/1.1" 200 

Stop the container:

$ docker stop httpd-hc

Linking Containers "mysql" and "adminer":

Run an instance of mysql and set an environment variable. Notice how we don't expose any ports on this container:

$ docker run --name mysql -e MYSQL_ROOT_PASSWORD=password -d mysql:5.7
Unable to find image 'mysql:5.7' locally
5.7: Pulling from library/mysql
27833a3ba0a5: Already exists 
864c283b3c4b: Pull complete 
…
Status: Downloaded newer image for mysql:5.7

Link the adminer container:

$ docker run --link "mysql:db" -p 8080:8080 adminer
Unable to find image 'mysql:5.7' locally
5.7: Pulling from library/mysql
27833a3ba0a5: Already exists 
864c283b3c4b: Pull complete 
…
Status: Downloaded newer image for mysql:5.7

Access adminer via browser and login:

overview

overview

Stop the container:

$ docker stop adminer
$ docker stop mysql


COMPOSE

Docker Compose

  • It's essentially a templated deployment model for multi-container applications. You use a YAML file. Rather than having to run multiple docker command lines, you place all the containers you need in one configuration file. And compose creates the links for you.
  • Note, compose does not allow for rolling updates and only deploys on one docker host.

Use Docker Compose to link containers "mysql" and "adminer":

Create the YAML file and include a persistent data volume:

$ vi docker-compose-db.yaml
version: '3.1'
	
services:
 db:
  image: mysql:5.7
  restart: always
  environment:
   MYSQL_ROOT_PASSWORD: password
  volumes:
   - dbdata:/var/lib/mysql
	
 adminer:
  image: adminer:latest
  restart: always
  ports:
   - 8080:8080
	
volumes:
 dbdata:
   driver: local

Run docker compose:

$ docker-compose -f docker-compose-db.yaml up -d
Creating dockerstuff_adminer_1 ... done
Creating dockerstuff_db_1      ... done

See the running container:

$ docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED              STATUS            PORTS                   NAMES
da0dc2067e76   mysql:5.7      "docker-entrypoint.s…"   About a minute ago   Up 58 seconds     3306/tcp, 33060/tcp     docker_db_1
5fdf1482cc10   adminer:latest "entrypoint.sh docke…"   About a minute ago   Up 58 seconds     0.0.0.0:8080->8080/tcp  docker_adminer_1

Access adminer via browser and login:

overview

overview

Create a "test" database and "test" table:

overview

overview

overview

Stop containers using docker compose:

$ docker-compose -f docker-compose-db.yaml down
Stopping dockerstuff_adminer_1 ... done
Stopping dockerstuff_db_1      ... done
Removing dockerstuff_adminer_1 ... done
Removing dockerstuff_db_1      ... done
Removing network dockerstuff_default

See the docker volume still exists:

$ docker volume ls
DRIVER              VOLUME NAME
local               dockerstuff_dbdata

See the volume contents via sudo and notice the "test" table created:

$ sudo ls /var/lib/docker/volumes/dockerstuff_dbdata/_data
auto.cnf    ca.pem	     client-key.pem  ibdata1	  ib_logfile1  performance_schema  public_key.pem   server-key.pem  TEST

Start the instances again:

$ docker-compose -f docker-compose-db.yaml up -d
Creating network "dockerstuff_default" with the default driver
Creating dockerstuff_db_1      ... done
Creating dockerstuff_adminer_1 ... done

Log back in and see the "test" DB we created has persisted:

overview



SCALING

There is a simple way within docker to scale an application, however the service container must be stateless e.g. you couldn't say have an authentication service running in one container maintaining state within that container, as if the app container issued another request and was load balanced over to another server container it wouldn't know the login state and therefore request another login.

overview

Create a docker compose file with a httpd service:

$ cat docker-compose-httpd.yaml
version: '3.1'
services:
 web:
   image: httpd:latest
   restart: always

Startup a single instance:

$ docker-compose -p scaletest -f docker-compose-httpd.yaml up -d
Starting scaletest_web_1 ... done

$ docker ps
CONTAINER ID   IMAGE          COMMAND              CREATED              STATUS           PORTS     NAMES
d947a5627c15   httpd:latest   "httpd-foreground"   About a minute ago   Up 23 seconds    80/tcp    scaletest_web_1

Scale out the instances for a total of 3 instances:

$ docker-compose -p scaletest -f docker-compose-httpd.yaml up -d --scale web=3
Starting scaletest_web_1 ... done
Creating scaletest_web_2 ... done
Creating scaletest_web_3 ... done
	
$ docker ps
CONTAINER ID   IMAGE          COMMAND              CREATED              STATUS           PORTS     NAMES
10ee4aeefcb8   httpd:latest   "httpd-foreground"   3 seconds ago        Up 1 second      80/tcp    scaletest_web_2
07470ec372d9   httpd:latest   "httpd-foreground"   3 seconds ago        Up 1 second      80/tcp    scaletest_web_3
d947a5627c15   httpd:latest   "httpd-foreground"   About a minute ago   Up 36 seconds    80/tcp    scaletest_web_1


SWARM

Docker Swarm

  • A cluster of docker hosts that work together to deploy containers. The coordination between the docker hosts is referred to as orchestration.
  • Made up of two different docket hosts - manager and worker.
  • Benefits - load balancing, highly available, scaling, rolling updates, self-heal.

Pre-requisites 3 VMs:

  1. VirtualBox - File, Preferences, Network and create a NAT Network called "NatNetwork" so the VMs can talk to each other. On your VirtualBox Linux VM select settings, network, select NAT Network.
  2. Clone your VM - Clone, generate new MAC addresses, and create a linked clone
  3. Verify each VM received its own IP address from the DHCP server
  4. Set hostnames dkmanager, workernode1 and workernode2 e.g. hostnamectl set-hostname workernode1
  5. Edit the host file on each VM adding the servers and IPs
  6. Verify you can ping between the 3 VM hosts
  7. Verify docker is working on all three nodes. (it should be fine as you cloned the primary VM)

Enable ports on the firewall:

$ firewall-cmd --permanent --add-port=2376/tcp
$ firewall-cmd --permanent --add-port=2377/tcp
$ firewall-cmd --permanent --add-port=7946/tcp
$ firewall-cmd --permanent --add-port=7946/udp
$ firewall-cmd --permanent --add-port=4789/udp
$ firewall-cmd --permanent --add-port=80/tcp
$ firewall-cmd --reload
$ systemctl restart docker

Launch the Swarm or Cluster:

$ docker swarm init --advertise-addr 10.0.2.15
Swarm initialized: current node (cthhb9ughwvwm2b8m6krtnzfi) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-3jn9z600gwpm9ot8fgiq8kufgow4chd1mni07svwe9i1o53wim-8hzm8brhnv4ruzke2xgjc6c79 10.0.2.15:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

Verify the manager status:

$ docker info
$ docker node ls

Add worker nodes:

[workernode1] $ docker swarm join --token SWMTKN-1-3jn9z600gwpm9ot8fgiq8kufgow4chd1mni07svwe9i1o53wim-8hzm8brhnv4ruzke2xgjc6c79 10.0.2.15:2377
[workernode2] $ docker swarm join --token SWMTKN-1-3jn9z600gwpm9ot8fgiq8kufgow4chd1mni07svwe9i1o53wim-8hzm8brhnv4ruzke2xgjc6c79 10.0.2.15:2377

See the list of node members:

$ docker node ls
ID                            HOSTNAME       STATUS    AVAILABILITY     MANAGER STATUS     ENGINE VERSION
cthhb9ughwvwm2b8m6krtnzfi *   dkmanager      Ready     Active           Leader             18.09.3
01xnssr9bs9z74bxssm2u2y25     workernode1    Ready     Active                              18.09.3
rdezth7cbxmh5jlpgg8pa29i9     workernode2    Ready     Active                              18.09.3

Launch the service in Swarm mode:

$ docker service create -p 80:80 --name webservice --replicas 3 httpd
$ docker service ls
ID               NAME         MODE         REPLICAS    IMAGE           PORTS
z96orvprn5s1     webservice   replicated   3/3         httpd:latest    *:80->80/tcp

Log into the web page from any node:

http://workernode1/
http://workernode2/
http://dkmanager/

Testing Container Self-Healing:

workernode2 ~]$ docker ps
CONTAINER ID    IMAGE         COMMAND             CREATED         STATUS         PORTS    NAMES
a02f005e5f5a    httpd:latest  "httpd-foreground"  7 minutes ago   Up 7 minutes   80/tcp   webservice.2.x1ci56r3a1ue9in51ryu8zsvc

Remove container on worker node 2:

workernode2 ~]$ docker rm a02f005e5f5a -f
a02f005e5f5a

Check whether a new container is deployed from the Manager node:

dkmanager ~]$ docker service ps webservice
ID             NAME             IMAGE          NODE          DESIRED STATE   CURRENT STATE              ERROR
kdteg161kjda   webservice.1     httpd:latest   workernode1   Running         Running 10 minutes ago                                    
xs29qsbumj8q   webservice.2     httpd:latest   workernode2   Running         Running 55 seconds ago                                    
x1ci56r3a1ue   \_ webservice.2  httpd:latest   workernode2   Shutdown        Failed about a minute ago  "task: non-zero exit (137)"   
v5g3gowwfitr   webservice.3     httpd:latest   dkmanager     Running         Running 10 minutes ago                                    

Scaling up the containers:

dkmanager ~]$ docker service scale webservice=5
webservice scaled to 5
overall progress: 5 out of 5 tasks 
1/5: running   [==================================================>] 
2/5: running   [==================================================>] 
3/5: running   [==================================================>] 
4/5: running   [==================================================>] 
5/5: running   [==================================================>] 
verify: Service converged 

$ docker service ls
ID               NAME         MODE         REPLICAS    IMAGE          PORTS
z96orvprn5s1     webservice   replicated   5/5         httpd:latest   *:80->80/tcp

$ docker service ps webservice
ID             NAME             IMAGE           NODE           DESIRED STATE   CURRENT STATE              ERROR
kdteg161kjda   webservice.1     httpd:latest    workernode1    Running         Running 15 minutes ago                                     
xs29qsbumj8q   webservice.2     httpd:latest    workernode2    Running         Running 6 minutes ago                                      
x1ci56r3a1ue   \_ webservice.2  httpd:latest    workernode2    Shutdown        Failed 6 minutes ago         "task: non-zero exit (137)"   
v5g3gowwfitr   webservice.3     httpd:latest    dkmanager      Running         Running 15 minutes ago                                     
2brymttzq6xy   webservice.4     httpd:latest    workernode1    Running         Running about a minute ago                                 
ofkjbi0hj1wr   webservice.5     httpd:latest    dkmanager      Running         Running about a minute ago     

Scale down the containers:

dkmanager ~]$ docker service scale webservice=2
webservice scaled to 2
overall progress: 2 out of 2 tasks 
1/2: running   [==================================================>] 
2/2: running   [==================================================>] 
verify: Service converged 

$ docker service ls
ID               NAME         MODE         REPLICAS    IMAGE          PORTS
z96orvprn5s1     webservice   replicated   2/2         httpd:latest   *:80->80/tcp
	
dkmanager ~]$ docker service ps webservice
ID             NAME             IMAGE           NODE           DESIRED STATE   CURRENT STATE              ERROR
kdteg161kjda   webservice.1     httpd:latest    workernode1    Running         Running 19 minutes ago                                 
xs29qsbumj8q   webservice.2     httpd:latest    workernode2    Running         Running 10 minutes ago                                 
x1ci56r3a1ue   \_ webservice.2  httpd:latest    workernode2    Shutdown        Failed 10 minutes ago    "task: non-zero exit (137)"   

Promote a worker node so that they can take over as a manager if needed:

dkmanager ~]$ docker node ls
ID                            HOSTNAME      STATUS     AVAILABILITY       MANAGER STATUS     ENGINE VERSION
cthhb9ughwvwm2b8m6krtnzfi *   dkmanager     Ready      Active             Leader             18.09.3
01xnssr9bs9z74bxssm2u2y25     workernode1   Ready      Active                                18.09.3
rdezth7cbxmh5jlpgg8pa29i9     workernode2   Ready      Active                                18.09.3
	
dkmanager ~]$ docker node promote workernode1
Node workernode1 promoted to a manager in the swarm.
	
dkmanager ~]$ docker node ls
ID                            HOSTNAME      STATUS     AVAILABILITY       MANAGER STATUS     ENGINE VERSION
cthhb9ughwvwm2b8m6krtnzfi *   dkmanager     Ready      Active             Leader             18.09.3
01xnssr9bs9z74bxssm2u2y25     workernode1   Ready      Active             Reachable          18.09.3
rdezth7cbxmh5jlpgg8pa29i9     workernode2   Ready      Active                                18.09.3

Constrain service to run on one node (e.g. if there was a specific volume they needed):

dkmanager ~]$ docker service ps webservice
ID              HOSTNAME      STATUS        AVAILABILITY   MANAGER STATUS     ENGINE VERSION
qodbwhfemxzh    webservice.1  nginx:latest  workernode1    Running            Running about an hour ago                       
g0tp3g6hi4fj    webservice.2  nginx:latest  workernode2    Running            Running about an hour ago                       
m00aojvje9cm    webservice.4  nginx:latest  dkmanager      Running            Running about an hour ago                       
	
dkmanager ~]$ docker service update --constraint-add "node.hostname==workernode1" webservice
webservice
overall progress: 3 out of 3 tasks 
1/3: running   [==================================================>] 
2/3: running   [==================================================>] 
3/3: running   [==================================================>] 
verify: Service converged 
	
dkmanager ~]$ docker service ps webservice
ID             NAME             IMAGE           NODE           DESIRED STATE   CURRENT STATE              ERROR
qodbwhfemxzh   webservice.1     nginx:latest    workernode1    Running         Running about an hour ago                       
nvy71utti8eu   webservice.2     nginx:latest    workernode1    Running         Running 13 seconds ago                          
g0tp3g6hi4fj   \_ webservice.2  nginx:latest    workernode2    Shutdown        Shutdown 14 seconds ago                         
k1eh2xgio1pe   webservice.4     nginx:latest    workernode1    Running         Running 9 seconds ago                           
m00aojvje9cm   \_ webservice.4  nginx:latest    dkmanager      Shutdown        Shutdown 10 seconds ago                         

Remove the service:

dkmanager ~]$ docker service rm webservice
webservice

Docker Swarm Load Balancing:

Load Balancing - See below for trying to access the nginx service on the worker server that is running a MySql container. The swarm service will automatically proxy the request over to the container that is running on the other node. Note: In real production you would have a HA proxy out front that points to all three servers. Let's prove this out - see below.

overview

Check docker service is running:

dkmanager ~]$ docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
yp4kb1p66bquae8x0vsjokhvx *   dkmanager           Ready               Active              Leader              18.09.3
0uez56kuhaa6dgv8wms6jkmel     workernode1         Ready               Active                                  18.09.3
t07id57u3n6y248rliobvjwx2     workernode2         Ready               Active                                  18.09.3

Create just one container:

dkmanager ~]$  docker service create -p 80:80 --name webservice --replicas 1 nginx

Validate the service is running:

dkmanager ~]$ docker service ps webservice
ID             NAME          IMAGE               NODE                DESIRED STATE       CURRENT STATE           ERROR     PORTS
qodbwhfemxzh   webservice.1  nginx:latest        workernode1         Running             Running 5 minutes ago 

Validate there is no containers running on the other servers:

workernode2 ~]$ docker ps
CONTAINER ID   IMAGE         COMMAND             CREATED             STATUS              PORTS               NAMES
	
dkmanager ~]$ docker ps
CONTAINER ID   IMAGE         COMMAND             CREATED             STATUS              PORTS               NAMES


STACK

Docker Stack Deploy

  • This basically does what docker compose does, however it allows to deploy across a swarm service.

Confirm you have a Swarm service running:

dkmanager dockerstuff]$ docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
yp4kb1p66bquae8x0vsjokhvx *   dkmanager           Ready               Active              Leader              18.09.3
0uez56kuhaa6dgv8wms6jkmel     workernode1         Ready               Active                                  18.09.3
t07id57u3n6y248rliobvjwx2     workernode2         Ready               Active                                  18.09.3

Create a compose yaml file:

services:
  apache_httpd:
    image: httpd:latest
    deploy:
      mode: replicated
      replicas: 2
      labels:
        com.docker.descr: "contact dave"
      restart_policy:
        condition: any
        delay: 5s
        max_attempts: 3
        window: 120s
      placement:
        constraints:
          - node.role == worker
      resources:
        limits:
          memory: 50M
        reservations:
          cpus: '0.10'
      update_config:
        parallelism: 1
        delay: 10s
        monitor: 5s

Deploy docker stack:

dkmanager dockerstuff]$ docker stack deploy -c docker-stackdeploy.yaml mystack
Creating network mystack_default
Creating service mystack_apache_httpd

Verify service is running. Note, we didn't have an exposed port!:

dkmanager dockerstuff]$ docker stack ls
NAME           SERVICES            ORCHESTRATOR
mystack        1                          Swarm
	
dkmanager dockerstuff]$ docker service ls
ID             NAME                   MODE            REPLICAS      IMAGE           PORTS
ksebzpin6ily   mystack_apache_httpd   replicated      2/2           httpd:latest        
	
dkmanager dockerstuff]$ docker service ps mystack_apache_httpd
ID             NAME                     IMAGE         NODE          DESIRED STATE   CURRENT STATE           ERROR     PORTS
8qrrmo16u7wo   mystack_apache_httpd.1   httpd:latest  workernode2   Running         Running 2 minutes ago                       
rz1e0978lgci   mystack_apache_httpd.2   httpd:latest  workernode1   Running         Running 2 minutes a

Perform a live update to the service, adding the exposed port that we forgot to include the yaml file:

dkmanager dockerstuff]$ docker service update --publish-add 80:80 mystack_apache_httpd
mystack_apache_httpd
overall progress: 2 out of 2 tasks 
1/2: running   [==================================================>] 
2/2: running   [==================================================>] 
verify: Service converged 

dkmanager dockerstuff]$ docker service ls
ID              NAME                   MODE           REPLICAS      IMAGE               PORTS
ksebzpin6ily    mystack_apache_httpd   replicated     2/2           httpd:latest        *:80->80/tcp
	
dkmanager dockerstuff]$ curl workernode1
<html><body><h1>It works!</h1></body></html>