Recently I’ve been using a good amount of Docker for various deployment pipelines. As everyone knows, I’m a huge Raspberry Pi fan, so I figured it would be a cool idea to bring the two together. After all, Docker was built using Golang which is cross architecture.
We’re going to see how to create Docker containers on a Raspberry Pi and figure out the limitations of using Docker on IoT based architectures.
Before getting too invested, it is important to note that not all Docker images will work on a Raspberry Pi. This is because most images were designed for PC architectures, not ARM. However, Docker Hub, has quite a few that are available and that number is growing every day. We’re going to limit our use of the pre-built images in this guide and build our own.
The goal here will be to create a Docker container that uses Node.js and runs a Ghost blogging application.
There are a few requirements that must be met in order to be successful in this guide.
I imagine Docker will work on multiple versions of the Raspberry Pi, but for convenience we’re going to use the latest which is the third generation. The flavor of Linux that you use shouldn’t matter, but we’re going to be using Debian. The assumption is that Debian is installed and configured on the Raspberry Pi. For instructions towards doing this, check out a previous article I wrote on the subject.
Before we begin to create containers on our Raspberry Pi, Docker needs to be installed and configured. From here on out, either SSH into your Raspberry Pi, or execute the following from a Terminal window on the Raspberry Pi.
From the Terminal, execute the following:
curl -sSL https://get.docker.com | sh
After Docker installs it is probably a good idea to tell Docker to start automatically whenever the Raspberry Pi is powered on. This can be done by executing the following:
sudo systemctl enable docker
Reboot your Raspberry Pi, and when it starts back up, you are now able to create containers on your device.
There are many ways to do this, and in all honesty the way you see now probably isn’t the easiest. The reason I’ve chosen to explain it this way is because of the limited pre-built images.
On the Raspberry Pi, create a directory called ghost. It doesn’t really matter where you create it, but this directory will be where the Docker configurations reside for the particular container that we want to create. The ghost directory should contain the following files:
The Dockerfile file has the image we plan to use and any setup that will happen. The configure-ghost.sh file is a script that the Dockerfile will execute to install things like Node.js and Ghost.
Open the Dockerfile file and include the following lines:
FROM armhf/debian
COPY configure-ghost.sh /opt
RUN chmod 755 /opt/configure-ghost.sh
CMD ["/opt/configure-ghost.sh"]
The first line says we are going to use the armhf/debian image found on Docker Hub. In this image we’re going to copy the configure-ghost.sh script to the containers /opt directory. The script likely won’t have the correct permissions so we’re going to make sure it has execute permissions with the chmod
command.
Finally, when we run our container it will run all the commands found in the configure-ghost.sh script.
So what does this configuration script look like?
Open the configure-ghost.sh file and include the following lines of Bash code:
#!/bin/bash
# Update Repositories
apt-get update
# Install dependencies
apt-get install -y curl
apt-get install -y unzip
apt-get install -y build-essential
# Install Node.js
curl -sL https://deb.nodesource.com/setup_4.x | bash -
apt-get install -y nodejs
# Download Ghost
curl -L https://ghost.org/zip/ghost-latest.zip -o ghost.zip
# Prepare Ghost
mkdir /var/www
unzip -uo ghost.zip -d /var/www/ghost
cd /var/www/ghost && npm install --production
cd /var/www/ghost && sed -i 's/127.0.0.1/0.0.0.0/g' config.example.js
# Start Ghost
cd /var/www/ghost && npm start --production
There is a lot going on in the above script. This is because the armhf/debian image we chose to use is pretty basic. It won’t have anything we need for running Ghost.
The first thing we’re doing is updating all the package repositories in the container. The Node.js and Ghost we want isn’t in the standard apt-get
repositories so we’ll have to install them manually. This requires packages like cURL, unzip, and build-essential.
With the appropriate Debian packages in place we can install Node.js:
curl -sL https://deb.nodesource.com/setup_4.x | bash -
apt-get install -y nodejs
Ghost is just a Node.js application so what we want to do is download the latest ZIP archive, extract it, configure it, and run it. When looking at our script, probably the most important line is the following:
cd /var/www/ghost && sed -i 's/127.0.0.1/0.0.0.0/g' config.example.js
We want to be able to access Ghost from our host machine. By default, Ghost will run on 127.0.0.1, but localhost to the container is not localhost to the Raspberry Pi. Instead we replace all occurrences of 127.0.0.1 with 0.0.0.0 in the Ghost configuration example file. This file eventually is renamed during the setup process.
Finally, Ghost is run in production mode.
Up until now we haven’t actually created the Docker container image. We put down the foundation which will be used next.
Execute the following to build the image based on the Dockerfile file and the configure-ghost.sh file:
docker build -t ghost .
In the above command we’re naming the the image ghost. It will download the armhf/debian image and the other commands found in the Dockerfile file.
You can verify that the ghost image was added by executing the following:
docker images
To start the container based on our new image, execute the following:
docker run -d -p 2368:2368 --name ghost ghost
The above command says to run the container in detached background mode with a port mapping of 2368. We are mapping this port because this is the port that Ghost operates on. If you have a domain name configured, you’ll probably want to open port 80 as well. The container will use the ghost image and the container will have a name of ghost.
After you run the above command, it is very important to note that it is not instant. The Raspberry Pi is not the fastest machine and there are a lot of commands in the configure-ghost.sh script. To track the status of your container, execute the following:
docker logs -f ghost
When the script finishes, navigate to http://localhost:2368 from your Raspberry Pi web browser. If you’re in headless mode, first find the IP address of your Raspberry Pi and navigate to http://IP:2368 from some other device on your network.
You just saw how to install Docker on a Raspberry Pi and add a container running the Ghost blogging platform. Like I said, the method we used to install Node.js and Ghost was not the easiest option we could have taken. For example, there is an armhf/node image we could have tried. However, we wanted to see creating our own via script.
Have your own variation of what was done in this guide, share it in the comments. It will give readers options towards getting the job done.