This tutorial will guide you through the process of setting up a Docker Swarm cluster on DigitalOcean. It’ll also show you how to deploy Traefik as a reverse proxy for your services, and Swarmpit as a web interface for your cluster. We’ll use Fedora 30 as the OS for this tutorial.

If you sign up to DigitalOcean using this link you’ll receive $50 to spend on their services over 30 days.

The finished cluster will consist of these components:

  • 3 VMs on DigitalOcean
  • Fedora 30
  • Traefik
  • HTTPS using Let’s Encrypt
  • SwarmPit

If you don’t want to host your services in the cloud, you could also run a Docker Swarm cluster like this on your own hardware, for example on a cluster of Raspberry Pis (ARM64), FriendlyElec Nano Pi M4s (ARM64) or Intel NUCs (x86_64).

Prepare

Make sure you have a domain name first of all. If you don’t have one, you can register one at Porkbun for example. In this tutorial we’ll use example.com, and you should replace it with your domain wherever you find it.

Next, create three VMs (Droplets) on DigitalOcean, all running Fedora 30 with “Private networking” enabled. Make sure they are all in the same datacenter. One of these VMs will be the manager, while the other two will be workers.

Login to each of the VMs and become root: sudo su - (if you’re not already logging in as root). Also make sure you don’t include the # at the beginning of each command if you’re copy-and-pasting.

Configure the VMs

The following should be done on all three (or however many you decided to make) VMs.

Install vim and upgrade the system:

# dnf install -y vim
# dnf update -y

Now, we’ll need the private IP address of each VM. You can find the private IP address in the DigitalOcean web console or using the DigitalOcean command line tool.

Add the hostnames and private IP addresses to the /etc/hosts file (replace example.com with your domain):

# echo "11.11.11.11 node-1.example.com" >> /etc/hosts # 11.11.11.11 is the private IP of node 1
# echo "22.22.22.22 node-2.example.com" >> /etc/hosts # 22.22.22.22 is the private IP of node 2
# echo "33.33.33.33 node-3.example.com" >> /etc/hosts # 33.33.33.33 is the private IP of node 3

Reboot if the kernel was upgraded when running dnf update -y:

# reboot

Log back in to each node and continue by adding the Docker CE repository:

# dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo

Install Docker, containerd and Docker Compose:

# dnf install -y docker-ce docker-ce-cli containerd.io docker-compose

Enable and start the Docker service:

# systemctl enable --now docker

Configure the Docker Swarm Manager

The following should be run only on the manager.

Initialize the Docker Swarm cluster by running the following, replacing 11.11.11.11 with the private IP address of the node you’re logged in to (master node):

# docker swarm init --advertise-addr 11.11.11.11

This will output a command looking something like this: docker swarm join --token [...]. Copy it and save it for later. We’ll use it to join the workers to the cluster.

We’ll need an internal network for Traefik. Let’s create it:

# docker network create --scope=swarm --driver=overlay proxy

Install Traefik and Configure with Let’s Encrypt

The following should be run only on the manager.

Make a directory to store the required files:

# mkdir -p /var/swarm/traefik

Install httpd-tools which we’ll need to be able to use htpasswd:

# dnf install -y httpd-tools

Create a basic HTTP auth file (.htpasswd) for the Traefik web interface (admin will be your username):

# htpasswd -c /var/swarm/traefik/.htpasswd admin
# chmod 0600 /var/swarm/traefik/.htpasswd

Create the Traefik configuration file (traefik.toml):

# vim /var/swarm/traefik/traefik.toml 

It should look like the configuration below. Copy and paste it, replacing example.com with your domain and [email protected] with your email address (email address will be used to generate your Let’s Encrypt certificates using the ACME protocol).

logLevel = "DEBUG"
InsecureSkipVerify = true
defaultEntryPoints = ["https", "http"]

[traefikLog]
filePath = "/traefik.log"

[accessLog]
filePath = "/access.log"

[entryPoints]
  [entryPoints.http]
  address = ":80"
    [entryPoints.http.redirect]
    entryPoint = "https"
  [entryPoints.https]
  address = ":443"
    [entryPoints.https.tls]

[api]
  entryPoint = "traefik"
  dashboard = true
  address = ":8080"

[file]
  watch = true
  filename = "/rules.toml"

[acme]
email = "[email protected]"
storage = "acme.json"
entryPoint = "https"
onDemand = false
acmeLogging = true
[acme.dnsChallenge]
  provider = "digitalocean"
  delayBeforeCheck = 0
[[acme.domains]]
  main = "*.example.com"
  sans = ["example.com"]

[docker]
domain = "example.com"
watch = true
swarmMode = true
network = "proxy"

For exact specifications of this file, please refer to the Traefik documentation.

Create a couple of required files (ACME configuration, Traefik rules and logs), and set permissions:

# touch /var/swarm/traefik/{rules.toml,acme.json,traefik.log,access.log}
# chmod 0600 /var/swarm/traefik/{rules.toml,acme.json}
# chmod 0744 /var/swarm/traefik/{traefik.log,access.log}

Continue by creating the Traefik docker-compose.yml file:

# vim /var/swarm/traefik/docker-compose.yml

Use the configuration below, replacing example.com with your domain name like earlier, and MY_DO_TOKEN with your DigitalOcean API token, which you can find in the DigitalOcean web console, under the “API” tab.

version: "3"

services:
  traefik:
    hostname: traefik
    image: traefik:1.7.12-alpine
    ports:
      - 80:80
      - 443:443
    volumes:
      - /run/docker.sock:/var/run/docker.sock:ro
      - /var/swarm/traefik/traefik.toml:/traefik.toml
      - /var/swarm/traefik/rules.toml:/rules.toml
      - /var/swarm/traefik/traefik.log:/traefik.log
      - /var/swarm/traefik/access.log:/access.log
      - /var/swarm/traefik/acme.json:/acme.json
      - /var/swarm/traefik/.htpasswd:/.htpasswd
    networks:
      - proxy
    deploy:
      labels:
        - traefik.enable=true
        - traefik.backend=traefik
        - traefik.backend.loadbalancer.swarm=true
        - traefik.port=8080
        - traefik.frontend.rule=Host:traefik.example.com
        - traefik.docker.network=proxy
        - traefik.frontend.headers.SSLRedirect=true
        - traefik.frontend.headers.STSSeconds=315360000
        - traefik.frontend.headers.browserXSSFilter=true
        - traefik.frontend.headers.contentTypeNosniff=true
        - traefik.frontend.headers.forceSTSHeader=true
        - traefik.frontend.headers.SSLHost=traefik.example.com
        - traefik.frontend.headers.STSIncludeSubdomains=true
        - traefik.frontend.headers.STSPreload=true
        - traefik.frontend.headers.frameDeny=true
        - traefik.frontend.auth.basic.usersFile=/.htpasswd
      placement:
        constraints:
          - node.role == manager
    environment:
      - DO_AUTH_TOKEN=MY_DO_TOKEN

networks:
  proxy:
    external: true

Finally we’re ready to deploy Traefik to our Docker Swarm cluster:

# docker stack deploy traefik --compose-file /var/swarm/traefik/docker-compose.yml

Traefik should be running within a couple of seconds to a few minutes.

Deploy Swarmpit as a Cluster Web Interface

The following should be run only on the manager.

Create a directory for our Swarmpit files:

# mkdir -p /var/swarm/swarmpit

Create the Swarmpit docker-compose.yml file:

# vim /var/swarm/swarmpit/docker-compose.yml

Use the configuration below, replacing example.com with your domain name like we’ve done before.

version: '3.3'

services:
  app:
    image: swarmpit/swarmpit:1.7
    environment:
      - SWARMPIT_DB=http://db:5984
    volumes:
      - /run/docker.sock:/var/run/docker.sock:ro
    networks:
      - proxy
      - net
    deploy:
      labels:
        - traefik.enable=true
        - traefik.backend=swarmpit
        - traefik.backend.loadbalancer.swarm=true
        - traefik.docker.network=proxy
        - traefik.frontend.rule=Host:swarmpit.example.com
        - traefik.port=8080
        - traefik.frontend.headers.SSLRedirect=true
        - traefik.frontend.headers.STSSeconds=315360000
        - traefik.frontend.headers.browserXSSFilter=true
        - traefik.frontend.headers.contentTypeNosniff=true
        - traefik.frontend.headers.forceSTSHeader=true
        - traefik.frontend.headers.SSLHost=swarmpit.example.com
        - traefik.frontend.headers.STSIncludeSubdomains=true
        - traefik.frontend.headers.STSPreload=true
        - traefik.frontend.headers.frameDeny=true
      resources:
        limits:
          cpus: '0.50'
          memory: 1024M
        reservations:
          cpus: '0.25'
          memory: 512M
      placement:
        constraints:
          - node.role == manager

  db:
    image: couchdb:2.3.0
    volumes:
      - dbdata:/opt/couchdb/data
    networks:
      - net
    deploy:
      resources:
        limits:
          cpus: '0.30'
          memory: 512M
        reservations:
          cpus: '0.15'
          memory: 256M

  agent:
    image: swarmpit/agent:latest
    environment:
      - DOCKER_API_VERSION=1.35
    volumes:
      - /run/docker.sock:/var/run/docker.sock:ro
    networks:
      - net
    deploy:
      mode: global
      labels:
        swarmpit.agent: 'true'
      resources:
        limits:
          cpus: '0.10'
          memory: 64M
        reservations:
          cpus: '0.05'
          memory: 32M

networks:
  net:
    driver: overlay
    attachable: true
  proxy:
    external: true

volumes:
  dbdata:
    driver: local

Now, deploy our Swarmpit web interface, database, agents and all:

# docker stack deploy swarmpit --compose-file /var/swarm/swarmpit/docker-compose.yml

It’ll take a little while before Swarmpit is up and running in the cluster.

Join Workers to the Cluster

The last step in this tutorial is joining the other nodes, the workers to the cluster. Hopefully you still have the command from the step when you created the cluster. Run the command you saved way back then:

# docker swarm join --token SWMTKN-1-[...] 11.11.11.11:2377

That’s it! You should now have a Docker Swarm cluster running on DigitalOcean!

You should be able to access your newly deployed Docker Swarm services by pointing your browser at the following URLs (replace example.com once again):

  • Traefik: treafik.example.com – log in with the credentials you chose when creating the .htpasswd file
  • Swarmpit: swarmpit.example.com – you’ll be asked to create an account on your first visit

Future tutorials on this site will feature interesting software you can deploy to your Docker Swarm cluster. 😊

Last Words

Phew! This was by far the longest tutorial yet. Hopefully the length matches it’s usefulness. 😀 If you’d like to learn more about Docker, here are some book recommendations:

Audible also has a bunch of books on Docker and other container technologies like Kubernetes. If you sign up using this link you’ll get 30 days for free!

Hope you enjoyed this (long) tutorial and that you learned something. I sure learned a lot writing it. 😊

Revision

2019-11-01 The hostnames in /etc/hosts were wrong. Thanks to deatharse for the catch!