本期目录
- 1. Dockerfile介绍
- 1.1 Dockerfile的重要性
- 1.2 构建三步骤
- 2. Dockerfile构建过程
- 2.1 Dockerfile内容基础
- 2.2 Dockerfile执行流程
- 2.3 总结
- 3. Dockerfile常用保留字指令
- 3.1 Dockerfile常用保留字一览
- 3.2 保留字讲解
- 4. 案例演示-自定义CentOS镜像
- 4.1 前期准备
- 4.2 编写Dockerfile
- 4.3 构建镜像
- 4.4 运行容器
- 专栏精选文章
1. Dockerfile介绍
Dockerfile 是用来构建 Docker 镜像的文本文件,是由一条条构建镜像所需的指令和参数构成的脚本。(可以理解为 Linux 的 Shell 脚本)
Dockerfile 官网文档:https://docs.docker.com/engine/reference/builder/
1.1 Dockerfile的重要性
前面我的这篇博文【docker commit】中讲解过使用 docker commit
来构建一个带 Vim 命令的 Ubuntu 镜像。在镜像不复杂时可以胜任,但是在实际开发中,微服务代码随时变化和改动,如果我们想把开发的微服务及其所有复杂的中间件以及环境等打包成镜像,用 docker commit
来一条一条地构建镜像就力不从心了。因此我们需要 Dockerfile 来一次搞定所有镜像的构建。
例如,Dockerfile 可以告诉 Docker 服务引擎,本镜像需要一次性添加 Vim 、ifconfig 、Tomcat 8.0 、JDK 1.8 … 只要按照 DockerFile 上的清单来添加,就可以做到构建镜像时一次成型。
1.2 构建三步骤
- 编写 Dockerfile 文件
docker build
命令构建镜像docker run
命令按照镜像运行容器实例
2. Dockerfile构建过程
2.1 Dockerfile内容基础
- 每条保留字指令都必须为大写字母,且后面要跟随至少一个参数;
- 指令按照从上到下的顺序依次执行;
#
表示注释;- 每条指令都会创建一个新的镜像层,并对镜像进行
commit
提交;
2.2 Dockerfile执行流程
- Docker 从基础镜像运行一个容器;
- 执行一条指令,并对容器作出修改;
- 执行类似
docker commit
的操作,提交一个新的镜像层; - Docker 再基于刚提交的镜像运行一个新容器;
- 执行 Dockerfile 中的下一条指令,直到所有指令执行完毕。
多个镜像层构成一个总的最终镜像提交版本供你使用。
2.3 总结
从应用软件的角度看,Dockerfile 、Docker镜像 、Docker 容器分别代表软件的三个不同阶段:
- Dockerfile 是软件的原材料;
- Docker 镜像是软件的交付品;
- Docker 容器则可以认为是软件的运行态,也即按照镜像运行的容器实例。
Dockerfile 面向开发,Docker 镜像成为交付标准,Docker容器则涉及部署与运维,三者缺一不可,合力充当 Docker 体系的基石。
3. Dockerfile常用保留字指令
这一节是本篇博文的核心内容,学会 Dockerfile 常用保留字,你就能看懂 Dockerfile 甚至亲自撰写 Dockerfile 。
开始之前,大家可以打开 Tomcat 官方的 Dockerfile 看一眼,有一个感性认识:[Tomcat Dockerfile] 。
3.1 Dockerfile常用保留字一览
保留字 | 简介 |
---|---|
FROM | 基础镜像 |
MAINTAINER | 镜像维护者的姓名和邮箱地址 |
RUN | 容器构建时需要执行的命令 |
EXPOSE | 当前容器对外暴露的端口 |
WORKDIR | 指定创建容器后,终端默认登录进来的工作目录,一个落脚点 |
USER | 指定该镜像以什么样的用户去执行,默认是root用户 |
ENV | 用来在构建镜像过程中设置环境变量 |
ADD | 将宿主机目录下的文件拷贝进镜像,且会自动处理URL和解压tar压缩包 |
COPY | 类似 ADD ,拷贝文件和目录到镜像中 |
VOLUME | 容器数据卷,用于数据保存和持久化工作 |
CMD | 指定容器启动后要干的事 |
ENTRYPOINT | 也是值得容器启动时要运行的命令 |
3.2 保留字讲解
【FROM】
基础镜像,当前镜像是基于哪个镜像的,指定一个已经存在的镜像作为模板,Dockerfile 的第一行必须是 FROM
。说人话就是你继承哪个镜像父类。
【RUN】
容器构建时需要执行的命令。即,当运行 docker build
时,就会读取 Dockerfile 中的 RUN
保留字的指令并执行。它有两种格式:Shell 格式和 Exec 格式。我个人更倾向于用 Shell 格式。
-
Shell 格式:
# <命令行命令>等同于在终端操作的Shell命令 RUN <命令行命令>
举例:
RUN yum -y install vim
意思就是,在
FROM
指定的基础镜像创建的容器里,执行yum -y install vim
这条 Linux Shell 命令,从而在基础镜像的容器中安装 Vim 。 -
Exec 格式:
RUN ["可执行文件", "参数1", "参数2"]
举例:
RUN ["./test.php", "dev", "offline"]
等价于
RUN ./test.php dev offline
【WORKDIR】
举例演示 WORKDIR 指的是哪里。例如,我创建一个 Tomcat 容器,以前台交互式启动。
$ docker run -it -p 8080:8080 billygoo/tomcat8-jdk8:latest /bin/bash
我们看到,一进入 Tomcat 容器实例,我们所在的工作目录就是 /usr/local/tomcat
。这就是 Dockerfile 保留字 WORKDIR
指定的。
【ENV】
举例:
ENV MY_PATH /usr/mytest
这个环境变量可以在后续的任何 RUN
命令中使用,这就如同在命令前面指定了环境变量前缀一样。也可以在其他指令中直接使用这些环境变量,例如:
WORKDIR $MY_PATH
【ADD】
将宿主机目录下的文件拷贝进镜像,且会自动处理URL和解压tar压缩包。粗浅地说,ADD
就是 docker cp
+ 解压命令的合体。我一般更常用 ADD
而不用 COPY
。
例如,我要在我构建的镜像中安装 JDK 8 ,那么就可以直接把 JDK 1.8 .tar.gz 这个压缩包直接从宿主机上拷贝进镜像中,并自动解压。
【COPY】
相当于 Docker 命令 docker cp
。粗浅地说,COPY
就是不带自动解压功能的 ADD
。将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 。
【CMD】
指定容器启动后要干的事。CMD
指令的格式和 RUN
相似,也是 Shell 格式和 Exec 格式两种格式:
-
Shell 格式:
# <命令行命令>等同于在终端操作的Shell命令 RUN <命令行命令>
-
Exec 格式:
RUN ["可执行文件", "参数1", "参数2"]
-
参数列表格式:
CMD ["参数1", "参数2", ...]
在指定了
ENTRYPOINT
指令后,用CMD
指定具体的参数。
注意:
- Dockerfile 中可以有多个
CMD
指令,但只有最后一个生效,且CMD
会被docker run
之后的参数替换。
举例:Tomcat 官网的 Dockerfile 中最后一行的命令:
我们分别演示带/不带参数的情况来运行 Tomcat 容器的情况。
首先是不带任何参数的情况:
$ docker run -it -p 8080:8080 billygoo/tomcat8-jdk8:latest
在 Windows 电脑上浏览器打开 http://192.168.168.101:8080
:
是能正常访问 Tomcat 首页的。
接下来,我们演示 docker run
后面带有 /bin/bash
参数来启动 Tomcat 容器,这个参数就会替换 Tomcat 官方 Dockerfile 中最后一行的 CMD
命令,从而无法访问 Tomcat 首页。
$ docker run -it -p 8080:8080 billygoo/tomcat8-jdk8:latest /bin/bash
因为在 docker run
最后加了 /bin/bash
参数,Tomcat 的 Dockerfile 最后一行 CMD
命令就相当于被替换成:
CMD ["/bin/bash", "run"]
此时再次在 Windows 电脑上浏览器打开 http://192.168.168.101:8080
就无法访问 Tomcat 首页,因为 docker run
最后加了 /bin/bash
参数替换了 Tomcat 的 Dockerfile 最后一行 CMD
命令。
【ENTRYPOINT】
类似于 CMD
指令,区别是 ENTRYPOINT
不会被 docker run
后面的命令覆盖,且这些命令行参数会被当做参数传递给 ENTRYPOINT
指令指定的程序。
命令格式:
ENTRYPOINT ["可执行文件", "参数1", "参数2", ...]
ENTRYPOINT
可以与 CMD
一起用,一般是变参才会使用 CMD
,这里的 CMD
等于是给 ENTRYPOINT
传参。
当指定了 ENTRYPOINT
后,CMD
的含义就发生了变化,不再是直接运行其命令而是将 CMD
的内容作为参数传递给 ENTRYPOINT
指令,他两个组合会变成 ENTRYPOINT "<CMD>"
。
举例:假设已通过 Dockerfile 构建了 nginx:test 镜像:
FROM nginx
ENTRYPOINT ["nginx", "-c"] # 固定参数
CMD ["/etc/nginx/nginx.conf"] # 变参
是否传参 | 按照Dockerfile编写执行 | 传参运行 |
---|---|---|
Docker命令 | docker run nginx:test | docker run nginx:test -c /etc/nginx/new.conf |
生成的实际命令 | nginx -c /etc/nginx/nginx.conf | nginx -c /etc/nginx/new.conf |
4. 案例演示-自定义CentOS镜像
在上面讲解中,如果仍然感到迷惑看不懂,这是非常正常的。下面我将通过一个 Dockerfile 的案例实际演示,来帮助大家更好地理解 Dockerfile 中常用保留字的使用。
4.1 前期准备
本次案例中,我们自定义一个带 JDK8 、Vim 、ifconfig 的 CentOS 7 镜像 mycentosjava8
。
从远程仓库中拉取 CentOS 7 的镜像。这个原始的 CentOS 镜像里是没有 JDK8 、Vim 、ifconfig 的。注意不要拉取 CentOS 的最新镜像。
$ docker pull centos:7
到 Oracle 官网下载 JDK 8 。注意要下载 Linux 版的 .tar.gz
格式的压缩包。
把这个压缩包上传至 Linux 宿主机。
4.2 编写Dockerfile
在 Linux 宿主机上创建一个文件夹存放 Dockerfile 。
$ mkdir -p /xsh/myfiles
$ cd /xsh/myfiles
进入这个目录并创建 Dockerfile 。
$ vim Dockerfile
Dockerfile 内容如下:
# 基础镜像,刚刚拉取的centos7镜像
FROM centos:7
# 指定作者和邮箱
MAINTAINER xsh<xiesihang@stu.ouc.edu.cn>
# 指定环境变量
ENV MYPATH /usr/local
# 指定登录镜像容器后的工作目录,引用上面定义的环境变量MYPATH
WORKDIR $MYPATH
# 安装Vim编辑器
RUN yum -y install vim
# 安装ifconfig查看网络IP
RUN yum -y install net-tools
# 安装JDK8及其lib库
RUN yum -y install glibc.i686
RUN mkdir /usr/local/java
# ADD是相对路径,把jdk-8u351-linux-x64.tar.gz添加到容器中,安装包的相对路径取决于相对于你的Dockerfile的位置,为了方便与Dockerfile放在同一目录
ADD jdk-8u351-linux-x64.tar.gz /usr/local/java/
# 配置Java环境变量
ENV JAVA_HOME /usr/local/java/jdk1.8.0_351
ENV JRE_HOME $JAVA_HOME/jre
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib:$CLASSPATH
ENV PATH $JAVA_HOME/bin:$PATH
# 对外暴露80端口
EXPOSE 80
# 打印环境变量MYPATH
CMD echo $MYPATH
# 走到这一条命令就打印出构建成功
CMD echo "success-------------ok"
# CentOS需要伪终端交互
CMD /bin/bash
按 :wq!
保存退出。我们就完成了 Dockerfile 的编写。
4.3 构建镜像
构建镜像的命令格式如下。
$ docker build -t 新镜像名字:TAG .
【注意】
- 上面
TAG
后面要加一个空格和一个点。- 要在 Dockerfile 所在目录下执行该命令。
举例:
本次案例的构建命令这样写:
$ docker build -t centosjava8:1.0 .
构建过程大概需要 10 分钟左右,如果看到上图,就说明构建成功了。如果失败了,则有可能是 yum 需要更新索引,这部分操作自行搜索即可。
查看自定义构建的镜像 centosjava8
。
$ docker images
可以看见,由于加入了 JDK8 、Vim 、ifconfig ,自定义构建的新 CentOS 镜像从原始的 204 MB 膨胀到 1.24 GB 。
4.4 运行容器
接下来我们创建这个新镜像的容器,来验证是否安装了 JDK8 、Vim 、ifconfig 。
$ docker run -it centosjava8:1.0 /bin/bash
登录进容器后,发现当前所在的工作目录就是 Dockerfile 中 WORKDIR
指定的目录 ENV MYPATH /usr/local
。
测试 Vim 编辑器是否可用。
$ vim a.txt
测试 ifconfig 命令是否可用。
$ ifconfig
测试 JDK8 是否可用。
$ java -version
三个全部都可以成功使用。那么到这里,自定义的 CentOS 镜像就创建成功了。
专栏精选文章
- 《Docker是什么?Docker从介绍到Linux安装图文详细教程》
- 《30条Docker常用命令图文举例总结》
- 《Docker如何构建自己的镜像?从镜像构建到推送远程镜像仓库图文教程》
- 《Docker多个容器和宿主机之间如何进行数据同步和数据共享?容器数据卷从介绍到常用操作图文教程》