COPY
format:
COPY <源路径>... <目标路径>
COPY ["<源路径1>",... "<目标路径>"]
And RUN
command, there are also two formats, similar to a command line, similar to a function call.
COPY
Construction of instructions from the context directory <源路径>
copied file / directory into the new layer of the mirror <目标路径>
position. such as:
COPY package.json /usr/src/app/
<源路径>
It may be a plurality, or even a wildcard, wildcard rules to meet its Go's filepath.Match
rules, such as:
COPY hom* /mydir/
COPY hom?.txt /mydir/
<目标路径>
Can be absolute in the vessel may be a path relative to the working directory (directory can work WORKDIR
instruction specified). Destination path does not need to be created in advance, if the directory does not exist will first create the missing directory before copying files.
Also, note that the use of COPY
the instruction, the source file will remain various metadata. Such as read, write, and execute permissions, file change time. This feature is useful for custom image. Especially when the build-related files are managed using Git.
ADD
ADD
And instructions COPY
consistent format and properties. However, COPY
on the basis of some added features.
For example, <源路径>
can be a URL
, in this case, Docker engine attempts to download files on this link <目标路径>
to go. After downloading the file permissions set automatically 600
, if this is not desired permissions, you also need to add an extra layer of RUN
permissions to adjust. In addition, if you download the compressed package, you need to decompress, the same is also a need for additional layer RUN
instruction decompressed. It is better to use direct RUN
instruction, and then use wget
or curl
tool download, processing authority, decompress, and then clean the useless files more reasonable. Therefore, this feature is actually not practical, but not recommended.
If <源路径>
a tar
compressed file, compressed format gzip
, bzip2
and xz
the case, the ADD
command will automatically decompress the compressed file to <目标路径>
go.
In some cases, this self-extracting feature is very useful, such as the official image ubuntu
of:
FROM scratch
ADD ubuntu-xenial-core-cloudimg-amd64-root.tar.gz / ...
However, in some cases, if we really want to copy a compressed file in, and understand compression, then not be able to use the ADD
commands.
In Docker official Dockerfile 最佳实践文档
requirements, as much as possible to use COPY
, because COPY
the semantics is clear, is to copy files, but rather ADD
contains a more complex function, its behavior is not necessarily very clear. The most suitable ADD
occasion, is mentioned the need for automatic decompression occasions.
Also note that the ADD
directive will make the mirrored cache invalidation building, which may lead to image building becomes slow.
So COPY
and ADD
when command is selected, you can follow this principle, all the files are copied using the COPY
instructions, only in the need for automatic decompression occasions ADD
.
#CMD
CMD
Format and instructions RUN
similar, are two formats:
shell
format:CMD <命令>
exec
format:CMD ["可执行文件", "参数1", "参数2"...]
- Parameter list
CMD ["参数1", "参数2"...]
format: . It is specifiedENTRYPOINT
after the command withCMD
the specified specific parameters.
Before the introduction of the container, when once said, Docker is not a virtual machine, the container is the process. Since it is a process, so when you start a container, you need to specify the parameters of the program and running. CMD
Command is used to specify the default startup command of the vessel master process.
You can specify a new command at run time to replace the default settings in the mirror command, for example, ubuntu
mirroring the default CMD
Shi /bin/bash
, if we direct docker run -it ubuntu
it, will directly enter bash
. We can also specify other commands to run at runtime, such as docker run -it ubuntu cat /etc/os-release
. This is a cat /etc/os-release
command replaces the default /bin/bash
command, and the output of the system version information.
On the instruction format it is generally recommended to use exec
format, such format when parsing will be parsed as JSON array, so be sure to use double quotes "
instead of single quotes.
If you use the shell
format, the actual command would be packaged as sh -c
execution of formal parameters. such as:
CMD echo $HOME
In actual implementation, it will be changed to:
CMD [ "sh", "-c", "echo $HOME" ]
This is the reason why we can use environment variables, because these environment variables will be analyzed and processed shell.
Mentioned CMD
would have to mention container application in question foreground and background execution. This is a confusing beginners often arise.
Docker is not a virtual machine, application containers should be executed before the station, rather than virtual machines, physical machines inside as with upstart / systemd to start the background service, there is no concept of background services in the container.
Some beginners will be CMD
written as:
CMD service nginx start
And then found immediately after the execution out of the container. Even within the container to use systemctl
the command only to discover that simply can not be executed. This is because not thoroughly understand the foreground, the background of the concept, did not tell the difference containers and virtual machines, still in the angle of the traditional virtual machines to understand the container.
For containers, the launcher application process is the container, the container is to master process exists, the main process exits, the container would be meaningless existence, and thus exit, it is not something other auxiliary processes need to be concerned.
The use of service nginx start
command, it is hoped upstart to form after Taiwan daemon start nginx
service. And it just said CMD service nginx start
can be understood as CMD [ "sh", "-c", "service nginx start"]
, therefore the main process actually sh
. Then when service nginx start
the end of the command sh
will be over, sh
as the main process exits, it will naturally make the container exit.
The correct approach is to directly execute nginx
the executable file, and requires previous stage in the form of running. such as:
CMD ["nginx", "-g", "daemon off;"]
#ENTRYPOINT
ENTRYPOINT
Format and RUN
instruction format as divided exec
form and shell
format.
ENTRYPOINT
The purpose and CMD
, are in the specified container and start the program parameters. ENTRYPOINT
It may be replaced at run-time, but compared CMD
to slightly cumbersome, need to pass docker run
parameters --entrypoint
to be specified.
When specified ENTRYPOINT
, the CMD
meaning of change occurs, it is no longer a direct operational command, but the CMD
contents as an argument to ENTRYPOINT
the instruction, in other words when the actual implementation, will become:
<ENTRYPOINT> "<CMD>"
With so CMD
later, why have ENTRYPOINT
it? This <ENTRYPOINT> "<CMD>"
what good it? Let's look at a few scenarios.
# Scene One: Let the mirror become like to use the same command
Suppose we learned that he needs a mirror image of the current public network IP, it can first CMD
be achieved:
FROM ubuntu:16.04
RUN apt-get update \
&& apt-get install -y curl \ && rm -rf /var/lib/apt/lists/* CMD [ "curl", "-s", "http://ip.cn" ]
If we use docker build -t myip .
to build a mirror, and if we need to query the current public network IP, only need to do:
$ docker run myip
当前 IP:61.148.226.66 来自:北京市 联通
Ah, so it looks like a mirror can be directly used as a command, but there is always a command parameters, if we want to add parameters? From the above example CMD
you can see the essence of command curl
, so if we want to display the HTTP header information, you need to add -i
parameters. Then we can directly add -i
parameters to docker run myip
it?
$ docker run myip -i
docker: Error response from daemon: invalid header field value "oci runtime error: container_linux.go:247: starting container process caused \"exec: \\\"-i\\\": executable file not found in $PATH\"\n".
We can see that the executable file can not find the error, executable file not found
. Before we said, behind the mirror name is command
, will replace the runtime CMD
defaults. So here's -i
to replace the original CMD
, rather than adding in the original curl -s http://ip.cn
behind. But -i
is not a command, so naturally can not be found.
So if we want to join -i
this argument, we must re-enter the complete command:
$ docker run myip curl -s http://ip.cn -i
This is obviously not a good solution, but using ENTRYPOINT
it can solve this problem. Now that we use ENTRYPOINT
to achieve this image:
FROM ubuntu:16.04
RUN apt-get update \
&& apt-get install -y curl \ && rm -rf /var/lib/apt/lists/* ENTRYPOINT [ "curl", "-s", "http://ip.cn" ]
This time let's try to use directly docker run myip -i
:
$ docker run myip
当前 IP:61.148.226.66 来自:北京市 联通
$ docker run myip -i
HTTP/1.1 200 OK
Server: nginx/1.8.0
Date: Tue, 22 Nov 2016 05:12:40 GMT
Content-Type: text/html; charset=UTF-8
Vary: Accept-Encoding
X-Powered-By: PHP/5.6.24-1~dotdeb+7.1
X-Cache: MISS from cache-2
X-Cache-Lookup: MISS from cache-2:80
X-Cache: MISS from proxy-2_6
Transfer-Encoding: chunked
Via: 1.1 cache-2:80, 1.1 proxy-2_6:8006
Connection: keep-alive
当前 IP:61.148.226.66 来自:北京市 联通
You can see, this time succeeded. This is because when there is ENTRYPOINT
, the CMD
content will be passed as a parameter ENTRYPOINT
, and here -i
is the new CMD
, and therefore passed as a parameter curl
to achieve our desired results.
# Preparatory work before the application is running: Scene Two
Start container is to start the main process, but sometimes, before starting the main process that requires some preparation.
For example, mysql
such as a database, the database may require some configuration, initialization of work, which should be resolved before the final mysql server is running.
In addition, you may want to avoid using root
the user to start the service, to improve security, but also need to start the service before to root
perform some necessary preparations identity, and finally switch to the service user who started the service. Or in addition to service, the other can still use the command root
executed as to facilitate debugging.
These preparations are container and CMD
has nothing to do, no matter CMD
how, we need to advance the work of a pretreatment. In this case, you can write a script, and then put ENTRYPOINT
in to perform, and this script will be received parameters (that is <CMD>
) as a command, the last execution in the script. For example, the official mirror redis
in is to do so:
FROM alpine:3.4
...
RUN addgroup -S redis && adduser -S -G redis redis ... ENTRYPOINT ["docker-entrypoint.sh"] EXPOSE 6379 CMD [ "redis-server" ]
Which can be seen in order to create a redis redis service user, and finally assigned ENTRYPOINT
to docker-entrypoint.sh
the script.
#!/bin/sh
...
# allow the container to be started with `--user`
if [ "$1" = 'redis-server' -a "$(id -u)" = '0' ]; then chown -R redis . exec su-exec redis "$0" "$@" fi exec "$@"
The script is based on the contents of CMD
the content to determine if it is redis-server
, then switch to the redis
user who started the server, otherwise still use root
the identity of execution. such as:
$ docker run -it redis id
uid=0(root) gid=0(root) groups=0(root)
2
#ENV
There are two formats:
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...
This command is very simple, it is to set the environment variable, whether it is behind the other instructions, such as RUN
, or run-time application, you can directly use environment variables defined here.
ENV VERSION=1.0 DEBUG=on \
NAME="Happy Feet"
This example demonstrates how to wrap, as well as the values contain spaces enclosed in double quotes way, and this behavior is consistent with Shell.
Defined environment variable, then the subsequent instruction, you can use this environment variable. For example, in the official node
mirror Dockerfile
, there is code like this:
ENV NODE_VERSION 7.2.0
RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
&& curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \ && gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \ && grep " node-v$NODE_VERSION-linux-x64.tar.xz\$" SHASUMS256.txt | sha256sum -c - \ && tar -xJf "node-v$NODE_VERSION-linux-x64.tar.xz" -C /usr/local --strip-components=1 \ && rm "node-v$NODE_VERSION-linux-x64.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt \ && ln -s /usr/local/bin/node /usr/local/bin/nodejs
Here we define the environment variable NODE_VERSION
, followed by RUN
this layer, use many times $NODE_VERSION
to operate customize. It can be seen in the future when the upgrade image builds, you only need to update 7.2.0
to, Dockerfile
build a maintenance easier.
The following instructions can support environment variable ADD
expansion: COPY
, ENV
, EXPOSE
, LABEL
, USER
, WORKDIR
, VOLUME
, STOPSIGNAL
, ONBUILD
, .
You can feel from the command list, environment variables can be used in many places, it is powerful. By environment variables, we can make a Dockerfile
production of more images, just use a different environment variable.
#VOLUME
The format is:
VOLUME ["<路径1>", "<路径2>"...]
VOLUME <路径>
We have said before, the container should be kept runtime container storage layer does not occur writes for database application classes need to preserve dynamic data, files should be stored in its database volume (volume), the later chapters we will introduce further roll Docker the concept of. In order to prevent run-time dynamic user forgets to save the file directory mounted as volumes, Dockerfile
we can pre-specify certain directories mounted as anonymous volume, so at run time if the user does not specify mount, its application can run normally , does not write data to a container for storing a large number of layers.
VOLUME /data
Here /data
directory will be automatically mounted at runtime anonymous volume, to any /data
information will not be written into the container for storing the recording layer, thus ensuring the free state of the container of the storage layer. Of course, you can override this mount runtime settings. such as:
docker run -d -v mydata:/data xxxx
In this command line, you use mydata
this name to the roll mount /data
this position, replacing Dockerfile
mount configuration defined volume of anonymity.
#EXPOSE
Format EXPOSE <端口1> [<端口2>...]
.
EXPOSE
Instruction is to provide services runtime container port statement, this is just a statement, and this statement will not use this service port will open at runtime. In Dockerfile written such a statement has two advantages, a mirror is to help users understand the guardian of this port mirroring services to facilitate configuration mapping; another is to use a random port mapping at run time, that is docker run -P
when, automatically random mapping EXPOSE
port.
In addition, there is a special use in earlier versions of Docker. All containers before running the default bridged network, so all containers have direct access to each other, so there are some security issues. So with a Docker engine parameter --icc=false
, when specified, the default will not be exchange of visits between the container, between each other unless the use of --links
the container argument can communicate, and only the mirror EXPOSE
port declared before they can be accessed. This --icc=false
usage, the introduction of the docker network
post has been basically no, you can easily achieve interconnection and isolation between the container via custom network.
To EXPOSE
and at run time using -p <宿主端口>:<容器端口>
separate. -p
, It is a mapping of host ports and container ports, in other words, the container port of the corresponding public service access to the outside world, but EXPOSE
merely a statement of what you plan to use a container port only, and does not automatically port mapping in the host.
#WORKDIR
Format WORKDIR <工作目录路径>
.
Use WORKDIR
command can specify the working directory (or called the current directory), after the current directory of each layer was changed to the specified directory, if the directory does not exist, WORKDIR
will help you to create the directory.
Prior to mention some common mistakes beginners is the Dockerfile
equivalent to the Shell scripts to write, this wrong understanding may also lead to the following error occurs:
RUN cd /app
RUN echo "hello" > world.txt
If this Dockerfile
post-build Mirror operation, you will find not find the /app/world.txt
file, its contents or not hello
. The reason is simple, in the Shell, successive two lines is the same process execution environment, so the previous command to modify memory state, it will directly affect after a command; and in Dockerfile
the two rows RUN
execution environment fundamentally different command is two completely different containers. This is for Dockerfile
errors caused by lack of understanding the concept of tiered storage building.
Each said before RUN
is to start a container, execute commands, and then submit the file storage layer changes. The first layer is RUN cd /app
performed just working directory of the current process of change, a change in a memory of it, the result will not cause any file changes. By the time the second layer, the start of a new container, the container with the first layer is more completely does not matter, naturally can not change memory before the process of building a layer of inheritance.
So if you need to change the position after the layers of the working directory, you should use the WORKDIR
command.