使用dockerfile构建镜像<一>
今天我们来学习一下利用dockerfile 来构建镜像。
我们知道 docker comit
命令也可以构建一个新的镜像,但是这个操作大部分情况下是不可控制的,这个操作对镜像的操作都是黑箱操作,除了制作镜像的人知道执行过什么命令,怎么生成的镜像,其他的人不知道。 而且: docker commit 这个操作,是在原有镜像的基础上,每执行一次命令,就会在原有镜像的基础上,再叠加上容器的存储层,并构成新的镜像。
这样如果使用docker commit 制作镜像,以及后期修改的话,每一次修改都会让镜像更加臃肿一次。
所以我们要使用dockerfile 来构建镜像, 按照官方文档的解释,dockerfile是一个文本文件,这个文本文件包含了用户想聚合或者配置到镜像里面所需要的所有指令。
怎么使用?
使用docker build
命令来构建镜像,但是这个命令需要一个Dockerfile文件 和 上下文 (或者直接理解为一个目录的内容吧,po主自己加的)。 构建上下文指的是在本地路径 PATH
或者 URL
范围内的所有文件。 PATH 指的是本地文件系统, URL 特指Git 仓库地址。
当前目录是这样的,存在两个文件:
比如我们现在运行
它做了什么呢,执行构建的是Docker守护进程,而不是docker cli,做的第一件事就是将.(当前整个目录)的文件发给Docker daemon。 大部分情况下,应该将Dockerfile放到一个空的目录下面,最好只放一个Dockerfile文件。(警告: 当然不要使用根目录,如果将dockerfile放到根目录/
,构建的时候,会将整个硬盘的所有内容copy一份到Docker daemon)。还好docker 有.dockerignore文件,类似.gitignore,此处省略多字,相信你能明白。
当然我们也可以指定Dockerfile的路径
|
|
可以构建不同tag的多个镜像,like this(本机测试):
|
|
最终的结果:
我们来看 Dockerfile的格式。
这里的INSTRUCTION指令有多个关键字: FROM
, RUN
, CMD
, LABEL
, EXPOSE
, ENV
, ADD
, COPY
, ENTRYPOINT
…
ENV
我们先说ENV,env 大白话可以理解为环境变量。语法是这样:
FROM
顾名思义就是我们要构建的镜像的来源或者基础,语法是这样:
RUN
RUN 命令是制作镜像里面常用的命令,有两种写法
tips: Dockerfile中的每一个指令都会在镜像上面新增一层中间层镜像,直至最外层镜像,所以执行RUN指令的时候,可以合并多个RUN 指令,最好不要一次性写多个,请看下面的操作
CMD
Docker不是虚拟机,容器就是进程,如果是进程,进程启动的时候,就需要指定启动程序或者参数。 在Dockerfile里面只能有一个CMD
,如果有多个的话,则只有最后一个CMD才会生效。CMD
的主要目的为正在运行的容器提供一些默认的值或者行为。有三种形式(一般推荐exec 格式):
- CMD [“executable”,”param1”,”param2”] (exec form, this is the preferred form)
- CMD [“param1”,”param2”] (as default parameters to ENTRYPOINT)
- CMD command param1 param2 (shell form)
tip: 第一种和第三种方式的命令,在docker run 的时候 后面可以执行其他命令,将这个命令给覆盖掉。比如我们Dockerfile是这样写的
|
|
或者
|
|
我们这样运行: docker run -it --rm ubuntu:v4 cat /etc/os-release
, 后面的 cat /etc/os-release
将默认的CMD命令给替换了,打印的结果长这样:
LABEL
LABEL
指令可以像镜像添加metadata元数据,和ENV
一样是key-value的形式
|
|
LABEL
和 RUN
指令一样,docker官方推荐将多个labels合并到一个单独的LABEL如果可能的话,每一条LABEL指令都会添加一个新的层,最后都会生成多个无效的中间层镜像,若存在多个同名的key,则最后一个会覆盖之前的key。
EXPOSE
这个不是暴露端口,该指令告知Docker这个容器在运行的时候监听的那些端口。这并不会让HOST访问到容器的这些端口,如果你要达到这个目的,要使用-p参数,比如我们启动一个nginx:
|
|
这样就会暴露容器的80端口给host的81端口,可以直接localhost:81访问container的nginx服务。也可以不这样做,比如我们在Dockerfile里面声明了多个端口: 80 90
在启动容器的时候这样写 docker run -d -P
–name container-name image:tag 这样会自动将container的90 80端口暴露给host。我们可以通过docker ps 查看ports这一项。
ENTRYPOINT
它和CMD 一样,都是指定容器的启动程序以及参数,但是和CMD 不同的是替换起来,要加一个参数 --entrypoint
两种形式:
- ENTRYPOINT [“executable”, “param1”, “param2”] (exec form, preferred)
- ENTRYPOINT command param1 param2 (shell form)
通常是和 CMD 一起使用,请查看docker 官方介绍