Deploy using docker swarm
Tags: docker, swarm, compose
Published: 2017-04-16 06:42:39

---
title: Deploy using docker swarm
published: true
description: I just finished an article about React and Redux and I wanted to show how I would deploy that using docker swarm.
tags: docker, swarm, compose
---

I just finished [this](https://dev.to/svinci/react-js-web-site-example-almost-real-life-like) tutorial that shows how to build a web site using ReactJS and Redux. For testing purposes I also showed how to build a REST API using nodeJS.

Now, let's deploy those two using docker swarm.

## Installation

First of all, you'll need to clone the React tutorial repository from [here](https://github.com/svinci/react-example).

Now, you'll need to install Docker. Installation instructions can be found [here](https://www.docker.com/).

Once you have docker running, you need to restart the docker daemon to make it run in swarm mode. This can be achieved by running the following command:

```
$ docker swarm init
```

Now you're good to go.

## Docker Images

Now we'll need to write the docker images for our API and our UI. 

### API Image

Let's start with the API. Here's the Dockerfile for our NodeJS API.

```
FROM node:boron

RUN mkdir -p /usr/src/app/config
WORKDIR /usr/src/app

COPY package.json /usr/src/app/
RUN npm install

COPY config/default.json /usr/src/app/config/default.json
COPY index.js /usr/src/app/index.js

EXPOSE 8100

CMD ["npm", "run", "start"]
```

We are building our image from `node:boron` (NodeJS 6). Then we're creating a directory in `/usr/src/app` to deploy the API there, copying the `package.json` there and installing dependencies.

Then we copy the configuration and our index.js file. We expose port 8100 and start the server.

Now we build the image running `docker build -t svinci/todo-api .`. Feel free to change the namespace.

### UI Image

Now, the UI will be a little more tricky. The container has to deploy an NGINX to work as a web server of the UI, and it has to have the rules to acces the API passing through it.

Let's start by making a directory called `conf` that will hold NGINX configuration. In it we'll write a file called `nginx.conf` with the following content.

```
user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections 1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '[$remote_addr] [$time_local] [$request] '
                      'status code: $status, response size: $body_bytes_sent, referer: "$http_referer", '
                      'user agent: "$http_user_agent", $request_time seconds';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;

    keepalive_timeout  65;

    gzip on;
    gzip_disable "msie6";

    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_buffers 16 8k;
    gzip_http_version 1.1;
    gzip_min_length 256;
    gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/vnd.ms-fontobject application/x-font-ttf font/opentype image/svg+xml image/x-icon;

    include /etc/nginx/conf.d/*.conf;
}
```

This basically sets the queue size, mime types, access log format and gzip. Now, in a subdirectory of `conf` called `conf.d`, we'll write the following in a file called `default.conf`.

```
server {
    listen       80;
    server_name  localhost;

    location /todos {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_pass http://api:8100;
    }

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

}
```

This sets the listening port to 80, configures a proxy pass to the API and sets the site entry point.

Now, with this done, here's the Dockerfile.

```
FROM nginx

COPY conf /etc/nginx

COPY index.html /usr/share/nginx/html
COPY bundle.js /usr/share/nginx/html

EXPOSE 80
```

We are building our image from `nginx`, copying our NGINX configuration, and copying our bundle and our index.html to the default static content location for the web server. Then we expose the port 80.

Now, before actually building the docker image, we have to do a tiny modification. This example was meant to be run locally, so we need to modify the file under `todo-site/src/client.js` to change `http://localhost:8100/todos` by just `/todos` so the UI executes the requests through our NGINX. It should look like this:

```
import * as superagent from "superagent";

export function get() {

    return new Promise((resolve, reject) => {
        superagent.get("/todos")
            .end((error, result) => {
                error ? reject(error) : resolve(result.body);
            });
    });

}

export function add(text) {

    return new Promise((resolve, reject) => {
        superagent.post("/todos")
            .send({'text': text})
            .end((error, result) => {
                error ? reject(error) : resolve(result.body);
            });
    });

}

export function toggle(id) {

    return new Promise((resolve, reject) => {
        superagent.patch("/todos/" + id)
            .end((error, result) => {
                error ? reject(error) : resolve(result.body);
            });
    });

}
```

Now we build the image running `npm install && npm run build && docker build -t svinci/todo-site .`.

## Compose

We now have to write our compose file. This file defines all the services we need running, with its dependencies, replication factor, networks and stuff. 

We need to write the below content into a file called `compose.yml` on the root of the repository.

```
version: "3"

services:
  api:
    image: svinci/todo-api
    networks:
      - todo_network
    ports:
      - 8100
    deploy:
      replicas: 1
      update_config:
        parallelism: 1
      restart_policy:
        condition: on-failure
  site:
    image: svinci/todo-site
    ports:
      - 80:80
    networks:
      - todo_network
    depends_on:
      - api
    deploy:
      replicas: 1
      update_config:
        parallelism: 1
      restart_policy:
        condition: on-failure

networks:
  todo_network:
```

We are defining two services here:

* api: The To Do REST API.
* site: Our NGINX with the static content.

As you see, we are setting the images it needs to use, and the network they belong to. 

We are also defining that the site depends on the API to work. 

Deployment configuration is being provided too. Notice the `replicas` under `deploy` for each service, we could have redundancy here out of the box. 

## Deploy

Now, let's deploy this. To deploy we need to run the following command:

```
docker stack deploy --compose-file=compose.yml todo_stack
```

If you visit `http://localhost/` in your browser after a couple seconds (NGINX takes like 15 seconds to start), you'll see the site fully working.

## Conclusion

It's fairly easy to dockerize stuff, and I really prefer to work this way as I don't need to install too much software on my computer (NGINX for example). Also, if you deploy like this in production, your local environment will ressemble production a lot, which is awesome.

You can find the source code of this example [here](https://github.com/svinci/react-example) on a branch called `docker-swarm`. 

See you in the comments!