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.