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:
FROM alpine3.6
ENV
commandsRUN
commands- A single
CMD
command
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 RUN
and CMD
.
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
style (known as shell form), they’re executed using /bin/sh -c
. In other words, they’re standard Linux commands/scripts. (If you don’t know the difference between sh
and 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 python3
.
You’re probably thinking, apart from the way in which they’re executed, what is the difference between RUN
and CMD
commands? Why can we run multiple RUN
commands, and only a single CMD
command?
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!
CMD
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
python3
executable - Python sees that it’s got nothing to process so it gracefully terminates
- Docker receives the
exit 0
exit code from Python and then shuts down the container
We can see this when we spin up mypy2
without the -it
or -d
flags:
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 hi.py
file.
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.
Summary
Let’s run through what we’ve learned in this post:
dockerfiles
are used to tell Docker Engine how to create our image- For the most part,
dockerfiles
are made up ofsh
scripts/commands - 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
CMD
command perdockerfile
- The
CMD
command 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 will@oznetnerd.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.
Leave a comment