Files

454 lines
14 KiB
Markdown
Raw Permalink Normal View History

2021-12-12 23:57:03 +00:00
---
2022-05-24 12:08:01 +01:00
title: Building a Docker Swarm With Pi's 👾 👾 👾 👾
2024-11-29 02:26:53 +00:00
description: Pis are very capable computers, when banded together they're almost unstoppable
tags:
- docker
- home
- lab
- raspberry
- pi
author: ahmza
date: 2021-12-12
2021-12-23 00:23:39 +00:00
draft: false
2024-01-05 23:54:51 +00:00
weight:
2024-11-29 02:26:53 +00:00
featureimage: feature.webp
lastmod:
2021-12-12 23:57:03 +00:00
---
2024-01-05 23:54:51 +00:00
> This post is no longer valid as the cluster runs Kubernetes. The build process
> is the same
2021-12-14 00:40:05 +00:00
## **Intro**
2024-01-05 23:54:51 +00:00
The _Raspberry Pi_ from it's first iteration has been a great hit. Adopted by
most as the computer of choice for projects. The Pi is a fully fledged computer
that fits in your pocket and the most recent edition boasts an ARM64 CPU and 8GB
of RAM.
Now one _Raspberry Pi 4 8GB_ is more than powerful enough for most workloads,
**so you may even get away with a single 4GB model**. Taking into consideration
their low price and power consumption why not go all out and build a cluster.
My excuse to build a Pi cluster: I would like to migrate the services running on
a **bare metal server that sucks a lot of power to a low power solution.**
2021-12-12 23:57:03 +00:00
### **My Setup**
2024-01-05 23:54:51 +00:00
- 4 x _Raspberry Pi 4 8GB_
- 4 x 32GB SD cards
- 4 x PoE hats
- 1 x 5 port PoE Switch
2024-11-29 02:26:53 +00:00
### **Point Of This post**
2021-12-12 23:57:03 +00:00
2024-01-05 23:54:51 +00:00
After following this post you will have setup _Docker swarm_ on a cluster of Pis
**- stay even if you have one -** and understand how to bend it
to your will 🦾.
### **Why** **_Docker Swarm_** 🧐
2021-12-12 23:57:03 +00:00
While we can manage each docker node - _Raspberry Pi_ - individually
, **_Docker swarm_ enables us to manage all the devices from a single
main node** making managing the cluster a lot easier.
Think of _Docker swarm_ as a set of 🤖 hands which will run tasks on
2024-01-05 23:54:51 +00:00
each individual node for us. Some of these tasks are:
- Cluster Management
- Deployment and Scaling across the cluster
- Multi-host networking
- load balancing
2024-11-29 02:26:53 +00:00
### **Well Why not** **_Kubernetes_**
2024-01-05 23:54:51 +00:00
Although _Kubernetes_ is awesome, it adds a bunch of complexity I'm not ready
for. As well as that, from my experience creating _docker-compose_ files is a
lot easier than writing manifests. **In the future I will try _Kubernetes_**
2021-12-12 23:57:03 +00:00
### **Required Stuff**
2024-01-05 23:54:51 +00:00
2021-12-12 23:57:03 +00:00
> The links below are reccomendations/stuff I used
2024-01-05 23:54:51 +00:00
- [_Raspberry Pi_ 4B](https://thepihut.com/products/raspberry-pi-4-model-b?variant=31994565689406) (Reccomend 4-8 @ 8GB) (Or newer)
- [_Raspberry Pi_ PoE Hats](https://thepihut.com/products/power-over-ethernet-poe-hat-d-for-raspberry-pi) ([PoE+](https://thepihut.com/products/raspberry-pi-poe-plus-hat)) (Check compatibility with Pi Version)
- [Cluster Case](https://thepihut.com/products/cluster-case-for-raspberry-pi) (Get any case you want)
- [PoE switch](https://www.amazon.co.uk/dp/B0769C24T1/) (get one with enough 1Gb ports for the number of pis you want)
- [RJ45 Ethernet Cable](https://www.amazon.co.uk/gp/product/B00C4U030G) (Recommend short cables as it will look neater)
- [Micro SD cards](https://www.amazon.co.uk/gp/product/B08J4HJ98L) (Get enough cards for the number of Pis, Get the biggest cards you can afford)
**This is an expensive project**, but the best part is **you can add to the
cluster over time** spreading the cost, which is how I managed to buy all the
hardware. If I could start again though I would **buy equipment that allows room
to grow** so an 8 port PoE switch instead of a 4 port if you know you will scale
to 8 nodes
This cluster should be capable of serving most of your needs even with 2 nodes.
Put it this way, if you have 4 x 8GB nodes then you have a combined total of:
- 32GB of RAM
- 16 physical CPU cores @ 1.5GHz (You can OverClock !!)
2021-12-12 23:57:03 +00:00
#### **Money Saving Tips**
2024-01-05 23:54:51 +00:00
- 1-2 nodes to start
- Drop PoE and use normal power cables
- Raspberry Pis with less memory i.e 4GB
- Cheaper case or make one using [these](https://www.amazon.co.uk/dp/B091KJNZLB/)
- Smaller SD Cards try not to go below 32GB (Recommended 64GB If money saved elsewhere)
2021-12-12 23:57:03 +00:00
## **BUILD TIME !!**
2021-12-14 00:40:05 +00:00
2024-11-29 02:26:53 +00:00
### **Image The SD Cards**
2024-01-05 23:54:51 +00:00
The CPU in a RaspberryPi 4 is the ARM64 architecture. When selecting Raspbian
pick the 64-BIT option. 32bit variants will work but not make use of the hardware
capabilities.[RaspberryPi Imager](https://www.raspberrypi.com/software/)
when prompted with **"Use OS Customisation"** go with **"Edit Settings"**.
General:
- Set the hostname
- Set a username and password
- Set Locale settings
2024-11-29 02:26:53 +00:00
Services:
2024-01-05 23:54:51 +00:00
- Enable SSH
- Allow public-key authentication only
- Set your SSH Key
> If you dont have an SSH key, read my post on SSH [Securing
> SSH](https://blog.ahmza.com/posts/securing-ssh/)
2021-12-12 23:57:03 +00:00
2024-11-29 02:26:53 +00:00
### Assemble the Cluster
2021-12-14 00:40:05 +00:00
2024-01-05 23:54:51 +00:00
Assembly depends on the case you bought, instructions should be included. Go
ahead and piece together the cluster 🛠
**There is an order of operations that I recommend:**
2021-12-14 00:40:05 +00:00
2024-01-05 23:54:51 +00:00
- Install PoE hats onto the Pis
- Install the SD Cards into the Pis
- Assemble the case and install Pis
- plug ethernet cables in
2021-12-23 00:47:49 +00:00
2024-01-05 23:54:51 +00:00
**Now allow the Pis to boot for a few minutes**
2021-12-14 00:40:05 +00:00
2024-11-29 02:26:53 +00:00
### **Sort Out the Network bits**
2021-12-14 00:40:05 +00:00
2024-01-05 23:54:51 +00:00
There are two methods you can use to set a static IP address, one is on your
DHCP server and the other is on the device itself. Personally I prefer to set
static IPs on the device itself so I will cover that here.
To find the current ip of your Pis you can go to your DHCP server and look for
the DHCP leases for the Pis.
2021-12-14 00:40:05 +00:00
2021-12-17 19:15:08 +00:00
> there are naming conventions for naming primary and secondary
2024-01-05 23:54:51 +00:00
> things but this is your cluster so be creative with it, I have my
> primary node named `Goob` and the secondary nodes named `Doris1` > `Doris2` and `Doris3` > [reference](https://www.youtube.com/watch?v=oFb7FVe8sdQ)
2021-12-17 19:15:08 +00:00
2021-12-14 00:40:05 +00:00
**SSH to the Pi** then open and edit the following files
2021-12-12 23:57:03 +00:00
2024-01-05 23:54:51 +00:00
- /etc/dhcpcd.conf
2024-11-29 02:26:53 +00:00
\_ knowing we will be using only static IPs here I delete
everything in the file add the following
2021-12-17 19:15:08 +00:00
2024-01-05 23:54:51 +00:00
```
interface eth0
static ip_address=<your-Pi-ip>/24
static routers=<your-router-ip>
static domain_name_servers=<your-dns-ip-usually-same-as-router>
```
2021-12-17 19:15:08 +00:00
2024-01-05 23:54:51 +00:00
**Now reboot the pi**
2021-12-17 19:15:08 +00:00
2024-01-05 23:54:51 +00:00
```bash
sudo shutdown -r now
```
2021-12-17 19:15:08 +00:00
2024-01-05 23:54:51 +00:00
### **Provision with** **_Ansible_**
2021-12-17 19:15:08 +00:00
2024-01-05 23:54:51 +00:00
Ansible will allow us to setup our RaspberryPi Cluster from a remote machine
where we only administer the commands once and Ansible will replicate across the
cluster.
2021-12-17 19:15:08 +00:00
2024-01-05 23:54:51 +00:00
> Make sure pipx is installed. [Install
> Instructions](https://pipx.pypa.io/stable/)
2021-12-17 19:15:08 +00:00
2024-01-05 23:54:51 +00:00
Install Ansible
[Install Docs](https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#installing-and-upgrading-ansible-with-pipx)
2021-12-17 19:15:08 +00:00
2024-01-05 23:54:51 +00:00
```bash
pipx install --include-deps ansible
```
2021-12-17 19:15:08 +00:00
2024-01-05 23:54:51 +00:00
A playbook defines steps _Ansible_ needs to take on the remote machine to
provision it. Playbooks are written in yaml so get ready to make sure your
indents are correct 😬
2021-12-17 19:15:08 +00:00
**Creating the playbook**
2024-01-05 23:54:51 +00:00
Copy what I have and it should work -- after changing a thing or two -- I
encourage you to read the Ansible
[Docs](https://docs.ansible.com/ansible/latest/user_guide/index.html) and make
any changes you feel are necessary.
2021-12-17 19:15:08 +00:00
2024-01-05 23:54:51 +00:00
> Check this [Gitlab Repo](https://gitlab.com/ahmza/ansible-playbooks/-/tree/main/docker-swarm?ref_type=heads) for the most up to date playbook.
2021-12-19 16:59:34 +00:00
2021-12-23 00:06:13 +00:00
**This script assumes you are using SSH keys, which you should but if
not you can skip the whole "Configure SSH" block,**
2024-01-05 23:54:51 +00:00
**_I RECCOMEND YOU USE SSH KEYS_** - [read this](https://blog.ahmza.com/posts/securing-ssh/)
2021-12-23 00:06:13 +00:00
2021-12-19 16:59:34 +00:00
**Create the deploy.yml**
2024-01-05 23:54:51 +00:00
```bash
# Open the yaml file in a text editor
vim deploy.yml
```
2021-12-17 19:15:08 +00:00
```yaml
2021-12-19 16:59:34 +00:00
---
- hosts: piCluster
become: yes
tasks:
2021-12-23 00:06:13 +00:00
- name: Configure SSH
block:
- name: Auth local SSH Keys
ansible.posix.authorized_key:
user: <pi_user>
state: present
key: "{{ item }}"
with_file:
- /home/<local_user>/.ssh/id_rsa.pub
- ./pi_rsa.pub
- name: Copy SSH Pi Intercoms Private Key
copy:
src: ./pi_rsa
dest: /home/<pi_user>/.ssh/id_rsa
2024-01-05 23:54:51 +00:00
mode: "0600"
2021-12-23 00:06:13 +00:00
owner: <pi_user>
group: <pi_user>
- name: Copy SSH Pi Intercoms Public Key
copy:
src: ./pi_rsa.pub
dest: /home/<pi_user>/.ssh/id_rsa.pub
2024-01-05 23:54:51 +00:00
mode: "0644"
2021-12-23 00:06:13 +00:00
owner: <pi_user>
group: <pi_user>
2021-12-19 16:59:34 +00:00
- name: Check for any updates and install them
apt:
update_cache: true
upgrade: dist
- name: Install Required build dependencies
apt:
update_cache: true
pkg:
- python3
- python3-pip
- vim
- curl
- name: Check Docker is installed
shell: "docker --version"
register: docker_version
2021-12-23 00:06:13 +00:00
- name: Install Docker
block:
- name: Download Docker script
command: curl -fsSL https://get.docker.com -o get-docker.sh
- name: Run Docker install script
command: sudo sh get-docker.sh
- name: Enable docker service
service:
name: "{{ item }}"
enabled: true
state: started
loop:
- docker
- containerd
- name: Add user to docker group
user:
name: <pi_user>
groups: docker
append: yes
2021-12-19 16:59:34 +00:00
when: docker_version.stdout.find('Docker version')
2021-12-23 00:06:13 +00:00
- name: check Docker compose is installed
shell: "docker-compose --version"
register: dc_version
2021-12-19 16:59:34 +00:00
- name: Install docker compose
command: sudo pip3 -v install docker-compose
when: dc_version.stdout.find('docker-compose version')
- name: wait for network on boot
command: raspi-config nonint do_boot_wait 0
- name: Reboot
reboot:
```
2021-12-12 23:57:03 +00:00
2021-12-19 16:59:34 +00:00
The steps this playbook takes are as follows:
2024-01-05 23:54:51 +00:00
2021-12-19 16:59:34 +00:00
1. Copy your SSH keys to the pi authorized_keys
2. Copy same Pi ssh key between the Pis for intercommunication
3. Check for and Install updates
2024-01-05 23:54:51 +00:00
4. Install any build dependencies
2021-12-19 16:59:34 +00:00
5. Check if Docker is Installed
2024-11-29 02:26:53 +00:00
- Download the Docker Script
- Install Docker from script
- Enable the Docker service
- Add user to Docker group
2021-12-19 16:59:34 +00:00
6. Check if docker-compose is installed
2024-11-29 02:26:53 +00:00
- Install docker-compose (This will take a while on each pi so be patient, grab a coffee)
2024-01-05 23:54:51 +00:00
7. Change raspi config to wait for network before boot
8. Reboot
2021-12-19 16:59:34 +00:00
take some time to read through the playbook, read the docs and try to
understand whats going on. Change the usernames to your username.
2024-01-05 23:54:51 +00:00
**Create an inventory hosts file in the same directory as the deploy.yml**
```bash
# Open file in a text editor
vim hosts
```
```yaml
2021-12-19 16:59:34 +00:00
[piCluster]
2024-01-05 23:54:51 +00:00
<ip address 1>
<ip address 2>
<ip address 3>
2021-12-17 19:15:08 +00:00
```
2024-01-05 23:54:51 +00:00
- **_[piCluster]_** - specifies a group of hosts
- **_`<ip address>`_** - change these to the Pis IP addresses
2021-12-19 16:59:34 +00:00
2021-12-23 00:06:13 +00:00
**create the Pi Intercommunication keys**
2024-01-05 23:54:51 +00:00
```bash
2021-12-23 00:06:13 +00:00
ssh-keygen
2024-01-05 23:54:51 +00:00
# Please note the ".pi_rsa" here
2021-12-23 00:06:13 +00:00
Enter file in which to save the key (/home/ahmza/.ssh/id_rsa): .pi_rsa
```
**heres the command to run the playbook.**
2021-12-19 16:59:34 +00:00
2024-01-05 23:54:51 +00:00
**_ansible-playbook -i hosts -kK deploy.yml_**
2021-12-19 16:59:34 +00:00
2024-01-05 23:54:51 +00:00
- _ansible-playbook_ - the command to run the playbook
- _-i hosts_ - specifies the host/inventory file
- _-kK_ - asks you for password for the SSH connection(-k) and the sudo password(-K)
- _deploy.yml_ - the _Ansible_ playbook
2021-12-19 16:59:34 +00:00
You can do so much with _Ansible_, for example mount network shares etc,
if you can do it in the cli you can do it in _Ansible_ !!
2021-12-23 00:06:13 +00:00
Now that the _Ansible_ script is done we can go ahead and actually
2021-12-19 16:59:34 +00:00
configure all the Pis into the cluster.
2024-11-29 02:26:53 +00:00
### **Creating/Joining The swarm**
2024-01-05 23:54:51 +00:00
Creating and joining the cluster is easy - It is manual and tedious so this is
something you could implement into the _Ansible_ playbook using host groups.
2021-12-19 16:59:34 +00:00
2021-12-23 00:06:13 +00:00
**Follow these steps to create the swarm:**
2024-01-05 23:54:51 +00:00
2021-12-23 00:06:13 +00:00
1. ssh to the main Pi, this will be the manager node.
2021-12-19 16:59:34 +00:00
2. run `docker swarm init`
2024-11-29 02:26:53 +00:00
- This will return a command and token. Make sure to copy them
2021-12-23 00:06:13 +00:00
3. exit the main Pi
2021-12-19 16:59:34 +00:00
4. ssh to one of the worker nodes
5. paste and run the copied command
6. do the same on the remaining worker nodes
2024-01-05 23:54:51 +00:00
You should now have a configured cluster !!
### **Optional Step - _Portainer_**
2021-12-23 00:06:13 +00:00
2024-01-05 23:54:51 +00:00
_Portainer_ is a webgui that will allow us to manage the cluster, it will
2021-12-23 00:06:13 +00:00
make deploying and destroying containers across the cluster a breeze.
2024-01-05 23:54:51 +00:00
**Install _Portainer_**
2021-12-23 00:06:13 +00:00
Firstly we want to download the yaml file that will be used to deploy
2024-01-05 23:54:51 +00:00
the _Portainer_ container on our cluster.
2021-12-23 00:06:13 +00:00
`curl -L https://downloads.portainer.io/portainer-agent-stack.yml -o portainer-agent-stack.yml`
2024-01-05 23:54:51 +00:00
Now we can actually deploy _Portainer_ to the cluster using the following
2021-12-23 00:06:13 +00:00
command:
`docker stack deploy -c portainer-agent-stack.yml portainer`
Once the stack is up and running navigate to:
`https://raspberry-ip:9443`
where raspberry-ip is the IP address of the manager node in the swarm.
2021-12-23 00:23:39 +00:00
### **Optional Step - Private Registry**
2021-12-23 00:06:13 +00:00
If you intend to build any of your own docker images then it may be useful
to create a private repository to host any images that you need to run
across the cluster.
to create this private repository create a file called docker-compose.yml
2024-01-05 23:54:51 +00:00
in a folder new folder called repository and enter the following.
2021-12-23 00:06:13 +00:00
2024-01-05 23:54:51 +00:00
```yaml
version: "3.9"
2021-12-23 00:06:13 +00:00
services:
registry:
image: registry:2
ports:
- 5000:5000
volumes:
- /home/<pi-user>/docker/registry/data:/var/lib/registry
```
**Be sure to change pi-user to your pi user**
**create a file /etc/docker/daemon.json and eter the following**
2024-01-05 23:54:51 +00:00
```bash
vim /etc/docker/daemon.json
```
```json
2021-12-23 00:06:13 +00:00
{
2024-01-05 23:54:51 +00:00
"insecure-registries": ["localhost:5000", "<pi-address>:5000"]
2021-12-23 00:06:13 +00:00
}
```
2024-01-05 23:54:51 +00:00
where **_pi-address_** is the ip address of the main swarm node.
\*\*restart the docker service
```bash
sudo systemctl restart docker
```
2021-12-23 00:06:13 +00:00
now we need to deploy the registry to the stack like we did with portainer
2024-01-05 23:54:51 +00:00
```bash
docker stack deploy -c docker-compose.yml registry
```
2021-12-19 16:59:34 +00:00
2021-12-23 00:06:13 +00:00
and we should have a private registry setup that we can use to push our
custom docker images to.
2021-12-23 00:47:49 +00:00
## **Conclusion**
2024-01-05 23:54:51 +00:00
2021-12-23 00:47:49 +00:00
Well done, you have successfully setup a docker swarm cluster on a bunch
of Pis and along the way you managed to learn a bit about SSH, Ansible,
docker-compose, yaml and networking on Pis. The next step would be to
setup a few containers but I'll leave that to you, dont be affraid to
2024-01-05 23:54:51 +00:00
play around and even ruin the cluster, you can always rebuild it have
2021-12-23 00:47:49 +00:00
fun 😊