Dockerfile是一个包含用于组合映像的命令的文本文档。可以使用在命令行中调用任何命令。 Docker通过读取Dockerfile中的指令自动生成映像。
成都创新互联公司于2013年创立,是专业互联网技术服务公司,拥有项目做网站、网站设计网站策划,项目实施与项目整合能力。我们以让每一个梦想脱颖而出为使命,1280元复兴做网站,已为上家服务,为复兴各地企业和个人服务,联系电话:18982081108
Dockerfile由一行行命令语句组成,并且支持以#开头的注释行。
一般Dockerfile分为四部分:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令。如下:
# This dockerfile uses the ubuntu image
# VERSION 2 - EDITION 1
# Author: Ray
# Command format: Instruction [arguments / command] ..
# Maintainer: docker_user (@docker_user)
MAINTAINER Ray 916551518@qq.com
# Commands to update the image
RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe" >> /etc/apt/sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf
# Commands when creating a new container
CMD /usr/sbin/nginx
其中,一开始必须指明所基于的镜像,接下来推荐说明维护者信息,再接下来就是镜像操作的指令,如RUN、COPY等。每运行一条指令,就会为镜像添加新的一层并提交,注:一个镜像最多不允许超过127层。最后时CMD指令,是指定运行容器时的操作指令。
格式为:FROM
第一条指令必须为FROM指令,如果在同一个dockerfile中创建多个镜像时,可以使用多个FROM指令(每个镜像一次,但是一般不会这么做)。
格式为:MAINTAINER
格式为:RUN
前者将在 shell 终端中运行命令,即 /bin/sh -c;后者则使用 exec 执行。指定使用其它终端可以通过第二种方式实现,例如 RUN ["/bin/bash", "-c", "echo hello"]。
每条 RUN 指令将在当前镜像基础上执行指定命令,并提交为新的镜像。当命令较长时可以使用 \ 来换行。(注:如果觉得镜像的层数可能过多,可以一个RUN指令后面接多条指令,中间使用&&进行拼接即可)。
格式为:格式为 COPY
作用:复制本地的
当使用本地目录为源目录时,推荐使用COPY。
使用COPY时,所指定的源文件/目录,也可以是其他镜像中的文件,格式如下:
COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf
格式为:ADD
该命令将复制指定的
在使用该指令的时候还可以加上 --chown=
ADD --chown=55:mygroup files* /mydir/
ADD --chown=bin files* /mydir/
ADD --chown=1 files* /mydir/
ADD 指令会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。
但在某些情况下,如果我们真的是希望复制个压缩文件进去,而不解压缩,这时就不可以使用 ADD 命令了。
因此在 COPY 和 ADD 指令中选择的时候,可以遵循这样的原则,所有的文件复制均使用 COPY 指令,仅在需要自动解压缩的场合使用 ADD。
格式为 ENV
举个栗子:
[root@master nginx]# cat Dockerfile
# test
FROM nginx:latest
MAINTAINER Ray <916551517@qq.com>
ENV var1 hello world
ENV var2 test
RUN echo ${var1},${var2} > /test.txt
#最终此镜像运行的容器中test.txt文件内容如下:
root@262f47a7682a:/# cat test.txt
hello world,test
#并且定义的变量存在该容器的环境变量中:
root@262f47a7682a:/# echo $var1
hello world
root@262f47a7682a:/# echo $var2
test
格式:ARG <参数名>[=<默认值>]
构建参数和 ENV 的效果一样,都是设置环境变量。所不同的是,ARG 所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的。但是不要因此就使用 ARG 保存密码之类的信息,因为 docker history 还是可以看到所有值的。
Dockerfile 中的 ARG 指令是定义参数名称,以及定义其默认值。该默认值可以在构建命令 docker build 中用 --build-arg <参数名>=<值> 来覆盖。
在 1.13 之前的版本,要求 --build-arg 中的参数名,必须在 Dockerfile 中用 ARG 定义过了,换句话说,就是 --build-arg 指定的参数,必须在 Dockerfile 中使用了。如果对应参数没有被使用,则会报错退出构建。从 1.13 开始,这种严格的限制被放开,不再报错退出,而是显示警告信息,并继续构建。这对于使用 CI 系统,用同样的构建流程构建不同的 Dockerfile 的时候比较有帮助,避免构建命令必须根据每个 Dockerfile 的内容修改。
格式为:EXPOSE
该指令的作用是告诉docker服务端容器暴露的端口号,供互联系统使用,在启动容器时需要通过-P,docker主机会自动分配一个端口转发到指定的端口。
它支持以下三种格式:
- CMD ["executable","param1","param2"] 使用 exec 执行,推荐方式;
- CMD command param1 param2 在 /bin/sh 中执行,提供给需要交互的应用;
- CMD ["param1","param2"] 提供给 ENTRYPOINT 的默认参数;
作用是指定启动容器时执行的命令,每个dockerfile只有一条CMD命令,如果指定了多条,那么前面的会被覆盖,只有最后一条指令生效。
如果用户启动容器时指定了运行的命令,则会覆盖掉CMD指定的命令。
如下:
[root@master nginx]# cat Dockerfile #dockerfile内容如下
# test
FROM nginx:latest
CMD echo hello world
CMD echo hello
[root@master nginx]# docker run -t ljz:v2 #只有最后一条CMD指令生效
hello
[root@master nginx]# docker run -t ljz:v2 echo 123456
#启动容器时又指定了其他指令,则会覆盖掉dockerfile中的所有指令
123456
一般我将CMD和ENTRYPOINT结合使用。也就是上面的第三种格式。
它支持下面两种格式:
- ENTRYPOINT ["executable", "param1", "param2"];
- ENTRYPOINT command param1 param2(shell中执行)。
配置容器启动后执行的命令,并且不可被docker run提供的参数覆盖。
每个dockerfile中只能有一个ENTRYPOINT ,当指定多个时,只有最后一个起效。
使用举例:
[root@master nginx]# cat Dockerfile #dokerfile文件如下
# test
FROM nginx:latest
ENTRYPOINT echo hello world
ENTRYPOINT echo hello
[root@master nginx]# docker run -t ljz:v3 #运行此镜像
hello
[root@master nginx]# docker run -t ljz:v3 echo 123456 #运行时指定的命令也不会被执行
hello
#但是可以通过“--entrypoint”指令将镜像中的ENTRYPOINT指令覆盖,只能是命令字
[root@master nginx]# docker run --entrypoint hostname -t ljz:v3
afb421b81a7d
在某种情况下,ENTRYPOINT和CMD组合使用能发挥更大的作用。
组合使用ENTRYPOINT和CMD, ENTRYPOINT指定默认的运行命令, CMD指定默认的运行参数。
举个栗子:
[root@master nginx]# cat Dockerfile #Dockerfile文件如下
# test
FROM centos:7
ENTRYPOINT ["/bin/ping","-c","3"]
CMD ["localhost"]
[root@master nginx]# docker run -t ljz:v4 #运行容器
PING localhost (127.0.0.1) 56(84) bytes of data.
64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.028 ms
64 bytes from localhost (127.0.0.1): icmp_seq=2 ttl=64 time=0.072 ms
64 bytes from localhost (127.0.0.1): icmp_seq=3 ttl=64 time=0.074 ms
查看容器最后一条执行的命令如下:
上面执行的命令是ENTRYPOINT和CMD指令拼接而成. ENTRYPOINT和CMD同时存在时, docker把CMD的命令拼接到ENTRYPOINT命令之后, 拼接后的命令才是最终执行的命令. 但是由于上文说docker run命令行执行时, 可以覆盖CMD指令的值. 如果你希望这个docker镜像启动后不是ping localhost, 而是ping其他服务器,, 可以这样执行docker run:
下表列出了如果把Shell表示法和Exec表示法混合, 最终得到的命令行, 可以看到如果有Shell表示法存在, 很难得到正确的效果:
Dockerfile Command
ENTRYPOINT /bin/ping -c 3
CMD localhost
#拼接后的指令如下:
/bin/sh -c '/bin/ping -c 3' /bin/sh -c localhost
ENTRYPOINT ["/bin/ping","-c","3"]
CMD localhost
#拼接后的指令如下:
/bin/ping -c 3 /bin/sh -c localhost
ENTRYPOINT /bin/ping -c 3
CMD ["localhost"]"
#拼接后的指令如下:
/bin/sh -c '/bin/ping -c 3' localhost
ENTRYPOINT ["/bin/ping","-c","3"]
CMD ["localhost"]
#拼接后的指令如下:
/bin/ping -c 3 localhost
从上面看出, 只有ENTRYPOINT和CMD都用Exec表示法, 才能得到预期的效果。
容器运行时应该尽量保持容器存储层不发生写操作,对于数据库类需要保存动态数据的应用,其数据库文件应该保存于卷(volume)中,为了防止运行时用户忘记将动态文件所保存目录挂载为卷,在 Dockerfile 中,可以事先指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也可以正常运行,不会向容器存储层写入大量数据。
指令格式为:VOLUME ["/data"]。
作用:/data 目录就会在运行时自动挂载为匿名卷,任何向 /data 中写入的信息都不会记录进容器存储层,从而保证了容器存储层的无状态化。当然,运行时可以覆盖这个挂载设置。比如:
docker run -d -v mydata:/data xxxx
在这行命令中,就使用了 mydata 这个命名卷挂载到了 /data 这个位置,替代了 Dockerfile 中定义的匿名卷的挂载配置。
这种方式是docker manager volumes数据持久化方式,不支持Bind mount挂载方式(也就是不支持指定本地的目录)。
在基于镜像运行容器后,可以通过命令“docker inspect container_name”查看容器的详细信息,在返回的结果中,查看MOUNT字段可以看到容器内对应的本地目录位置,如下:
[root@master volumes]# docker inspect web02
返回的结果如下:
命令格式为:USER <用户名>[:<用户组>]。
指定运行容器时的用户名或 UID,后续的 RUN 也会使用指定用户。
USER 指令和 WORKDIR 相似,都是改变环境状态并影响以后的层。WORKDIR 是改变工作目录,USER 则是改变之后层的执行 RUN, CMD 以及 ENTRYPOINT 这类命令的身份。
当然,和 WORKDIR 一样,USER 只是帮助你切换到指定用户而已,这个用户必须是事先建立好的,否则无法切换。
RUN groupadd -r redis && useradd -r -g redis redis
USER redis
RUN [ "redis-server" ]
如果以 root 执行的脚本,在执行期间希望改变身份,比如希望以某个已经建立好的用户来运行某个服务进程,不要使用 su 或者 sudo,这些都需要比较麻烦的配置,而且在 TTY 缺失的环境下经常出错。建议使用 gosu。
# 建立 redis 用户,并使用 gosu 换另一个用户执行命令
RUN groupadd -r redis && useradd -r -g redis redis
# 下载 gosu
RUN wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/1.7/gosu-amd64" \
&& chmod +x /usr/local/bin/gosu \
&& gosu nobody true
# 设置 CMD,并以另外的用户执行
CMD [ "exec", "gosu", "redis", "redis-server" ]
格式为:WORKDIR /path/to/workdir。
为后续的 RUN、CMD、ENTRYPOINT 指令配置工作目录。
可以使用多个 WORKDIR 指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。例如
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
则最终路径为 /a/b/c。
格式为:ONBUILD [INSTRUCTION]。
配置当所创建的镜像作为其它新创建镜像的基础镜像时,所执行的操作指令。
例如,Dockerfile 使用如下的内容创建了镜像 image-A。
[...]
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
[...]
如果基于 image-A 创建新的镜像时,新的Dockerfile中使用 FROM image-A指定基础镜像时,会自动执行ONBUILD 指令内容,等价于在后面添加了两条指令。
FROM image-A
#Automatically run the following
ADD . /app/src
RUN /usr/local/bin/python-build --dir /app/src
使用 ONBUILD 指令的镜像,推荐在标签中注明,例如 ruby:1.9-onbuild。