Getting started with containers? One cool way to start is by making a container that can run an application that is unavailable on your host operating system.
First up, you need to get a container runtime installed. I prefer
docker will work as well. For the simplest installation experience, I would recommend the desktop variants.
Once you have those installed, you can almost always use
docker interchangeably on the command line. We will be using the command line because, at the time of writing, Docker Desktop doesn’t support building containers in the GUI.
First up, make a new directory somewhere and create a file called
Containerfile. Note the lack of extension you Windows users! Windows really doesn’t like creating files with no extension so be sure to make sure it doesn’t add
.txt or something. You may have heard of a
Dockerfile before but the two filenames are also interchangable. However, you may have a lot more luck googling “
Dockerfile” because it has been around longer. As a quick side bar, we use
Containerfile because all the tools support a shared specification created by the Open Container Initiative (OCI) called the OCI Image Format Specification. If you want to learn more, you should probably wait, but you can go here if you really want to :).
Creating the Containerfile
OK, so now you have your file. At the top of the file, add the following line:
What this is saying is we are going to base our container on an image of Fedora, a Linux distribution. In the above,
FROM is the instruction and is typically capitalized. We could also use a smaller base image like
fedora-minimal but we are going as simple as possible for this tutorial.
Next, we need to add our cool app and we use the Fedora package manager
dnf to do so. You may have used a package manager in the past (e.g.
pip) but did not realize that this is a whole class of software. If you want to learn about them, they are really interesting, you can check out the Wikipedia page. Now add the following line:
RUN dnf install -y cowsay
As you might imagine,
dnf won’t install something without confirmation by default. As a result, we need to pass
-y to get it to install without confirmation. Why do we need this you may ask? Well, when we build our container it is not an interactive process so we can’t say “Yes” to
Now you may ask, what is
cowsay well, you can look for yourself, or wait for the reveal!!
Now let’s do the last line of our
Containerfile. Here it is:
Here we have what the container should do when it launches. Specifically, we want the container to launch
cowsay and pass it any parameters that were on the command line. We will get to more on that in a minute.
Building the Container Image
OK, now save your file and open a command line. Let’s build the container.
podman build -t simple-cowsay .
As I said before, you can swap
docker depending on what you installed. Fair warning, this might take awhile, might be time for a coffee. You should now see an output like the following (not exactly but similar):
STEP 1/3: FROM fedora STEP 2/3: RUN dnf install -y cowsay Updating and loading repositories: Fedora 38 - x86_64 - Updates 100% | 372.7 KiB/s | 11.4 MiB | 00m31s Fedora 38 openh264 (From Cisco) - x86_ 100% | 899.0 B/s | 6.5 KiB | 00m07s Fedora 38 - x86_64 100% | 2.1 MiB/s | 34.5 MiB | 00m16s Repositories loaded. Package Arch Version Repository Size Installing: cowsay noarch 3.7.0-7.fc38 fedora 72.5 KiB Installing dependencies: groff-base x86_64 1.22.4-11.fc38 fedora 3.8 MiB <snip /> perl-vars noarch 1.05-497.fc38 updates 4.8 KiB Installing weak dependencies: perl-NDBM_File x86_64 1.15-497.fc38 updates 29.7 KiB Transaction Summary: Installing: 62 packages Downloading Packages: [ 1/62] perl-Carp-0:1.52-490.fc38.noarc 100% | 13.2 KiB/s | 28.8 KiB | 00m02s <snip /> [62/62] perl-NDBM_File-0:1.15-497.fc38. 100% | 30.1 KiB/s | 23.6 KiB | 00m01s -------------------------------------------------------------------------------- [62/62] Total 100% | 321.9 KiB/s | 8.5 MiB | 00m27s Verifying PGP signatures Running transaction [1/2] Verify package files 100% | 1.6 KiB/s | 62.0 B | 00m00s [2/3] Prepare transaction 100% | 2.3 KiB/s | 62.0 B | 00m00s [3/4] Installing ncurses-0:6.4-3.202301 100% | 38.1 MiB/s | 624.1 KiB | 00m00s <snip /> >>> Stop trigger-install scriptlet: glibc-common-0:2.37-1.fc38.x86_64 -------------------------------------------------------------------------------- [64/64] Total 100% | 54.3 MiB/s | 30.1 MiB | 00m01sRemoved 82 files, 11 directories. 0 errors occurred. --> e010c8ceb6f7 STEP 3/3: ENTRYPOINT ["/bin/cowsay"] COMMIT simple-cowsay --> 0afbf280e77a Successfully tagged localhost/simple-cowsay:latest
Running the Container
Once that completes, let’s run it!
podman run -it simple-cowsay 'Cows are AWESOME!!'
Now you can play with it to your hearts content!
A Little Bit Extra
If you want to do a slightly more advanced version, you can also clean up the container after the installation of
cowsay. When you create a container image, each line in the
Containerfile is independent and creates a layer that is carried through to the final image. When we install things in the container image using the package manager, the package manager has to download all the dependency information to figure out what your the thing you want to install needs. As you might imagine, this can be a lot of content and it is a permanent part of the container image after that layer. As a result, it is recommended that you remove the content you don’t need after the installation. In order to do that we add
dnf clean all -y to the installation line. We need to replace the installation line like this:
RUN dnf install -y cowsay && dnf clean all
RUN instruction is just the equivalent of running the command in Linux. In Linux, the
&& just links two commands together and if the first one is successful, the second one runs. Now before you rebuild the container, run
podman images and note the size of your
simple-cowsay container. Now rebuild the container and see how much smaller it is by running
podman images again.
The Whole Shebang
The whole file, just in case:
FROM fedora RUN dnf install -y cowsay && dnf clean all ENTRYPOINT ["cowsay"]