Usually we're used to using Docker run to execute a Docker container, so what exactly does Docker do after we execute Docker run? This article provides a detailed explanation of the principles of Docker run by tracing the execution process of Docker run (Docker 1.9), using and configuring the Volume,network and Libcontainer.
First, the user docker run
creates a container that is running through the Docker client input. The main task of the Docker client is to send such two requests after parsing a set of parameters provided by the user:
"POST""/containers/create?"+containerValues"POST""/containers/"+createResponse.ID
So the client's work is completed, it is clear that the client does very little, mainly responsible for the Docker daemon to send the request. However, through the two requests sent by the client, we can naturally docker run
divide the entire execution process into create and start two stages. The docker run
timing points of each key event are labeled separately. I will describe the implementation process separately from this two phases and combine them docker run
.
1 Create stage
The main work of this phase of Docker daemon is to analyze and organize the post form submitted by client, and obtain the portable configuration parameter structure Config and the non-portable configuration structure body hostconfig. Daemon then calls the Daemon.newcontainer function to create a basic container object, and fills the information saved in the Config and hostconfig into the container object. Of course, the container object at this time is not a specific physical container, which holds all user-specified parameters and some of the default configuration information generated by Docker. Finally, Docker encodes the container object in JSON and then saves it to its corresponding state file.
Once the above process is complete, the basic configuration information of a container is fully available and the user can use it docker inspect
to view the various configuration information for the container.
2 Start Stage
After the Create phase is completed, the client then sends a start request to start a real physical container. When the Docker daemon receives the start request, it uses various configuration parameters from the container object configured in the Create phase to complete the registration of the volume mount, the creation and creation of the container network, and the start of the physical container. The following is an introduction to the container's network environment creation process.
2.1 Volume mount point registration
Docker Daemon in the process of configuring Hostconfig to the container configuration information, The Daemon.registermountpoints function is called to register the volume related information in the Post form provided by the client and is stored in the container's configuration information in mountpoint form, and is mounted when the physical container is actually started.
Volume mount points can be divided into two categories, one for using mount points in other containers, and another for user-specified bindings. Let's look at the registration process for the two volume mount points.
1. Use mount points in other containers: When registering such mount points, the container's ID is used first to find the corresponding struct in Docker daemon. It then iterates through all of the mount points and registers all of the mount point information in the current container structure.
2. User-specified bindings mount: User-specified binding mounts can be in the format of source path:destination path or name:destination path. If the user enters a parameter that is the format of the source path:destination path, daemon resolves the source path and Destination path in it, and uses them to register the corresponding mount point. If the user input parameter is in the format of Name:destination path, then daemon will find out if the user-supplied name already corresponds docker volume
to a mount point information that has already been created, and if so, The information of this mount point and the user-supplied destination path are used to register the mount point of this container. If the name does not have a corresponding mount point in the daemon, daemon creates a directory under its default folder as the source path in the mount point, and then uses the user-supplied destination path and the self-created source Path is registered with the mount point of this container.
After the mount point registration has been completed, daemon will update all mount point information to the container's configuration information for later use.
2.2 Creation of the network
Docker Daemon uses the network-related parameters in the Post form provided by the client to complete the creation and configuration of the container network stack by invoking the daemon.initializenetworking function. The Daemon.initializenetworking function accomplishes the creation and configuration of the container's network stack through a series of calls to the Docker's network-dependent library (i.e., libnetwork).
To understand the execution flow of the network portion of the Docker container, first clear the three core concepts in libnetwork.
Sandbox (sandbox): A sandbox contains information about a container's network stack. The sandbox can manage the interface, routing, and DNS settings of the container. The implementation of the sandbox can be Linux Network Namespace, FreeBSD jail, or a similar mechanism. A sandbox can have multiple endpoints (Endpoint) and multiple networks (network).
Endpoint (Endpoint): An endpoint can join a sandbox and a network. The endpoint can be implemented as Veth pair, Open vswitch internal port, or similar device. An endpoint can belong to only one network and belongs to only one sandbox.
Network: A network is a set of endpoints that can be interconnected directly. The implementation of the network can be Linux Bridge,vlan and so on. A network can contain multiple endpoints.
With the above three core concepts in perspective, we look at the container network stack creation process from the Docker source and through the default network mode (bridge mode) in Docker.
After the Docker daemon is started, a default network is created, which essentially works by creating a default bridge named Docker0.
After you determine the default bridge, daemon calls container. Buildcreateendpointoptions to create the configuration information for the endpoint in this container. Then call Network.createendpoint to create the corresponding endpoint using the information configured above. In bridge mode, the device created by Libnetwork is Veth pair. Libnetwork calls Netlink.linkadd (Veth) to create a veth pair, adding one of the veth devices to the Docker0 bridge and the other for the sandbox.
Next daemon calls Daemon.buildsandboxoptions to create a sandbox for this container, and then calls Network.newsandbox to create a new sandbox that belongs to this container. Libnetwork after receiving the sandbox request, a new netns is created for the container using the system call, and the Netns path is written back to the corresponding container's configuration information for subsequent use.
Finally, Daemon will call EP. Join (SB) adds the endpoint to the container's sandbox. First, the endpoint is added to the container's sandbox, then the endpoint IP information and the gateway and other information are configured, and all the information is updated to the corresponding container configuration information.
2.3 Creating and starting a container
After completing the various preparations for creating the container, Docker daemon completes the container creation and start-up through a series of calls to Libcontainer. Libcontainer is the runtime library of Docker, which can create and run a container through the configuration parameters provided by the caller, let's look at how Docker uses the parameters in the structure previously configured to create and run a container through Libcontainer.
2.3.1 Creating logical containers container and logical processes
The so-called logical container container and logic processes are not the containers and processes that are actually running, but the structures defined in the Libcontainer. The logical container container contains various configuration information such as Namespace,cgroups,device and Mountpoint. The logical process includes the instructions to be run in the container with its parameters and environment variables.
Docker Daemon will call Execdriver. Run to complete and Libcontainer a series of interactive work. All the parameters associated with the new container will be loaded into the struct config that can be used by the Libcontainer first. Then use config as the parameter to invoke Libcontainer. New () generates the factory factory used to produce container. Call Factory again. Create (config), a logical container container that contains the config is generated. The next call to Newprocess (config) is to populate the process structure with information about the commands to be run in the container in config, which is the logical process. Use container. Start (process) to launch the logical container.
2.3.2 Start the logical container container
Docker Daemon calls Linuxcontainer.start to start the logical container. The main task of this function is to call newparentprocess () to generate a parentprocess instance (struct) and a pipeline for Runc to communicate with each other in the container init process.
In parentprocess instances, there is a very important field that is the cmd in addition to the pipelines and various basic configurations that record future communication with the process in the container.
The cmd field is a struct defined in the Os/exec package. The Os/exec package is primarily used to create a new process and execute the specified command in this process. Developers can import the Os/exec package in the project, then populate the CMD structure, the path and program name of the program to be run, the parameters required by the program, environment variables, various operating system-specific properties and extended file descriptors.
In Docker, the program populates the Application path field of CMD with/proc/self/exe (that is, the application itself, Docker). The parameter field, args, is populated with Init, which indicates that the container is initialized. The Sysprocattr field is populated with properties such as the namespace that are required for various Docker (including the Netns path described earlier).
Then call Parentprocess.cmd.Start () to start the Init process in the physical container. The process number of the INIT process in the physical container is then added to the Cgroup control group to implement resource control for the process within the container. The configuration parameters are then routed through the pipeline to the INIT process. Finally, the pipeline waits for the INIT process to complete all initialization work based on the above configuration, or exits with an error.
2.3.3 Configuration and creation of physical containers
The init process in the container first calls the Startinitialization () function, which receives various configuration parameters from the parent process through the pipeline. The container is then configured as follows:
1. Add the init process to its specified namespace, where the init process is added to the previously created Netns, so that the INIT process has its own stand-alone network stack, completing the final step of network creation and configuration.
2. Set the session ID of the process.
3. Using the system call, mount the previously registered mount point to the physical host, and complete the creation of the volume.
4. Mount the file system under the specified directory and switch the root directory to the newly mounted file system. Set hostname to load profile information.
5. Finally use the EXEC system call to execute the program that the user specified to run in the container.
This completes the process of creating and starting a container.
Docker Run execution Flow details (with volume,network and libcontainer as clues)