Grape City provides developers with professional development tools, solutions and services to empower developers.

In the last section, we introduced how the original PaaS platforms like Cloud Foundry solved the container problem. This article will show you how Docker solves the consistency and reusability problems encountered by Cloud Foundry, and compare and analyze the differences between Docker and traditional virtual machines.

Docker’s improvements over Cloud Foundry

Use “Mount Namespace” to solve the consistency problem

In the first section of this series, we mentioned that Docker is rapidly replacing Cloud Foundry with Docker Image. What exactly is Docker Image and how do you solve consistency problems by using different file systems for different containers? For a moment, let’s look at the isolation and Namespace mechanism mentioned in the previous section.

Mount Namespace. The “Mount” in the name reminds us that this mechanism is related to file Mount content. The Mount Namespace is used to isolate the Mount directory of a process, so let’s take a “simple” example to see how it works.

(C language to develop a container without file isolation)

This is a simple C code that contains just two logic: 1. A child process is created in the main function and a CLONE_NEWNS parameter is passed to implement the Mount Namespace; 2. 2. Call /bin/bash to run the shell inside a child process.

Let’s compile and execute the program:

gcc -o ns ns.c

./ns

We are now in the shell of the child process. Here we can run ls/TMP to see the structure of the directory and compare it to the host:

(/ TMP directory inside and outside the container)

And what we find is that the data presented on both sides is exactly the same. Following the conclusion of CPU Namespace in the previous section, you should see two different file directories. Why is that?

The contents of folders inside and outside the container are the same because we changed the Mount Namespace. The Mount Namespace modifies the process’s perception of the “Mount point” of the file system. This means that any directory generated after a Mount operation is a new system, and if it is not mounted, it is identical to that of the host.

How do you solve this problem and implement file isolation? We just need to tell the process that a Mount operation is needed when we create it, in addition to declaring a Mount Namespace. Simply modify the code for the new process and run View:

Code and execution effect to implement file isolation

At this point, file isolation is successful and the child’s/TMP has been mounted into TMPFS (a memory drive). This creates a completely new TMP environment, so the child’s newly created directory host is no longer visible.

The simple code above comes from the implementation of the Docker image. Docker image is essentially a encapsulation of rootfs in file operation. Docker encapsulates rootfs of an operating system required by an application through Mount Namespace, which changes the dependency between application and operating system, that is, the original application runs within the operating system. Docker encapsulates the “operating system” into the dependent library of the application, so as to solve the problem of consistency of the application running environment. Wherever the application runs, the system has become a “dependency library,” which ensures consistency.

Layers are used to solve reusability problems

After implementing file system isolation and solving the consistency problem, we also need to face the problem of reusability. In practice, it is unlikely that we will mount a new rootfs every time we make an image, which is time consuming and laborious, and the “disc” without any programs will take up a lot of disk space to mount these contents.

Therefore, Docker mirroring uses another technology, UnionFS, and a new concept, Layer, to optimize the disk footprint of each image and improve its reusability.

Let’s take a quick look at what UnionFS does. UnionFS is a federated mount feature that lets you mount files from multiple paths into the same directory. For example, we now have the following directory structure:

Use the tree command to view the two folders containing A and B.

There are two files A and X in the directory A, and two files B and X in the directory B. With the function of unionFS, we can mount these two directories into the directory C, and the effect is as follows:

mount -t aufs -o dirs=./a:./b none ./C

Use the tree command to see the effect of a federated mount.

In the end, there is only one copy of X in the C directory, and if we modify A, B and X in the C directory, the files in the previous directories A and B will also be modified. Docker uses this technique to mount the files in its image jointly. For example, the /sys,/ etc, and/TMP directories can be mounted separately into rootfs to create what looks like a complete rootfs in the child process, but does not take up any extra disk space.

On top of this, Docker also innovates a layer concept of its own. First, it mounts the files in the rootfs needed by the system kernel into a “read-only layer,” and the user’s applications, system configuration files, and so on, which can be modified into the “read-write layer.” We can also mount initialization parameters into a special “init layer” when the container starts. At the end of container startup, these three layers are again jointly mounted, resulting in rootfs in the container.

(Docker’s read-only layer, read-write layer, and init layer)

From the above description, we can see that the read-only layer is best suited for a fixed version of the file, with almost no change in the code to achieve maximum reuse. For example, the Movable Type Public Cloud is developed based on.NET Core, and the basic environment we use it will be designed in the read-only layer. Every time we get the latest image, because each copy of the read-only layer is exactly the same, so there is no need to download it.

Docker’s “layer” explains why Docker images are only slow at the first download, while subsequent images are fast, and while each image looks like a few hundred megabytes, it doesn’t end up taking up that much of the hard disk on the machine. Smaller disk space and faster load times have significantly improved Docker’s reusability.

Docker container creation process

This is how Docker containers work. In combination with the previous article, we can summarize the process of creating a Docker container as follows:

  • Enable Linux Namespace configuration;
  • Set the specified Cgroups parameter;
  • The root directory of the process
  • Co-mount the layers of files

Exception: Differences between Docker and traditional virtual machines

In fact, Docker also does a lot of functions, such as permission configuration, DeviceMapper and so on. What is said here is just a conceptual explanation of the universal nature, and the various implementations at the bottom are also very complex concepts. Specifically, what’s the difference between a container and a traditional virtual machine?

In fact, container technology and virtual machine are two means to realize virtualization technology, but the virtual machine is through the Hypervisor control hardware, simulated a Guestos to do virtualization, its internal is a virtual operating system is almost real, internal and external is completely isolated. Container technology is a one-time isolation and allocation of system resources through software such as Docker Engine by means of Linux operating system. The comparison between them is roughly as follows:

(Docker vs virtual machine)

Virtual machines are physically isolated and are more secure than Docker containers, but there is a consequence: without optimization, a KVM running CentOS can start up with 100~200MB of memory on its own. In addition, the user application also runs in the virtual machine, and the application system calls the host’s operating system inevitably need to be intercepted and processed by the virtualization software, which itself will bring performance loss, especially the loss of computing resources, network and disk I/O is very large.

Containerized applications, on the other hand, are still normal processes on the host, which means that the losses due to virtualization do not exist; Containers that use Namespace as a means of isolation, on the other hand, do not require a separate Guest OS, so the additional resource footprint of the container is almost negligible.

As a result, this “agile” and “efficient” container has become a standout for PaaS platforms that require more fine-grained resource management. The container that seems to solve everything. Are there no faults?

Containers are particularly bad. First of all, since containers are emulated isolation, resources that cannot be emulated in Namespace: for example, the operating system kernel cannot be isolated completely. The programs inside the container share the operating system kernel with the host computer. That is to say, a Linux host with a low version is likely to be unable to run the high-version container. Another classic chestnut is time. If the system time is changed in the container by some means, the host time will change as well.

Another drawback is security. General enterprise, it is not the container directly exposed to the external users use directly, because the container can directly operate the kernel code, if the hacker can change the kernel program by certain means, then you can black out the entire host machine, which is why our own projects from start to write their own Docker directly to finally abandoned. At present, there are two general methods to solve the security problem. One is to restrict the operation permissions of the process in Docker and control its value to operate the system devices we want it to operate. However, this requires a lot of customized code, because we may not know what it needs to operate. Another way is to add a layer of virtual machine implementation sandbox around the container, which is the main implementation of many header factories today.

summary

Docker beats its predecessor Cloud Foundry by virtue of consistency and reusability. This article introduces one specific change Docker makes to containers, as well as the obvious drawbacks of containers. In the next article, we will introduce how Docker became lonely and who is the rising star in the Docker era. Stay tuned.