--- title: Building a Docker Swarm With Pi's date: 2021-12-12T17:23:32Z author: "ahmza" draft: true weight: tags: ["Homelab","Docker","Docker Swarm","Raspberry Pi","Pi4"] description: "Pis are very capable computers, when banded together they're almost unstoppable" canonicalURL: "https://blog.ahmza.com/Page%28/posts/building-a-docker-swarm-with-pis.md%29" cover: image: "/img/pi-docker-swarm/pi-swarm.gif" # image path/url alt: "Pi Docker Swarm" # alt text caption: "" # display caption under cover relative: false # when using page bundles set this to true showtoc: true searchHidden: false --- ## **Intro** The _Raspberry Pi_ from it's first iteration has been a great hit. Adopted by many as the computer of choice for projects, and for very good reason. The Pi is a fully fledged computer that fits in your pocket and the most recent edition has blown fans away with the flagship model boasting an ARM64 CPU and 8GB of RAM. Now one _Raspberry Pi 4 8GB_ is more than powerful enough for most workloads and in some cases totally unnecessary, **so you may even get away with a single 4GB model**. However taking into consideration their relatively low price and power consumption why not go all out and build a cluster. > **You can never have too many _Raspberry Pis_, just not enough Ideas** My excuse to build a Pi cluster is, I would like to migrate the services currently running on a **bare metal server that sucks a lot of power to a low power solution.** What better way to do that than a Pi cluster. ### **My Setup** * 4 x _Raspberry Pi 4 8GB_ * 4 x 32GB SD cards * 4 x PoE hats * 1 x 5 port PoE Switch ![Insert Image of cluster here]() ### **Point of this post** Hopefully after following this post you will have setup _Docker swarm_ on a cluster of Pis **- stay even if you currently only have one -** and understand how to bend it to your will 🦾. ### **Why** ***Docker Swarm*** 🧐 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 each individual node for us. These tasks can consist of: * Cluster Management * Deployment and Scaling across the cluster * Multi-host networking * load balancing * etc... ### **Well why not** ***Kubernetes*** Although _Kubernetes_ is awesome, **it adds a bunch of complexity that is simply not needed in a home lab environment** where we only have a small cluster. As well as that, from my experience creating ```docker-compose``` files is a lot easier than writing manifests. **_Docker swarm_ can do everything I want it to and requires less effort** which is great if you're like me 🥱 😂 ### **Required Stuff** > The links below are reccomendations/stuff I used * [_Raspberry Pi_ 4B](https://thepihut.com/products/raspberry-pi-4-model-b?variant=31994565689406) (Reccomend 4-8 @ 8GB) * [_Raspberry Pi_ PoE Hats](https://thepihut.com/products/power-over-ethernet-poe-hat-d-for-raspberry-pi) (If you can find the [PoE+](https://thepihut.com/products/raspberry-pi-poe-plus-hat) version get them) * [Cluster Case](https://thepihut.com/products/cluster-case-for-raspberry-pi) (Get any case you want) * [PoE switch](https://www.amazon.co.uk/gp/product/B076982FVC/) (Get one that has enough ports for the nuber of pis you want) * [RJ45 Ethernet Cable](https://www.amazon.co.uk/gp/product/B00C4U030G) (Reccomend 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 without a doubt an expensive project**, however in my eyes it's the cost of another server 😂 and the best part about this project is **you can add to the cluster over time** spreding the cost which is how I managed to purchase all the hardware. A suggestion would be to **buy equipment that allows you room to grow** so an 8 port PoE switch instead of a 4 port if you know you will scale to 8 nodes If you build this then you can safely say another server is not needed. This cluster should be more than capable to serve all your home needs even if you only have 2 nodes. Put it this way, if you have 4 8GB nodes then you have a combined total of: * 32GB of RAM * 16 physical CPU cores @ 1.5GHz #### **Money Saving Tips** * Just get 1-2 nodes to start * cut PoE and use normal power cables * Use Raspberry Pis with less memmory eg 4GB * Grab a really cheap case or make one using [these](https://www.amazon.co.uk/dp/B091KJNZLB/) * Use smaller SD Cards try not to go below 32GB (Reccomended 64GB If money saved elsewhere) ## **BUILD TIME !!** Pull yourself up by your bootstraps were jumping into this one runnig 💨 ### **Image the SD Cards** So interesting fact the CPUs used in the raspberry pi 4 use an ARM64 archetecture. When looking for images of Raspbian you will only find 32bit variants which will work however will not make use of all the hardware which is just a waste. 64bit images exist however are well hidden, Dont fear 😨 **you can download the latest images from the [raspberrypi.org index](https://downloads.raspberrypi.org/raspios_arm64/images/)** To write the cards I use the [**Raspberry Pi imager**](https://www.raspberrypi.com/software/). once you have **rpi-imager** installed and you have the 64bit raspbian image, open ```sudo rpi-imager``` and on the "CHOOSE OS" section go to the bottom and select use custom image. > We dont need to unzip the file as the tool is clever enough to handle that for us **Before removing the SD card, mount the boot partition - usually the first partition, look for a file called cmdline.txt - and create an empty file called ssh** ```sudo touch ssh``` this basically enables SSH on the Pis first boot allowing us to access the Pis in a totally headless manner. ### Assemble the cluster So assembly is very dependant on the kit you bought, and more often than not the stuff needed here comes with instructions or is self explanitory. So go ahead and assemble the cluster 🛠 **There is an order of operations that I reccomend:** * Install PoE hats onto the Pis * Install the SD Cards into the Pis * Assemble the case and install Pis * plug in ethernet cables **Now allow the Pis to boot for a few minutes so it can resize the file system** ### **Sort out the Network bits** This is the easy part, 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. > there are naming conventions for naming primary and secondary 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) **SSH to the Pi** then open and edit the following files * /etc/hosts * edit this line with your hostname: ```127.0.1.1 goob``` * /etc/hostname * edit this file with your hostname: ```goob``` * /etc/dhcpcd.conf * knowing we will be using only static IPs here I delete everything in the file add the following * ``` interface eth0 static ip_address=10.10.10.20/24 static routers=10.10.10.1 static domain_name_servers=10.10.10.1 ``` **Now reboot the pi** ```sudo shutdown -r now``` **Once the Pis are back up log into each one and change the default passwords** you may also want to create a new user for yourself but thats totally optional. ### **Provision with** ***Ansible*** you want to **create an _Ansible_ control node** and that is as simple as installing a package onto your machine. _Ansible_ is built using python which makes it easy to install as **we can use the python pip package manger.** To check you have it installed do ```pip --version``` if it's not installed do the following ```curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py``` ```python get-pip.py --user``` **once you have pip installed proceed to installing _Ansible_** [Docs](https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html) ```pip install ansible``` Now that _Ansible_ is installed we can create a runbok that defines the steps _Ansible_ needs to take on the remote machine to correctly provision it. This playbook is written in yaml so get ready to make sure your indents are correct 😬 **Creating the playbook** now you can copy what I have and it should work - after changing a thing or two - which is fine however I encourage you to copy what I have, read the [Docs](https://docs.ansible.com/ansible/latest/user_guide/index.html) and make any changes you feel are necessary. > Check the [git repo](https://gitlab.com/AHMZA/pi-docker-swarm-ansible-deploy/-/tree/main) for the most up to date yaml file im using. **Create the deploy.yml** ```yaml --- - hosts: piCluster become: yes tasks: - name: Auth local SSH Keys ansible.posix.authorized_key: user: state: present key: "{{ item }}" with_file: - /home//.ssh/id_rsa.pub - ./pi_rsa.pub - name: Copy SSH Pi Intercoms Private Key copy: src: ./pi_rsa dest: /home//.ssh/id_rsa mode: '0600' - name: Copy SSH Pi Intercoms Public Key copy: src: ./pi_rsa.pub dest: /home//.ssh/id_rsa.pub mode: '0644' - 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 - name: check Docker compose is installed shell: "docker-compose --version" register: dc_version - name: Download Docker script command: curl -fsSL https://get.docker.com -o get-docker.sh when: docker_version.stdout.find('Docker version') - name: Run Docker install script command: sudo sh get-docker.sh when: docker_version.stdout.find('Docker version') - name: Install docker compose command: sudo pip3 -v install docker-compose when: dc_version.stdout.find('docker-compose version') - name: Enable docker service service: name: "{{ item }}" enabled: true state: started loop: - docker - containerd when: docker_version.stdout.find('Docker version') - name: Add user to docker group user: name: groups: docker append: yes when: docker_version.stdout.find('Docker version') - name: wait for network on boot command: raspi-config nonint do_boot_wait 0 - name: Reboot reboot: ``` The steps this playbook takes are as follows: 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 4. Install any build dependancies 5. Check if Docker is Installed 6. Check if docker-compose is installed * Check this because if they are already installed we can skip these steps and save time 7. Download the Docker Script 8. Install Docker from script 9. Install docker-compose (This will take a while on each pi so be patient, grab a coffee) 10. Enable the Docker service 11. Add user to Docker group 12. Change raspi config to wait for network before boot 13. Reboot take some time to read through the playbook, read the docs and try to understand whats going on. Change the usernames to your username. Make the hosts file in the same directory as the deploy.yml ``` yaml [piCluster] 10.10.10.20 10.10.10.21 10.10.10.22 ``` * `[piCluster]` - specifies a group of hosts * `10.10.10.xx` - specifies the Pis IP heres the command to run the playbook. `ansible-playbook -i hosts -kK deploy.yml -c paramiko` * `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 * `-c paramiko` - Use the paramiko connection method, I found this works best with ssh keys 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_ !! Now that the ansible script is done we can go ahead and actually configure all the Pis into the cluster. ### **Creating/Joining the swarm ** Creating and joining the cluster is easy - It is a bit manual so this is something you could impliment into the ansible playbook using multiple host groups. Keep an eye out on the git repo as I may implement this myself. **Follow the steps below to create the swarm:** 1. ssh to the main swarm node, this will be the manager node. 2. run `docker swarm init` * This will return a command and token. Make sure to copy this token to your clipboard 3. exit the main swarm node 4. ssh to one of the worker nodes 5. paste and run the copied command 6. do the same on the remaining worker nodes You should now have a correctly configured cluster !!