Cloud Native 5 Minutes at a Time: Build Image from Dockerfile

Eric Gregory - February 11, 2022
image
One of the biggest challenges for implementing cloud native technologies is learning the fundamentals—especially when you need to fit your learning into a busy schedule. In this series, we’ll break down core cloud native concepts, challenges, and best practices into short, manageable exercises and explainers, so you can learn five minutes at a time. These lessons assume a basic familiarity with the Linux command line and a Unix-like operating system—beyond that, you don’t need any special preparation to get started. In the last lesson, we practiced creating, interrogating, and deleting containers. When we created a new container, we built it on the foundation of a container image—now it’s time to explore exactly what that is, how it works, and how we can build images from Dockerfiles.

Table of Contents

  1. What is a Container?
  2. Creating, Observing, and Deleting Containers
  3. Build Image from Dockerfile←You are here
  4. Using an Image Registry
  5. Volumes and Persistent Storage
  6. Container Networking and Opening Container Ports
  7. Running a Containerized App
  8. Multi-Container Apps on User-Defined Networks

What is a container image?

You can think of a container image as a photograph—a snapshot of a container’s filesystem state, frozen in time. In the last lesson, when we wanted to create a new container, we started with a container image called alpine. This was a snapshot of a filesystem with only the bare-bones binaries and libraries of a Linux distribution called Alpine Linux, and nothing else. This open source Linux distribution is well-suited to containerization because it is so lightweight: it packs only what is needed to spin up containers quickly and efficiently, so if we want to quickly create a container running a new process, the alpine image is a great choice. That gives us a nice glimpse of container images’ utility. To get a process running on an isolated filesystem, we didn’t have to install and configure a whole new system. Instead, with a single command, we conjured a prefabricated base image. As you’ve probably already guessed, container images can provide a lot more than empty canvases. If we can capture filesystem states in an image, we can capture states with more complex prerequisites, configuration options, and processes ready to go. This allows us to…
  • Trivially replicate containers running complex processes
  • Shape more advanced “building blocks” for development
  • Create a container image repository that acts as a “single source of truth” for software modules used throughout an organization
... and much more besides. As we’ll see over the course of this series, the modularity facilitated by container images can transform the way you develop and deploy software.

Using Dockerfiles to create container images

Sometimes, you’ll want to create a container image based on a container on your system—like taking a live snapshot. You can find a detailed tutorial on that process here. For the purposes of this series, we’re going to focus on a different approach: using a Dockerfile. A Dockerfile is a set of instructions for creating a container, all set down in a simple text file. Here, we can specify not only a base image to start from, but applications to install and processes to run. That allows us to accomplish much more complicated tasks with easily replicable “recipes.” Let’s see what this looks like in action. Make sure your container engine is running, and then bring up the terminal. Try entering the following command:
docker container run alpine curl google.com
Based on our last lesson, we would expect this to create a new container based on the alpine image and then run the curl tool (which transfers data from—or to—a server) with the parameter “google.com.” With that parameter, we would expect to see some HTML downloaded from the address we specified. Instead, we likely get output something like this:
docker: Error response from daemon: OCI runtime create failed: container_linux.go:380: starting container process caused: exec: "curl": executable file not found in $PATH: unknown.
Translation: our new container doesn’t know what we’re asking it to do, because it doesn’t have curl installed. Well, we can take care of that with a Dockerfile. Create a new folder called “curler” and inside of that folder, create an empty file called Dockerfile, which shouldn’t have a file extension. (If you’re not sure how to do this, use the command touch Dockerfile in your folder and open the file with the editor of your choice.) Now add the following lines to Dockerfile and save:
FROM alpine:latest
RUN apk update
RUN apk add curl
This recipe instructs the container engine to build an image based on the most recent version of the alpine image, and then to run alpine’s package manager apk, update apk’s index of available packages, and then add the package for curl. In other words, we’re installing the curl tool. Let’s put our Dockerfile into action by creating an image from these instructions. Run the following command:
docker image build -t curler .
This command tells the Docker engine to build a new image, and -t is shorthand for --tag, which we’re using to name our image “curler.” Finally, the period at the end gives Docker the location of the Dockerfile we’re using for this image—in this case, our current working directory. The container engine then builds an image based on our instructions (actually, a series of images in swift succession based on each step) and adds it to our local selection of images. If we run...
docker image ls
…we should see it in our image listing. The images listed will vary depending on your environment, but you should see curler among them, and the output should look something like this:
REPOSITORY        TAG            IMAGE ID       CREATED         SIZE
curler            latest         a46b2fdd95c9   1 minute ago    9.97MB
nginx             latest         605c77e624dd   6 weeks ago     141MB
postgres          latest         7526db3fb03b   2 months ago    53.6MB
alpine            latest         c059bfaa849c   2 months ago    5.59MB
...
Now let’s try using it. Run:
docker container run curler curl google.com
We’re trying to do the same thing as before—run curl with the parameter “google.com.” This time, we should get a better result:
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
Dload  Upload   Total   Spent    Left  Speed
0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>301 Moved</TITLE></HEAD><BODY>
<H1>301 Moved</H1>
The document has moved
<A HREF="http://www.google.com/">here</A>.
</BODY></HTML>
100   219  100   219    0     0    180      0  0:00:01  0:00:01 --:--:--   180
Success! We downloaded some very simple placeholder HTML from the address google.com. That’s it for this lesson—remember to clean up your containers. You may also wish to delete the new curler image from among your local images. To do this, you should first delete any containers depending on it, and then run:
docker image rm curler
Next time, we’ll dive deeper into the use and management of images.
{
  "$experimentIndex": 0,
  "$variantIndexes": [
    1
  ],
  "$activeVariants": [
    "AltVariant"
  ],
  "$classes": [
    "exp-alternate-ad-placement-1"
  ],
  "name": "alternate-ad-placement",
  "experimentID": "ca62VGC4QDaNqECV8gH-kg",
  "variants": [
    "OriginalVariant",
    "AltVariant"
  ]
}