Create a Docker Swarm Cluster on DigitalOcean
Table of Contents
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:
- Docker: Up & Running: Shipping Reliable Containers in Production
- Docker Deep Dive
- The Docker Book: Containerization is the new virtualization
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!