Tutorial with Docker Swarm and Stack
Tags: docker
Published: 2020-10-01 12:21:58

# Objectives

We will
1. Create a docker image and push it to docker hub
2. Pull the image from the hub and create a stack.


# Procedures

First I created a directory as below.

```
$ mkdir docker-stack-tutorial
```

We will create the `Dockerfile`, and some python scripts.
The directory should look like below.

```
$ tree
.
├── Dockerfile
├── app.py
└── requirements.txt

0 directories, 3 files
```

The dockerfile is as below.

```
# Dockerfile

FROM python:3.7

WORKDIR /app

ADD . /app

RUN pip install -r requirements.txt

EXPOSE 80

ENV NAME World

CMD ["python", "app.py"]
```

app.py for python is written as below.

```
# app.py


from flask import Flask
from redis import Redis, RedisError
import os
import socket

# Connect to Redis
redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)

app = Flask(__name__)

@app.route("/")
def hello():
    try:
        visits = redis.incr('counter')
    except RedisError:
        visits = "<i>cannot connect to Redis, counter disabled</i>"

    html = "<h3>Hello {name}!</h3>" \
           "<b>Hostname:</b> {hostname}<br/>" \
           "<b>Visits:</b> {visits}"
    return html.format(name=os.getenv('NAME', "world"), hostname=socket.gethostname(), visits=visits)

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=80)

```

Finally, the requirements.txt is:

```
# requirements.txt
Flask
Redis
```


Now we will build and run the docker image.

```
$ docker build -t python-server .
Sending build context to Docker daemon   5.12kB
Step 1/7 : FROM python:3.7
 ---> 5c1cd4638fb7
............

$ docker run -p 4000:80 python-server
 * Serving Flask app "app" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://0.0.0.0:80/ (Press CTRL+C to quit)
```

Go to `localhost:4000` and you should see this page (For some reason, redis was not working.):

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/kfdhmu4ipqjsk04ujpmi.png)

We see that the container id is `99f360ce0c36` here. We will commit the image, and push it to docker hub.

```
$ docker login
Authenticating with existing credentials...
Login Succeeded

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS              PORTS                  NAMES
99f360ce0c36        python-server       "python app.py"     About a minute ago   Up About a minute   0.0.0.0:4000->80/tcp   keen_galois

$ docker commit 99f360ce0c36 python-server:latest
sha256:9dbcf7df033bb595c6e4c6528dd125716f11355d43dcea4dc8ccbaaced38c44c

$ docker tag python-server:latest USERNAME/python-server:latest

$ docker push USERNAME/python-server:latest
```

Now we will create our `docker-compose.yml` file.

```
version: "3"
services:
  web:
    image: USERNAME/python-server:latest
    deploy:
      replicas: 5
      resources:
        limits:
          cpus: "0.1"
          memory: 50M
      restart_policy:
        condition: on-failure
    ports:
      - "80:80"
    networks:
      - webnet
networks:
  webnet:
```

We will initialize the swarm and deploy it.

```
$ docker swarm init
Swarm initialized: current node (nrgwdwv97ioguu0gqq7t19kln) is now a manager.

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

    docker swarm join --token SWMTKN-1-3lcxtiaau24gw21h2bgkd1ptavl56yczedbcm7d4e865naj8ks-7c5sgf7jh4youa7xsq2l3dt3j 192.168.65.3:2377

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

$ docker stack deploy -c docker-compose.yml getstartedexample
Creating network getstartedexample_webnet
Creating service getstartedexample_web
```

We can see all the nodes here using `ps`.

```
$ docker stack ps getstartedexample
ID                  NAME                      IMAGE                                  NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
x3xradislkhr        getstartedexample_web.1   USERNAME/python-server:latest   docker-desktop      Running             Running 12 seconds ago
shlygtlq66e0        getstartedexample_web.2   USERNAME/python-server:latest   docker-desktop      Running             Running 12 seconds ago
uk1wexhimc8a        getstartedexample_web.3   USERNAME/python-server:latest   docker-desktop      Running             Running 12 seconds ago
rijovk7yzr48        getstartedexample_web.4   USERNAME/python-server:latest   docker-desktop      Running             Running 12 seconds ago
avlqa3hxgbt2        getstartedexample_web.5   USERNAME/python-server:latest   docker-desktop      Running             Running 12 seconds ago

```

When you access `localhost`, you should see the node ids every time you refresh.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/mhqoctf3w6pgoe9zo6sz.png)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/7rmwewrxqo59fh6topon.png)

Use the command below to exit.

```
$ docker stack rm getstartedexample
Removing service getstartedexample_web
Removing network getstartedexample_webnet
```