Docker Quick Start (2) Docker Quick Start (1)

The previous article " Docker Quick Start (1) " introduced the basic concepts of docker and related operations of images. This article will further introduce images, containers and Dockerfiles.

1 image file

(1) Docker packages the application and its dependencies in an image file.
(2) Only through this image file can a Docker container be generated. An image file can be thought of as a template for a container. Docker generates an instance of the container from the image file.
(3) The same image file can generate multiple container instances running at the same time.
(4) image is a binary file. In actual development, an image file is often generated by inheriting another image file and adding some personalized settings.
(5) The image file is universal, the image file of one machine is copied to another machine, and it can still be used.
(6) Generally speaking, in order to save time, we should try to use the image files made by others instead of making them ourselves. Even if it is to be customized, it should be processed based on other people's image files, rather than made from scratch.
(7) In order to facilitate sharing, after the image file is created, it can be uploaded to the online warehouse. Docker's official repository, Docker Hub, is the most important and most commonly used image repository. In addition, it is also possible to sell self-made image files.

Here are some examples of commands:

docker container run hello-world

This command will generate a running container instance based on the image file.
Note: The docker container run command has the function of automatically grabbing image files. If it is found that there is no specified image file locally, it will be automatically grabbed from the repository. Therefore, the previous docker image pull command is not a required step.
If the operation is successful, as follows:

[@sjs_123_183 ~]# docker container run hello-world

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://cloud.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/engine/userguide/

[@ sjs_123_183 ~] #

After the above content is output, hello world will stop running and the container will automatically terminate. Some containers provide services and do not terminate automatically, such as Ubuntu images:

[@sjs_123_183 ~]# docker container run -it ubuntu bash
root@cd902f829884:/#
root@cd902f829884:/# pwd
/
root@cd902f829884:/#  

At this time, we open another terminal and pass:

docker container ls  

You can see the container instance running on this machine:

[@sjs_123_183 ~]# docker container ls
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
cd902f829884        ubuntu              "bash"              50 seconds ago      Up 50 seconds                           nifty_pike

through this time

docker container kill [CONTAINER ID]  

This command can terminate a running container. as follows:

[@sjs_123_183 ~]# docker container ls
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
cd902f829884        ubuntu              "bash"              4 minutes ago       Up 4 minutes                            nifty_pike
[@sjs_123_183 ~]# docker container kill cd9
cd9

2 container files

The container instance generated by the image file is itself a file, called a container file. That is, once the container is generated, two files exist at the same time: the image file and the container file. And closing the container doesn't delete the container files, it just stops the container.

docker container kill [CONTAINER ID] # List running containers
docker container ls --all # List all containers on the machine, including terminated containers  

as follows:

[@sjs_123_183 ~]# docker container ls --all
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                            PORTS               NAMES
cd902f829884        ubuntu              "bash"                   6 minutes ago       Exited (137) About a minute ago                       nifty_pike
3ca03ca1cd7c        hello-world         "/hello"                 11 minutes ago      Exited (0) 11 minutes ago                             laughing_booth
d225defb10db        ubuntu              "bash"                   13 minutes ago      Exited (137) 13 minutes ago                           fervent_galileo
77182065e27d        ubuntu              "bash"                   16 minutes ago      Exited (127) 15 minutes ago                           ecstatic_kilby
ebf4e2421f51        hello-world         "/hello"                 20 minutes ago      Exited (0) 20 minutes ago                             heuristic_albattani
081ccb2d6eed        nginx               "nginx -g 'daemon of…"   4 weeks ago         Exited (0) 4 weeks ago                                adoring_poincare
fea01895c580        hello-world         "/hello"                 4 weeks ago         Exited (0) 4 weeks ago                                vibrant_goldwasser

The output of the command includes the container ID, that is, the CONTAINER ID. This ID is required in many places, such as the docker container kill command used to terminate the container in the previous section.
The container files that are terminated will still occupy hard disk space and can be deleted using the docker container rm [CONTAINER ID] command. See also:

[@sjs_123_183 ~]# docker container ls --all
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                            PORTS               NAMES
cd902f829884        ubuntu              "bash"                   6 minutes ago       Exited (137) About a minute ago                       nifty_pike
3ca03ca1cd7c        hello-world         "/hello"                 11 minutes ago      Exited (0) 11 minutes ago                             laughing_booth
d225defb10db        ubuntu              "bash"                   13 minutes ago      Exited (137) 13 minutes ago                           fervent_galileo
77182065e27d        ubuntu              "bash"                   16 minutes ago      Exited (127) 15 minutes ago                           ecstatic_kilby
ebf4e2421f51        hello-world         "/hello"                 20 minutes ago      Exited (0) 20 minutes ago                             heuristic_albattani
081ccb2d6eed        nginx               "nginx -g 'daemon of…"   4 weeks ago         Exited (0) 4 weeks ago                                adoring_poincare
fea01895c580        hello-world         "/hello"                 4 weeks ago         Exited (0) 4 weeks ago                                vibrant_goldwasser
[@ sjs_123_183 ~] # docker container rm 3ca ebf
3ca
ebf
[@sjs_123_183 ~]# docker container ls --all
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                        PORTS               NAMES
cd902f829884        ubuntu              "bash"                   10 minutes ago      Exited (137) 5 minutes ago                        nifty_pike
d225defb10db        ubuntu              "bash"                   17 minutes ago      Exited (137) 17 minutes ago                       fervent_galileo
77182065e27d        ubuntu              "bash"                   20 minutes ago      Exited (127) 19 minutes ago                       ecstatic_kilby
081ccb2d6eed        nginx               "nginx -g 'daemon of…"   4 weeks ago         Exited (0) 4 weeks ago                            adoring_poincare
fea01895c580        hello-world         "/hello"                 4 weeks ago         Exited (0) 4 weeks ago                            vibrant_goldwasser  

The above example is that we delete two hello-world container files by container ID.

3 Write Dockerfile 

After learning to use image files, the next question is, how to generate image files? If you want to promote your own software, you must make your own image files. This requires the use of the Dockerfile file. It is a text file used to configure the image. Docker generates a binary image file based on this file. The customization of the image is actually to customize the configuration and files added to each layer. If we can write each layer of modification, installation, construction, and operation commands into a script, and use this script to build and customize the image, it can be reused and the image construction is transparent. Dockerfile is a text file that contains instructions, each instruction builds a layer , so the content of each instruction is to describe how the layer should be constructed .

Take the nginx image as an example, this time we use a Dockerfile to customize it.

mkdir -p /search/odin/xnginx # Create a new directory
cd /search/odin/xnginx # cd to this directory
touch Dockerfile # Create a file called Dockerfile    

Write the following two lines in the Dockerfile, save and exit:

FROM nginx
RUN echo '<h1>Hello, Docker! Hello, xnginx!</h1>' > /usr/share/nginx/html/index.html  

This Dockerfile is very simple and involves two instructions, FROM and RUN。

(1) FROM specifies the base image
custom image, which must be based on an image and customized on it. Just like we ran a container with an nginx image before and then modified it, the base image must be specified.
And FROM is to specify the base image , so FROM is a necessary instruction in a Dockerfile , and it must be the first instruction .

There are a lot of high-quality official images on the Docker Store, and there are service images that can be used directly, such as nginx, redis, mongo, mysql, httpd, php, tomcat, etc. There are also some that are convenient for development, construction, and operation. Mirrors of applications in various languages, such as node, openjdk, python, ruby, golang, etc. We can look for an image that best matches our final goal and customize it as the base image.
If the corresponding service mirror is not found, the official mirror also provides some more basic operating system mirrors, such as ubuntu, debian, centos, fedora, alpine, etc. The software libraries of these operating systems provide us with a wider expansion space .

In addition to choosing an existing image as the base image, Docker also has a special image called scratch. This image is a virtual concept and does not actually exist, it represents a blank image.

FROM scratch
...

If you use scratch as the base image, it means that you are not based on any image, and the instructions written next will exist as the first layer of the image.
It is not uncommon to copy executable files directly into images without any system as the basis, such as swarm, coreos/etcd. For programs statically compiled under Linux, there is no need for an operating system to provide runtime support, and all the required libraries are already in the executable file, so direct FROM scratch will make the image smaller. Many applications developed in the Go language use this method to create images, which is one of the reasons why some people think that Go is a language that is particularly suitable for container microservice architectures.

(2) RUN execution command
The RUN command is used to execute command line commands. Due to the power of the command line, the RUN command is one of the most commonly used commands when customizing images. There are two formats:

Format one:
shell format: RUN <command>, as if entered directly on the command line. The RUN instruction in the Dockerfile just written is in this format.
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html

Format two:
exec format: RUN ["executable", "parameter 1", "parameter 2"], which is more like the format in a function call.

RUN can execute commands like a shell script. When writing Dockerfile, many beginners will assign each command to a RUN like a shell script, for example:

FROM debian:jessie

RUN apt-get update
RUN apt-get install -y gcc libc6-dev make
RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz"
RUN mkdir -p /usr/src/redis
RUN tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1
RUN make -C /usr/src/redis
RUN make -C /usr/src/redis install

Every instruction in a Dockerfile creates a layer, and RUN is no exception . The behavior of each RUN is the same as the process of creating a mirror manually just now: create a new layer, execute these commands on it, and after the execution is completed, commit the modification of this layer to form a new mirror.
The above way of writing creates a 7-layer mirror. This is completely pointless, and many things that are not needed at runtime are loaded into the image, such as the build environment, updated packages, and so on. The result is a very bloated, multi-layered image that not only increases build and deploy time, but is also prone to errors.

The correct spelling is:

FROM debian:jessie

RUN buildDeps='gcc libc6-dev make' \
    && apt-get update \
    && apt-get install -y $buildDeps \
    && wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz" \
    && mkdir -p /usr/src/redis \
    && tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \
    && make -C /usr/src/redis \
    && make -C /usr/src/redis install \
    && rm -rf /var/lib/apt/lists/* \
    && rm redis.tar.gz \
    && rm -r /usr/src/redis \
    && apt-get purge -y --auto-remove $buildDeps

(1) All the previous commands have only one purpose, which is to compile and install the redis executable.
So there is no need to build many layers, it's just one layer thing. Instead of using many RUN to one-to-one correspondence between different commands, just use one RUN instruction and use && to concatenate the required commands. Simplify the previous 7 layers to 1 layer. When writing a Dockerfile, always remind yourself that you're not writing a shell script, but defining how each layer should be built.
(2) Line breaks are also performed here for formatting.
Dockerfile supports the command line wrapping method of adding \ at the end of the Shell class, and the format of commenting with # at the beginning of the line. Good formatting, such as line breaks, indentation, comments, etc., will make maintenance and troubleshooting easier, which is a good habit.
(3) You can also see that at the end of this group of commands, a command for cleaning is added , the software required for compiling and building is deleted, all downloaded and expanded files are cleaned up, and the apt cache files are also cleaned up.
This is a very important step. As we said before, the image is a multi-layer storage, and the things in each layer will not be deleted in the next layer, but will always follow the image. Therefore, when building an image, make sure that each layer only adds what really needs to be added, and any extraneous things should be cleaned up.
One of the reasons why many people start to learn Docker and create very bloated images is that they forget that irrelevant files must be cleaned up at the end of each layer of construction .

4 Build the image

Now that we understand the contents of this Dockerfile, let's build this image. Execute in  Dockerfile the directory where the file is located:

docker build -t nginx:v2 .  

Details are as follows:

[@sjs_123_183 xnginx]# docker build -t nginx:v2 .
Sending build context to Docker daemon  2.048kB
Step 1/2 : FROM nginx
 ---> c5c4e8fa2cf7
Step 2/2 : RUN echo '<h1>Hello, Docker! Hello, xnginx!</h1>' > /usr/share/nginx/html/index.html
 ---> Running in e955070ac2c9
Removing intermediate container e955070ac2c9
 ---> 1beca7b40dee
Successfully built 1beca7b40dee
Successfully tagged nginx:v2
[@ sjs_123_183 xnginx] #

From the output of the command, we can clearly see the image construction process.
In Step 2, as we said before, the RUN instruction starts a container e955070ac2c9, executes the required commands, and finally submits the layer 1beca7b40dee, and then deletes the used container e955070ac2c9.
Here we use the docker build command to build the image. Its format is:

docker build [options] <context path/URL/->

Here we specified the name of the final image -t nginx:v2, now let's start the container we built:

docker run --name webserver -d -p 80:80 nginx:v2

This command will start a container with the nginx image, named webserver, and map port 80, so that we can use the curl command to access the nginx server. Details are as follows:

[@sjs_123_183 ~]# docker run --name webserver -d -p 80:80 nginx:v2
a9f012a96d98262bffd30286c9d23dfe929b032c2149fa67105d29ddde71b763
[@sjs_123_183 ~]# curl http://127.0.0.1:80
<h1>Hello, Docker! Hello, xnginx!</h1>
[@ sjs_123_183 ~] #  

It can also be accessed through a browser, if you are a local browser, you need to visit: http://localhostSince I am on a remote linux, I only need to access the ip.

5 Context Path

Careful students may have noticed that the docker build command has a . at the end. . represents the current directory, and the Dockerfile is in the current directory, so many beginners think that this path is specifying the path where the Dockerfile is located, which is actually inaccurate.
The command format of docker build has been mentioned earlier. The . number is the specified context path. So what is context?

(1) First of all, we need to understand the working principle of docker build. Docker is divided into Docker engine (that is, server-side daemon) and client-side tools at runtime. The Docker engine provides a set of REST APIs, called Docker Remote API, and client tools such as docker commands interact with the Docker engine through this set of APIs to complete various functions.
Therefore, although on the surface we seem to be executing various docker functions locally, in fact, everything is done on the server side (Docker engine) using the form of remote calls. Also because of this C/S design, it is easy for us to operate the Docker engine of the remote server.

(2) When we build an image, not all customizations will be completed through the RUN command, and it is often necessary to copy some local files into the image, such as through the COPY command, the ADD command, and so on. The docker build command builds the image, but it is not built locally, but on the server side, that is, in the Docker engine . So in this client/server architecture, how can the server get local files ?
This introduces the concept of context. When building, the user will specify the path to build the image context . After the docker build command knows this path, it will package all the content under the path and upload it to the Docker engine. In this way, after the Docker engine receives this context package, the expansion will get all the files needed to build the image.

If  Dockerfile it is written like this in:

COPY ./package.json /app/

This is not to copy the package.json in the directory where the docker build command is executed, nor the package.json in the directory where the Dockerfile is located, but the package.json in the context directory .
Therefore, the paths of source files in instructions such as COPY are relative paths. This is also why beginners often ask why COPY ../package.json /app or COPY /opt/xxxx /app doesn't work, because those paths are out of context and the Docker engine cannot get files in those locations. If those files are really needed, they should be copied to the context directory.
Now you can understand that this . in the command docker build -t nginx:v2 . is actually in the directory of the specified context. The docker build command will package the contents of the directory to the Docker engine to help build the image.

There is this sentence in the output of the docker build command:

Sending build context to Docker daemon  2.048kB  

This is actually the process of sending the context.

(1) It is important to understand the build context for image builds to avoid making some undeserved mistakes.
For example, some beginners find that COPY /opt/xxxx /app does not work, so they simply put the Dockerfile in the root directory of the hard disk to build. It turns out that after the docker build is executed, it is extremely slow and easy to send a thing of tens of GB. Build failed. That's because this is making docker build pack the entire hard drive, which is clearly a misuse.

(2) The correct way is to put the Dockerfile in an empty directory, or in the project root directory .
If there is no required file in this directory, you should make a copy of the required file. If there is something in the directory that you really don't want to pass to the Docker engine when building, you can write a .dockerignore with the same syntax as .gitignore, which is used to exclude things that do not need to be passed to the Docker engine as a context.

(3) So why do some people mistake . to specify the directory where the Dockerfile is located?
This is because by default, if no additional Dockerfile is specified, a file named Dockerfile in the context directory will be used as the Dockerfile.
This is just the default behavior. In fact, the file name of Dockerfile does not need to be Dockerfile, and it does not need to be in the context directory. For example, you can specify a file as Dockerfile with the -f ../Dockerfile.php parameter.
Of course, it is generally customary to use the default file name Dockerfile and place it in the image build context directory.

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325961656&siteId=291194637