Why is software so difficult?
7 Years and 700 million dollars, That’s how much the Psyche mission costs to develop. Even after all that work the launch has been delayed. This is a big deal. Due to the long timescales of interplanetary travel, missing a launch window may mean the loss of the one and only shot to deliver a science payload.
It’s not any major glitches or errors, it’s just that there simply wasn’t enough time to test the software to an adequate degree before the expected launch. The engineers working on the spacecraft are more skilled than anyone else but that doesn’t mean that their work is easy. Software is absurdly complicated.
This image is a visualization of a dependency graph of a software project. It’s reminiscent of those “follow the wire/pipe” to match the colors that were in the back of the magazines in the dentist’s waiting room, but with an additional couple of orders of magnitude of flexibility.
Dependencies may cause problems
the whole point of having software dependencies is to not reinvent the wheel. You should always try to build on the best of what other people have already figured out. Therefore you should separate your work into small modules that other people can use or improve upon.
In a software team, there are multiple people working on the same project, sometimes even the same file. When an engineer adds a module that requires a change to the way that the software is built there is a major issue that can arise. A rule of thumb is that you only change the code that you need to change and don’t touch anything else. If the engineer made a change to their computer by installing whatever program they needed in order to create their module and then don’t notify everyone else that they changed their system, there could be a situation where everyone else gets an error because they don’t all have the same configuration.
This leads to the forbidden phrase “It works on my machine”. For a small project, it’s a nuisance to go back and compare the changes between an individual team member’s configuration and the configuration that everyone else is working with. On a project of any substantial size, it can be an absolute nightmare. To ensure that this doesn’t happen typically there is a build server with its own configurations that can be used as the single source of truth for the project. One way to implement this is by using a container, which is a program that separates itself from the computer it’s running on and can be configured as minimally as possible for the purpose of running tests and building the final product.
Docker
Docker is a program that can create and manage containerized systems and run them without affecting the configuration of the host computer. Users can also share their docker containers with others
A popular way of standardizing a sandboxed system for testing and development was to use a program called a virtual machine(VM). The virtual machine is exactly what it sounds like, a computer program that pretends to be an entire computer itself. This approach works, but there is a lot of overhead and space that’s required because you have to have a completely new operating system for every virtual machine. The container can do the same thing as the virtual machine but with fewer intermediate steps, which means that it can be faster and take up less space than the virtual machine.
Saving, modifying, and sharing these containers aid in collaboration among the software team. It eliminates the “It works on my machine” because everyone is using the same machine container with the same configurations.
How Docker Works
Docker has something called images. A docker image is the set of instructions that are used to build a container. This image is a template that can be shared with other people so that they can create their own identical docker container.
These images can also be used in automated test systems to see how the programs run on different systems and versions.
The docker container is built from the docker image and contains all the necessary files to emulate a different system.
The container is built on a base image, usually an existing operating system. Engineers add whatever modules that they would like on top of that base system.
Example
Zephyr is a Real-Time Operating System (RTOS). Just like the operating system on your personal computer but it is designed to be lightweight. Most embedded applications such as wearable smart devices, and small computers like the Chromebook, use an RTOS. Even though it’s lightweight it is a serious piece of kit and therefore has some complicated tools to set up. Thankfully we can install those tools in a container to make them easier to use and separate them from our physical system.
Building a Dockerfile
A Dockerfile is the set of commands that are used to set up a docker image. The Dockerfile builds the image up from the base system and installs any programs that a user specifies. Therefore it has only what you absolutely need and nothing else. This keeps the memory footprint down to a minimum but also prevents unnecessary complexity.
Let’s walk through what you should put in a dockerfile for buildin the Zephyr RTOS that we talked about above.
FROM ubuntu: focal
This line selects the base image as an Ubuntu system, specifically the focal release (20.04.4)
RUN apt-get update
RUN apt-get -y install sudo
These command are commands for the Ubuntu system that install programs that we will need later.
RUN sudo apt update
RUN sudo apt -y upgrade
These lines update the operating system
COPY ./zephyr_install.sh /
RUN chmod 777 zephyr_install.sh
These commands copy and run a custom install script to install the tools to run the Zephyr RTOS.
Building a Docker Image
Once you have an image, it needs to be built into a container. The way to do this is to use your Dockerfile to build the Docker Image
The command that is used to build a Docker Image is
docker build -t <image name> .
This command tells docker to build a new image called <image name> (image name is a placeholder) from the docker file in the current directory. In this example I named the docker image zephyrenv.
Running a Container
The next step is to use the Docker image to create an actual container that we can run programs in.
The command to build a container is:
docker run --name <container name> -it <image name>
For this example we will use the command:
docker run --name zerphyrdev -it zephyrenv
This command starts the container and opens up a text-based window just as if it was its own separate computer.
We can now use this container to build our software without having to worry about mismatched environments between different team members.
Conclusion
Creating software is very complicated, and with lots of people working on it at the same time there are many possibilities for things to go wrong. Docker is a great tool to help streamline the software development process and is free and relatively simple to use.