Skip to main content

Create a Docker Swarm Cluster on DigitalOcean

·

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), Khadas VIM4 (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. 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 data center. One of these VMs will be the manager, while the other two will be workers.

Log in 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 decide 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 into 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 that looks 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 the 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 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 its usefulness. 😀 If you’d like to learn more about Docker, here are some book recommendations:

Audible also has many 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 #

2023-08-31 Revised language

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