五、Dockerfile
5.1 简介
DockerFile是用来构建Docker镜像的构建文件,是由一系列构建镜像所需的指令和参数构成的脚本 Docker的编写规则:
- 每条保留字指令都必须为大写字母且后面要跟随至少一个参数
- 指令顺序执行,遵循从上到下原则
- #表示注释
- 每条指令都会创建一个新的镜像层,并对镜像进行提交
5.2 DockerFile执行流程
docker从基础镜像运行一个容器 顺序执行一条指令对容器做出修改 执行类似docker commit的操作提交一个新的镜像层 docker基于刚才提交的镜像运行一个新的容器 执行DockerFile中下一条指令直到文件中的所有指令都执行完成
- Dockerfile其实就是把单独的docker操作命令融入到一个Dockerfile脚本文件中,自动化的实现复杂的容器创建、操作与运行的过程。
5.3 Dockerfile指令
注意事项:
- CMD 指令指定的程序可被 docker run 命令行参数中指定要运行的程序所覆盖。如果 Dockerfile 中如果存在多个 CMD 指令,仅最后一个生效。
- 如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。
- ENV设置的环境变量,在后续的指令中,也可以使用。而ARG设置的环境变量仅 Dockerfile 内有效。
- 用 WORKDIR 指定的工作目录,会在构建镜像的每一层中都存在。以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR 会帮你建立目录。
5.4 实例
5.4.1 单文件复合指令
输入如下:
上面一共输入了5行指令,前两行
FROM postgres:latest
LABEL maintainer="LL"
第一行这里我拉取了postgres的镜像,没有指定仓库地址就是从docker hub上拉取,指定的标签是latest;第二行输入了我自己的信息;
RUN apt-get update & apt-get install vim -y
第三行执行了更新以及安装一个vim,这里写到了一行是因为每执行一次RUN就会在docker上新建一层镜像,所以分开来写很多个RUN的结果就是会导致整个镜像无意义的过大膨胀。
ENV POSTGRES_PASSWORD:password
EXPOSR 5100
最后这两行分别是指定了postgres的密码以及对外暴露出了5100端口;
至此一个Dockerfile就写完了;
使用build命令构建:
- 我是在虚拟机上直接下载的最新版docker,版本是24.05,Docker在19.03 版本之后就会默认启用docker buildx ,因此我用了docker buildx;
构建完看一下自己的镜像有没有成功:
可以看到列表中多了一个postgres1的镜像;那么也就是构建成功了;使用run指令运行: 之后通过ps指令可以看到已经运行成功;
5.4.2 单文件单指令
前面把 apt-get update & apt-get install vim -y 写在了同一行,原因是为了减少docker创建的镜像层数,但是这样写是否真的有效?测试一下 修改dockerfile代码如下:
- 里面第三行的 RUN echo 是修改时区与镜像用的源,因为我是在虚拟机上测试的,所以下载很慢,这样来加快下载加快镜像构建
之后构建为postgres2并查看大小:
可以看到postgres2大小为493MB而postgres1是414,说明少构建镜像层可以有效减少镜像的占用内存。
当然也不是说减少构建的层数在任何时候都一定是最优解,比如某一次执行的过程中某一层出错,对其进行修正后再次Build,此时其实出错之前已有的镜像层都是可复用的,如果这些镜像层是每层使用run指令构建的话,那么就可以在修改错误之后继续构建,可以大大减少后续的构建时间,当然多构建的还是会多占用内存的。所以具体想要时间换空间还是空间换时间需要根据具体使用场景决定。
5.4.3 多文件构建
将所有的构建过程(包括项目的依赖、编译、测试、打包过程)全部包含在一个Dockerfile中时,这样写出来的Dockerfile文件会特别长,当需要的东西越来越多的时候可维护性指数级将会下降;并且随着镜像层次过多,镜像的体积会逐步增大,部署也会变得越来越慢。 比如以Golang为例,它运行时不依赖任何环境,只需要有一个编译环境,那这个编译环境在实际运行时是没有任务作用的,编译完成后,那些源码和编译器已经没有任务用处了也就没必要留在镜像里。 那么就可以一些基础的环境拆分出来,比如拆分为多个Dockerfile,然后通过脚本将它们进行组合。 也可以通过 docker compose 来实现这个目的,这个还在学文档后续补充。