Walkthrough - Docker

The following walkthrough is a good way to get a glimpse of what Docker can do. It is mostly inspired by Coffee with Mich - Docker commands.

Express

What is Docker?

  • Eliminates “works on my machine”

  • Docker provides containers (vs VM)

  • Images have a name and a tag, latest is a “magical” tag, the others should “never” be overwritten.

Cheat sheet

  • Images commands
    • Show the list of local images: docker images

    • Pull an image from a registry: docker pull {image[:tag]}

    • Delete an image locally: docker rmi {image[:tag]}

    • Tag an image: docker tag {source_image[:tag]} {target_image[:tag]}

  • Container commands
    • Show the list of running containers: docker ps

    • Show the list of all containers: docker ps -a

    • Launch a container, give it a name and autoclean it when it’s done running: docker run --name {name} --rm {imagename}

    • Get a bash shell into a running container: docker -ti exec {container id} bash

    • Launch a container, map a directory and a port: docker run --rm -v $HOME:/host_home -p 0.0.0.0:8080:80 {imagename}

    • Remove a stopped container: docker rm {container id}

Detailed

What is Docker?

Docker is a software technology providing containers, promoted by the company Docker, Inc. Docker provides an additional layer of abstraction and automation of operating-system-level virtualization on Windows and Linux. Docker uses the resource isolation features of the Linux kernel such as cgroups and kernel namespaces, and a union-capable file system such as OverlayFS and others to allow independent “containers” to run within a single Linux instance, avoiding the overhead of starting and maintaining virtual machines (VMs). [1]

By providing well-defined environments, Docker is good at eliminating the famous “but it works on my machine!” statement.

Key concepts

Containers

You can think of a Docker container as a physical shipping container. It’s a box where you store and run an application and all of its dependencies. Just as cranes, trucks, trains, and ships can easily work with shipping container, so can Docker run, copy, and distribute containers with ease. Docker completes the traditional container metaphor by including a way to package and distribute software. The component that fills the shipping container role is called an image. [DIA02]

Images

A Docker image is a bundled snapshot of all the files that should be available to a program running inside a container. You can create as many containers from an image as you want. But when you do, containers that were started from the same image don’t share changes to their file system. When you distribute software with Docker, you distribute these images, and the receiving computers create containers from them. Images are the shippable units in the Docker ecosystem. [DIA02]

A docker image is composed of layers of intermediate images.

Repository

A repository is a named bucket of images. The name is similar to a URL. [DIA03]

Image Commands

To list the available images on the current machine:

This command returns 5 columns. While tag, image id, created and size are self-explanatory, repository represents the repository and name of the image. As an example, an image with the value mongo in the repository field is a local image named mongo. An image with the value quay.io/datacratic/baseimage is an image named baseimage from the repository quay.io/datacratic.

The -a flag, if used, will also display all intermediate images.

The easiest way to share built images is through registries. The official docker public registry contains a ton of images.

To download an image from a registry, use the docker pull command. So, say we want to download the nginx:1.13.6 image, we would run:

Hint

You don’t need to run docker pull prior to using an image because if you try to launch a container based on an image that you don’t have locally, docker will pull it automatically.

Here, we didn’t put anything in front of nginx. This is because when there is no repository name found before the image name, docker assumes it points to the official registry.

Container commands

To see all running containers

This command returns 7 columns.

  • container id: A unique auto-generated id representing the container.

  • image: The image repository, name and tag used to launch the container.

  • names: The unique name representing the container.

  • command: The command that the container runs.

  • created: How long ago the container was created.

  • status: The container status and its associated duration.

  • ports: Containers can expose some of their ports. Whenever they do, they are listed here.

Launch the ubuntu image with the following command.

Nothing seems to happen? This is normal. You just launched what is called an “operating system container”. That is a container that accomplishes nothing on its own.

Let’s relaunch it and “log” into it.

Notice that your prompt (likely) changed and that you are now root. You have successfully launched a container and logged into it. By default, you are always root in docker.

Container Lifecycles

  1. Run exit to exit the container.

  2. Run docker ps. You shouldn’t see your container.

  3. Run docker ps -a. You should see your container this time, along with the one that seemed to do nothing a couple of steps ago. The thing is that docker ps doesn’t show stopped containers. That’s why you see them with the -a flag. To restart a stopped container, run

Since we’re still working with our “operating system” container, it still doesn’t seem to do a thing, but you get the idea.

Now let’s introduce the exec command. First, relaunch a container and log into it.

From a different shell window, run docker ps to find out the container id of the container you just launched, and then run

Et voilà. You have a bash shell into a running container. To demonstrate it, from one of your terminal window run touch tralala and from the other run ls. You should see the file named tralala.

Let’s introduce the kill command. To demonstrate it, exit docker from one of your terminals from the container and run

Your other terminal should now have exited the container because you just killed it.

Let’s now demonstrate how we clean up stopped containers.

Hint

To have your container auto-cleaned when it is stopped, launch it with the --rm flag.

Mapping entities to containers

Volumes

To map a volume/directory to docker, use the -v flag when you launch it.

To map multiple directories, you can have multiple -v flags. To add a layer of security, you can also map directories as read only. To do so, add :ro at the end of the directive. So to reuse the previous example, it would become $HOME:/host_home:ro. If you restart the container with that command and try to run touch cant_touch_this you will get an error.

Caution

  • With the default settings, docker runs as root so any file you create while inside a container will be owned by root.

  • The source path of the volume mapping must be an absolute path. a/b/c won’t work, but /home/alone/a/b/c will.

  • If the source volume does not exist, it will be created and owned by root. (This is especially nasty and a source of errors.)

  • You can use the -v flag to mount single files.

Ports

To map a port to a container, use the -p flag. Let’s demonstrate it with nginx.

This launches an nginx (v1.13.6) container and map host port 8080 to container port 80. Now, run from a host terminal

You should get html content returned by nginx and you should see a log line in the terminal that runs the container. You can now exit it.

Exposing a port this way only makes it available from the host. If you want to expose it to the network, prefix it with 0.0.0.0:. So the previous example would become -p 0.0.0.0:8080:80.

[DIA02] (1,2)

Jeff Nickoloff. “Docker in Action” book, section 1.1.4.

[DIA03]

Jeff Nickoloff. “Docker in Action” book, section 3.1.1.