- Learning Docker
- Pethuru Raj Jeeva S. Chelladhurai Vinod Singh
- 3504字
- 2021-07-16 14:07:03
Working with an interactive container
In the first chapter, we ran our first Hello World!
container to get a feel of how the containerization technology works. In this section, we are going to run a container in an interactive mode. The docker run
subcommand takes an image as an input and launches it as a container. You have to pass the -t
and -i
flags to the docker run subcommand in order to make the container interactive. The -i
flag is the key driver, which makes the container interactive by grabbing the standard input (STDIN
) of the container. The -t
flag allocates a pseudo-TTY or a pseudo terminal (terminal emulator) and then assigns that to the container.
In the following example, we are going to launch an interactive container by using the ubuntu:14.04
image and /bin/bash
as the command:
$ sudo docker run -i -t ubuntu:14.04 /bin/bash
Since the ubuntu
image has not been downloaded yet, if we use the docker pull
subcommand, then we will get the following message and the run
command will start pulling the ubuntu
image automatically with the following message:
Unable to find image 'ubuntu:14.04' locally Pulling repository ubuntu
As soon as the download is completed, the container will be launched along with the ubuntu:14.04
image. It will also launch a bash shell within the container, because we have specified /bin/bash
as the command to be executed. This will land us in a bash prompt, as shown here:
root@742718c21816:/#
The preceding bash prompt will confirm that our container has been launched successfully, and it is ready to take our input. If you are wondering about the Hex number 742718c21816
in the prompt, then it is nothing but the hostname of the container. In the Docker parlance, the hostname is the same as the container ID
.
Let's quickly run a few commands interactively, and then confirm that what we mentioned about the prompt is correct, as shown here:
root@742718c21816:/# hostname 742718c21816 root@742718c21816:/# id uid=0(root) gid=0(root) groups=0(root) root@742718c21816:/# echo $PS1 ${debian_chroot:+($debian_chroot)}\u@\h:\w\$ root@742718c21816:/#
From the preceding three commands, it is quite evident that the prompt was composed by using the user ID, the hostname, and the current working directory.
Now, let's use one of the niche features of Docker for detaching it from the interactive container and then look at the details that Docker manages for this container. Yes, we can detach it from our container by using the Ctrl + P and Ctrl + Q escape sequence. This escape sequence will detach the TTY from the container and land us in the Docker host prompt $
, however the container will continue to run. The docker ps
subcommand will list all the running containers and their important properties, as shown here:
$ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 742718c21816 ubuntu:14.04 "/bin/bash" About a minute ago Up About a minute jolly_lovelace
The docker ps
subcommand will list out the following details:
CONTAINER ID
: This shows the containerID
associated with the container. The containerID
is a 64 Hex digit long random number. By default, thedocker ps
subcommand will show only 12 Hex digits. You can display all the 64 digits by using the--no-trunc
flag (for example:sudo docker ps --no-trunc
).IMAGE
: This shows the image from which the Docker container has been crafted.COMMAND
: This shows you the command executed during the container launch.CREATED
: This tells you when the container was created.STATUS
: This tells you the current status of the container.PORTS
: This tells you if any port has been assigned to the container.NAMES
: The Docker engine auto-generates a random container name by concatenating an adjective and a noun. Either the containerID
or its name can be used to take further action on the container. The container name can be manually configured by using the--name
option in thedocker run
subcommand.
Having looked at the container status, let's attach it back to our container by using the docker attach
subcommand as shown in the following example. We can either use the container ID
or use its name. In this example, we have used the container name. If you don't see the prompt, then press the Enter key again:
$ sudo docker attach jolly_lovelace root@742718c21816:/#
Note
The Docker allows attaching with a container any number of times, which proves to be very handy for screen sharing.
The docker attach
subcommand takes us back to the container prompt. Let's experiment a little more with the interactive container that is up and running by using these commands:
root@742718c21816:/# pwd / root@742718c21816:/# ls bin dev home lib64 mnt proc run srv tmp var boot etc lib media opt root sbin sys usr root@742718c21816:/# cd usr root@742718c21816:/usr# ls bin games include lib local sbin share src root@742718c21816:/usr# exit exit $
As soon as the bash exit command is issued to the interactive container, it will terminate the bash shell process, which in turn will stop the container. As a result, we will land on the Docker Host's prompt $
.
Tracking changes inside containers
In the previous section, we demonstrated how to craft a container taking ubuntu
as a base image, and then running some basic commands, such as detaching and attaching the containers. In that process, we also exposed you to the docker ps
subcommand, which provides the basic container management functionality. In this section, we will demonstrate how we can effectively track the changes that we introduced in our container and compare it with the image from which we launched the container.
Let's launch a container in the interactive mode, as we had done in the previous section:
$ sudo docker run -i -t ubuntu:14.04 /bin/bash
Let's change the directory to /home
, as shown here:
root@d5ad60f174d3:/# cd /home
Now we can create three empty files by using the touch
command as shown in the following code snippet. The first ls -l
command will show that there are no files in the directory and the second ls -l
command will show that there are three empty files:
root@d5ad60f174d3:/home# ls -l total 0 root@d5ad60f174d3:/home# touch {abc,cde,fgh} root@d5ad60f174d3:/home# ls -l total 0 -rw-r--r-- 1 root root 0 Sep 29 10:54 abc -rw-r--r-- 1 root root 0 Sep 29 10:54 cde -rw-r--r-- 1 root root 0 Sep 29 10:54 fgh root@d5ad60f174d3:/home#
The Docker engine elegantly manages its filesystem and it allows us to inspect a container filesystem by using the docker diff
subcommand. In order to inspect the container filesystem, we can either detach it from the container or use another terminal of our Docker host and then issue the docker diff
subcommand. Since we know that any ubuntu
container has its hostname, which is a part of its prompt, and it is also the container's ID
, we can directly run the docker diff
subcommand by using the container ID
that is taken from the prompt, as shown here:
$ sudo docker diff d5ad60f174d3
In the given example, the docker diff
subcommand will generate four lines, shown here:
C /home A /home/abc A /home/cde A /home/fgh
The preceding output indicates that the /home
directory has been modified, which has been denoted by C,
and the /home/abc
, /home/cde
and the /home/fgh
files have been added, and these are denoted by A
. In addition, D
denotes deletion. Since we have not deleted any files, it is not in our sample output.
Controlling Docker containers
So far, we have discussed a few practical examples for clearly articulating the nitty-gritty of the Docker containers. In this section, let us introduce a few basic as well as a few advanced command structures for meticulously illustrating how the Docker containers can be managed.
The Docker engine enables you to start
, stop
, and restart
a container with a set of docker
subcommands. Let's begin with the docker stop
subcommand, which stops a running container. When a user issues this command, the Docker engine sends SIGTERM (-15) to the main process, which is running inside the container. The SIGTERM signal requests the process to terminate itself gracefully. Most of the processes would handle this signal and facilitate a graceful exit. However, if this process fails to do so, then the Docker engine will wait for a grace period. Even after the grace period, if the process has not been terminated, then the Docker engine will forcefully terminate the process. The forceful termination is achieved by sending SIGKILL (-9). The SIGKILL signal cannot be caught or ignored, and so it will result in an abrupt termination of the process without a proper clean-up.
Now, let's launch our container and experiment with the docker stop
subcommand, as shown here:
$ sudo docker run -i -t ubuntu:14.04 /bin/bash root@da1c0f7daa2a:/#
Having launched the container, let's run the docker stop
subcommand on this container by using the container ID
that was taken from the prompt. Of course, we have to use a second screen or terminal to run this command, and the command will always echo back to the container ID
, as shown here:
$ sudo docker stop da1c0f7daa2a da1c0f7daa2a
Now, if we switch to the screen or terminal, where we were running the container, we will notice that the container is being terminated. If you observe a little more closely, you will also notice the text exit
next to the container prompt. This has happened due to the SIGTERM handling mechanism of the bash shell, as shown here:
root@da1c0f7daa2a:/# exit $
If we take it one step further and run the docker ps
subcommand, then we will not find this container anywhere in the list. The fact is that the docker ps
subcommand, by default, always lists the container that is in the running state. Since our container is in the stopped state, it has been comfortably left out of the list. Now, you might ask, how do we see the container that is in the stopped state? Well, the docker ps
subcommand takes an additional argument -a
, which will list all the containers in that Docker host irrespective of its status. This can be done by running the following command:
$ sudo docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES da1c0f7daa2a ubuntu:14.04 "/bin/bash" 20 minutes ago Exited (0) 10 minutes ago desperate_engelbart $
Next, let's look at the docker start
subcommand, which is used for starting one or more stopped containers. A container could be moved to the stopped state either by the docker stop
subcommand or by terminating the main process in the container either normally or abnormally. On a running container, this subcommand has no effect.
Let's start
the previously stopped container by using the docker start
subcommand, as follows:
$ sudo docker start da1c0f7daa2a da1c0f7daa2a $
By default, the docker start
subcommand will not attach to the container. You can attach it to the container either by using the -a
option in the docker start
subcommand or by explicitly using the docker attach
subcommand, as shown here:
$ sudo docker attach da1c0f7daa2a root@da1c0f7daa2a:/#
Now let's run the docker ps
and verify the container's running status, as shown here:
$ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES da1c0f7daa2a ubuntu:14.04 "/bin/bash" 25 minutes ago Up 3 minutes desperate_engelbart $
The restart
command is a combination of the stop
and the start
functionality. In other words, the restart
command will stop
a running container by following the precise steps followed by the docker stop
subcommand and then it will initiate the start
process. This functionality will be executed by default through the docker restart
subcommand.
The next important set of container-controlling subcommands are docker pause
and docker unpause
. The docker pause
subcommands will essentially freeze the execution of all the processes within that container. Conversely, the docker unpause
subcommand will unfreeze the execution of all the processes within that container and resume the execution from the point where it was frozen.
Having seen the technical explanation of pause and unpause
, let's see a detailed example for illustrating how this feature works. We have used two screen or terminal scenarios. On one terminal, we have launched our container and used an infinite while loop for displaying the date and time, sleeping for 5 seconds, and then continuing the loop. We will run the following commands:
$ sudo docker run -i -t ubuntu:14.04 /bin/bash root@c439077aa80a:/# while true; do date; sleep 5; done Thu Oct 2 03:11:19 UTC 2014 Thu Oct 2 03:11:24 UTC 2014 Thu Oct 2 03:11:29 UTC 2014 Thu Oct 2 03:11:34 UTC 2014 Thu Oct 2 03:11:59 UTC 2014 Thu Oct 2 03:12:04 UTC 2014 Thu Oct 2 03:12:09 UTC 2014 Thu Oct 2 03:12:14 UTC 2014 Thu Oct 2 03:12:19 UTC 2014 Thu Oct 2 03:12:24 UTC 2014 Thu Oct 2 03:12:29 UTC 2014 Thu Oct 2 03:12:34 UTC 2014 $
Our little script has very faithfully printed the date and time every 5 seconds with an exception at the following position:
Thu Oct 2 03:11:34 UTC 2014 Thu Oct 2 03:11:59 UTC 2014
Here, we encountered a delay of 25 seconds, because this is when we initiated the docker pause
subcommand on our container on the second terminal screen, as shown here:
$ sudo docker pause c439077aa80a c439077aa80a
When we paused our container, we looked at the process status by using the docker ps
subcommand on our container, which was on the same screen, and it clearly indicated that the container had been paused, as shown in this command result:
$ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES c439077aa80a ubuntu:14.04 "/bin/bash" 47 seconds ago Up 46 seconds (Paused) ecstatic_torvalds
We continued on to issuing the docker unpause
subcommand, which unfroze our container, continued its execution, and then started printing the date and time, as we saw in the preceding command, shown here:
$ sudo docker unpause c439077aa80a c439077aa80a
We explained the pause
and the unpause
commands at the beginning of this section. Lastly, the container and the script running within it had been stopped by using the docker stop
subcommand, as shown here:
$ sudo docker stop c439077aa80a c439077aa80a
Housekeeping containers
In many of the previous examples, when we issued docker ps -a
we saw the many stopped containers. These containers could continue to stay in the stopped status for ages, if we chose not to intervene. At the outset, it may look like a glitch, but in reality, we can perform operations, such as committing an image from a container, restarting the stopped container, and so on. However, not all the stopped containers will be reused, and each of these unused containers will take up the disk space in the filesystem of the Docker host. The Docker engine provides a couple of ways to alleviate this issue. Let's start exploring them.
During a container startup, we can instruct the Docker engine to clean up the container as soon as it reaches the stopped state. For this purpose, the docker run
subcommand supports an --rm
option (for example: sudo docker run -i -t --rm ubuntu:14.04 /bin/bash
).
The other alternative is to list all the containers by using the -a
option of the docker ps
subcommand and then manually remove them by using the docker rm
subcommand, as shown here:
$ sudo docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 7473f2568add ubuntu:14.04 "/bin/bash" 5 seconds ago Exited(0) 3 seconds ago jolly_wilson $ sudo docker rm 7473f2568add 7473f2568add $
Two docker
subcommands, that is, docker rm
and docker ps
, could be combined to automatically delete all the containers that are not currently running, as shown in the following command:
$ sudo docker rm 'sudo docker ps -aq --no-trunc'
In the preceding command, the command inside the back quotes will produce a list of the full container IDs of every container, running or otherwise, which will become the argument for the docker rm
subcommand. Unless forced with the -f
option to do otherwise, the docker rm
subcommand will only remove the container that is not in the running state. It will generate the following error for the running container and then continue to the next container on the list:
Error response from daemon: You cannot remove a running container. Stop the container before attempting removal or use -f
Building images from containers
So far, we have crafted a handful of containers by using the standard base images busybox
and ubuntu
. In this section, let us see how we can add more software to our base image on a running container and then convert that container into an image for future use.
Let's take ubuntu:14.04
as our base image, install the wget
application, and then convert the running container to an image by performing the following steps:
- Launch an
ubuntu:14.04
container by using thedocker run
subcommand, shown here:$ sudo docker run -i -t ubuntu:14.04 /bin/bash
- Having launched the container, let's quickly verify if
wget
is available for our image or not. We have used thewhich
command withwget
as an argument for this purpose and, in our case, it returns empty, which essentially means that it could not find anywget
installation in this container. This command is run as follows:root@472c96295678:/# which wget root@472c96295678:/#
- Now let's move on to the next step which involves the
wget
installation. Since it is a brand newubuntu
container, before installingwget
, we must synchronize with theubuntu
package repository, as shown here:root@472c96295678:/# apt-get update
- Once the
ubuntu
package repository synchronization is over, we can proceed toward installingwget
, as shown here:root@472c96295678:/# apt-get install -y wget
- Having completed the
wget
installation, let's confirm our installation ofwget
by invoking thewhich
command withwget
as an argument, as shown here:root@472c96295678:/#which wget /usr/bin/wget root@472c96295678:/#
- Installation of any software would alter the base image composition, which we can also trace by using the
docker diff
subcommand introduced in Tracking changes inside containers section of this chapter. From a second terminal or screen, we can issue thedocker diff
subcommand, as follows:$ sudo docker diff 472c96295678
The preceding command would show a few hundred lines of modification to the
ubuntu
image. This modification includes the update on package repository,wget
binary, and the support files forwget
. - Finally, let's move to the most important step of committing the image. The Docker
commit
subcommand can be performed on a running or a stopped container. When commit is performed on a running container, the Docker engine will pause the container during thecommit
operation in order to avoid any data inconsistency. We strongly recommend performing thecommit
operation on a stopped container. We can commit a container to an image by thedocker commit
subcommand, as shown here:$ sudo docker commit 472c96295678 \ learningdocker/ubuntu_wget a530f0a0238654fa741813fac39bba2cc14457aee079a7ae1fe1c64dc7e1ac25
We have committed our image by using the name learningdocker/ubuntu_wget
.
Step by step, we saw how to create an image from a container. Now, let's quickly list the images of our Docker host and see if this newly created image is a part of the image list by using the following command:
$ sudo docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE learningdocker/ubuntu_wget latest a530f0a02386 48 seconds ago 221.3 MB busybox buildroot-2014.02 e72ac664f4f0 2 days ago 2.433 MB ubuntu 14.04 6b4e8a7373fe 2 days ago 194.8 MB
From the preceding docker images
subcommand output, it is quite evident that our image creation from the container has been quite successful.
Now that you have learned how to create an image from the containers by using a few easy steps, we would encourage you to predominantly use this method for testing purposes. The most elegant and the most recommended way of creating an image is to use the Dockerfile
method, which will be introduced in the next chapter.
Launching a container as a daemon
We have already experimented with an interactive container, tracked the changes that were made to the containers, created images from the containers and then gained insights in the containerization paradigm. Now, let's move on to understanding the real workhorse of the Docker technology. Yes that's right. In this section, we will walk you through the steps that are required for launching a container in the detached mode; in other words, we will learn about the steps that are required for launching a container as a daemon. We will also view the text that is generated in the container.
The docker run
subcommand supports an option -d
, which will launch a container in a detached mode, that is, it will launch a container as a daemon. For the purpose of illustration, let's resort to our date and time script, which we used in the pause and unpause
container example, as shown here:
$ sudo docker run -d ubuntu \ /bin/bash -c "while true; do date; sleep 5; done" 0137d98ee363b44f22a48246ac5d460c65b67e4d7955aab6cbb0379ac421269b
The docker logs
subcommand is used for viewing the output generated by our daemon container, as shown here:
$ sudo docker logs \ 0137d98ee363b44f22a48246ac5d460c65b67e4d7955aab6cbb0379ac421269b Sat Oct 4 17:41:04 UTC 2014 Sat Oct 4 17:41:09 UTC 2014 Sat Oct 4 17:41:14 UTC 2014 Sat Oct 4 17:41:19 UTC 2014
- Kali Linux Web Penetration Testing Cookbook
- AngularJS Testing Cookbook
- 算法精粹:經典計算機科學問題的Java實現
- C#程序設計(慕課版)
- Serverless computing in Azure with .NET
- C語言從入門到精通
- INSTANT Yii 1.1 Application Development Starter
- Python開發基礎
- Distributed Computing in Java 9
- Application Development with Swift
- Getting Started with React VR
- Python程序設計現代方法
- Spring Microservices
- Neo4j權威指南 (圖數據庫技術叢書)
- 程序員的算法趣題2