8.7 KiB
title, date, description, tags, slug, draft
title | date | description | tags | slug | draft | ||
---|---|---|---|---|---|---|---|
Setup a Docker Swarm cluster for less than $30 / month | 2022-02-13 | Build an opinionated containerized platform for developer... |
|
build-your-own-homelab-docker-swarm-cluster | true |
{{< lead >}} Build your own cheap while powerful self-hosted complete CI/CD solution by following this opinionated guide 🎉 {{< /lead >}}
Why Docker Swarm 🧐 ?
Because Docker Swarm Rocks !
Even if Docker Swarm has lost the enterprise graduate orchestration containers war, you don't have to throw yourself into all Kubernetes fuzzy complicated things for a simple homelab, unless for custom training of course.
If you know how to use docker-compose, you're already ready for Docker Swarm which use almost the same API with addition of specific deploy config.
I'll try to show you step by step how to install your own cheap containerized cluster for less than $30 by using Hetzner, one of the best Cloud provider on European market, with cheap but powerful VPS.
So the prerequisites before continue :
- Have some knowledge on docker-compose setups
- Be comfortable with SSH terminal
- Registered for a Hetzner Cloud account
- A custom domain, I'll use
example.org
here - A account to a transactional mail provider as mailgun, sendgrid, sendingblue, etc.
{{< alert >}} You can of course apply this guide on any other cloud provider, but I doubt that you can achieve lower price. {{< /alert >}}
Final goal 🎯
In the end of this multi-steps guide, you will have complete working production grade secured cluster, backup included, with optional monitoring and complete development CI/CD workflow.
1. Cluster initialization 🌍
- Initial VPS setup for docker under Ubuntu 20.04 with proper Hetzner firewall configuration
Docker Swarm
installation, 1 manager and 2 workersTraefik
, a cloud native reverse proxy with automatic service discovery and SSL configurationPortainer
as simple GUI for containers management
2. The stateful part 💾
For all data critical part, I choose to use 1 dedicated VPS. We will install :
GlusterFS
as network filesystem, configured for cluster nodesLoki
withPromtail
for centralized logs, fetched from data node and docker containersPostgreSQL
as main production databaseMySQL
as additional secondary database (optional)
Note as I will not set up this for HA (High Availability) here, as it's a complete another topic. So this data node will be our SPF (Single Point of Failure) with only one file system and DB.
{{< alert >}}
There are many debates about using databases as docker container, but I personally prefer use managed server for better control, local on-disk performance, central backup management and easier possibility of database clustering.
Note as on the Kubernetes world, run containerized databases becomes reality thanks to powerful operators that provide easy clustering. The is obviously no such things on Docker Swarm 🙈
{{< /alert >}}
Data Backup (optional)
Because backup should be taken care from the beginning, I'll show you how to use Restic
for simple backups to external S3 compatible bucket.
3. Testing the cluster ✅
We will use the main portainer GUI in order to install following tools :
pgAdmin
andphpMyAdmin
as web database managers (optional)Diun
(optional), very useful in order to be notified for all used images update inside your Swarm cluster- Some demo containerized samples that will show you how simple is it to install self-hosted web apps thanks to your shiny new cluster as
redmine
,n8n
4. Monitoring 📈
This is an optional part, feel free to skip. We'll set up production grade monitoring and tracing with complete dashboards.
Prometheus
as time series DB for monitoring- We will configure many metrics exporter for each critical part (Data node, PostgreSQL, MySQL, containers detail thanks to
cAdvisor
) - Basic usage of PromQL
- We will configure many metrics exporter for each critical part (Data node, PostgreSQL, MySQL, containers detail thanks to
Jaeger
as tracing tools- We will use
Elasticsearch
as main data storage
- We will use
Traefik
configuration for metrics and trace as perfect sampleGrafana
as GUI dashboard builder with many battery included dashboards- Monitoring all the cluster
- Node, PostgreSQL and MySQL metrics
- Navigate through log history of all containers and data server node thanks to
Loki
like ELK, with LogQL
5. CI/CD setup 💻
Gitea
as lightweight centralized control version, in case you want get out of Github / GitLab CloudPrivate docker registry
with minimal UI for all your custom app images that will be built on your development process and be used as based image for your production docker on clusterDrone CI
as self-hosted CI/CD solutionSonarQube
as self-hosted quality code control
Finally, we'll finish this guide by a simple mini-app development with above CI/CD integration !
Cluster Architecture 🏘️
Note as this cluster will be intended for developer user with complete self-hosted CI/CD solution. So for a good cluster architecture starting point, we can imagine the following nodes :
manager-01
: The frontal manager node, with proper reverse proxy and some management toolsworker-01
: A worker for your production/staging appsrunner-01
: An additional worker dedicated to CI/CD pipelines executiondata-01
: The critical data node, with attached and resizable volume for better flexibility
{{< mermaid >}} flowchart TD subgraph manager-01 traefik((Traefik))<-- Container Discovery -->docker[Docker API] end subgraph worker-01 my-app-01((My App 01)) my-app-02((My App 02)) end subgraph runner-01 runner((Drone CI runner)) end subgraph data-01 logs[Loki] postgresql[(PostgreSQL)] files[/GlusterFS/] mysql[(MySQL)] end manager-01 == As Worker Node ==> worker-01 manager-01 == As Worker Node ==> runner-01 traefik -. reverse proxy .-> my-app-01 traefik -. reverse proxy .-> my-app-02 my-app-01 -.-> postgresql my-app-02 -.-> mysql my-app-01 -.-> files my-app-02 -.-> files {{< /mermaid >}}
Note as the hostnames correspond to a particular type of server, dedicated for one task specifically. Each type of node can be scale as you wish :
manager-0x
For advanced resilient Swarm quorumworker-0x
: For better scaling production apps, the easiest to set uprunner-0x
: More power for pipeline executiondata-0x
: The hard part for data HA, with GlusterFS replications, DB clustering for PostgreSQL and MySQL, etc.
{{< alert >}}
For a simple production cluster, you can start with only manager-01
and data-01
as absolutely minimal start.
For a development perspective, you can skip worker-01
and use manager-01
for production running.
You have plenty choices here according to your budget !
{{< /alert >}}
Hetzner VPS 🖥️
Here some of the cheapest VPS options we have :
- CPX11 (AMD) = 2C/2G/40Go = €4.79
- CX21 (Intel) = 3C/4G/80Go = €5.88
- CPX21 (AMD) = 3C/4G/80Go = €8.28
My personal choice for a good balance between cheap and well-balanced cluster :
manager-01
: CX21, I'll privilege RAMrunner-01
: CPX11, 2 powerful core is better for buildingworker-01
anddata-01
: CX21 VS CPX21 (just a power choice matter)
We'll take additional volume of 60 Go for €2.88
We finally arrive to following respectable budget range : €25.31 - $31.31
The only difference being choice between Xeon VS EPIC as CPU power for worker
and data
nodes, which will our main production application nodes. A quick sysbench will indicates around 70-80% more power for AMD (test date from 2022-02).
Choose wisely according to your needs.
If you don't need of worker
and runner
nodes, with only one simple standalone docker host without Swarm mode, you can even go down to €14,64 with only 2 CX21 in addition to volume.
{{< alert >}}
If you intend to have your own self-hosted GitLab for an enterprise grade CI/CD workflow, you should run it on node with 8 GB of RAM.
4 GB is doable if you run just one single GitLab container on it with Prometheus mode disabled and external PostgreSQL.
{{< /alert >}}
Let's party
Before continue I presume you have :
- Hetzner cloud account ready
- Configured hcloud cli
Firstly create the new context for your new project :
{{< tabs tabTotal="2" >}} {{< tab tabName="Bash" >}}
cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 64 | head -n 1
{{< /tab >}} {{< tab tabName="Powershell" >}}
-join ((48..57) + (65..90) + (97..122) | Get-Random -Count 64 | % {[char]$_})
{{< /tab >}} {{< /tabs >}}