使用 Dockerfile 定制镜像
- 使用 Dockerfile 定制镜像
- 用 Nginx 做个示例
- FROM 指定基础镜像
- RUN 执行命令
- shell格式
- exec格式
- 构建镜像
- 指令详解
- COPY 复制文件
- ADD 更高级的复制文件
- CMD 容器启动命令
- ENTRYPOINT 入口点
- ENV 设置环境变量
- ARG 构建参数
- VOLUME 定义匿名卷
- EXPOSE 声明端口
- WORKDIR 指定工作目录
- USER 指定当前用户
- HEALTHCHECK 健康检查
- ONBUILD 为他人做嫁衣裳
使用 Dockerfile 定制镜像
- 镜像是多层存储,每一层是在前一层的基础上进行的修改;而容器同样也是多层存储,是在以镜像为基础层,在其基础上加一层作为容器运行时的存储层。
- 镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么镜像无法重复、构建透明性和体积的问题就都会解决。这个脚本就是 Dockerfile。
- Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。
用 Nginx 做个示例
[root@ ~]# mkdir /usr/local/test
[root@ ~]# cd /usr/local/test
[root@ test]# vim Dockerfile
[root@ test]# docker build -t test:1 .
Dockerfile的内容如下:
FROM nginx
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
再启动nginx就会发现,首页不是官方默认的内容,而是显示Hello, Docker!
FROM 指定基础镜像
-
FROM
指令表示指定一个基础镜像,这是每个Dockerfile
文件都必须有的,并且必须是第一条指令。
RUN 执行命令
-
RUN
指令是用来执行命令行命令的,有两种格式:
shell格式
按命令行的写法:
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
exec格式
RUN [“可执行文件”, “参数1”, “参数2”],这更像是函数调用中的格式。
Dockerfile 中每一个指令都会建立一层,因此在编写 Dockerfile 的时候要尽量合并命令。而且如果存在需要编译安装的软件,在命令最后,一定清理为了安装所下载和其余产生的不必要的文件,不然在执行下一个命令后,就无法修改上一层的内容,镜像就可能变得越来越臃肿。
Union FS 是有最大层数限制的,比如 AUFS,曾经是最大不得超过 42 层,现在是不得超过 127 层。
构建镜像
[root@ test]# docker build -t test:1 .
- 这里需要注意命令最后的
.
,这里表示 Dockerfile 所在的文件夹,并将这个目录作为操作目录,docker build
会把这个目录下的所有文件都上传到 Docker 引擎以帮助构建镜像。 - 如果有在构建时不希望上传到 Docker 引擎的文件,可以以
.gitignore
一样的语法写一个.dockerignore
。
指令详解
COPY 复制文件
COPY [--chown=<user>:<group>] <源路径>... <目标路径>
# 源路径可以使用满足 Go 的 filepath.Match 规则的通配符
COPY hom* /mydir/
COPY hom?.txt /mydir/
- <目标路径> 可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用 WORKDIR 指令来指定)。目标路径不需要事先创建,如果目录不存在会在复制文件前先行创建缺失目录。
- 此外,还需要注意一点,使用 COPY 指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等。这个特性对于镜像定制很有用。特别是构建相关文件都在使用 Git 进行管理的时候。
- 在使用该指令的时候还可以加上
--chown=<user>:<group>
选项来改变文件的所属用户及所属组。
ADD 更高级的复制文件
- ADD 指令和 COPY 的格式和性质基本一致。但是在 COPY 基础上增加了一些功能。
- ADD 指令的<源路径> 可以是一个 URL,这种情况下,Docker 引擎会试图去下载这个链接的文件放到 <目标路径> 去。下载后的文件权限自动设置为 600,如果这并不是想要的权限,那么还需要增加额外的一层 RUN 进行权限调整,另外,如果下载的是个压缩包,需要解压缩,也一样还需要额外的一层 RUN 指令进行解压缩。所以不如直接使用 RUN 指令,然后使用 wget 或者 curl 工具下载,处理权限、解压缩、然后清理无用文件更合理。因此,这个功能其实并不实用,而且不推荐使用。
- 在 Docker 官方的 Dockerfile 最佳实践文档 中要求,尽可能的使用 COPY,因为 COPY 的语义很明确,就是复制文件而已,而 ADD 则包含了更复杂的功能,其行为也不一定很清晰。最适合使用 ADD 的场合,就是需要自动解压缩的场合。
CMD 容器启动命令
- CMD指令有两种格式。
- shell 格式:CMD <命令>
- exec 格式:CMD [“可执行文件”, “参数1”, “参数2”…]
- 参数列表格式:CMD [“参数1”, “参数2”…]。在指定了 ENTRYPOINT 指令后,用 CMD 指定具体的参数。
- 在指令格式上,一般推荐使用 exec 格式,这类格式在解析时会被解析为 JSON 数组,因此一定要使用双引号 ",而不要使用单引号。
- Docker 不是虚拟机,容器中的应用都应该以前台执行,而不是像虚拟机、物理机里面那样,用 upstart/systemd 去启动后台服务,容器内没有后台服务的概念,所以无法以后台守护进程形式启动服务。
ENTRYPOINT 入口点
这个指令貌似是在执行docker run
命令的时候,把所带的参数带到容器的构建脚本的CMD
指令后面,没有很懂,后面弄明白了再补充。
ENV 设置环境变量
相当于为后面的命令设置一个常量,有两种格式
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...
可以在以下指令中使用:ADD
、COPY
、ENV
、EXPOSE
、LABEL
、USER
、WORKDIR
、VOLUME
、STOPSIGNAL
、ONBUILD
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" \
&& gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \
&& grep " node-v$NODE_VERSION-linux-x64.tar.xz\$" SHASUMS256.txt | sha256sum -c - \
&& tar -xJf "node-v$NODE_VERSION-linux-x64.tar.xz" -C /usr/local --strip-components=1 \
&& rm "node-v$NODE_VERSION-linux-x64.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt \
&& ln -s /usr/local/bin/node /usr/local/bin/nodejs
ARG 构建参数
格式:ARG <参数名>[=<默认值>]
顾名思义,这个参数是在构建的时候使用的,构建结束后不会保存在容器中,在构建的时候可以用--build-arg <参数名>=<值>
来覆盖。
VOLUME 定义匿名卷
格式:
VOLUME ["<路径1>", "<路径2>"...]
VOLUME <路径>
这个参数和docker run -d -v mydata:/data xxxx
中的-v是同样的效果,把容器中的数据保存到本地,这样在销毁容器的时候就不会丢失重要数据。
EXPOSE 声明端口
格式为 EXPOSE <端口1> [<端口2>...]
这个只是声明,属于一种说明,并不会做实际的端口映射,是为了方便这个脚本的使用者了解这个镜像服务的守护端口。
WORKDIR 指定工作目录
格式为 WORKDIR <工作目录路径>
RUN cd /app
RUN echo "hello" > world.txt
简单的说,上面的两个RUN
指令其实是在不同存储层,所以第二个RUN
指令并不是继承于第一个指令,如果你想实现连续的路径文件操作,就可以定义WORKDIR
。
USER 指定当前用户
格式:USER <用户名>[:<用户组>]
和WORKDIR
的效果差不多,定义以后的操作,就是基于定义的用户做操作。
HEALTHCHECK 健康检查
格式:
-
HEALTHCHECK [选项] CMD <命令>
:设置检查容器健康状况的命令。 -
HEALTHCHECK NONE
:如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令。
HEALTHCHECK 支持下列选项:
-
--interval=<间隔>
:两次健康检查的间隔,默认为 30 秒。 -
--timeout=<时长>
:健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默认 30 秒。 -
--retries=<次数>
:当连续失败指定次数后,则将容器状态视为 unhealthy,默认 3 次。
实例:
FROM nginx
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
HEALTHCHECK --interval=5s --timeout=3s \
CMD curl -fs http://localhost/ || exit 1
可以看到STATUS显示了healthy,可以用docker inspect
来查看容器的健康状态log。
$ 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
ONBUILD 为他人做嫁衣裳
格式:ONBUILD <其它指令>
。
这个指令是自己本身被构建的时候并不会被指令,而是在其它镜像要以自己为基础镜像的时候才会执行。
举个例子:
下面的脚本在执行的时候ONBUILD
的三行指令并不会执行。
FROM node:slim
RUN mkdir /app
WORKDIR /app
ONBUILD COPY ./package.json /app
ONBUILD RUN [ "npm", "install" ]
ONBUILD COPY . /app/
CMD [ "npm", "start" ]
但一个新的镜像要以上一个镜像为基础镜像时,ONBUILD
的三行指令就会触发。
FROM my-node
适合在创建多个类似应用的时候使用。