Create as small a Docker container as possible

Source: Internet
Author: User
This is a creation in Article, where the information may have evolved or changed.

Note: This article was written by Adriaan de Jonge, the original address of this article is the Create the smallest Possible Docker Container

When we are using Docker, you will soon notice that you are downloading many megabytes as your pre-configured container. A simple Ubuntu container can easily exceed a few megabytes, and with the software installed on it, the size increases gradually. In some cases, you don't need to use Ubuntu for everything. For example, if you simply want to run a Web service and write it using GO, there's no need to use any tools around it.

I've been looking for the smallest possible container to start with and found a:

docker pull scratch

Scratch Mirror is perfect, true perfection! It's simple, compact and fast. It does not contain any bugs, security leaks, slow code or technical debt. This is because it is an empty mirror. Except for a bit of metadata added by Docker. In fact, you can use the following command to create your own scratch image as described in the Docker documentation.

tar cv --files-from /dev/null | docker import - scratch

So this is probably the smallest Docker image.

Or can we talk more about this? For example, how do you use scratch mirroring? This poses some challenges for yourself.

Creating content for Scratch images

What can we run in an empty image? An executable program that has no dependencies. Do you have an executable program that is not dependent?

I used to write code using Python,java and Javascript. Each of these languages/platforms requires a run-time installation. Recently, I started to involve the Go (or Golang if you like) platform. It seems that Go is statically connected. So I tried to compile a simple Web service output Hello world and run it in the scratch container. Here is the code for this Hello World Web service:

package mainimport (    "fmt"    "net/http")func helloHandler(w http.ResponseWriter, r *http.Request) {    fmt.Fprintln(w, "Hello World from Go in minimal Docker container")}func main() {    http.HandleFunc("/", helloHandler)    fmt.Println("Started, serving at 8080")    err := http.ListenAndServe(":8080", nil)    if err != nil {        panic("ListenAndServe: " + err.Error())    }}

Obviously, I can't compile my Web service in the scratch container because there is no Go compiler in the container. Just as I'm working on a Mac, I can't compile Linux binaries (in fact, it's possible to cross-compile the Go source on different platforms, but this will be covered in another blog post).

So, first I need a Docker container with Go compilers. Let's get Started:

docker run -ti google/golang /bin/bash

Inside this container, I can build a WEB service through the code I've submitted to a GitHub repository.

go get github.com/adriaandejonge/helloworld

The go get command is a variant of the Go Build command that runs the Get and build remote dependencies. You can run the results of the executable:

$GOPATH/bin/helloworld

It works, but it's not what we want. We need the Hello World container to run inside the scratch container. So, in fact, we need a Dockerfile:

FROM scratchADD bin/helloworld /helloworldCMD ["/helloworld"]

And then start it, unfortunately, we start Google/golang This method of container, there is no way to build this Dockerfile. So, first, we need a way to access Docker from within this container.

Calling Docker from within Docker

When you use Dokcer, you will sooner or later encounter the need to access Docker from within Docker. There are several ways to implement it. You can use recursion and run Docker in Docker. Nonetheless, this may seem complicated and result in a large container. You can also access the Docker server outside the instance with some additional command options:

docker run -v /var/run/docker.sock:/var/run/docker.sock -v $(which docker):$(which docker) -ti google/golang /bin/bash

Before you continue, you rerun the Go compiler, because Docker forgot that we had compiled it before during the restart.

go get github.com/adriaandejonge/helloworld

When we start this container, the -v parameters create a volume in the Docker container and allow you to supply a file from the Docker machine as input. /var/run/docker.sockis a UNIX socket that allows you to access the Docker service through this. (which docker)part is a very clever method, which provides a path to the Docker executable file in the container, rather than hard coding. However, you need to be careful when you use this command with Boot2docker on your Mac. If the Docker executable file is in a different location than the Boot2docker virtual machine, it will cause a mismatch. So you might want to use /usr/local/bin/docker hard-coded ways to $(which docker) Replace, and if you run on different systems, /var/run/docker.sock there are opportunities in different locations and you need to make the appropriate adjustments.

You can now use Dockerfile in the $GOPATH directory of the Google/golang container to point to in this example /gopath . In fact, I've checked this on GitHub Dockerfile , so you can copy it from the Go build directory to the desired location, like this:

cp $GOPATH/src/github.com/adriaandejonge/helloworld/Dockerfile $GOPATH

You need to copy this as a binary compilation file, which is now located in the $GOPATH/bin, and it is not possible to include files from the parent directory when building a Dockerfile. So after copying, the next step is:

docker build -t adejonge/helloworld $GOPATH

When all is done, Docker gives the following response:

Successfully built 6ff3fd5a381d

Allows you to run this container:

docker run -ti --name hellobroken adejonge/helloworld

Unfortunately, the response from Docker is as follows:

2014/07/02 17:06:48 no such file or directory

So what the hell is going on? We have an executable static link in the Scratch container. Did we make a mistake?

It turns out that Go is not a static link library. Or at least not all of the libraries. Under Linux, we can use the LDD command to see the dynamic link library:

Get the following response:

linux-vdso.so.1 => (0x00007fff039fe000)libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f61df30f000)libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f61def84000)/lib64/ld-linux-x86-64.so.2 (0x00007f61df530000)

So before we run our Web service, I need to tell the go compiler the actual static link.

Create an executable static link in Go

To create an executable static link, we need to tell go to use the CGO compiler instead of the go compiler. The command is as follows:

CGO_ENABLED=0 go get -a -ldflags '-s' github.com/adriaandejonge/helloworld

CGO_ENABLEDEnvironment variables tell go to use the CGO compiler instead of the go compiler. -aparameters tell GO heavy pay to build all the dependencies. Otherwise, you will end up with a dynamic link dependency. The final -ldflags '-s' parameter is a very good extension. It probably reduces the file size of executable file 50%. You can also use this without CGO. Size reduction is the result of debugging information removed.

In order to determine, run the LDD command:

Return is:

not a dynamic executable

You can also rerun the steps around the executable file that creates the Docker container from scratch.

docker build -t adejonge/helloworld $GOPATH

If all goes well, Docker will respond as follows:

Successfully built 6ff3fd5a381d

Allows you to run this container:

docker run -ti --name helloworld adejonge/helloworld

The response is as follows:

Started, serving at 8080

So far, there are many manual steps and many errors in place. Let's exit the Google/golang container and continue from the perimeter server:

<Press Ctrl-C>exit

You can check that the Docker container and the mirror are not present:

docker ps -adocker images -a

You can use the following command to clean up:

docker rm -f helloworlddocker rmi -f adejonge/helloworld

Create a Docker container to create a Docker container

So far we've taken so many steps that we can also record in Dockerfile and Docker will do the work for us:

FROM google/golangRUN CGO_ENABLED=0 go get -a -ldflags '-s' github.com/adriaandejonge/helloworldRUN cp /gopath/src/github.com/adriaandejonge/helloworld/Dockerfile /gopathCMD docker build -t adejonge/helloworld gopath

I checked the Dockerfile in a separate GitHub warehouse called Adriaandejonge/hellobuild. It can be built using the following command:

docker build -t adejonge/hellobuild github.com/adriaandejonge/hellobuild

Provides the -t parameter named Adejonge/hellobuild Mirror and its newest implicit label. These names make it easier for you to remove the image later. Next, you can use a parameter to create a container from this image as we saw earlier in this article:

docker run -v /var/run/docker.sock:/var/run/docker.sock -v $(which docker):$(which docker) -ti --name hellobuild adejonge/hellobuild

Providing --name hellobuild parameters makes it easier to remove containers after running. In fact, you can do this because after you run this command, you have created a Adejonge/helloworld image:

docker rm -f hellobuilddocker rmi -f adejonge/hellobuild

Now you can create a new container named HelloWorld based on the Adejonge/helloworld image, as you did before:

docker run -ti --name helloworld adejonge/helloworld

Because all of these steps are run from the same command, there is no need to open a bash shell in Docker. You can add these steps into a bash script, run it automatically, and for your convenience, I've added these scripts to the Hellobuild GitHub repository.

Also, if you want to try a container as small as possible, but don't want to follow the steps in the blog, you can use the pre-built image I checked into the Docker Hub repository.

docker pull adejonge/helloworld

With Docker images-a, you can see that the size is 3.6MB. Of course, you can make it smaller if you succeed in creating an executable that is smaller than the Web service I wrote using Go. Using C or a compilation, you can do that. Still, you can't make it smaller than the scratch mirror.

Extended Reading

    • Optimizing DOCKER IMAGES

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.