Build a python image from a Container

Create Docker Image From Containers

Introduction

We’ve seen how to create a basic image using a Dockerfile. However, the Dockerfile isn’t the only way to create an image. You can also use the docker commit command to commit changes that you’ve made to an existing container. My recommendation is that you should use the Dockerfile when creating images.

When you use the Dockerfile you get to treat that image as code in a sense. That means you can commit it to source control, you can easily share that Dockerfile with other developers, and you can use it in automated testing to build the image, et cetera. So, while I recommend using the Dockerfile, it’s not the only option.

In this post, I will demostrate how to use docker commit command which allows you to save whatever changes you’ve made to a container.

Create an image that has python installed from base Ubuntu image

Currently we have the following images and no running containers. We have the greeting image, we have the hello world image, and the Ubuntu image. I’m going to use the Ubuntu as the base for this new image.

(base) shravan-docker# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
(base) shravan-docker# docker images
REPOSITORY                        TAG                 IMAGE ID            CREATED             SIZE
greeting                          latest              b5ab325138b1        17 hours ago        1.88MB
ubuntu                            latest              4e5021d210f6        4 weeks ago         64.2MB
hello-world                       latest              fce289e99eb9        15 months ago       1.84kB

I want to create an image that has Python installed. Now, if this wasn’t a demo I’d recommend that you use the official Python image. However, for the sake of this demo I’m going to install it in an Ubuntu container.

Let’s fire up the container by running docker run -it ubuntu /bin/bash. Okay great, we’re at the bash prompt for the container and I want to show that Python is not installed.

(base) shravan-docker# docker run -it ubuntu /bin/bash
root@83b4f474e280:/# python
bash: python: command not found
root@83b4f474e280:/#

So at a minimum the Python binary can’t be found in any of the directories in the path environment variable. Before we install python, we need to update the apt cache by running apt update, then run apt install python. This is should now install Python, which can be verified as:

root@83b4f474e280:/# python --version
Python 2.7.17
root@83b4f474e280:/#

Okay, we’re now left with a container that has Python installed. The next step is to turn this into an image. Exit out of the bash prompt, which will stop the container. If I list off the images, you can see that we still don’t have our image. We do, however, have a container. The way that you turn a container into an image is with the commit command.

(base) shravan-docker# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                     PORTS               NAMES
83b4f474e280        ubuntu              "/bin/bash"         8 minutes ago       Exited (0) 8 seconds ago                       determined_jennings
(base) shravan-docker# docker commit 83b4f474e280 ubuntu_python
sha256:792689ff7abed201618df0665e1f5a65900f08e1cda8e2b9c48c9267cff6a93b
(base) shravan-docker# docker images
REPOSITORY                        TAG                 IMAGE ID            CREATED             SIZE
ubuntu_python                     latest              792689ff7abe        6 seconds ago       128MB
greeting                          latest              b5ab325138b1        19 hours ago        1.88MB
ubuntu                            latest              4e5021d210f6        4 weeks ago         64.2MB
hello-world                       latest              fce289e99eb9        15 months ago       1.84kB

Notice how the image is now in the list of images. Here’s the problem with this image. We didn’t specify a new command for this image. So it’s just going to use the default for the Ubuntu image, which in this case happens to be the bash command. It’s not because we ran the bash command, that’s because whoever created the Ubuntu image used that as a default.

So if we run a container based on this new image, since it’s using bash as the default and I’m not using the interactive and a TTY for the pseudoterminal, it’s just going to run the executable and then exit out and so what we’re left with is a stopped container. So, if the default command that you wanted to use was bash then you’d be all set.

However, in this example what I’d rather do is use Python since we took the time to install it. So I am going to remove the image that we created so that we can commit a new one with a new command.

(base) shravan-docker# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                     PORTS               NAMES
4d76ba267016        ubuntu              "/bin/bash"         31 seconds ago      Exited (0) 3 seconds ago                       reverent_galois
(base) shravan-docker#

Perfect, okay now it’s roughly the same command that we used before only this time we’re going to use the --change flag to change the command instruction. So, running docker commit --change='CMD ["python", "-c", "import this"]' 4d76ba267016 ubuntu_python. Notice the image is now in the list.

(base) shravan-docker# docker commit --change='CMD ["python", "-c", "import this"]' 4d76ba267016 ubuntu_python
sha256:1ede71222aef92600ec3b3bc352f5773df1f96096e6d73bbabd247cc602281ec
(base) shravan-docker# docker images
REPOSITORY                        TAG                 IMAGE ID            CREATED             SIZE
ubuntu_python                     latest              1ede71222aef        14 seconds ago      128MB
greeting                          latest              b5ab325138b1        20 hours ago        1.88MB
ubuntu                            latest              4e5021d210f6        4 weeks ago         64.2MB
hello-world                       latest              fce289e99eb9        15 months ago       1.84kB

Now, if we create a container based off this image, we should expect to see the python command executed inside the container.

(base) shravan-docker# docker run ubuntu_python
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases arent special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one and preferably only one obvious way to do it.
Although that way may not be obvious at first unless you are Dutch.
Now is better than never.
Although never is often better than right now.
If the implementation is hard to explain, its a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea lets do more of those!
(base) shravan-docker# docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                      PORTS               NAMES
5ea8de84049c        ubuntu_python       "python -c 'import t…"   21 seconds ago      Exited (0) 20 seconds ago                       musing_bohr
4d76ba267016        ubuntu              "/bin/bash"              5 minutes ago       Exited (0) 5 minutes ago                        reverent_galois
(base) shravan-docker#

Summary

So, what have we learned in this post? We learned that docker allows us to create an image based on an existing container by using the commit command. It also allows you to change instructions using the --change flag. We also just grazed the idea of running a python script inside a docker container.

Tags:

Updated: