I have been trying to set up a production server for our college project in Raspberry Pi 3 using Docker for a couple of days.
The Pi is running Manjaro ARM Minimal 21.07 OS and is manually configured with Docker. Our project uses Django for backend providing API for frontend which is built using ReactJS.
Here, Nginx is used for the following things:
- To serve frontend build files.
- To serve static and media files of the backend server.
- To serves API to the client using reverse proxy.
First Approach
My first approach was to build docker images for Nginx and backend inside the Raspberry Pi and running the containers. But this led to few problems:
- Based on the low specs of the Pi, it took a very long time to install the pip requirements and also node packages.
- Building the ReactJS app failed with the error -
javascript heap out of memory
.
There is nothing much that can be done for problem 1 but there might be something that can be done for problem 2. But because it was already taking a long time to install the requirements dragged me to the second approach.
Second Approach
Build once, deploy anywhere.
Instead of building docker images inside the Pi, I could actually build the images on my working PC and transfer the images to load in Pi. Then, I only have to start the respective containers in Pi. But the problem was the difference in machine architecture the image was built for. By default, Docker builds an image with the architecture of the machine it is used to build. But docker also supports multi-arch images with buildx
and when running these images, docker automatically selects the image variant that matches OS and architecture of the system 1.
We need to run a container with multiarch/qemu-user-static
. This enables the execution of different multi-arch containers 2. We also need to create a new builder and use it to build our image. We can see information about the new builder using buildx inspect
command.
$ docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
$ docker buildx rm mybuilder
$ docker buildx create --name mybuilder
$ docker buildx use mybuilder
$ docker buildx inspect --bootstrap
Finally build the image.
$ docker buildx build --tag myimage:latest --platform linux/arm64 -o type=docker,dest=- . > myimage.tar
The above build will save the Docker image tarball myimage.tar
in the current directory which can later be loaded into Pi.
$ docker load --input myimage.tar
Notice --platform linux/arm64
in build command which will create an image for arm64 architecture. We can also specify multiple platforms but at the time of this post type=docker
only exports a single-platform result image 3. Omitting type
completely and using --push
will push a multi-platform image to Docker Hub but I only need to run the project locally.
Finally, I used docker-compose to run multiple containers required for the project and mapped the ports, making it ready to be used over a local network.