Docker之 Dockerfile 使用教程
0x01 引言
实际环境中,项目所需要的容器可能没有很符合的公共镜像,就需要在公共镜像的基础上重新构建镜像,除了可以从容器中用 docker commit
命令创建,还可以使用 Dockerfile
文件来直接定制镜像
0x02 简介
Dockerfile 是用来构建 Docker 镜像的构建文件,它是由一系列命令和参数构成的脚本,Docker
通过读取 Dockerfile
中的指令自动生成映像
Dockerfile、Docker 镜像、Docker 容器的区别
Dockerfile | Docker 镜像 | Docker 容器 |
---|---|---|
软件的原材料 | 软件的交付品 | 软件的运行态 |
面向开发 | 交付标准 | 部署与运维 |
定义进程需要的一切东西 | 根据 Dockerfile 定义构建镜像 | 容器是直接提供服务的 |
构建镜像
使用 docker build
命令会根据 Dockerfile
文件及上下文构建新 Docker
镜像。构建上下文是指 Dockerfile
所在的本地路径或Git仓库地址。构建上下文环境会被递归处理,所以构建所指定的路径还包括了子目录,而 URL
还包括了其中指定的子模块
保留字
保留字及其代表含义
保留字 | 注释 |
---|---|
[FROM 镜像:版本标签] | 当前新镜像是基于哪个镜像 |
[LABEL key=value] | 也叫 MAINTAINER 脚本说明,打标签 |
[RUN 命令] | 容器构建时需要运行的命令 |
[EXPOSE 端口] | 当前镜像对外暴露的端口,在 docker run -p 的时候生效 |
[EVN key] [value] | 设置容器的环境变量,能够被文件调用 |
[WORKDIR 目录] | 指定工作目录 |
ADD “源文件” “目标路径” | 将宿主机目录下的文件拷贝进镜像,如果文件是 tar 压缩文件会自动解压,如果文件是 url 则不会解压 |
COPY “源文件” “目标路径” | 和 ADD 类似,只是少了解压功能 |
[VOLUME 目录] | 在主机上创建一个挂载,挂载到容器的指定路径,用于数据保存和持久化工作 |
[CMD 命令] | 容器启动时要运行的命令,存在有多个 CMD 指令时只有最后一个生效,会被 docker run 之后的参数替换 |
ENTRYPOINT | 容器启动时运行的命令,一定会执行 |
ONBUILD | 当构建一个被继承的 Dockerfile 时运行命令,父镜像在被子继承后父镜像的 ONBUILD 被触发 |
注意:编写时能不加引号就不要加
FROM 指定基础镜像
定制的镜像基本基于公共镜像的基础上,后续的操作就是在这个镜像的基础上进行修改
Dockerfile 的第一条指令必须为 FROM
指令,可以使用多个 FROM
指令
格式
1 | FROM <image> |
第一行必须指定基础容器,这里的是 tomcat8
1 | FROM tomcat:8 |
MAINTAINER 指定维护者信息
格式
1 | MAINTAINER <name> |
注意:MAINTAINER
指令已经被抛弃,建议使用 LABEL 指令
1 | MAINTAINER hello <admin@2ba.cc> |
LABEL 为镜像添加元数据
为镜像添加标签,一个 LABEL
就是一个键值对,也可以一行指定多个键值对
1 | LABEL <key>=<value> <key>=<value> <key>=<value> ... |
可以用一些标签来申明镜像的作者、版本等
1 | # 多行指定信息 |
如果新添加的 LABEL 和已有的 LABEL 同名,则新值会覆盖掉旧值
RUN
每条 RUN 指令将在当前镜像的基础上执行指定命令,并提交为新的镜像
多个操作建议使用 &&
连接,这样能够减少镜像分层,每个 RUN 一层 \
可以换行
一种直接接Shell命令
1 | RUN <command> |
一种接可执行文件,可包含参数传递
1 | RUN ["executable", "参数1", "参数2"] |
Dockerfile 的指令每执行一次都会在 Docker 上新建一层,所以过多的命令就会有过多的无意义的层,容易造成镜像膨胀过大,在写法上可以进行优化,如下这样以 &&
符号链接命令,这样执行后,只会创建一层镜像
1 | FROM centos |
以上执行会创建 3 层镜像
可简化为以下格式
1 | FROM centos |
CMD 容器启动命令
指定启动容器时执行的命令,类似于 RUN 指令,但二者运行的时间点不同
CMD
在docker run
容器启动后运行RUN
在docker build
构建镜像时执行
每个 Dockerfile 只能有一条 CMD 命令,多个 CMD
也只会执行最后一个
如果 docker run
的时候后面有命令,如 /bin/sh
,则 CMD
不会执行
1 | CMD <命令> # shell 格式 |
示例
1 | CMD echo "This is a test." | wc - |
COPY 复制文件
复制指令,从上下文目录中复制文件或目录 到容器里指定路径
1 | COPY [--chown=<user>:<group>] <源路径> <目标路径> |
[--chown=]
:改变容器内文件的拥有者和属组<源路径>
:源文件或者源目录,可以是多个,支持 通配符<目标路径>
:指定容器内路径,不存在的话会自动创建
ADD 更高级的复制文件
使用格式跟 COPY 一致(同样需求下,官方推荐使用 COPY)
该命令将复制指定的 <src>
到容器中的 <dest>
1 | ADD <src>... <dest> |
其中 <src>
可以是 Dockerfile 所在目录的一个相对路径的文件或目录;也可以是一个 URL,还可以是一个 tar文件,tar 类型文件会自动解压(网络压缩资源不会被解压),可以访问网络资源,类似 wget
示例
1 | ADD hom* /mydir/ # 添加所有以"hom"开头的文件 |
ENV 设置环境变量
定义了环境变量,那么在后续的 RUN 指令中,就可以使用这个环境变量
格式
1 | ENV <key> <value> |
这个指令很简单,就是设置环境变量而已,无论是后面的其它指令,如 RUN 还是运行时的应用,都可以直接使用这里定义的环境变量
1 | ENV JAVA_HOME /usr/java/jdk1.8.0_202-amd64 |
ENTRYPOINT
配置容器启动后执行的命令,一定会执行,并且不可被 docker run
提供的参数覆盖
每个 Dockerfile 中只能有一个 ENTRYPOINT
,当指定多个 ENTRYPOINT
时,只有最后一个生效
1 | ENTRYPOINT ["executable", "参数1", "参数2"] |
VOLUME 定义匿名卷
用于指定持久化目录
一个卷可以存在于一个或多个容器的指定目录,该目录可以绕过联合文件系统,并具有以下功能
- 卷可以容器间共享和重用
- 容器并不一定要和其它容器共享卷
- 修改卷后会立即生效
- 对卷的修改不会对镜像产生影响
- 卷会一直存在,直到没有任何容器在使用它
格式
1 | VOLUME ["<路径1>", "<路径2>"...] |
示例
1 | VOLUME "/data" |
WORKDIR 指定工作目录
指定的工作目录,会在构建镜像的每一层中都存在,WORKDIR
指定的工作目录,必须是提前创建好的
1 | WORKDIR <工作目录路径> |
示例
1 | WORKDIR /a # 这时工作目录为 /a |
EXPOSE 声明端口
指定于外界交互的端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务
在启动容器时需要通过 -P
参数让 Docker 主机分配一个端口转发到指定的端口。使用 -p
参数则可以具体指定主机上哪个端口映射过来
格式
1 | EXPOSE <端口1> [<端口2>...] |
示例
1 | EXPOSE 80 443 |
注:EXPOSE并不会让容器的端口访问到主机。要使其可访问,需要在 docker run
运行容器时通过-p
来发布这些端口,或通过 -P
参数来发布 EXPOSE 导出的所有端口
USER 指定当前用户
指定运行容器时的用户名或 UID,后续的 RUN 也会使用指定用户。当服务不需要管理员权限时,可以通过该命令指定运行用户。用户和用户组必须提前已经存在
格式
1 | USER <用户名>[:<用户组>] |
示例
1 | USER daemon |
0x03 例子
Dockerfile 由一行行命令语句组成,并且支持已 #
开头的注释行
一般而言,Dockerfile 的内容分为四个部分
- 基础镜像信息
- 维护者信息
- 镜像操作指令
- 容器启动时执行指令
Dockerfile 完整 demo
1 | cat<<'EOF'>dockerfile |
构建镜像
1 | docker build -f ./tomcat_dockerfile -t mytomcat:7 . |
选项 | 注释 |
---|---|
-f |
构建文件路径 |
-t |
镜像名称:版本号 |
. |
当前路径 |
启动镜像
1 | docker run -it --name ok -p 9080:8080 mytomcat:7 |