官术网_书友最值得收藏!

  • 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 container ID associated with the container. The container ID is a 64 Hex digit long random number. By default, the docker 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 container ID 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 the docker 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:

  1. Launch an ubuntu:14.04 container by using the docker run subcommand, shown here:
    $ sudo docker run -i -t ubuntu:14.04 /bin/bash
    
  2. Having launched the container, let's quickly verify if wget is available for our image or not. We have used the which command with wget as an argument for this purpose and, in our case, it returns empty, which essentially means that it could not find any wget installation in this container. This command is run as follows:
    root@472c96295678:/# which wget
    root@472c96295678:/#
    
  3. Now let's move on to the next step which involves the wget installation. Since it is a brand new ubuntu container, before installing wget, we must synchronize with the ubuntu package repository, as shown here:
    root@472c96295678:/# apt-get update
    
  4. Once the ubuntu package repository synchronization is over, we can proceed toward installing wget, as shown here:
    root@472c96295678:/# apt-get install -y wget
    
  5. Having completed the wget installation, let's confirm our installation of wget by invoking the which command with wget as an argument, as shown here:
    root@472c96295678:/#which wget
    /usr/bin/wget
    root@472c96295678:/#
    
  6. 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 the docker 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 for wget.

  7. 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 the commit operation in order to avoid any data inconsistency. We strongly recommend performing the commit operation on a stopped container. We can commit a container to an image by the docker 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
主站蜘蛛池模板: 南雄市| 恩施市| 墨江| 绥江县| 沈阳市| 宜良县| 舒城县| 琼结县| 东乡| 怀来县| 日照市| 贺州市| 岐山县| 屏东县| 瓮安县| 漾濞| 洛浦县| 格尔木市| 邯郸市| 鸡泽县| 台中县| 原平市| 合作市| 太仆寺旗| 阳新县| 台前县| 漳平市| 曲沃县| 洛浦县| 巨鹿县| 玛纳斯县| 平凉市| 石首市| 微山县| 泽普县| 古田县| 襄汾县| 广汉市| 临海市| 讷河市| 梁平县|