一、什么是Dockerfile
镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,这个脚本就是 Dockerfile。Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。
二、一个简单的实践
需求:我们来定制这样的一个镜像:将tomcat的首页修改成我们定制的,运行这个容器,访问8080端口,可以打开这个页面而不是我们熟悉的tom猫。实际上这个操作和部署一个web项目是相似的。
1. 在一个空白目录中,建立一个文本文件,并命名为 Dockerfile:
比如我们在/usr/local下创建一个docker目录,在docker下创建myTomcat目录,在myTomcat下创建Dockerfile文件
cd /usr/local
mkdir docker
cd docker
mkdir myTomcat
vi Dockerfile
2. 编写Dockerfile文件
#FROM 指定基础镜像
FROM tomcat
#WORKDIR 切换工作目录
WORKDIR /usr/local/tomcat/webapps/ROOT/
#RUN 执行命令
RUN rm -fr *
RUN echo "Hello Dockerfile" > /usr/local/tomcat/webapps/ROOT/index.html
3. 构建镜像
在 Dockerfile 文件所在目录执行:
docker build [选项] <上下文路径/URL/->
#例如(指定了最终镜像的名称为test,.表示当前路径)
docker build -t test .
这样,四条语句便会被依次执行
4. 运行容器
这个时候可以看到当前已经有了名为test的镜像
运行容器,查看效果:
docker run -p 80:8080 test
这样的话,在任何一个装有docker的机器上拉去到这个镜像,都能实现一样的效果,从而不会再出现 「这段代码在我机器上没问题啊」 这类问题
[关于这里的端口设置:]
-
-p 80:8080是指将宿主机的80端口映射到docker上tomcat使用的8080端口,这样当用户访问80端口时,实际上是访问docker容器的8080端口。 - 这样的话,就可以也就可以使用多个tomcat,映射到宿主机的不同端口。可以使用
docker run -d -p 8080:8080 test,docker run -d -p 8081:8080 test…后台运行多个web容器,各容器之间互不影响,只要保证宿主机的端口互不相同即可 - -P:可以将容器内部使用的网络端口映射到我们使用的主机上随机分配的一个暴露的可用端口
三、Dockerfile常用命令
1. FROM 指定基础镜像
- 所谓定制镜像,那一定是以一个镜像为基础,在其上进行定制。而 FROM 就是指定基础镜像,因此一个 Dockerfile 中 FROM 是必备的指令,并且必须是第一条指令。
- 除了选择现有镜像为基础镜像外,Docker 还存在一个特殊的镜像,名为 scratch。这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像。如果你以 scratch 为基础镜像的话,意味着你不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在。
2. RUN 执行命令
- shell 格式:
RUN <命令>,就像直接在命令行中输入的命令一样。刚才写的 Dockerfile 中的 RUN 指令就是这种格式。 - exec 格式:
RUN ["可执行文件", "参数1", "参数2"],这更像是函数调用中的格式。
3. COPY 复制文件
格式:COPY <源路径>... <目标路径>
- COPY 指令将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置。
- <源路径> 是相对于镜像构建上下文的相对路径
- <目标路径> 可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用 WORKDIR 指令来指定)。目标路径不需要事先创建,如果目录不存在会在复制文件前先行创建缺失目录。
4. ADD 更高级的复制文件
ADD 指令和 COPY 的格式和性质基本一致。但是在 COPY 基础上增加了一些功能。
- 比如 <源路径> 可以是一个 URL,这种情况下,Docker 引擎会试图去下载这个链接的文件放到 <目标路径> 去。
- 如果 <源路径> 为一个 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,ADD 指令将会自动解压缩这个压缩文件到 <目标路径> 去。
5. WORKDIR 指定工作目录
格式:WORKDIR <工作目录路径>
- 使用 WORKDIR 指令可以来指定工作目录(或者称为当前目录),以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR 会帮你建立目录。
- 在 Shell 中,连续两行是同一个进程执行环境,因此前一个命令修改的内存状态,会直接影响后一个命令;而在 Dockerfile 中,这两行 RUN 命令的执行环境根本不同,是两个完全不同的容器。
所以对于
RUN cd /app
RUN echo "hello" > world.txt
无法实现想象中的功能,应该使用WORKDIR 指令改变各层的工作目录的位置
6. CMD 容器启动命令
CMD 指令的格式和 RUN 相似,也是两种格式:
- shell 格式:
CMD <命令> - exec 格式:
CMD ["可执行文件", "参数1", "参数2"...] - 参数列表格式:
CMD ["参数1", "参数2"...]。在指定了 ENTRYPOINT 指令后,用 CMD 指定具体的参数。
Docker 不是虚拟机,容器就是进程。既然是进程,那么在启动容器的时候,需要指定所运行的程序及参数。CMD 指令就是用于指定默认的容器主进程的启动命令的。
7. EXPOSE 暴露端口
格式:EXPOSE <端口1> [<端口2>...]
EXPOSE 指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。在 Dockerfile 中写入这样的声明有两个好处,一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;另一个用处则是在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。