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.
Let’s start with -i only:
console
badamiak@dockerhost:~$ docker run -i debian
ping 172.17.0.1 -c 5
PING 172.17.0.1 (172.17.0.1): 56 data bytes
64 bytes from 172.17.0.1: icmp_seq=0 ttl=64 time=0.097 ms
64 bytes from 172.17.0.1: icmp_seq=1 ttl=64 time=0.077 ms
64 bytes from 172.17.0.1: icmp_seq=2 ttl=64 time=0.082 ms
64 bytes from 172.17.0.1: icmp_seq=3 ttl=64 time=0.095 ms
64 bytes from 172.17.0.1: icmp_seq=4 ttl=64 time=0.076 ms
--- 172.17.0.1 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.076/0.085/0.097/0.000 ms
tcpdump
badamiak@dockerhost:~$ sudo tcpdump -i docker0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes
23:40:03.905470 IP6 fe80::42:acff:fe11:2 > ip6-allrouters: ICMP6, router solicitation, length 16
23:40:15.028432 ARP, Request who-has 172.17.0.1 tell 172.17.0.2, length 28
23:40:15.028448 ARP, Reply 172.17.0.1 is-at 02:42:16:34:b1:f4 (oui Unknown), length 28
23:40:15.028453 IP 172.17.0.2 > 172.17.0.1: ICMP echo request, id 5, seq 0, length 64
23:40:15.028475 IP 172.17.0.1 > 172.17.0.2: ICMP echo reply, id 5, seq 0, length 64
23:40:16.030929 IP 172.17.0.2 > 172.17.0.1: ICMP echo request, id 5, seq 1, length 64
23:40:16.030956 IP 172.17.0.1 > 172.17.0.2: ICMP echo reply, id 5, seq 1, length 64
23:40:17.032375 IP 172.17.0.2 > 172.17.0.1: ICMP echo request, id 5, seq 2, length 64
23:40:17.032405 IP 172.17.0.1 > 172.17.0.2: ICMP echo reply, id 5, seq 2, length 64
23:40:18.034833 IP 172.17.0.2 > 172.17.0.1: ICMP echo request, id 5, seq 3, length 64
23:40:18.034869 IP 172.17.0.1 > 172.17.0.2: ICMP echo reply, id 5, seq 3, length 64
23:40:19.036412 IP 172.17.0.2 > 172.17.0.1: ICMP echo request, id 5, seq 4, length 64
23:40:19.036441 IP 172.17.0.1 > 172.17.0.2: ICMP echo reply, id 5, seq 4, length 64
23:40:20.041641 ARP, Request who-has 172.17.0.2 tell 172.17.0.1, length 28
23:40:20.041679 ARP, Reply 172.17.0.2 is-at 02:42:ac:11:00:02 (oui Unknown), length 28
As we can see, input is redirected to container and container output is redirected back to host.
Now for -t only:
badamiak@dockerhost:~$ sudo tcpdump -i docker0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes
23:46:15.081837 IP6 fe80::42:acff:fe11:2 > ip6-allrouters: ICMP6, router solicitation, length 16
23:46:19.089818 IP6 fe80::42:acff:fe11:2 > ip6-allrouters: ICMP6, router solicitation, length 16
^C
2 packets captured
2 packets received by filter
0 packets dropped by kernel
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.
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:
docker run debian ping 172.17.0.1 -c 5
console
badamiak@dockerhost:~$ docker run debian ping 172.17.0.1 -c 5
PING 172.17.0.1 (172.17.0.1): 56 data bytes
64 bytes from 172.17.0.1: icmp_seq=0 ttl=64 time=0.096 ms
64 bytes from 172.17.0.1: icmp_seq=1 ttl=64 time=0.069 ms
64 bytes from 172.17.0.1: icmp_seq=2 ttl=64 time=0.077 ms
64 bytes from 172.17.0.1: icmp_seq=3 ttl=64 time=0.067 ms
64 bytes from 172.17.0.1: icmp_seq=4 ttl=64 time=0.062 ms
--- 172.17.0.1 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.062/0.074/0.096/0.000 ms
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:
docker run -it debian
console
badamiak@dockerhost:~$ docker run -it debian
root@2f41c9ed26cb:/# exit
exit
badamiak@dockerhost:~$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2f41c9ed26cb debian "/bin/bash" 7 seconds ago Exited (0) 4 seconds ago admiring_hoover
badamiak@dockerhost:~$
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:
console
badamiak@dockerhost:~$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
badamiak@dockerhost:~$
So I start with no stored containers. Then I start the Docker container three times without –rm switch:
console
badamiak@dockerhost:~$ docker run debian
badamiak@dockerhost:~$ docker run debian
badamiak@dockerhost:~$ docker run debian
badamiak@dockerhost:~$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6252be2356ed debian "/bin/bash" 4 seconds ago Exited (0) 3 seconds ago modest_allen
1e10ce08a831 debian "/bin/bash" 17 seconds ago Exited (0) 15 seconds ago thirsty_lamarr
bca7d5ae1d47 debian "/bin/bash" 22 seconds ago Exited (0) 20 seconds ago berserk_engelbart
badamiak@dockerhost:~$
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:
console
badamiak@dockerhost:~$ docker run --rm debian
badamiak@dockerhost:~$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6252be2356ed debian "/bin/bash" 15 seconds ago Exited (0) 14 seconds ago modest_allen
1e10ce08a831 debian "/bin/bash" 28 seconds ago Exited (0) 27 seconds ago thirsty_lamarr
bca7d5ae1d47 debian "/bin/bash" 33 seconds ago Exited (0) 32 seconds ago berserk_engelbart
badamiak@dockerhost:~$
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 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.
console
badamiak@dockerhost:~$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
badamiak@dockerhost:~$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
badamiak@dockerhost:~$ df /
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sda1 7736784 2429116 4891620 34% /
badamiak@dockerhost:~$ docker pull ubuntu
Using default tag: latest
latest: Pulling from library/ubuntu
f069f1d21059: Pull complete
ecbeec5633cf: Pull complete
ea6f18256d63: Pull complete
54bde7b02897: Pull complete
Digest: sha256:bbfd93a02a8487edb60f20316ebc966ddc7aa123c2e609185450b96971020097
Status: Downloaded newer image for ubuntu:latest
badamiak@dockerhost:~$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest 0f192147631d 8 days ago 132.8 MB
badamiak@dockerhost:~$ df /
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sda1 7736784 2595552 4725184 36% /
badamiak@dockerhost:~$ docker run --name instance1 ubuntu
badamiak@dockerhost:~$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ec6458250c9b ubuntu "/bin/bash" 3 seconds ago Exited (0) Less than a second ago instance1
badamiak@dockerhost:~$ df /
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sda1 7736784 2595672 4725064 36% /
badamiak@dockerhost:~$ docker run --name instance2 ubuntu
badamiak@dockerhost:~$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2a1a5090e8e5 ubuntu "/bin/bash" 1 seconds ago Exited (0) Less than a second ago instance2
ec6458250c9b ubuntu "/bin/bash" 4 seconds ago Exited (0) 1 seconds ago instance1
badamiak@dockerhost:~$ df /
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sda1 7736784 2595812 4724924 36% /
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:~$
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.
We use cookies to ensure that we give you the best experience on our website. If you continue to use this site we will assume that you are happy with it.Ok