Jacob Tomlinson's profile picture Jacob Tomlinson
Home Blog Talks Newsletter About

Interactive Docker containers

4 minute read #intro-to-docker, #tutorial

Interactive shell

In this post we are going to create a container we can interact with. We can then have a poke around inside the container and see what it is and how it works.

Open your terminal and run:

docker run --rm -ti ubuntu /bin/bash

This will download the ubuntu image from Docker Hub and start a new container form it. There are a couple of new arguments here that we haven’t seen yet. -t tells docker to assign a pseudo tty and -i keeps stdin open to allow us to interact with the shell. We have also added the command /bin/bash to the end, this overrides the default command set in the image.

Once the container runs you should get something similar to:

root@4e8a1c232723:/#

The hostname of your container will be different but you should see that you are running a bash session as root within your container.

Seeing a prompt like this feels very much like being inside a virtual machine. However as this is a container you are sharing the kernel with the host system and any other containers you run simultaneously.

Filesystem

If you type ls it will list the root filesystem of the container, this is the filesystem which came inside the Docker image and your bash process is contained inside it.

# ls
bin   dev  home  lib64  mnt  proc  run   srv  tmp  var
boot  etc  lib   media  opt  root  sbin  sys  usr

You can move around the filesystem, edit files and execute commands. However if you create or modify a file and then exit from the container those changes will not be persisted. This is because each instance of a container is an ephemeral copy of the base image. Using a technology called copy-on-write Docker doesn’t actually have to copy the base image for each container. It stores all changes separately to the base image and merges them together cleverly when the containers accesses the filesystem. When the container exits these changes are discarded.

It is possible to persist data between containers using volumes, these will be covered in a later post.

You can also find the container images themselves stored under /var/lib/docker on the host system. They are just directories like any other but Docker bind-mounts these to create the layered copy-on-write filesystem and uses chroot to contain the processes within the image.

Process namespace

If you list the processes running on the system you will only see two, the bash session and the ps itself.

# ps aux
USER       PID %CPU %MEM    VSZ   RSS START   TIME COMMAND
root         1  0.0  0.3  18180  3340 14:14   0:00 /bin/bash
root        20  0.0  0.2  15572  2080 14:45   0:00 ps aux

This is different to a virtual machine in may ways. For starters your bash session has PID 1. On all linux systems PID 1 is the init system, that may be init.d, systemd, upstart or something else. An init system handles the starting of all the other processes required to run your operating system, this includes networking, cron, disk mounts, graphical user interfaces and everything else you use. However in a container we already have a functioning operating system, the host. The container can therefore do one thing and one thing only without worrying about running an operating system, so having a single process with PID 1 is logical.

If you were to run the ps aux command on the linux host you would see all the running processes, including those inside the container.

$ ps aux
USER       PID %CPU %MEM    VSZ   RSS START   TIME COMMAND
root         1  0.1  0.1   9764  1736 13:49   0:05 init
...
root      1522  0.0  0.3  18180  3340 14:14   0:00 /bin/bash
...
docker    2028  0.0  0.1  13016  1740 14:45   0:00 ps aux

Here we can see the true PID 1 of the system, we can also see the same /bin/bash process from our container but with PID 1522 instead of 1. The reason our container thinks the process is PID 1 is due to kernel namespaces. The process is in both the main namespace, with PID 1522, and in the container namespace with PID 1. This is the containment part of containers, when we run ps within the container it is restricted to the container namespace and can only see the other processes inside the container.

Conclusion

Containers appear to be a whole different system, but we’ve demonstrated here that they are just normal linux processes running on a normal linux machine. The only difference is they are kept separate from the other things running on the system and have access to a subset of resources. The exciting thing is each container can have a different subset of resources. They can behave like an entirely separate system with different tools and dependancies even if they conflict with the packages on the host system or in other containers.


Have thoughts?

I love hearing feedback on my posts. You should head over to Twitter and let me know what you think!

Spotted a mistake? Why not suggest an edit!