Setup multiple Raspberry Pi different network configurations
-
For multiple projects we are setting up 2 to 6 RPi's per project. Each project runs on a different network and for some of these networks we can use DHCP, for others the RPi's have to connect using a static IP address.
Also every RPi needs a different hostname in sequential order. For instance for a project with three RPi's, the hostnames need to be PROCCESS1, PROCESS2, PROCESS3.
All RPi's run the exact same software and every RPi has a single monitor connected.
The current workflow is:
- Burn a prepared OS image for every RPi on a microSD
- Boot up the RPi using the microSD and manually set the hostname
- If needed, set the IP address to a static one
- Turn on the overlay file system so the system is read-only
- Repeat for the amount of RPi's needed
This is a tedious task where errors are made and takes a lot of time. Step 1 can not be automated, since we don't have the hardware (altough the EtcherPro would eventually save a lot of time). Automating step 3 and 4 would be great if that can be realized. I've read a bit about Ansible and also encountered Chef and Puppet but I haven't dived into any of them yet.
Is using Ansible with playbooks a good direction to develop further? Would it be possible to automate the setup using this tool? Is there a better/easier solution or would it not be possible at all? If someone has some tips or can guide in the right direction that would be great.
-
Even without Ansible or other config management tool, there's no reason any of those steps need to be manual: you could drive everything with a shell script, thus reaping all the benefits of automation: no more errors, consistent deployments, and faster configuration. Using Ansible can help make the process more modular and more structured, but it doesn't fundamentally allow you to do anything new.
Ignoring the question of what tooling you use to drive the configuration, I'd like to suggest an option that you may not have considered: it's possible to perform all the configuration on the image before burning it onto a microsd card. You can mount the raspberry pi os image on a local directory, and then make all your configuration changes there. I use a process like this:
Download and unzip the raspberry pi os image
Make a working copy of the image (this way you can re-use the base image multiple times to create different configured images):
# cp 2022-01-28-raspios-bullseye-armhf-lite.img work.img
I like to extend the image a bit so there's room for installing new software:
# truncate -s 2500MB work.img
Attach the work image to a loop device:
# losetup -fnP --show work.img /dev/loop0
Grow the filesystem to use the additional space:
# parted /dev/loop0 resizepart 2 100% # e2fsck -f /dev/loop0p2 # resize2fs /dev/loop0p2
Mount the filesystems locally:
# mount /dev/loop0p2 /mnt # mount /dev/loop0p1 /mnt/boot
Now you're in a position where you can create and modify configuration files on the image, in order to...
- Configure networking
- Configure the hostname
- Enable ssh
- Configure wifi
- Install ssh keys
- Etc.
But what if you want to do other things, like update or install packages? If you install the
qemu-user-static
package and properly configure thebinfmt_misc
module (modern versions of theqemu-user-static
package will do this for you automatically), you will be able to run Raspberry Pi binaries on your host. This allows you to run commands inside the image, like this:# systemd-nspawn -D /mnt apt update # systemd-nspawn -D /mnt apt upgrade -y # systemd-nspawn -D /mnt apt install -y git
Or inject a shell script:
# systemd-nspawn -D /mnt --bind=$PWD/config.sh:/config.sh sh /config.sh
Here I'm using
systemd-nspawn
, which is a little likechroot
(in this example), except it takes care of mounting special filesystems like /sys and /proc and has all sorts of interesting options (like the--bind
option for bind mounting files or directories into the image).
When you're all done, unmount the filesystem and disconnect the loop device:
# umount /mnt/boot /mnt # losetup -d /dev/loop0
And write the customized image to an sd card:
# dd if=work.img of=/dev/sdb bs=4M
You can wrap the entire process described here in a script, and have it take parameters to configure networking (specify a static address or use dhcp), etc.