Skip to main content.

Using Docker

Objectives

About Docker

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.

Understanding Docker

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.

Using a Docker Image from Docker Hub

  1. Make sure Docker Desktop is running
  2. Type the following command in your terminal: docker run -dp 80:80 docker/getting-started
  3. Open your browser to http://localhost
  4. Read the page, follow instructions, and ask questions.

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.

  1. Run docker ps
  2. In the Docker Desktop, go to the container and view the logs.
  3. Stop the container and delete it.
  4. Run docker run --name docker_start -dp 80:80 docker/getting-started

    What is different?

  5. Run docker ps

    What does docker ps do?

Note that

Now let's take a look inside the container:

  1. In Docker Desktop, click on your container and use the command-line interface (CLI). This opens an interactive shell into the container.
  2. Since you can tell it's a web application of some sort (since you looked at it in your browser), search for an index file: find / -name "index.*"

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.

Maintaining your container

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.

Other Things You Can Do

For much more, see the Docker documentation or find your favorite tutorial site.

Another Example

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.

What are options for the various docker commands?

Run docker run --help

  1. What does the -d option do?
  2. What option automatically delete a container when it exits?
  3. Explain what this command is doing: docker run --name docker_start -dp 80:80 docker/getting-started Label the parts.

Sharing files with your container

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.

Using a Dockerfile

You have looked at a few Dockerfiles. Let's use one that we made: Dockerfile.

  1. Read the Dockerfile, so you have some idea of what it's doing.
  2. Build a container image on your host.
    1. Create an empty directory/folder and download/copy the Dockerfile into it with the name Dockerfile.
    2. Then in a terminal window on your host, enter the directory (with cd)
    3. Run docker build -t image397 .

      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.

    4. Run the image using:
      docker run --name cs397 -it image397 bash

      This created/ran the container cs397 using the image image397

      You're now "in" the container, using the bash shell.

Using bind mounts

  1. Create a directory/folder on your host to bind mount into your container(s). Use the mkdir command or select new folder in the menu of a window on the containing folder. Name the directory docker-share. In a host terminal window, change into that directory (cd).
  2. Create a container from the image and attach the bind mount. Suppose the new directory/folder's path is /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.

  3. cd to the /cs397 directory. What is in that directory?
  4. Run /usr/games/cowsay hi

    What did that do?

  5. Run /usr/games/cowsay hi > hi.out
  6. View the contents of the directory.
  7. In another terminal (on the host machine, not in the container), look at the contents of your docker-share directory. What do you see?
  8. Create a new file in docker-share called cool. The contents of the file should be cool.
  9. Back in the container, view the contents of the directory (should still be in /cs397). How cool is that?
  10. Run /usr/games/cowthink < cool
  11. Run /usr/games/cowthink < cool > cool.out
  12. View the contents of cool.out
  13. In that other terminal on your host machine, view the contents of docker-share. Explain what is happening!

Analyze and Synthesize

Reflect on what you have done (and hopefully learned):

A More Real-World Use Case

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!

Learning More

Return to the docker getting-started image docker-start container and continue through the tutorial in the browser.