Dockerfile指令详解
FROM指定基础镜像
- 定制镜像一定是以一个镜像为基础的,FROM就是指定基础镜像的指令,它是必备的第一条指令。
- 基础镜像可以是
- 服务类镜像:
nginx、redis、mongo、mysql、httpd、tomcat
等 - 也可以是语言运行环境镜像:
node、openjdk、python、ruby、golang
等 - 还可以是更基础操作系统镜像:
ubuntu、debian、centos、fedora、alpine
等 - 还可以是scratch,表示空白镜像
- 服务类镜像:
RUN执行命令
-
RUN是用来执行命令行命令的,其有两种格式:
shell格式:
RUN <命令>
,如RUN apt-get install -y gcc
exec格式:
RUN ["可执行文件或命令", "参数1", "参数2", ...]
,如RUN ["apt-get", "install", "-y", "gcc"]
-
每写一个RUN,镜像就会多一层,
建议写成
RUN 命令1&&命令2&&命令3
而不是
RUN 命令1 RUN 命令2 RUN 命令3
COPY复制文件
-
COPY
有两种格式:shell格式:
RUN [--chown=<user>:<group>] <源路径>... <目标路径>
exec格式:
COPY [--chown=<user>:<group>] ["<源路径1>",... "<目标路径>"]
其中
--chown=<user>:<group>
是可选参数,会改变复制到镜像内的拥有者和所属组-
源文件可以是通配符表达式,规则要满足于
Go
的filepath.Match
COPY hom* /mydir/ COPY hom?.txt /mydir/
目标路径可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用
WORKDIR
指令来指定)。目标路径不需要事先创建,如果目标目录不存在会在复制文件前先行创建
ADD更高级的复制文件
-
ADD和COPY的格式和性质基本一致,但是在COPY基础上增加了一些功能,既如果源路径为一个tar压缩文件,压缩格式为
gzip
,bzip2
以及xz
时,ADD指令会将其解压到目标路径,如FROM scratch ADD ubuntu-xenial-core-clouding-amd64-root.tar.gz /
CMD容器启动命令
- 类似于
RUN
指令,但二者运行的时间点不同-
CMD
在docker run
时运行(docker start
时不运行也会运行,即容器启动就会运行) - RUN是在docker build时运行
-
-
CMD
为docker run
启动的容器指定默认要运行的程序,程序运行结束,容器也就结束了 -
CMD
指令指定的程序可被docker run
命令行参数中指定要运行的程序所覆盖 - 如果
Dockerfile
中存在多个CMD
指令,仅最后一个生效 -
CMD
有三种格式,分别是:- shell格式:
CMD <shell命令>
- exec格式:
CMD ["可执行文件或命令", "参数1", "参数2", ...]
- 参数列表格式:
CMD ["参数1", "参数2", ...] #该写法是为ENTRYPOINT指令指定的程序提供默认参数
- shell格式:
ENTRYPOINT
- 类似于
CMD
指令,但不会被docker run
的命令行参数指定的指令所覆盖,而且这些命令行参数会被当做参数送给ENTRYPOINT
指令指定的程序。只有使用了--entrypoint
才可以覆盖ENTRYPOINT
指令指定的程序 - 当
Dockerfile
中有多个ENTRYPOINT
时,仅最后一个生效 - 可以搭配
CMD
使用,此时CMD
相当于是在给ENTRYPOINT
传参,当在docker run
中指定参数时,docker run
中的参数会取代CMD
的参数传递给ENTRYPOINT
指定的命令
ENV设置环境变量
用于设置环境变量,定义了环境变量,在后续的指令中就可以使用这个环境变量
-
格式
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>
-
使用示例
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"
ARG构建参数
- 构建参数,与
ENV
作用一致,但作用域不相同。ARG
设置的环境变量仅在docker build
的过程中有效,构建好的镜像内不存在此变量 -
ARG
指定的参数值在docker build
时可以用--build-arg <参数名>=<值>
来覆盖 - 格式:
ARG <参数名>[=<默认值>]
VOLUMN定义匿名卷
- 格式为:
VOLUMN ["<路径1>", "路径2"...]
VOLUMN <路径>
- 为了防止容器内的重要数据因为容器重启而丢失,且避免容器不断变大,应该将容器内的某些目录挂载到宿主机上。
- 在
Dockerfile
中,我们可以事先指定某些目录挂载为匿名卷,这样在docker run
时若用户不用-v
指定挂载,那么就会将Dockerfile
中用VOLUMN
指定的目录挂载为匿名卷,否则按-v
的指定进行挂载
EXPOSE声明端口
格式:
EXPOSE <端口1> [<端口2> ...]
-
EXPOSE指定了该镜像生成容器时提供服务的端口,可以配合
docker run -P(大写)
使用,也可以配合docker run --net=host
使用-
举例
FROM nginx EXPOSE 80
docker build -t nginx:test .
以上指令指定了该镜像生成容器时,容器内提供服务的端口为80
当执行命令
docker run --name nginxtest -d -P nginx:test
时,会随机将宿主机上某个端口映射到容器的80端口当执行命令
docker run --name nginxtest -d --net=host nginx:test
时,会将宿主机上的80端口映射到容器的80端口(可以暂时这么理解)
-
要将EXPOSE和
-p <宿主机端口>:<容器端口>
区分开来,前者只是声明了容器将用什么端口提供服务,后者则是手动指定了宿主机和容器端口的映射
WORKDIR指定工作目录
格式:
WORKDIR <工作目录路径(必须是提前创建好的)>
-
比较
# 示例1 RUN cd /app RUN echo "hello" > hello.txt
# 示例2 WORKDIR /app RUN echo "hello" > hello.txt
示例1中因为两个
RUN
是不同层,所以hello.txt
文件并不会出现在/app
目录下示例2中用
WORKDIR
指定了工作路径,之后所有指令中的当前目录都是/app
USER指定当前用户
格式:
USER <用户名>[:<用户组>]
-
USER
指令和WORKDIR
相似,都是改变环境并影响以后的层。WORKDIR
是改变工作目录,USER是改变之后各层中命令(由RUN
、CMD
、ENTRYPOINT
指令指定)的执行用户-
举例
RUN groupadd -r redis && useradd -r -g redis redis USER redis RUN [ "redis-server" ]
由于
USER redis
指令,所以redis-server
这个命令由redis
用户执行的
-
USER
指令指定的用户必须是事先建立好的
HEALTHCHECK健康检查
-
格式:
-
HEALTHCHECK [选项] CMD <命令>
:设置检查容器健康状况的命令 -
HEALTHCHECK NONE
:如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令
-
HEALTHCHECK
指令是告诉Docker应该如何判断容器的状态是否正常,这是Docker1.12
引入的新指令在没有
HEALTHCHECK
指令之前,Docker引擎只可以通过容器内主进程是否退出来判断容器状态是否异常。很多情况这没问题,但是当程序进入死锁或死循环状态,应用进程不退出,但已经无法提供服务了。在Docker1.12
之前,Docker对此并无办法。在
Docker1.12
之后,Docker提供了HEALTHCHECK
指令,通过该指令指定一行命令,用这行命令来判断容器主进程是否服务正常,可以更真实的反应容器实际状态当在
Dockerfile
中指定了HEALTHCHECK
指令后,用其镜像启动的容器初始状态会为starting,在HEALTHCHECK
指令检查成功后变为healthy
,如果连续失败一定次数,则会变为unhealthy
-
HEALTHCHECK
支持下列指令-
--interval=<间隔>
:两次健康检查的间隔,默认为30秒 -
--timeout=<时长>
:健康检查命令执行后,等待响应的超时时间,如果超过这个时间,本次健康检查视为失败,默认为30秒 -
--retries=<次数>
:当连续失败指定的次数后,则将容器状态视为unhealthy
,默认3次
-
和
CMD
、ENTRYPOINT
一样,HEALTHCHECK
只可以出现一次,如果写了多个,只有最后一个生效在
HEALTHCHECK [选项] CMD
后面的命令,格式和ENTRYPOINT
一样,分为shell
和exec
格式。命令的返回值决定了该次健康检查的成功与否:0-成功;1-失败;2-保留,不要使用2这个值。-
举例
-
假设我们有个镜像是个最简单的Web服务,我们希望增加健康检查来判断其Web服务是否在正常工作,我们可以用
curl
来帮助判断,其Dockerfile
的HEALTHCHECK
可以这么写:FROM nginx RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/list* HEALTHCHECK --interval=5s --timeout=3s\ CMD curl -fs http://localhost/ || exit 1
这里设置了每5秒检查一次,如果健康检查命令超过3秒没响应就视为失败,并且使用
curl -fs http://localhost/ || exit 1
作为健康检查命令使用
docker build
来构建这个镜像$ docker build -t myweb:v1 .
构建好之后,启动容器:
$ docker run -d --name web -p 80:80 myweb:v1`
当运行该镜像后,可以通过
docker container ls
看到最初的状态为(health: starting)
:$ docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 03e28eb00bd0 myweb:v1 "nginx -g 'daemon off" 3 seconds ago Up 2 seconds (health: starting) 80/tcp, 443/tcp web
再等待几秒钟后,再次
docker container ls
,就会看到健康状态变化为(healthy)
$ docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 03e28eb00bd0 myweb:v1 "nginx -g 'daemon off" 18 seconds ago Up 16 seconds (healthy) 80/tcp, 443/tcp web
如果健康检查连续失败超过了重试次数,状态就会变为
(unhealthy)
为了帮助排障,健康检查命令的输出(包括
stdout
和stderr
)都会被存储在健康状态里,可以用docker inspect
查看$ docker inspect --format '{{json .State.Health}}' web | python -m json.tool { "FailingStreak": 0, "Log": [ { "End": "2016-11-25T14:35:37.940957051Z", "ExitCode": 0, "Output": "<!DOCTYPE html>\n<html>\n<head>\n<title>Welcome to nginx!</title>\n<style>\n body {\n width: 35em;\n margin: 0 auto;\n font-family: Tahoma, Verdana, Arial, sans-serif;\n }\n</style>\n</head>\n<body>\n<h1>Welcome to nginx!</h1>\n<p>If you see this page, the nginx web server is successfully installed and\nworking. Further configuration is required.</p>\n\n<p>For online documentation and support please refer to\n<a href=\"http://nginx.org/\">nginx.org</a>.<br/>\nCommercial support is available at\n<a href=\"http://nginx.com/\">nginx.com</a>.</p>\n\n<p><em>Thank you for using nginx.</em></p>\n</body>\n</html>\n", "Start": "2016-11-25T14:35:37.780192565Z" } ], "Status": "healthy" }
-
ONBUILD为他人做嫁衣
- 格式:
ONBUILD <其他指令>
-
ONBUILD
是一个特殊的命令,它后面跟的是其他指令,比如RUN
,COPY
等,而这些指令,在当前镜像构建时并不会被执行。只有当以当前镜像为基础,去构建新镜像时才被执行