Docker lets you run a Linux development environment of your choice. Under the hood, Docker Desktop launches a virtual machine (guest) on your computer that runs alongside the host operating system installed on your computer. The guest VM runs a Linux OS. Docker also installs application software. You can use that software to build a Docker image and launch a Linux container in the guest VM from the image.
Using Docker, we can all run the same development environment using the Docker configuration file (Dockerfile).
The docker command. Once you have installed Docker Desktop, the best way to manage your containers is to run the docker command in a terminal window on your host computer. There is extensive documentation on the docker command. It is a "swiss army knife" with many arguments and multiple ways to do the same thing.
Docker can be confusing. Apart from all the virtual magic, the basic mental model is hard to get straight from all the words about it on the web. It is important to be precise with terminology.
The key thing to understand is that a container has a bag of files (a file tree) and zero or more active processes. The file tree is a customized (layered) view of a Linux file system. The container's file tree is seeded with a logical copy of a file tree in the container's initial image. But the file tree also includes any files created by processes running within the container. Any process that runs within the container sees the container's file tree, and may modify it. You can run many containers from the same image, and they each have their own file tree. If a process modifies the file tree in one container, those changes are not visible from other containers.
It is easy to create a new container, even by accident. You can
use docker run
to run a command (process) within a
container, say, bash or another command shell. You use
the -it
flags to run the container/process
in interactive mode, so that you can type commands to it and
see the output directly in your terminal window. docker run
always creates a new container, so any files created by a
previous run are no longer visible to the new process.
If your process in the container exits (e.g., you exit your shell), then the container becomes dormant and you return to your host shell. But the container's bag of files remains, and you can use docker start to launch another process (e.g., another shell) within the same container, where the files are visible. If you want to reclaim the disk space, you can delete the container's files with the docker rm command. If you are finished and you want to delete an image, use the command docker image prune after deleting (with docker rm) any containers created from the image.
To understand Docker, it is important to distinguish in your mind
three things: the image, the container's process (or processes), and
the container's bag of files (its file layers or file tree or file
system). There is also a Dockerfile
, which is a text
file of commands to configure the image. That's four kinds of
objects, each with its own lifecycle, and with dependency
relationships among them.
You pulled the
image Getting Started and then ran a container
built/based on that image. Check out the image's web page in Docker
Hub. Then, look at its Dockerfile
. I don't expect you
to understand what's going on; just want to reveal a bit about what is
behind the curtain.
What is different?
What does docker ps
do?
Note that
docker/getting-started
image again (as above), it
will not pull the image again because you already have it. (If you
didn't delete it.)
-dp 80:80
the web server in the
container would not be bound to a port on the host machine, so you
couldn't access it from your browser.
Now let's take a look inside the container:
Let's look at some documentation for an image. On DockerHub, search for python
and look at its image. Skim through the documentation.
Discussion: What is the difference between a container and an image?
Continue on in the tutorial in the browser.
Installing software. While running in your
container, you may find that various tools are missing. You can use
the apt-get
command to install them. This is true, in
general, but note that if an image uses "Alpine-Linux", that is a
super stripped down version of Linux (requiring only around 5MB of
space) and you may not be able to use things like apt or bash.
Getting another shell in the same container.
Instead of using docker run (which starts a new container),
use docker exec. E.g.: docker exec -ti docker_start
sh
This is an alternative to using Docker Desktop to get to the
command-line interface.
Restarting an existing container. If you exit the
shell, reboot your computer, or destroy the terminal window, don't
worry. Everything is OK. Your container is still there. More
precisely, your processes (e.g., your command shell) are dead, but the
container's file tree is still there. You can get back into it by
saying:
docker start -i docker_start
That starts your existing named container
(docker_start
) again, using the same configuration and
command as specified in docker run that created the
container. Specifically, for this example, it launches a new sh shell
into the existing docker_start
container in interactive
mode (-i
). Again, you get a sh command prompt for a
shell running in the container, and everything should look just as it
did.
Remember if you use docker run again it creates a new container, or it gives you an error for running two containers with the same name. A new container might be OK if the only files you modified are in the bind mount directory, but it takes up more space, and any files you modified in the old container (e.g., to install missing packages with apt-get) do not appear in the new container.
docker rm NAME
: Destroy the container with NAME and its
file system, killing any processes in the container. Any directories
that were bind mounts in the container are preserved on your host.
You might have to stop it first, e.g.: docker stop NAME
For much more, see the Docker documentation or find your favorite tutorial site.
Let's do another example of using an existing Docker image from Docker Hub. This one is a little simpler and describes what is happening within the terminal.
Read what it says happened.
Note that the container doesn't keep running in this case:
docker ps
The command that was run exited, and the container is done.
This image is built from the very basic image scratch
.
The hello
script is copied to /
within the
container. Then, the hello
executable is run.
That should give you an error because you already have a container
named hello
.
Run docker run --help
-d
option do?
docker run --name docker_start -dp 80:80 docker/getting-started
Label the parts.
Conveniently, a container's file tree may also include bind mounts, which attach folders/directories in the host file system so that processes in the container can operate on them. You select a directory that exists in your host file system and then command docker to bind mount it into a container's file tree, so that the directory is visible (usually at a different pathname) to processes running in the container. Then, if you destroy the container, then the mounted directory and its files survive in your host file tree. You can even share the directory among multiple containers over time or at the same time.
It is useful to bind mount your git local repository into your container, so that you can edit source files on the host using your favorite editor application, and then build/run them in the Linux container. You can run git commands on the host or in your container. But you have to do the git config and install your keys and so on in both places.
You have looked at a few Dockerfiles. Let's use one that we made: Dockerfile.
Don't forget the . (current directory)! That build command creates
an image based on the Dockerfile, and gives the image the name (tag)
image397
. If everything goes well you have a named
image. You don't see it, but it is there.
What are two ways to check if the image was created? Then, use those ways to check/confirm that the image was created.
This created/ran the container cs397
using the
image image397
You're now "in" the container, using the bash shell.
docker-share
. In a host terminal window,
change into that directory (cd).
/Users/sprenkle/docker-share
. You want the bind mount
to appear in the container file tree at /cs397
: docker run --name cs397 --mount type=bind,source=/Users/sprenkle/docker-share,destination=/cs397 -it image397 bash
If you got an error about the name of the container being a
duplicate, then remove the container using
docker rm
cs397
Make sure the path to the source has been updated appropriately.
This will create the container cs397
using the
image image397
WITH the directory /cs397
in
the container mapped to your docker-share
directory.
Again, you're now "in" the container, using the bash shell.
/cs397
directory. What is in that directory?
What did that do?
docker-share
directory.
What do you see?
docker-share
called cool
. The contents of the file should
be cool
.
/cs397
). How cool is that?
cool.out
docker-share
. Explain what is happening!
Reflect on what you have done (and hopefully learned):
Your boss assigned you to a project where you need to run a web application server (Apache's Tomcat). We'll see how this goes, with applying what you've learned!
docker-share
directory.
docker-share
directory.
/usr/local/tomcat/webapps
Return to the docker getting-started
image docker-start
container and continue through the
tutorial in the browser.