I needed a way to collaborate on raspberry pi development. I wanted to make it possible for others to reproduce my builds without demanding that they manually execute command after command to reproduce my project. I also wanted a solution that saves time by caching redundant downloads. So I built the “Builderhotspot“
There is a disadvantage to using the builderhotspot- not everyone has at least 2 raspberry pi’s.
I needed a solution that works if you have only one pi, so I turned to docker. I’ve created a docker-compose file that will spin up an ansible container & an apt-cacher-ng container which can be used to push firmware images to any devices on your network with the hostname of “ansibledest.local”
This tutorial assumes you have docker installed on a host system- and that somewhere on your network is a raspberry pi attached under the hostname of “ansibledest.local”
Step 1: Clone the FirmwareBuilderContainers project into your directory of choice.
On your host operating system, cd into a directory where you want to host your Firmware Builder Containers. I use ~/Development/containers.
git clone git@github.com:CaptainMcCrank/FirmwareBuilderContainers.git
Step 2: Modify the Docker-compose script to reflect the details of your host system
There are 3 modifications you’ll need to make to the docker-compose file you just cloned:
DOCKER_HOST
The docker-compose file’s DOCKER_HOST variable tunes the recipient device to use your local apt-cache-ng container. This reduces redundant apt-get install downloads. It sets the server hostname values in the /etc/apt/sources.list and /etc/apt/sources.list.d/raspi.list files on the recipient device so that it pulls downloads from the caching server. My firmware recipes use this value as control logic.
The value will be “builderhotspot.local” if you use the builderhotspot to push recipes to devices. Since we’re going to use containers- we need to set the value to reflect your host os’s hostname. To get the hostname of a system, you can use different commands on Windows, macOS, and Linux. Here are the commands for each operating system:
Windows
Command Prompt:
hostname
PowerShell:
hostname
or
$env:COMPUTERNAME
Mac & Linux
hostname
Alternatively, you can also use the uname -n command to retrieve the hostname on a linux system. Open the docker-compose.yml file and browse to the section for the ansible container. Note the “environment:” section. Change the DOCKER_HOST value to reflect your host system’s hostname.
VOLUMES
We need to expose two directories from the host system to the ansible container. This is done by specifying a volume & indicating where in the container it should be accessible.
The first volume is our playbooks directory. This is where we will story playbooks for all the projects we want to push to devices. You can git clone playbooks for other projects into this directory from the Host operating system. They will be exposed in the /home/pi/Playbooks directory on your ansible container.
Change the first volume’s value to reflect the correct directory on your host system. Be sure to retain
:/home/pi/Playbooks
at the end of the first line. This is how we specify the location. My playbooks are designed to run on both the builderhotspot as well as via containers- but if you modify this second value, the playbooks won’t work without modifications.
The second volume is for enabling mdns resolution. In our playbooks we want to use hostnames to specify the recipient device. This makes pushing a playbook to a target device easy. You don’t have to discover the recipient device’s IP address. You only need to know it’s hostname. This keeps life simple. If you’re on linux, the best way to do this is to share the avahi-daemon socket on the host system as a volume. If you skip this step, name resolution won’t work within the ansible container. I don’t think you need to change this value on a mac- it seems to work on my system. I still need to test this on a Windows machine to confirm mdns works. works.
Step 3: Build & launch the containers
cd ~/Development/containers/FirmwareBuilderContainers
Build the containers & run them detached.
docker-compose up --build -d
Step 4: Attach to the ansible container & test connectivity:
docker exec -it ansible bash
If you have an ansibledest system running on your network, you should be able to ping it:
root@docker-desktop:/# ping ansibledest.local
PING ansibledest.local (192.168.6.247) 56(84) bytes of data.
64 bytes from 192.168.6.247 (192.168.6.247): icmp_seq=1 ttl=63 time=0.898 ms
64 bytes from 192.168.6.247 (192.168.6.247): icmp_seq=2 ttl=63 time=1.12 ms
Step 5: Detach from your containers & deactivate the containers:
From within the ansible container, type “exit” to leave the container. Then use the docker-compose command to deactivate the containers:
docker-compose down
Step 6 (From this point forward, the only commands you’ll really need when building):
From now on, we skip the build commands. Run the docker-compose command from within the container directory on your host system:
docker-compose up
docker-exec -it ansible bash
Step 7: Building a firmware example
You now should be good to go to use Docker Containers for pushing my firmware recipes to your devices. To try out the “hack this wifi” firmware, Go to your Host OS’s terminal and cd into the playbook directory ( I use /Users/Patrick/Development/Playbooks/DockerVolume). Run the following command:
git clone https://github.com/CaptainMcCrank/Learn_Linux_Networking_And_Hacking.git
Attach to your container:
docker-exec -it ansible bash
And now cd into your playbooks directory:
cd /home/pi/Playbooks/
If you run ls, you should see the “Learn_Linux_Networking_And_Hacking” directory. cd into it:
cd /home/pi/Playbooks/Learn_Linux_Networking_And_Hacking
If your ansibledest system is online, you can copy the container’s ssh key to the destination system, which enables you to use ansible to install software on the recipient device:
ssh-copy-id pi@ansibledest.local
And now you can deploy the firmware:
ansible-playbook run.yml
If for some reason you forgot to set your hosts file, you can fix this in ansible for your container’s session with the “export DOCKER_HOST=hostname.local” command- where hostname.local is your host system’s hostname.