关于Dockerfile
在Docker中创建镜像最常用的方式,就是使用Dockerfile。Dockerfile是一个Docker镜像的描述文件,我们可以理解成火箭发射的A、B、C、D…的步骤。Dockerfile其内部包含了一条条的指令,每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。
一个Dockerfile的示例如下所示:
由上可知,Dockerfile结构大致分为四个部分:
(1)基础镜像信息
(2)维护者信息
(3)镜像操作指令
(4)容器启动时执行指令。
Dockerfile每行支持一条指令,每条指令可带多个参数,支持使用以#号开头的注释。下面会对上面使用到的一些常用指令做一些介绍。
DockerFile构建过程解析
基础知识
- 每条保留字指令都必须为大写字母后面要跟随至少一个参数
- 指令从上到下顺序执行
- #表示注释
- 每条指令都会创建一个新的镜像层,并对镜像进行提交
大致流程
- docker从基础镜像运行一个容器
- 执行一条指令并对容器进行修改
- 执行类似于docker commit的操作提交一个新的镜像
- docker再基于别提交的新的镜像运行一个新的容器
- 执行dockerfile的下一个指令再从执行第2点直到没有指令
构建镜像对的命令
docker build -t nginx:v1 .
docker build -t 构建镜像 nginx:v1 构建镜像的名称和标签 . 代表当前目录(不完全正确,这里指的上下文)
※举两个例子:
首先我们要理解 docker build 的工作原理。Docker 在运行时分为 Docker 引擎(也就是服务端守护进程)和客户端工具。Docker 的引擎提供了一组 REST API,被称为 Docker Remote API,而如 docker 命令这样的客户端工具,则是通过这组 API 与 Docker 引擎交互,从而完成各种功能。因此,虽然表面上我们好像是在本机执行各种 docker 功能,但实际上,一切都是使用的远程调用形式在服务端(Docker 引擎)完成。也因为这种 C/S 设计,让我们操作远程服务器的 Docker 引擎变得轻而易举。
当我们进行镜像构建的时候,并非所有定制都会通过 RUN 指令完成,经常会需要将一些本地文件复制进镜像,比如通过 COPY 指令、ADD 指令等。而 docker build 命令构建镜像,其实并非在本地构建,而是在服务端,也就是 Docker 引擎中构建的。那么在这种客户端/服务端的架构中,如何才能让服务端获得本地文件呢?
这就引入了上下文的概念。当构建的时候,用户会指定构建镜像上下文的路径,docker build 命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。这样 Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件。
Dockerfile常用指令
FROM:指明构建的新镜像是来自于哪个基础镜像,例如:
MAINTAINER:指明镜像维护着及其联系方式(一般是邮箱地址),例如:
RUN:构建镜像时运行的Shell命令,例如:
又如,我们在使用微软官方ASP.NET Core Runtime镜像时往往会加上以下RUN命令,弥补无法在默认镜像下使用Drawing相关接口的缺憾:
CMD:启动容器时执行的Shell命令,例如:
CMD类似于RUN的命令,用于运行程序,但二者的运行时间不同:
- CMD 在docker run 时运行。
- RUN 是在 docker build。
EXPOSE:声明容器运行的服务端口,例如:
EXPOSE指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务.
在Dockerfile中写入这样的声明有两个好处:
- 帮助镜像使用者理解这个镜像服务的端口,以方便配置映射;
- 另个用处是在运行时使用随机端口映射时,也就是 docker run -P,会自动随机映射
启动tomcat:docker run -p 8080:8080 tomcat
-P:左边宿主机端口,右边容器端口
-p:宿主机段端口随机分配一个端口,让外部进行访问
ENV:设置环境内环境变量,例如:
ADD:拷贝文件或目录到镜像中,例如:
ADD 命令和COPY命令差不多,比COPY高级
- ADD 的优点:在执行 <源文件> 为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,会自动复制并解压到 <目标路径>。
- ADD 的缺点:在不解压的前提下,无法复制 tar 压缩文件。会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。具体是否使用,可以根据是否需要自动解压来决定。
- PS:如果是URL或压缩包,会自动下载或自动解压。
COPY:拷贝文件或目录到镜像中,用法同ADD,只是不支持自动下载和解压,例如:
COPY 复制文件:
COPY <源路径>... <目标路径>
COPY ["<源路径1>",... "<目标路径>"] 目标路径也可以成为镜像的目录
COPY 指令将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置。
例如:COPY package.json /usr/src/app/
ENTRYPOINT:
启动容器时执行的Shell命令,同CMD类似,只是由ENTRYPOINT启动的程序不会被docker run命令行指定的参数所覆盖,而且,这些命令行参数会被当作参数传递给ENTRYPOINT指定指定的程序,例如:
PS:Dockerfile文件中也可以存在多个ENTRYPOINT指令,但仅有最后一个会生效。
VOLUME:指定容器挂载点到宿主机自动生成的目录或其他容器,例如:
PS:一般不会在Dockerfile中用到,更常见的还是在docker run的时候指定-v数据卷。
作用:Docker容器的数据持久化.
定义:数据卷是一个可以提供一个或多个容器使用的特殊目录,它绕过UFS
特性:
- 数据卷可以在容器之间共享和重用
- 对数据卷的修改会立即生效
- 对数据卷的更新,不会影响镜像
- 数据卷会默认一直存在,即使容器被删除
- 命令:-v数据卷启动
USER:为RUN、CMD和ENTRYPOINT执行Shell命令指定运行用户,例如:
WORKDIR:为RUN、CMD、ENTRYPOINT以及COPY和AND设置工作目录,例如:
HEALTHCHECK:告诉Docker如何测试容器以检查它是否仍在工作,即健康检查,例如:
其中,一些选项的说明:
- --interval=DURATION (default: 30s):每隔多长时间探测一次,默认30秒
- -- timeout= DURATION (default: 30s):服务响应超时时长,默认30秒
- --start-period= DURATION (default: 0s):服务启动多久后开始探测,默认0秒
- --retries=N (default: 3):认为检测失败几次为宕机,默认3次
一些返回值的说明:
- 0:容器成功是健康的,随时可以使用
- 1:不健康的容器无法正常工作
- 2:保留不使用此退出代码
ARG
在构建镜像时,指定一些参数,例如:
这时,我们在docker build时可以带上自定义参数user了,如下所示:
综合Dockerfile案例
下面是一个Java Web应用的镜像Dockerfile,综合使用到了上述介绍中最常用的几个命令:
有了Dockerfile,就可以创建镜像了:
最后,可以通过以下命令创建容器: