In the previous post we may have started running before we could walk. In this post we’ll first take a few steps back to make sure we cover the basics before diving deeper.
Building an image
When we examined the
python:3.6.3-alpine3.6 image’s dockerfile, we saw the components which are used to create a Docker image:
- A single
OK, so we already know that the
FROM alpine3.6 command means that the Python image is going to use the Apline 3.6 as its Linux operating system.
ENV, as its name suggests, is used to specify environment variables for the image. That leaves us with
If you’ve used Linux, it’s highly likely that the
RUN commands look familiar. Here’s why - as described in this GoinBigData post, when
RUN commands are written in the
/bin/sh -c. In other words, they’re standard Linux commands/scripts. (If you don’t know the difference between
bash, have a read of this StackOverflow question.)
To sum up what we’ve just covered - Docker Engine examines the
dockerfile, spins up a Linux OS (Alpine in this instance), runs a bunch of
sh commands and sets a few environment variables. It really is that simple.
OK, now let’s now move on to the final piece of the puzzle - the
CMD command. As described in the GoinBigData post linked to above, the
CMD ["python3"] style is known as “exec form”. Instead of running through
/bin/sh -c, exec form runs the executable directly, which in this case is
You’re probably thinking, apart from the way in which they’re executed, what is the difference between
CMD commands? Why can we run multiple
RUN commands, and only a single
Docker Engine will execute
RUN commands during the creation of an image. For example, it will install the Python, create symbolic links, remove unnecessary files, etc, as part of the image creation process. Once done, the image is ready to be used as a container, and this is where the
CMD command comes into play.
Warning: Hold onto your hat, we’re about to go deep again!
In Part 1 of this series I said that “When done correctly, containers only have a single concern (formally referred to as a single process)”. In the case of the Python image we’ve been dissecting, that’s exactly what the
CMD takes care of.
Let’s spin up another container:
will@ubuntu:~$ sudo docker run -d --rm --name mypy python:3.6.3-alpine3.6 75baeb52916d44e15b881ab4fb89c5e3e1283cc08cc0f5b5de8ea13209a8335a
In the above command, the flags used achieve the following:
-d: Makes the container a daemon. This is useful for us in this instance because we don’t want to be dropped into the Python interpreter.
--rm: Delete’s the container as soon as it is stopped
Now that we’ve got our container up and running and we’re still on our Docker Host (as opposed to the Python interpreter), let’s do some poking around.
will@ubuntu:~$ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 75baeb52916d python:3.6.3-alpine3.6 "python3" 8 minutes ago Up 8 minutes mypy
We can see the
python3 command was issued after the container was launched. That’s fine, we expected to see that. Let’s now see what the container has to say for itself:
will@ubuntu:~$ sudo docker exec mypy ps PID USER TIME COMMAND 1 root 0:00 python3 7 root 0:00 ps
The container is running only running
python3 and the
ps command we’ve just issued. That’s it. As fascinating as that is, what’s more interesting is the fact that
python3 is running as
PID 1. I won’t bore you with the details, but in a nutshell it is “a process that must be running at all times, if this process ends, the kernel enters into a panic mode, after which you cannot do anything else, except rebooting” (reference).
In other words, the
CMD command tells Docker what it needs to keep track of in order to determine whether a container should stay up or be brought down. This brings us back to why we needed the
-it flags when spinning up our Python container. Without them, Docker does the following:
- Spins up the container
- Launches the
- Python sees that it’s got nothing to process so it gracefully terminates
- Docker receives the
exit 0exit code from Python and then shuts down the container
We can see this when we spin up
mypy2 without the
will@ubuntu:~$ sudo docker run --name mypy2 python:3.6.3-alpine3.6 will@ubuntu:~$ sudo docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2342211efc48 python:3.6.3-alpine3.6 "python3" 4 seconds ago Exited (0) 2 seconds ago mypy2
Using the container
Being dropped into the Python interpreter isn’t all that useful, nor is having the container spun up only to be brought down shortly thereafter. Having Docker execute a “hello” message is much more useful, so let’s go ahead and do that now:
sudo docker run --name mypy3 --rm -v "$PWD"/hi.py:/hi.py python:3.6.3-alpine3.6 python3 /hi.py hello!
Side Note: Notice how in Part 1 we didn’t have to specify the
python executable after the image name, but this time we did? That’s because specifying anything after the image name overrides the
CMD command in the Dockerfile. I’ll cover this in more detail in Part 3.
What we’ve done with the above command is called a bind mount. Bind mounts allow us to mounted a file or directory on the Docker Host to a file or directory inside of our container. That is to say, the
hi.py file resides on our Docker Host machine, but we’ve made it available inside of our container too. We’ve then used Python inside of the container to run the bound
In the interest of keeping this post to a reasonable length, I’ll leave storage at that for now and will tackle it further in a future post.
Let’s run through what we’ve learned in this post:
dockerfilesare used to tell Docker Engine how to create our image
- For the most part,
dockerfilesare made up of
- Images are always based on other images, e.g the Python image we’ve been discussing is based on Alpine Linux
- We can only have
CMDcommand tells Docker which executable to monitor
As always, if you have any questions or have a topic that you would like me to discuss, please feel free to post a comment at the bottom of this blog entry, e-mail at email@example.com, or drop me a message on Reddit (OzNetNerd).
Note: The opinions expressed in this blog are my own and not those of my employer.