RUN
RUN指令用于指定镜像构建时要运行的命令,RUN指令将在当前镜像之上的新层中执行任何命令并提交结果。生成的提交镜像将用于下一步Dockerfile。
RUN有两种格式:
- RUN command
- RUN ["executable", "param1", "param2"]
RUN echo "Hello, World"
RUN ["echo", "Hello, World"]
[root@instance-1kdjwtam ~]# docker build -t node8:latest .
Sending build context to Docker daemon 31.74kB
Step 1/3 : FROM ubuntu
---> 93fd78260bd1
Step 2/3 : RUN echo "Hello, World"
---> Running in b5a9998db260
Hello, World
---> f4363ff71188
Removing intermediate container b5a9998db260
Step 3/3 : RUN echo Hello, World
---> Running in ec633aef9ee2
Hello, World
---> 185ab922341f
Removing intermediate container ec633aef9ee2
Successfully built 185ab922341f
Successfully tagged node8:latest
第7行和第12行输出就是以上两中不同格式的RUN命令的执行结果。
当使用第一种格式是默认使用的是/bin/sh -c,如果要使用除/ bin/sh之外的其他shell,可以手动指定。
RUN /bin/bash -c "echo Hello, World"
RUN ["/bin/bash", "-c", "echo Hello, World"]
注意:这个时候我们要执行的命令和参数要写在一起(一个双引号内)。
Dockerfile中每一个指令都会建立一层,然后在其上执行这些命令,执行结束后,commit 这一层的修改,生成新的镜像。生成的镜像将用于下一步Dockerfile指令。层数多了我们的镜像就会变得非常臃肿,而且其中还会包含很多运行时不需要的东西,比如编译环境、安装的软件包。下面我们来看一下比较正确的Dockerfile文件写法。
FROM centos
RUN rely="wget gcc zlib-devel openssl openssl-devel make" \
&& yum install -y $rely \
&& curl -SL https://www.python.org/ftp/python/3.6.4/Python-3.6.4.tgz | tar -zxC /root \
&& /root/Python-3.6.4/configure --prefix=/usr/local/python36\
&& make \
&& make install \
&& yum remove -y $rely
&& rm -rf /root/Python-3.6.4
Dockerfile支持将在命令的末尾添加'\'来使命令换行并通过 && 将多个命令连接起来,这一点和shell脚本类似。这样多个RUN指令就可以连接成一个RUN指令运行,也就是将多层变成了1层,还有一点我们在编译安装完成之后要记得清除运行时不需要的软件包和文件。这样我们生成的镜像才会简洁。
CMD
CMD指令用于指定一个容器启动时要运行的命令。这有点类似于RUN指令,只是RUN指令是指定镜像构建时要运行的命令,而CMD是指定容器被启动是要运行的命令。这和使用docker run命令启动一个容器时在后面指定的要运行的命令类似。
CMD指令有三种格式:
- CMD ["executable","param1","param2"](推荐形式)
- CMD ["param1","param2"](作为ENTRYPOINT的默认参数)
- CMD command param1 param2(shell形式)
以下两行命令的效果是一样的
docker run -it -d node1 /bin/bash
CMD ["/bin/bash"]
也可以为要运行的命令添加参数。
CMD ["/bin/bash", "-l"]
注意:使用docker run执行一个命令时会覆盖CMD指令,如果我们在Dockerfile中指定了CMD命令,
并且在docker run命令行中也指定了要运行的命令,那么命令行中的命令或覆盖掉Dockerfile中的CMD指令。
实践:
Dockerfile文件内容
FROM centos
CMD ["echo", "Hello, World"]
使用Dockerfile构建镜像
docker build -t node:latest .
创建并运行容器
[root@instance-1kdjwtam ~]# docker run -it node
Hello, World
可以看出我并没有在docker run命令的末尾指定要运行的命令,但是却打印出来了一句话。这是因为docker是用来CMD指令中指定的命令。上面我们说过CMD指令指定的命令是在容器运行的时候执行的,这一现象很好的证明了这一点。
我们再来看一下在docker run末尾也指定了要运行的命令时的情况。
[root@instance-1kdjwtam ~]# docker run -it node /bin/ps
PID TTY TIME CMD
1 pts/0 00:00:00 ps
通过执行结果我们可以清楚的看到,当我们在docker run的末尾指定了要运行的命令/bin/ps时,会覆盖CMD指令指定的命令。还有一点我们需要注意,在Dockerfile中如果指定了多个CMD指令,只有最后一个CMD指令会生效其他的都会被覆盖掉。
ENTRYPOINT
ENTRYPOINT指令与CMD指令很类似,也很容易和CMD指令弄混。我们来看一下ENTRYPOINT命令。ENTRYPOINT用于指定一个容器启动时要运行的命令。并且一个Dockerfile文件中也只能指定一个ENTRYPOINT。看到这里确实跟CMD命令很像,不,应该说是一样。但是docker为什么会提供两个功能相同的命令呢?其实我们能想到既然docker提供了两个命令,那么他们的功能肯定会有所不同。下面我们来看一下他们的不同之处。前面我们说了CMD命令会被docker run末尾指定的命令覆盖,但是ENTRYPOINT指令指定的命名不容易在启动容器的时候被覆盖。实际上docker run尾部指定的任何参数都会被当做参数再次传递给ENTRYPOINT指令指定的命令。
ENTRYPOINT指令有两种格式:
- ENTRYPOINT ["executable", "param1", "param2"]
- ENTRYPOINT command param1 param2
无参数形式:
ENTRYPOINT ["/bin/ps"]
有参数形式:
ENTRYPOINT ["/bin/ps", "-aux"]
注意:在Dockerfile文件中,CMD指令和ENTRYPOINT指令至少要有一个。其实我们在运行容器时通过docker run 的 --entrypoint 选项也是可以覆盖Dockerfile中的ENTRYPOINT指令的。
实践:
Dockerfile文件内容
FROM centos
ENTRYPOINT ["echo", "Hello"]
使用Dockerfile构建镜像
docker build -t node2:latest .
创建并运行容器
[root@instance-1kdjwtam ~]# docker run -it node2
Hello
我们看到,我们在Dockerfile中通过ENTRYPOINT指定的命令成功的执行了。我们下面再来看一下在docker run尾部指定参数的情况。
[root@instance-1kdjwtam ~]# docker run -it node2 World
Hello World
CMD和ENTRYPOIN结合使用:
当CMD和ENTRYPOIN同时使用时就相当于给ENTRYPOIN指令指定的命令设置了一个默认参数,当我们没有在docker run末尾传递参数时,那么就是按照CMD指定的参数执行。这种情况我们应该使用数组格式指定CMD和ENTRYPOIN指令。
ENTRYPOINT ["/usr/sbin/nginx"]
CMD ["-h"]
未指定参数:
[root@instance-1kdjwtam ~]# docker run -it node3
nginx version: nginx/1.14.0 (Ubuntu)
Usage: nginx [-?hvVtTq] [-s signal] [-c filename] [-p prefix] [-g directives]
Options:
-?,-h : this help
-v : show version and exit
-V : show version and configure options then exit
-t : test configuration and exit
-T : test configuration, dump it and exit
-q : suppress non-error messages during configuration testing
-s signal : send signal to a master process: stop, quit, reopen, reload
-p prefix : set prefix path (default: /usr/share/nginx/)
-c filename : set configuration file (default: /etc/nginx/nginx.conf)
-g directives : set global directives out of configuration file
指定参数:(-t:测试配置并退出)
[root@instance-1kdjwtam ~]# docker run -it node3 -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
注意:如果同时指定了CMD和ENTRYPOIN那么CMD指令指定的内容就只能是ENTRYPOIN指令指定的命令的参数。
ENTRYPOINT ["/usr/sbin/nginx"]
CMD ["echo", "Hello, World"]
启动容器时报错
[root@instance-1kdjwtam ~]# docker run -it node4
nginx: invalid option: "echo"
WORKDIR
WORKDIR指令用来在从镜像创建容器时,在容器内部设置工作目录,CMD、ENTRYPOINT、RUN指定的命令会在WORDDIR指定的目录下运行。如果该目录不存在,WORKDIR会自动创建目录。
COPY
COPY 指令用来将构建目录下的文件和目录复制到新的一层的镜像中。COPY指令有如下两种格式,一种是命令行格式一种是数组格式。
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"] (包含空格的路径需要使用该格式)
注:该--chowm功能仅在用于构建Linux容器的Dockerfile上受支持,并且不适用于Windows容器。
src 可以是多个,每个src都可以包含通配符,其通配符规则要满足 Go 的filepath.Match规则,例如:
COPY hom* /mydir/
COPY hom?.txt /mydir/
使用 COPY 指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等。
在使用该指令的时候还可以加上 --chown=<user>:<group>
选项来改变文件的所属用户及所属组。
COPY --chown=55:mygroup files* /mydir/
COPY --chown=bin files* /mydir/
COPY --chown=1 files* /mydir/
COPY --chown=10:11 files* /mydir/
ADD
ADD同COPY指令一样也是用来将构建目录下的文件和目录到新的一层的镜像中。ADD的src 可以是一个 URL
,这种情况下,Docker 引擎会试图去下载这个链接的文件放到 dest 去。下载后的文件权限自动设置为600。我们可以通过RUN指令去修改这个权限。如果src是本地的压缩文件,则将其解压缩为目录。如果是一个URL将不会自动解压。需要通过RUN指令解压。
ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"] (包含空格的路径需要使用该格式)
EXPOSE
EXPOSE指令声明Docker容器在运行时侦听指定的端口。可以指定端口是侦听TCP还是UDP,如果未指定协议,则默认为TCP。
EXPOSE指令实际上不开启端口。它的作用是在构建映像的人和运行容器的人之间起到一种文档的作用,关于哪些端口要进行映射需要在运行容器时通过docker run-p 来指定映射一个或多个端口,-P参数会自动随机映射EXPOSE指定的端口。
EXPOSE 80
EXPOSE 80/udp
写在最后:
Dockerfile指令不区分大小写。但是,命名约定为全部大写。