When using Docker, the most common commands are the subcommands related to Docker Container and Docker Image. Of course, when there are no management commands (or groups) initially, The most common commands are docker run docker commit docker build and docker images.

Docker build, or Docker image build. For simplicity, all of the following commands use Docker build.

Docker Image

A Docker Image is usually referred to as an Image. An Image is a file composed of multiple layers that are used to execute code (commands) and so on inside a container. Each image is essentially built against the full executable version of the application, and it is important to note that it depends on the host’s system kernel. This will create one or more container instances when the user is running the image.

Dockerd

Dockerd is the Docker server. By default, Dockerd provides Unix Domain Socket connections. Of course, Dockerd can also listen on a port to provide external services. So sometimes we can use the Docker Daemon on the server to provide services, to speed up the build and solve some network problems and things like that.

Okay, so the basic concepts are understood, so let’s get started.

Using Dockerfile

We know there are many ways to build an image. In this article, we will only use Dockerfile to build an image through the docker build method.

For simplicity, let’s start with a simple Dockerfile. Build a kubectl tool for use in a container (of course, it was chosen because Kubectl is large enough and usability is not a concern, which will be explained later)

FROM scratch

LABEL maintainer='Jintao Zhang <moelove.info>'

ADD kubectl /kubectl
ENTRYPOINT [ "/kubectl" ]
Copy the code

Dockerfile is simple enough to just copy in the kubectl binary and set Entrypoint to Kubectl.

Dockerd in Docker

Personally, IN order to avoid environmental pollution, most things are done in containers. Including dockerd I also opened in the container. I will not explain the principle of this, please refer to my previous article or share. It’s easy to use:

docker run --privileged -d -P docker:stable-dind
Copy the code

Note that -p is used so that a port is mapped locally at random, or you can specify port 2375 directly in the container.

(Tao) ➜  build git:(master) docker ps                                                       
CONTAINER ID        IMAGE                       COMMAND                  CREATED             STATUS              PORTS                     NAMES
b56f6483614d        docker:stable-dind          "Dockerd - entrypoint...."9 hours ago Up 9 hours 0.0.0.0:32769->2375/ TCP trusting_babbageCopy the code

build

We directly use the dockerd started in the container to build, through the above docker ps command can see that it is mapped to the local port 32769. So we build with the following command:

(Tao) ➜ kubectl git:(master) Docker-h 0.0.0.0:32769 images REPOSITORY TAG IMAGE ID CREATED SIZE (Tao) ➜ kubectl Git (master) docker -h 0.0.0.0:32769 build-tlocal/kubectl. Sending Build context to Docker Daemon 55.09MB Step 1/4: FROM scratch --> Step 2/4: LABEL Maintainer ='Jintao Zhang <moelove.info>'
 ---> Running in ebcf44071bf0
Removing intermediate container ebcf44071bf0
 ---> eb4ea1725ff2
Step 3/4 : ADD kubectl /kubectl
 ---> 1aad06c4dbb4
Step 4/4 : ENTRYPOINT [ "/kubectl" ]
 ---> Running in 2fc78fe974e3
Removing intermediate container 2fc78fe974e3
 ---> 457802d4bf3e
Successfully built 457802d4bf3e
Successfully tagged local/kubectl:latest (Tao) ➜ kubectl git (master) docker -h 0.0.0.0:32769 images REPOSITORY TAG ID CREATED SIZElocal/kubectl       latest              457802d4bf3e        3 seconds ago       55.1MB
Copy the code

Looking at the log and the results, you can see that we have successfully built the mirror we needed. With all that said, we’ve only just begun today.

In-depth principle

Dockerd service

At the beginning of this article, I already mentioned that Dockerd is the backend service of Docker, via the above

Docker -h 0.0.0.0:32769 imagesCopy the code

-h specifies the local dockerd service on port 32679. This is actually an HTTP service. Let’s verify this.

(Tao) ➜ kubectl git:(master) curl -i 0.0.0.0:32769/_ping HTTP/1.1 200 OK Api -version: 1.37 Docker-Experimental:falseOstype: Linux Server: Docker/18.03.1 -CE (Linux) Date: Tue, 04 Sep 2018 17:20:51 GMT Content-Length: 2 Content-type: text/plain; charset=utf-8 OK%Copy the code

You can see a few key pieces of information apI-version: 1.37 this indicates the Version of the Api currently in use. This article uses the example 1.37, which is the current stable Version. We can also view it through docker version.

(Tao) ➜ kubectl git:(master) Docker -h 0.0.0.0:32769 version Client: version: 18.06.0 -CE API version: 1.37 (fast from 1.38) Go version: GO1.10.3 Git Commit: 0ffa825 Built: Wed Jul 18 19:11:45 2018 OS/Arch: linux/amd64 Experimental:falseServer: Engine: Version: 18.03.1 -CE API Version: 1.37 (minimum Version 1.12) Go Version: go1.9.5 Git commit: 9ee9f40 Built: Thu Apr 26 07:23:03 2018 OS/Arch: linux/amd64 Experimental:false
Copy the code

It can be seen that the CLI version of Docker I am using locally is higher. When I connect to the earlier version of Dockerd, the API version is degraded to the same as the dockerd version.

Of course, you might ask, what if the dockerd version is higher? This is actually my everyday development environment, and most oF the apis don’t matter, but that’s not the point today.

root@bdcdac73ee20:/# docker versionClient: Version: 17.06.0 -CE API Version: 1.30 Go Version: go1.8.3 Git Commit: 02C1d87 Built: Fri Jun 23 21:15:15 2017 OS/Arch: Linux /amd64 Server: Version: dev API Version: 1.39 (minimum Version 1.12) Go Version: Go1.10.3 Git Commit: E8CC5A0B3 Built: Tue Sep 4 10:00:36 2018 OS/Arch: Linux/AMd64 Experimental:false
Copy the code

build context

Back to our build process above. We can see the first line of the log:

. Sending Build context to Docker Daemon 55.09MBCopy the code

From this log, we can get two messages:

  • The build process is to send the build context to Dockerd, and the actual build pressure is on dockerd
  • 55.09 MB was sent

The first conclusion, which we discussed in the last section, is to focus on the second.

(Tao) ➜ kubectl git:(master) ls-al total amount 53808 drwxrwxr-x. 2 Tao Tao 4096 9月 5 01:00.3 Tao Tao 4096 9月 5 00:57.. -rw-rw-r--. 1 Tao tao 109 September 5 01:00 Dockerfile -rwxrwxr-x. 1 Tao tao 55084063 September 5 00:53 kubectl (tao) ➜ kubectl Git (master) du -sh.53m. (Tao) ➜ kubectl git (master) du -sh kubectl Dockerfile 53M kubectl 4.0K DockerfileCopy the code

According to our Dockerfile, we need to put the kubectl binary package into the image, so you won’t be surprised if the build context is about 2M bigger than the binary.

But I’ve done another example here, and without going into detail, the code can be found on my GitHub. Here are the results:

2 Tao Tao 4096 9月 5 01:45.drwxrwxr-x. 4 Tao Tao 4096 9月 5 01:44..  -rw-rw-r--. 1 tao tao 77 9月 5 01:45 dockerfile-rw-rw-r --. 1 tao tao 61 9月 5 01:45 file (tao) ➜ text git:(master) du -b Dockerfile file 77 Dockerfile 61 file (Tao) ➜ text git:(master) docker -h 0.0.0.0:32769 build --no-cache=true -t local/file. Sending Build context to Docker Daemon 3.072kB...Copy the code

I’m sure you’ve seen what I mean by this result, so let’s continue to explore this process.

/ build request

As we said earlier, this is just a normal HTTP request, so surely we can just grab the packet and see what’s going on?

It’s easy to access the /build interface from the dockerd address using the POST method. In reality, the prefix is added, which is the version number I mentioned above. In the current environment, the /v1.37/build interface is used.

And this request carries some very useful parameters, and header information. Let me just say it briefly:

Header

There are two main headers for a build request

  • Content-TypeThe default value isapplication/x-tarTo identify yourself as an archive.
  • X-Registry-ConfigThis header contains the address and authentication information of Registry and is encoded in Base64. Those who are familiar with Docker or have read my previous articles should know that docker CLI will save the authentication information to the local after login succeeds, and the password will be saved in base64. This information is encoded in Base64 during build. It can also be seen from here that when using remote Dockerd, TLS should be configured as far as possible to prevent man-in-betweenattacks, resulting in password leakage and other situations.

Parameters

In the request parameters, it makes sense to list a few:

  • tThat’s who we aredocker build -t, and we can specify more than one at a time-tBuild multiple images with different names simultaneously.
  • memory cpusetcpusThese are mainly for resource constraints
  • buildargsIf you want to see this parameter, remember the DockerfileARGUse of instructions

Of course, the process we want to explore is the request header, the input stream of the entire request, must be a tar package and support the identity (uncompressed), Gzip, Bzip2, XZ compression algorithms.

implementation

Let’s look at the basic implementation:

func (cli *Client) ImageBuild(ctx context.Context, buildContext io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) {
	query, err := cli.imageBuildOptionsToQuery(options)
	iferr ! = nil {return types.ImageBuildResponse{}, err
	}

	headers := http.Header(make(map[string][]string))
	buf, err := json.Marshal(options.AuthConfigs)
	iferr ! = nil {return types.ImageBuildResponse{}, err
	}
	headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf))

	headers.Set("Content-Type"."application/x-tar")

	serverResp, err := cli.postRaw(ctx, "/build", query, buildContext, headers)
	iferr ! = nil {return types.ImageBuildResponse{}, err
	}

	osType := getDockerOS(serverResp.header.Get("Server"))

	return types.ImageBuildResponse{
		Body:   serverResp.body,
		OSType: osType,
	}, nil
}
Copy the code

conclusion

This article mainly focuses on the process and principle of Docker build. The reason why I write this article is mainly because we are closely related to the image and it is the first step we use. In many cases, the push to containerize the business also comes with performance optimizations and other specifications.

In fact, there are many details about the build, if I have time, I will update the next post.


You can subscribe to my official account [MoeLove] through the following TWO-DIMENSIONAL code.