Dockerfile
是一个用来构建镜像的文本文件,里面包含了一条条构建镜像所需的指令和说明。
官方文档:Dockerfile reference
指令说明
文件中的每一行都是一个指令,每一个指令都是大写字母(不是强制的),指令是是从往下进行执行的,每一个指令都会创建一个新的镜像层,并提交。
FROM 指定基础镜像
FROM
指令用来指定基础镜像,必须的指令,并且是第一条指令
Docker 还存在一个特殊的镜像,名为 scratch
。这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像。
1 | FROM nginx:alpine |
COPY 复制文件
COPY
指令将从构建上下文目录中 <源路径>
的文件/目录复制到新的一层的镜像内的 <目标路径>
位置
1 | COPY my.cnf /etc/mysql/conf.d/my.cnf |
源路径可以是多个,也可以使用通配符来匹配,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等,可以通过 --chown=<user>:<group>
和 --chmod=<perms>
来改变复制到容器之后文件的所属用户和权限
目标路径可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用 WORKDIR
指令来指定)。目标路径不需要事先创建,如果目录不存在会在复制文件前先行创建缺失目录。目标路径不需要事先创建,如果目录不存在会在复制文件前先行创建缺失目录
1 | COPY hom* /mydir/ |
ADD 更高级的复制文件
ADD
指令和 COPY
的格式和性质基本一致。在 COPY
基础上增加了一些功能。
- 源路径可以是一个URL,会尝试下载然后复制到目标位置(权限为0666),修改权限需要再来一层
RUN
指令 - 如果
<源路径>
为一个tar
压缩文件的话,压缩格式为gzip
,bzip2
以及xz
的情况下,ADD
指令将会自动解压缩这个压缩文件到<目标路径>
去
RUN 执行命令
RUN
指令是用来执行命令行命令的(构建镜像时运行的指令)
有两种方式
- shell格式:就像直接在命令行中输入的命令一样
- exec格式:
RUN ["可执行文件", "参数1", "参数2"]
1 | # shell格式 |
每个指令都会生成一层,Run
指令也是同样,所以在使用的时候最好是通过 &&
来连接各个命令,这样可以减少生成的层数。
1 | RUN set -x; buildDeps='gcc libc6-dev make wget' \ |
拿上面的例子来说,在命令的最后添加了清理工作的命令,删除了为了编译构建所需要的软件,清理了所有下载、展开的文件,并且还清理了 apt
缓存文件。这是很重要的一步,这样会减小镜像的体积。
CMD 容器启动命令
CMD
指令就是用于指定默认的容器主进程的启动命令的。RUN
指令是在镜像构建的时候执行的,CMD
指令是容器启动的时候(docker run)执行的。
命令格式
- shell格式:
CMD <命令>
- exec格式:
CMD ["可执行文件", "参数1", "参数2"]
使用 shell 格式的话,实际的命令会被包装为 sh -c
的参数的形式进行执行。
1 | CMD echo $HOME |
需要注意的地方
CMD
指令指定的程序可被docker run
命令行参数中指定要运行的程序所覆盖。- 如果
Dockerfile
中如果存在多个CMD
指令,仅最后一个生效。
1 | FROM ubuntu |
1 | # (假设没有第二个CMD指令的情况)这样写就是用cat /etc/os-release 替换了 /bin/bash |
ENTRYPOINT 入口点
ENTRYPOINT
指令的和 CMD
一样,都是在指定容器启动程序及参数。ENTRYPOINT
在运行时也可以替代,需要通过docker run
的参数--entrypoint
来指定。
当指定了ENTRYPOINT
后,CMD
的含义就发生了改变,不再是直接的运行其命令,而是将CMD
的内容作为参数传给ENTRYPOINT
指令,换句话说实际执行时,将变为:<ENTRYPOINT> "<CMD>"
需要注意的地方
docker run -it ubuntu cat /etc/os-release
执行时候执行的命令,也会被当做ENTRYPOINT
的参数1
2
3
4
5FROM ubuntu:18.04
RUN apt-get update \
&& apt-get install -y curl \
&& rm -rf /var/lib/apt/lists/*
ENTRYPOINT [ "curl", "-s", "http://myip.ipip.net" ]1
2
3
4$ docker build -t myip .
# -i 会作为ENTRYPOINT指令的参数,这样就能输出header头信息了
$ docker run myip -i如果有多个
ENTRYPOINT
指令最后一条生效
ENV 设置环境变量
ENV
指令用来设置环境变量,定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。
格式有两种:
ENV [key] [value]
ENV [key1]=[value1] [key2]=[value2]
1 | ENV VERSION=1.0 DEBUG=on \ |
下列指令可以支持环境变量展开: ADD、COPY、ENV、EXPOSE、FROM、LABEL、USER、WORKDIR、VOLUME、STOPSIGNAL、ONBUILD、RUN。
ARG 构建参数
ARG <参数名>[=<默认值>]
ARG 和 ENV 的效果一样,都是设置环境变量。不过作用域不一样。ARG 设置的环境变量仅对 Dockerfile 内有效,也就是说只有 docker build
的过程中有效,构建好的镜像内不存在此环境变量。
构建命令 docker build
中可以用 --build-arg <参数名> = <值>
来覆盖。
1 | # 只在 FROM 中生效 |
VOLUME 定义匿名卷
定义匿名数据卷。在启动容器时忘记挂载数据卷,会自动挂载到匿名卷。
作用:
- 避免重要的数据,因容器重启而丢失,这是非常致命的。
- 避免容器不断变大。
EXPOSE 暴露端口
格式为 EXPOSE <端口1> [<端口2>...]
EXPOSE
指令是声明容器运行时提供服务的端口,这只是一个声明,在容器运行时并不会因为这个声明应用就会开启这个端口的服务。
作用:
- 帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射
- 运行时使用随机端口映射时,也就是
docker run -P
时,会自动随机映射EXPOSE
的端口
WORKDIR 指定工作目录
格式: WORKDIR <工作目录路径>
使用 WORKDIR
指令可以来指定工作目录(或者称为当前目录),以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR
会帮你建立目录。
1 | WORKDIR /app |
会在/app/word.txt
写入内容
USER 指定当前用户
格式:USER <用户名>[:<用户组>]
USER
指令和 WORKDIR
相似,都是改变环境状态并影响以后的层。WORKDIR
是改变工作目录,USER
则是改变之后层的执行 RUN
, CMD
以及 ENTRYPOINT
这类命令的身份。
注意,USER 只是帮助你切换到指定用户而已,这个用户必须是事先建立好的,否则无法切换。