OpenShift for Mere Mortals: Images

OpenShift for Mere Mortals: Images

In my previous post, I talked about the high level concepts behind what containers are. Today I'm going to shift the focus to images - the blueprints that define how a container is constructed when it starts up.

This post will have some examples in it that you are more than welcome to try out yourself. I will be using Docker for my examples, but most of the concepts apply to other container image formats as well. You can install Docker locally from docker.io or you can run these commands interactively from a site like play-with-docker.com.

Most developers are familiar with the term image (especially in the context of virtual machines), but if you aren't familiar, it is really quite simple. Imagine that aliens have invaded and you want to build a robot army to fight them. You could just start building robots from raw materials, but that would take a lot longer. Fortunately for you and the rest of life on earth, you have some parts sitting around that have already been constructed. Container images are like these parts - the act of building a container is just like assembling parts to build a robot. These images give you a foundation to build upon that accelerates the development and deployment of applications (instead of taking time to install software like JBoss or MySQL, you just utilize the image where the software is already installed).

Some hands-on examples will help clarify this process. The simplest example of running a container is the classic hello-world, which you can run with this command:


$ docker run hello-world

When you run this command for the first time, you will see some additional output:


Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
9bb5a5d4561a: Already exists
Digest: sha256:f5233545e43561214ca4891fd1157e1c3c563316ed8e237750d59bde73361e77
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
...

When you tell the container engine that you want to run the hello-world program in a container, you are referencing an image that is already defined somewhere, usually on a central registry. Docker doesn't have the hello-world image locally, and so it pulls the image down from that central registry on the internet and stores it in a local registry. When you run the command again, it doesn't pull the image down again, but rather uses the one locally cached. Thus, it is like you asked the engine to build you a robot and it went and fetched the parts and then assembled it for you. In this case, I picked something really simple, but there are images that have all kinds of software libraries installed so that you can quickly spin up complex applications.

The process works in reverse as well - I can take my robot apart and turn it back into parts if I want, and with containers, I can take a running container and create an image from it. For example, I can start an nginx container like this:


$ docker run -d --name nginx nginx
51872b1ab7f03798f7c62ca67467cb6ad872d0bb52f7acaa371842a022914573

I can validate the container was started like this:


$ docker ps --format "{{.Image}} {{.Ports}} {{.CreatedAt}}" 
nginx 80/tcp 2018-05-07 14:04:35 +0000 UTC

Don't worry too much about the syntax of these commands - in this case, I asked Docker to tell me the running containers (ps) and format the output to include the image name, what ports it listens on, and when it was created. Suppose I go into this container by running an exec command and I write a file into that container that didn't exist in the image:


$ docker exec -it nginx bash
root@51872b1ab7f0:/# echo hello > /hello.txt
root@51872b1ab7f0:/# exit

The container filesystem no longer matches the filesystem that was captured in the original image. If I restart the container, the hello.txt file will not exist any more. I can, however, use the commit command to create a new image from the running container:


$ docker commit nginx my_nginx
sha256:55c1c3d352709da27238e52afe719399879c1fc33fd1cd91cfe0056932776f1e

Now when I look at my images that contain nginx in the name, I will see the new image that I created from the container:


$ docker images --format '{{.Repository}} {{.CreatedAt}}' | grep nginx
my_nginx 2018-05-07 14:09:12 +0000 UTC
nginx 2018-04-30 13:55:45 +0000 UTC

The new image is almost the same as the original image, but if I were to start a container using the my_nginx image, the hello.txt file would exist in it. I have essentially created a new blueprint from which I can create containers.

Typically, however, images are created from a description file that has some instructions that tell the engine how to build the engine. This is similar to how a blueprint would describe the parts of my robot. In the case of Docker, we can use a Dockerfile to build our image. That will be the subject of the next post in this series.

Conclusion

This week I described the concept of a container image and how it relates to the containers that are created from them. Next week in part three I'll cover how images are created from source and talk about the concept of image layers.

Related Article