Working With Containers
Overview
We've successfully deployed a couple of containers into our Docker engine. In this section, we'll dig a little deeper into working with and interacting with containers.
Listing Images
As we experienced with running the Whalesay container multiple times, the actual image was only downloaded, uncompressed and built once. On all subsequent executions, a new container was simply instantiated based on the image. Docker keeps a local repository of images currently in use; and, those images cannot be deleted until all dependent containers have been deleted.
To view a list of currently downloaded images, type the following in the command prompt:
docker images
The output should look similar to the following:
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest 48b5124b2768 4 months ago 1.84kB
docker/whalesay latest 6b362a9f73eb 2 years ago 247MB
There's a few things that are reported to us here.
First, we see the repository, including the namespace, of the image. The hello-world
is what we would consider a library image. In other words, it's an image that, for lack of a better way to describe it, is 'built-in' to Docker. For whalesay
, we see that the repository is docker
. We'll talk more about repositories below.
The second column shows us the current tag of the image. We look at tagging in the next workshop section.
The third column displays a unique id of the image. Just so that you know, we can refer to the image in our commands throughout the exercise by the full name as it's listed under REPOSITORY
, the image id, or simply use the first 3 characters in the image id (e.g. hello-world
could also be referenced by 48b5124b2768
or 48b
). The thing is, the minimum is 3 characters, but if you have multiple images that have the same first 3 characters, you may need to use a couple of more until you reach a differentiator.
The created column does not report when you downloaded the image locally. Instead, it reports the date of when the image was created by its owner/designer. This is a great column for DevOps to use when quickly trying to determine when a particular image was created.
The size column reports the total size of the image - a sum of all layers comprised to make the image.
Docker Repositories/Registries
A registry is a service - public or private - from which images can be hosted and pulled by other users. Images are stored in these repositories under namespaces which are, typically, usernames or organizational names.
In the above example, the whalesay
image is located under the docker
namespace. This means that the image belongs to and is managed by the Docker organization.
Microsoft's public registry is hosted by Docker. You can visit Microsoft's registry at https://hub.docker.com/u/microsoft/. As you view the available images (which are only available for Windows-based machines), you'll see that each begin with microsoft/
. If we were to pull an image created and maintained by Microsoft into our local Docker instance, we would see the image prefaced by that namespace.
Inspecting Images
There are a couple of ways we can get some greater details about our images. We can view the underlying metadata of our image; and, we can see the build history of the image.
Image Inspection (Metadata)
To view the metadata of an image, we'll need to inspect it. From the command prompt, type the following:
docker image inspect docker/whalesay
You should see something that begins with the following:
[
{
"Id": "sha256:6b362a9f73eb8c33b48c95f4fcce1b6637fc25646728cf7fb0679b2da273c3f4",
"RepoTags": [
"docker/whalesay:latest"
],
"RepoDigests": [
"docker/whalesay@sha256:178598e51a26abbc958b8a2e48825c90bc22e641de3d31e18aaf55f3258ba93b"
],
"Parent": "",
"Comment": "",
"Created": "2015-05-25T22:04:23.303454458Z",
"Container": "5460b2353ce4e2b3e3e81b4a523a61c5adc238ae21d3ec3a5774674652e6317f",
"ContainerConfig": {
"Hostname": "9ec8c01a6a48",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/bin/sh",
"-c",
"#(nop) ENV PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Image": "5d5bd9951e26ca0301423625b19764bda914ae39c3f2bfd6f1824bf5354d10ee",
"Volumes": null,
"WorkingDir": "/cowsay",
"Entrypoint": null,
"OnBuild": [],
"Labels": {}
},
"DockerVersion": "1.6.0",
...
Take a moment to examine the metadata. As you look through this information, you will find various attributes that describe the image.
Image History
The last bit of information that the inspect
command reported was a set of layers:
"Layers": [
"sha256:1154ba695078d29ea6c4e1adb55c463959cd77509adf09710e2315827d66271a",
"sha256:528c8710fd95f61d40b8bb8a549fa8dfa737d9b9c7c7b2ae55f745c972dddacd",
"sha256:37ee47034d9b78f10f0c5ce3a25e6b6e58997fcadaf5f896c603a10c5f35fb31",
"sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef",
"sha256:b26122d57afa5c4a2dc8db3f986410805bc8792af3a4fa73cfde5eed0a8e5b6d",
"sha256:091abc5148e4d32cecb5522067509d7ffc1e8ac272ff75d2775138639a6c50ca",
"sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef",
"sha256:d511ed9e12e17ab4bfc3e80ed7ce86d4aac82769b42f42b753a338ed9b8a566d",
"sha256:d061ee1340ecc8d03ca25e6ca7f7502275f558764c1ab46bd1f37854c74c5b3f",
"sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"
]
Other than the layer id's, this doesn't really tell you much. To see the actual build history of the image, type the following command:
docker image history docker/whalesay
You'll will then see something similar to the following:
IMAGE CREATED CREATED BY SIZE COMMENT
6b362a9f73eb 2 years ago /bin/sh -c #(nop) ENV PATH=/usr/local/bin:... 0B
<missing> 2 years ago /bin/sh -c sh install.sh 30.4kB
<missing> 2 years ago /bin/sh -c git reset --hard origin/master 43.3kB
<missing> 2 years ago /bin/sh -c #(nop) WORKDIR /cowsay 0B
<missing> 2 years ago /bin/sh -c git clone https://github.com/mo... 89.9kB
<missing> 2 years ago /bin/sh -c apt-get -y update && apt-get in... 58.6MB
<missing> 2 years ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 2 years ago /bin/sh -c sed -i 's/^#\s*\(deb.*universe\... 1.9kB
<missing> 2 years ago /bin/sh -c echo '#!/bin/sh' > /usr/sbin/po... 195kB
<missing> 2 years ago /bin/sh -c #(nop) ADD file:f4d7b4b3402b5c5... 188MB
Keep in mind that these are layers. So the way to read this is from bottom-up, meaning that the last row is actually the first layer. Then, as you go up the list, additional layers are applied. The bottom layer is usually a base image, such as an OS and, because it's the OS, is usually larger in size. Then, actions or commands are applied to that image that make up the additional layers. Some of the last actions to be applied to this image were running an install script and setting some environment variables.
Let's take a look at another example. Download another image to your local Docker engine by typing the following command:
docker image pull a11smiles/softcover
This is an image stored under my personal namespace in the public Docker registry. Softcover is a ruby-based application used to build digital books for various platforms. Running this command will take a few minutes, but, as you will observe, there's quite a few layers to this image.
Once this image download and reconstruction has completed, check out the history of the image:
docker image history a11smiles/softcover
You will see all of the commands, which include installing multiple dependency libraries for Softcover (e.g. apt-get install ...
), issued to build the image. Some of these dependencies are quite large, ranging from 815kB to almost 4GB. Image the time it would require to download all of these requirements manually. This doesn't include the time it would take to properly and consistently configure them for each developer and/or environment. With the Softcover container, in this case, simply download the image and run the container with supplying your book's source to create a digital publication. This can be easily wired up in an automated build process.
Listing Containers
Not only can we list our local images, but we can also list our local, instantiated containers.
Running Containers
Type in the command line:
docker ps
You should see some like the following:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
This tells us that no containers are currently running.
Non-running Containers
We can see previously ran containers by typing in the following:
docker ps -a
This will render a report similar to the following:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a883ff18a967 docker/whalesay "cowsay Hola!" 26 hours ago Exited (0) 26 hours ago sad_goldstine
baa0591c4392 hello-world "/hello" 26 hours ago Exited (0) 26 hours ago jovial_raman
You may see more depending on how many times you instantiated the whalesay
image. The important thing to see from this report is the Status of containers. All containers, in this report, have exited and are no longer running.
One thing to note is that even though the containers have finished executing, they have not been deleted. This allows you to enter into a stopped container. One reason this may be useful is if an application has thrown an exception and quit, you may want to enter into the container to check out some log files or something of that nature to debug the application. Once you're done with the container, you must delete it. We will see how to do this in the next page of the workshop.
Running a Container
We've already instantiated a couple of images. We do this by the following command:
docker run <namespace/image>
There are two primary types of processes - short-running and long-running.
Short-running Processes
Short-running process are just like the images we've already ran - hello-world
and whalesay
. Another example is the softcover
image. A short-running process typically runs a single command, performing a single function, then exits. This function could be displaying a message, sending an email, or building an application or other resource (e.g. a book, help documentation, etc).
Every time we we run a short-running process, using the above command another container is instantiated. This is why a new container was created ever time you ran whalesay
.
Long-running Processes
Long-running processes are typically service-based applications (e.g. web servers, service buses, queues, etc.). We can also run an OS as a container. Try typing in the following:
docker run -it ubuntu /bin/bash
This command will will download an ubuntu
image and run the bash
shell in interactive mode (-i
) by opening up a terminal (-t
). When this runs, you will be automatically placed inside of the running Ubuntu container (notice the change in the command prompt (e.g. something similar to root@<container id>:/#
)). If you are familiar with Ubuntu, feel free to take a look around. To exit the container, simply type in exit
. This will return you to your host machine.
Let's now run Ubuntu in detached mode. Instantiating a container in interactive mode assumes we are going to interact with the running container. However, typical long-running processes (like web servers) don't require command line interaction. We'll run a web server later in this workshop. However, for the moment, let's mimic a long-running process by running Ubuntu in an infinite sleep mode. Execute the following command:
docker run -d ubuntu sleep inf
Again, this will run an Ubuntu container in detached mode (-d
) and set it to sleep infinitely.
Let's see the container running in the background by typing in:
docker ps
You should see something like:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
094012b145c8 ubuntu "sleep inf" 2 seconds ago Up 2 seconds vigorous_yalow
You'll see under the Status column, that it's reporting an up-time for the container. We don't typically run commands against long-running processes, but just for the practice, issue the following command (substitute the container id with your id):
docker exec 094 ls
This will run a list
command against the current directory inside of the container and return a list of subfolders.
While the container is running, let's look at one more interesting thing. At the command prompt, type the following:
ps aux
This will list all of the currently running processes. As you scroll and look at the background processes, you should see a line similar to the following. The process id, memory consumption, etc. may be different but try to find the last column.
root 55515 0.0 0.0 4380 664 ? Ss 01:39 0:00 sleep inf
What you see here is that container processes are exposed to the host kernel and have access to host system resources.
Now let's stop the container. Technically, we could kill the process using traditional Linux (UNIX) commands, but this would leave our Docker engine in an unclean state and would require us to do some additional clean up. Let's stop the container using Docker commands.
docker stop 094
(NOTE: Again, 094
is the first 3 characters of my container's id. You'll need to use the id assigned to your container.)
Running docker ps
again should show you that no more containers are currently running.
Re-running a Stopped Container
There will be times when you may need to re-run a stopped container. Find one of the stopped 'whalesay' containers by typing the following:
docker ps -a
My output looks like the following:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
094012b145c8 ubuntu "sleep inf" 27 minutes ago Exited (137) 4 minutes ago vigorous_yalow
a883ff18a967 docker/whalesay "cowsay Hola!" 27 hours ago Exited (0) 38 minutes ago sad_goldstine
baa0591c4392 hello-world "/hello" 27 hours ago Exited (0) 27 hours ago jovial_raman
By typing the following command, I can re-run my whalesay
container without instantiating a new container and without having to supply any parameters. Basically, I can re-run the container as it was originally ran.
docker start -a a88
(NOTE: Again, a88
is the first 3 characters of the stopped whalesay
container's id.)
With this command, we are restarting a stopped container and attaching (-a
) to the container's output (STDOUT/STDERR) to view any messages. In our case, the message is a simple whale with a speech bubble.
Wow, congratulations! You just successfully completed what may seem like a crash course in managing containers. However, there's really not much more to it than this. As with anything, the more you play with Docker, the more familiar and confident you become with it.