DockerFile
1、基本说明
Dockerfile 是用来构建 Docker 镜像的文本文件,是由一条条构建镜像所需的指令和参数构成的脚本。
在一个文件夹中,如果有一个名字为 Dockfile 的文件,其内容满足语法要求,在这个文件夹路径下执行如下命令,就可以按照描述构建一个镜像。
1 |
|
dockerfile 示例
1 |
|
FROM node:8.4
:该 image 文件继承官方的 node image,冒号表示标签,这里标签是8.4,即8.4版本的 node。COPY . /app
:将当前目录下的所有文件(除了.dockerignore排除的路径),都拷贝进入 image 文件的/app目录。WORKDIR /app
:指定接下来的工作路径为/app。RUN npm install
:在/app目录下,运行npm install命令安装依赖。注意,安装后所有的依赖,都将打包进入 image 文件。EXPOSE 3000
:将容器 3000 端口暴露出来, 允许外部连接这个端口。CMD node demos/01.js
:它表示容器启动后自动执行node demos/01.js
2、Dockfile 语法
Dockerfile的基本指令有很多个,常用的有:FROM、MAINTAINER、RUN、CMD、EXPOSE、ENV、ADD、COPY、ENTRYPOINT、VOLUME、USER、WORKDIR、ONBUILD。下面对这些指令的用法一一说明。
所有的命令都用的是双引号,不是单引号,如果命令没有空格,不加引号也可以。
2.0、Dockerfile 基础
- 每条保留字指令都必须为大写字母且后面要跟随至少一个参数。
- 指令按照从上到下,顺序执行。
#
表示注释。- 每条指令都会创建一个新的镜像层并对镜像进行提交。
2.1、FROM
用法:
1
2
3
4# tag 或 digest 是可选的,如果不使用这两个值时,会使用 latest 版本的基础镜像
FROM <image>
FROM <image>:<tag>
FROM <image>:<digest>说明
- FROM 拉取镜像的地址。其指定一个构建镜像的基础源镜像,如果本地没有就会从公共库中拉取,没有指定镜像的标签会使用默认的latest标签。
- FROM 必须 是 Dockerfile 中第一条非注释命令。
- 可以出现多次,如果需要在一个 Dockerfile 中构建多个镜像。
2.2、MAINTAINER
用法:
MAINTAINER <name> <email>
描述:描述镜像的创建者,名称和邮箱
2.3、RUN
用法:有两种格式
1
2
3
4# shell 格式
RUN <command>
# exec 格式
RUN ["executable", "param1", "param2"]说明
- RUN 命令是一个常用的命令,执行完成之后会成为一个中间镜像,这里也是指镜像的分层构建。
- 一句RUN就是一层,也相当于一个版本。这就是缓存的原理。我们知道docker是镜像层是只读的,所以你如果第一句安装了软件,用完在后面一句删除是不可能的。所以这种情况要在一句RUN命令中完成,可以通过&符号连接多个RUN语句。
- RUN后面的必须是双引号不能是单引号(没引号貌似也不要紧),command是不会调用shell的,所以也不会继承相应变量,要查看输入RUN “sh” “-c” “echo” “$HOME”,而不是RUN “echo” “$HOME”。
- RUN 指令创建的中间镜像会被缓存,并会在下次构建中使用。如果不想使用这些缓存镜像,可以在构建时指定
--no-cache
参数,如:docker build --no-cache
。
2.4、CMD
用法:CMD 有以下三种格式:
1
2
3CMD ["executable","param1","param2"]
CMD ["param1","param2"]
CMD command param1 param2说明
- CMD 指定在启动容器的时候第一个要运行的命令。
- CMD 在 Dockerfile 可以有多个,只有最后一个会有效。如果执行docker run的时候提供了命令项,就会覆盖掉这个命令。没提供就会使用构建时的命令。
注意:与 RUN 指令的区别:RUN 在 build 的时候执行,并生成一个新的镜像,CMD 在容器 run 的时候执行,在构建时不进行任何操作。
2.5、ENTRYPOINT
用法:
1
2
3ENTRYPOINT ["executable", "param1", "param2"]
ENTRYPOINT command param1 param2
ENTRYPOINT "command" "param1" "param2"说明
ENTRYPOINT 也是用来指定一个容器启动时要运行的命令
ENTRYPOINT 与 CMD 非常类似,不同的是通过docker run执行的命令不会覆盖 ENTRYPOINT,而docker run命令中指定的任何参数,都会被当做参数再次传递给 ENTRYPOINT。
- 如,执行
docker run <image> -d abc
时,-d abc
将被传递给入口点。 - ENTRYPOINT可以和CMD一起用,一般是变参才会使用 CMD ,这里的 CMD 等于是在给 ENTRYPOINT 传参。
- 如,执行
可以通过
docker run --entrypoint
重写 ENTRYPOINT 入口点。如docker run --entrypoint=/bin/bash you_image
Dockerfile 中可以有多个 ENTRYPOINT 命令,仅最后一个生效。
2.6、ENV
用法:格式有两种:
1
2ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...说明:
设置容器的环境变量,无论是后面的其它指令如 RUN,还是运行时的应用,都可以直接使用这里定义的环境变量,容器运行的时候这个变量也会保留。
定义环境变量的同时,可以引用已经定义的环境变量。在ENV指令中,可以直接引用如下环境变量:
- HOME,用户主目录
- HOSTNAME,默认容器的主机名
- PATH,
- TERM,默认xterm
示例
1
2
3
4
5
6ENV PATH /usr/local/bin:$PATH
ENV name1=ping name2=on_ip
# 下面这个例子中演示了如何换行,以及对含有空格的值用双引号括起来的办法,这和 Shell 下的行为是一致的
ENV VERSION=1.0 DEBUG=on \
NAME="Happy Feet"
2.7、ADD
用法:
1
2ADD <源路径>... <目标路径>
ADD ["<源路径>",... "<目标路径>"]说明:
- 更高级的复制文件。
- 复制本机文件或目录或远程文件,添加到指定的容器目录,支持GO的正则模糊匹配。路径是绝对路径,不存在会自动创建。如果源是一个目录,只会复制目录下的内容,目录本身不会复制。
- ADD 指令和 COPY 的格式和性质基本一致。但是在 COPY 基础上增加了一些功能。比如<
- 源路径>可以是一个 URL,这种情况下,Docker 引擎会试图去下载这个链接的文件放到<目标路径>去。
- ADD命令会将复制的压缩文件夹自动解压,这也是与COPY命令最大的不同。
注意
- 如果 docker 发现文件内容被改变,则接下来的指令都不会再使用缓存。关于复制文件时需要处理的/,基本跟正常的 copy 一致
2.8、COPY
用法
1
2COPY <源路径>... <目标路径>
COPY ["<源路径1>",... "<目标路径>"]说明
- COPY除了不能自动解压,也不能复制网络文件。其它功能和ADD相同。
- 此外,还需要注意一点,使用 COPY 指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等。这个特性对于镜像定制很有用。特别是构建相关文件都在使用 Git 进行管理的时候。
2.9、EXPOSE
用法:
EXPOSE <port/协议> [<port/协议>...]
示例:
1
2EXPOSE 8080
EXPOSE 8090/udp说明:
- EXPOSE 暴露的端口更像是 显式地声明 该容器提供的服务需要用到的端口,一定程度上提供了操作的便利,也提高了 Dockerfile 的可读性和可维护性。
- 而且 EXPOSE 并不会直接将端口自动和宿主机某个端口建立映射关系。如果 EXPOSE 暴露的端口确定要和某个宿主机端口建立映射关系,还是要用到
docker run -p -P
参数。 - 如果
docker run
指定了自动映射 -P,那么会将所有 EXPOSE 暴露的端口随机映射到宿主机的高阶端口。 - 如果
docker run
指定了--net=host
宿主机网络模式,容器中 EXPOSE 指令暴露的端口会直接使用宿主机对应的端口,不存在映射关系。
2.10、VOLUME
用法:
VOLUME ["path"]
说明
在主机上创建一个挂载,挂载到容器的指定路径。
docker run -v命令也能完成这个操作,而且更强大。这个命令不能指定主机的需要挂载到容器的文件夹路径。但docker run -v可以,而且其还可以挂载数据容器。
一个卷可以存在于一个或多个容器的指定目录,该目录可以绕过联合文件系统,并具有以下功能:
- 卷可以容器间共享和重用
- 容器并不一定要和其它容器共享卷
- 修改卷后会立即生效
- 对卷的修改不会对镜像产生影响
- 卷会一直存在,直到没有任何容器在使用它
VOLUME 让我们可以将源代码、数据或其它内容添加到镜像中,而又不并提交到镜像中,并使我们可以多个容器间共享这些内容。
2.11、USER
用法:USER daemon
说明
指定运行容器时的用户名或UID,后续的RUN、CMD、ENTRYPOINT也会使用指定的用户运行命令。
使用USER指定用户时,可以使用用户名、UID 或 GID,或是两者的组合。以下都是合法的指定试:
1
2
3
4
5
6USER user
USER user:group
USER uid
USER uid:gid
USER user:gid
USER uid:group镜像构建完成后,通过 docker run 运行容器时,可以通过 -u 参数来覆盖所指定的用户。
2.12、WORKDIR
用法:
WORKDIR /path/to/workdir
说明
- 通过WORKDIR设置工作目录后,Dockerfile 中其后的命令 RUN、CMD、ENTRYPOINT、ADD、COPY 等命令都会在该目录下执行。
- 可以使用多个WORKDIR指令,后续参数如果是相对路径,则会基于之前的命令指定的路径。如:最终的路径就是
1
2WORKDIR /home
WORKDIR test/home/test
。 - path路径也可以是环境变量,比如有环境变量
HOME=/home
,WORKDIR $HOME/test
也就是/home/test
。
2.13、ONBUILD
用法:
ONBUILD [INSTRUCTION]
说明
- 配置当前所创建的镜像作为其它新创建镜像的基础镜像时,所执行的操作指令。意思就是,这个镜像创建后,如果其它镜像以这个镜像为基础,会先执行这个镜像的ONBUILD命令。
- 通常用在当镜像被使用时,可能需要做一些预处理的工作。
2.14、LABEL
语法
1
LABEL <key>=<value> <key>=<value> <key>=<value> ...
说明
- LABEL用于为镜像添加元数据,元数以键值对的形式指定
- 使用LABEL指定元数据时,一条LABEL指定可以指定一或多条元数据,指定多条元数据时不同元数据之间通过空格分隔。推荐将所有的元数据通过一条LABEL指令指定,以免生成过多的中间镜像。
示例:通过LABEL指定一些元数据:
1
LABEL version="1.0" description="这是一个Web服务器" by="IT笔录"
指定后可以通过
docker inspect
查看:1
2
3
4
5
6docker inspect itbilu/test
"Labels": {
"version": "1.0",
"description": "这是一个Web服务器",
"by": "IT笔录"
},
2.15、ARG
语法
ARG <name>[=<default value>]
说明:ARG 用于指定传递给构建运行时的变量:
示例:
通过 ARG 指定两个变量1
2ARG site
ARG build_user=IT笔录以上我们指定了 site 和 build_user 两个变量,其中 build_user 指定了默认值。在使用 docker build 构建镜像时,可以通过
--build-arg <varname>=<value>
参数来指定或重设置这些变量的值。1
docker build --build-arg site=itiblu.com -t itbilu/test .
这样我们构建了 itbilu/test 镜像,其中 site 会被设置为 itbilu.com,由于没有指定 build_user,其值将是默认值 IT 笔录。
2.16、STOPSIGNAL
语法
STOPSIGNAL signal
STOPSIGNAL用于设置停止容器所要发送的系统调用信号
所使用的信号必须是内核系统调用表中的合法的值,如:SIGKILL。
2.17、SHELL
语法
SHELL ["executable", "parameters"]
说明:
- SHELL用于设置执行命令(shell式)所使用的的默认 shell 类型
- SHELL在Windows环境下比较有用,Windows 下通常会有 cmd 和 powershell 两种 shell,可能还会有 sh。这时就可以通过 SHELL 来指定所使用的 shell 类型:
1
2
3
4
5
6
7# Executed as powershell -command Write-Host hello
SHELL ["powershell", "-command"]
RUN Write-Host hello
# Executed as cmd /S /C echo hello
SHELL ["cmd", "/S"", "/C"]
RUN echo hello
3、Dockerfile 例子
构建Nginx运行环境
1 |
|
4、原则与建议
- 容器轻量化。从镜像中产生的容器应该尽量轻量化,能在足够短的时间内停止、销毁、重新生成并替换原来的容器。
- 使用
.gitignore
。在大部分情况下,Dockerfile 会和构建所需的文件放在同一个目录中,为了提高构建的性能,应该使用.gitignore
来过滤掉不需要的文件和目录。 - 为了减少镜像的大小,减少依赖,仅安装需要的软件包。
- 一个容器只做一件事。解耦复杂的应用,分成多个容器,而不是所有东西都放在一个容器内运行。如一个 Python Web 应用,可能需要 Server、DB、Cache、MQ、Log 等几个容器。一个更加极端的说法:One process per container。
- 减少镜像的图层。不要多个 Label、ENV 等标签。
- 对续行的参数按照字母表排序,特别是使用
apt-get install -y
安装包的时候。 - 使用构建缓存。如果不想使用缓存,可以在构建的时候使用参数
--no-cache=true
来强制重新生成中间镜像。
5、Docker 执行 Dockerfile 的大致流程
- Docker 从基础镜像运行一个容器。
- 执行一条指令并对容器作出修改。
- 执行类似
docker commit
的操作提交一个新的镜像层。 - Docker 再基于刚提交的镜像运行一个新容器。
- 执行 dockerfile 中的下一条指令直到所有指令都执行完成。