Docker

During research on how to use Docker, when I came to the point of running interactive containers I got interested in how Docker would behave, if -i or -t option was passed independently.
Below is what I found out.

Although we called a ping command, ‘tcpdump’ shows that nothing was sent. That means that the input was not redirected to the container.
In summary, it is possible to work with -i only for interactive mode, although you will see the output of the called commands after they finish execution.
-t option activates terminal emulation mode meaning, that the output of the executing command will be printed during its execution.
Just try to compare how Docker behavior will differ if you call:

docker run debian ping google.com -c 5

and

docker run -t debian ping google.com -c 5

You may prefer to use -t only, if all you want to do is to check default container command output in real-time, without interacting with it.

Docker quickstart series

Intro

In the previous part of Docker quickstart series, I’ve covered basic concepts behind Docker and its installation. I’ve also shown how to run a simple test container named “hello world” with the command:

docker run hello-world

In this part of Docker quickstart, I will start with running containers in interactive mode with volume mounting and port forwarding. Then I will show you how to attach to a running container, read a containers output and briefly explain container configuration info.

Docker run

parameterless

docker run <image>

In the first part of this quickstart, I’ve shown you how to test if your instance of Docker is running by starting a sample container. The CLI (command line interface) call I’ve used then was ‘run’.
In the most basic call, without any options, it just starts an image and executes its ENTRYPOINT or CMD. ENTRYPOINT and CMD are dockerfile directives and will be covered in another part of this quickstart. For now, all you have to know is that it runs default command specified for container.

with specific command

To start Docker with user specified command call:

docker run <image> <command>

Let’s say that all you want to do with your container is to test if it can connect with host. For that I will use the most basic tool which is ping. All I have to do to start ping from container is to call:

So we’ve specified that Docker should create container using Debian image, then inside the container it should run ping to dockerhost with 5 packets.

interactive mode

Another way to run a container is to start it in interactive mode. For example, if you start the Debian image, it will immediately stop. It’s because no action command was specified to run.
Now let’s assume that you want to interact with your container. To do so, all you have to do is to add -i and -t switches to Docker’s ‘run’ command. -i switch runs the container in interactive mode, meaning your input will be redirected to the container. -t switch is used to emulate the terminal.
Command pattern there will be:

docker run -it <image> <command>

For example: as bash is the default command for Debian image one can skip the <command>. So, to start it in interactive mode all you have to do is type:

As you can see, I am connected to Debian container CLI logged in as root.

At this point, I got interested in what would happen, if -i or -t was passed independently. Details on what happens under such conditions are here: Docker ‘run’ -i/-t independently.

removing container

Up to this moment every time we’ve started a container, after it’s been finished, it was left on the drive, ready to be reexecuted. Such behavior is not always expected, e.g. if we are not going to reuse existing containers, as with time the count of stored containers that are not running will stack up to a tremendous level and make it hard to manage containers. The easiest way to get rid of an exited container is to remove it right after its execution stops. Fortunately, we can specify that the instance of an image we create with ‘run’ command should be removed when it finishes as part of ‘run’ command itself. The switch that determines that is –rm.

docker run --rm debian

Now let’s see an example. First, I list all containers that are stored in exited status:

As you can see on the attached console listing, I have three stored containers in exited state, one for each time I’ve called Docker ‘run’. Then I start another Debian container, but this time with –rm switch:

As you can see, despite having started another container, it is not visible on containers list as it got removed right after the execution finished.

This is just one of two main methods for removing existing containers. That one that is more than enough as long as you don’t use daemon containers, or you don’t reuse already existing containers. I will describe the other method another time in an article about containers management.

detached/daemon mode

Up to this point, I’ve described how to run a container that does its job and then exits, but sometimes you need to run your container as a background service. This container mode called ‘daemon’ or ‘detached state’ may be required for file hosting services, database servers, monitoring services or other continuously running applications. To run such an application you follow the ‘docker run’ command with ‘-d’ switch.

What the -d switch does, is it starts the application and then detaches the console from Docker process. For example, if you want to start nginx server you probably want it to run even if you disconnect from your Docker hosting machine. To achieve that, you have to detach your console from the nginx instance. To do that you just simply call:

docker run -d nginx

In the result Docker will return the container id and detach the console.

In the excerpt below, first, I simply start an nginx container. Console is attached to the process. Then I break the program with “ctrl + c”. Ps returns no results for nginx processes, as these were killed by break. After that, I start the detached container. What Docker does there is what follows:

starts the nginx container

detaches the console, which means that container process is running in an independent context

prints out the container id.

Now, when I call ps, it returns that nginx processes are running. I may now exit from the console and the nginx processes will still be running in the background.

Not so long ago I had to do research on how to run a dotnet application on Docker. Thanks to that I’ve learned the basics of Docker. The text below is based on the notes that I’ve written down in the process.

Docker quickstart series

What is Docker

Docker is an application packaging platform and runtime. Each image (Docker’s name for application package) consists of layers containing application dependencies and the application itself. Layers may contain system, runtime, libraries, etc. What’s important is that each layer is readonly. Thanks to packaging one may start several separated instances of one image on one physical machine.

How it differs from a virtual machine

Taking the above into account one could say that Docker is in fact a virtual machine. Well, things are getting a bit complicated there.

Docker is not a virtual machine yet it virtualizes some resources. For example, each container has an isolated file system or network (although it may be changed). On the other hand, each process has its hook on the host machine. Just take a look at “ps” output:

As you can see, starting Docker container (instance of image) host has started several new processes. I am talking about PIDs 19495, 19510, 19522 and 19535. The container that I’ve started is a dotnet core web host. That’s the reason why there are running dotnet processes. As I don’t have dotnet installed on host, it proves that the container processes are delegated to Docker’s host.

When you consider the above, the difference between Docker and a virtual machine is getting clearer. It is that VMs are virtualizing whole hardware whilst Docker exposes “bare-metal” devices as they are seen by the host machine.

Another difference is that setting up a VM requires you to store whole stack used by your application. I mean that VMs have to store a lot more data than containers. VMs are using virtual storage devices that contain bootloader, operating system, drivers, libraries and last but not least your hosted application. If you want to scale an application horizontally you have to clone a complete virtual drive image. Docker architecture limits the amount of data that has to be stored for a container. By providing readonly layers it makes it possible to reuse the image, so several containers may run using single image. Atop of all readonly layers Docker inserts a read/write persistence layer. That persistence together with a container config (network, volumes, environment vars, etc.) is what differs the containers from each other. See the console output below to notice that running ubuntu under Docker consumed about 130MB for image, then starting new instance adds under 150KB to the disk usage. The reason is that both containers are using one image. The only stored information is the difference between the image and the container. It works a bit like snapshots in VMs but with Docker you can run several snapshots simultaneously.

Installing Docker Engine

There are two ways to install Docker.
The easy way using shell script provided by Docker inc.
The hard way if you don’t trust third party shell scripts. The hard way can be found at Docker page.

While learning Docker I’ve used the easy way so I will describe this one here. Using the easy method you don’t have to care about any dependencies or setting up the system (except for installing it of course). As I am no Linux geek, I’ve decided to use the easy way.

Basically all you have to do to install Docker is to call:

wget -qO- get.docker.com | sh

What it does is:

“wget -qO- get.docker.com” downloads the script from ‘get.docker.com’. The ‘-q’ flag stands for quiet, and means that ‘wget’ won’t write progress to standard output (stdout). The ‘-O’ flag redirects downloaded content to the specified file. By using ‘-O–‘ we basically say that ‘wget’ should write the output to stdout which I use in the next step.

By using the pipe sign I redirect the output of the ‘left command’ to the ‘right command’s standard input stream (sdtin).

‘sh’ processes data from stdin. Due to the fact that I’ve redirected the ‘wget’ stdout to the ‘sh’, the ‘sh’ command executed the downloaded script.

After the script processing ends you may call ‘docker -v’ to verify installation.

badamiak@dockerhost:~$ docker -v
Docker version 1.11.2, build b9f10c9

Test run

After installation you may want to try to run your first container. Docker provides an official image for Hello-World application. All you have to do is to call ‘docker run hello-world’. If everything is working right you will see the line ‘Hello from Docker!’ in the result.

console

badamiak@dockerhost:~$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
c04b14da8d14: Pull complete
Digest: sha256:0256e8a36e2070f7bf2d0b0763dbabdd67798512411de4cdcf9431a1feb60fd9
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker Hub account:
https://hub.docker.com
For more examples and ideas, visit:
https://docs.docker.com/engine/userguide/
badamiak@dockerhost:~$

badamiak@dockerhost:~$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
c04b14da8d14: Pull complete
Digest: sha256:0256e8a36e2070f7bf2d0b0763dbabdd67798512411de4cdcf9431a1feb60fd9
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker Hub account:
https://hub.docker.com
For more examples and ideas, visit:
https://docs.docker.com/engine/userguide/
badamiak@dockerhost:~$

Summary

After reading through this post you should be able to run your own instance of Docker and also a sample application contained in the hello-world image.
I hope that I’ve also explained the basic concepts behind Docker and its architecture.
In the next part of this tutorial I will cover running a parametrized container, setting up virtual networks and attaching host directories as volumes to a container.
If you have any questions or remarks please write a comment and I shall soon address your concerns.