Dockerfile是构建docker镜像的脚本文件
Dockerfile有很多的指令构成,指令由上到下依次运行。
每一条指令就是一层镜像,层越多,体积就越大,启动速度也越慢
井号开头的行是注释行。指令写大写写小写都行,但一般都写为大写。每一行中间都可以有若干空行
在有的github项目中会给你Dockerfile,以便你能更方便的配环境
可以使用docker build将Dockerfile构建为镜像,命令为 docker build -t [镜像名称] .
- 关于docker build的详细使用方法在 4.docker镜像及相关命令 有提到
目录
1 引用 FROM
FROM是引用基础镜像,基础镜像就是官方或者别的做好的,我们一般站在巨人的肩膀上添加新的功能
在hello world的例子中,hello world的镜像就是基于scratch镜像的再创作
- 默认情况下FROM会先从本地拉取,如果本地没有就会到源拉取
scratch是空的镜像,相当于面向对象编程中的基类。scratch只在Dockerfile中继承,不能通过pull拉取,不能run,没有tag
镜像不必须带FROM,不带FROM的镜像叫做基础镜像。比如scratch镜像的第一句就不是FROM scratch
我们自己写Dockerfile的时候基本都要带FROM,在别人的镜像上就行修改
FROM引用的是旧层,不产生新层
2 复制 ADD
ADD不太好用,还是用COPY要更好用一点
从Dockerfile文件所在的机器 复制文件到 镜像中。在hello world的例子中是将 hello 这个可执行文件(这里用的是相对路径[相对路径指的是相对Dockerfile文件的路径],也可以使用绝对路径),复制到镜像的 / 位置(根路径)
使用ADD指令,如果将可执行文件hello替换成一个压缩文件,压缩文件复制到容器后会自动解压
使用ADD指令,如果将可执行文件hello替换成一个url,url会自动下载到容器的指定目录中(相当于wget)
如果将可执行文件hello替换成一个文件夹(文件夹最后必须要加上斜杠),那么就会将文件夹中所有内容复制到容器的指定位置
3 复制 COPY
COPY的功能与ADD相似,同样是从Dockerfile文件所在的机器 复制文件到 镜像中。
与ADD的区别为
- 使用COPY从复制压缩包后不会自动解压
- 使用COPY不能复制url
4 定义维护者信息 MAINTAINER
- 官方不建议使用MAINTAINER指令,但一些老的Dockerfile中会有MAINTAINER,能看懂就行
实际就是写创作者的名字,我简单做个例子
然后我们build一下
build之后可以看到MAINTAINER写的内容
5 定义元数据 LABEL
LABEL写什么东西都可以,我们简单做个例子
查看元数据的时候发现Author并没有被覆盖
而是把LABEL的内容都写在Labels中了
每个镜像层都由 镜像文件系统 和 镜像json文件 两部分构成。LABEL命令虽然没有改变镜像文件系统,但是改变了镜像的json文件,所以LABEL也会产生新的一层
6 定义工作目录 WORKDIR
我们创建一个ubuntu的容器,然后开启容器,发现工作目录默认在 / 这个位置
我们可以更改操作目录,比如我们想将工作目录搞到 /home 下
进入后发现工作目录是/home
WORKDIR可以写多个,比如我这样写,那么进去后的目录就为/usr/local。相当于后面是前面的相对路径,而不是覆盖掉前面的路径
7 定义变量 ENV
还是改变工作目录,这次我们用变量的形式来搞。ENV定义变量,后面使用$来调用变量
可以成功调用变量
同一行可以写多个,比如
8 执行命令 RUN
在ubuntu镜像中没有ifconfig这个命令,我现在想搞个有ifconfig的镜像
那么我们需要在镜像创建的时候就安装,需要执行一些命令
在构建的时候你就可以看到执行的过程
这样创建的镜像运行后就有ifconfig这个指令了
我们不建议RUN分多行写,因为这样会产生多层
我们可以把两行合起来写
这样这个RUN就只有一层了
如果比较长的话影响观感,可以这样分行来写
也可以用下面的语法来写,EXECUTABLE为可执行的东西,后面PARAM1,PARAME2是EXECUTABLE的参数
9 打开容器后执行的语句 CMD
9.1 ls命令简介
我们用ls来验证CMD,如果只输入ls,那么出现的结果是这样的
如果加入参数 -l 那么出现的结果是这样的,我们可以发现结果中有一些指向
如果再加入 -a,出现的结果是这样的。我们发现结果中出现了隐藏文件
9.2 中括号写法
9.2.1 多个参数
我们简单做个例子
发现结果中有指向,并且有隐藏文件,说明-l与-a生效了
9.2.2 参数和值
像ls这种-l,-a这种参数不加任何的值,有的参数可以加值,比如 /bin/bash 的-c参数,-c参数可以加命令,我们简单用一下
也可以在docker ps -a 中查看到运行的命令
从这里你就看出,相当于是只要空格你就写个逗号,然后凑个数组
9.3 直接写
我们简单写一下
可以通过docker ps -a看一下
9.4 CMD给ENTRYPOINT提供参数
第三种是提供给ENTRYPOINT的参数。如果CMD不是为ENTRYPOINT提供参数,那么不建议ENTRYPOINT与CMD同时出现
如果使用CMD给ENTRYPOINT添加参数,ENTRYPOINT必须为中括号的写法。我们简单用一下
- CMD与ENTRYPOINT谁写前面谁写后面都行
发现可以达到效果
由于docker run的COMMAND这个参数本质上是覆盖CMD,所以可以在docker run中给ENTRYPOINT参数,比如
相当于把 -l -s 替换成了 -s
10 打开后容器执行的语句 ENTRYPOINT
与CMD的区别为CMD可以通过docker run 的参数替代,但是ENTRYPOINT不会被替代,也就是说运行容器后怎么都会执行一次ENTRYPOINT的内容
docker run可以定义CMD与参数,Dockerfile中的CMD也可以定义CMD与参数,但你不能用docker run的CMD配Dockerfile的参数,也不能用Dockerfile的CMD配docker run 的参数。比如你Dockerfile中有CMD,然后docker run中只给参数,这样是不对的
ENTRYPOINT本身两种写法,一种带中括号
一种不带中括号
用法与CMD一致,就不举例子了
ENTRYPOINT与CMD指令会将启动命令写在json文件中,改动了json文件从而会产生新的镜像层
11 定义变量 ARG
ENV的值不能被build的参数 --build-arg 覆盖,但是ARG可以
我们简单做个例子
build的时候发现这两个变量都可以调用
我们此时尝试对name与age这两个变量进行覆盖
发现name(ENV定义的变量)不能覆盖,age(ARG定义的变量)可以被覆盖
一个ARG命令只能定义一个变量,如果要定义多个变量需要多个ARG
12 子镜像中要做的事情 ONBUILD
12.1 镜像的父子关系
在helloworld的例子中,我们通过Dockerfile创建的helloworld镜像 的 父镜像 是 scratch。helloworld镜像称为scratch的子镜像
如果B镜像的Dockerfile通过FROM使用了helloworld镜像,那么B镜像为helloworld镜像的子镜像
12.2 简单使用
我现在Dockerfile内容如下
build之后run,发现ifconfig用不了
我们此时再搞一个如下的Dockerfile将其命名为son
然后build->run->ifconfig
发现可以使用,这个就可以证明 ONBUILD 不是构建自己镜像时候做的事情,而是构建子镜像做的事情
13 准备暴露的端口 EXPOSE
EXPOSE这个参数是给人看的,不是给机器看的。EXPOSE参数对生成镜像没有作用,暴露端口的时候依然要用 -p
我们简单用一下
14 挂载数据卷 VOLUME
VOLUME后接的是容器内的挂载点,可以是一个可以是多个
一般不使用VOLUME,因为使用VOLUME只能通过-v覆盖的方式自定义宿主机内的挂载点。
14.1 中括号
比如我在容器中创建 /home/A 和 /home/B 这两个挂载点
把上面的dockerfile创建为镜像
14.1.1 直接启动
我们先直接启动该镜像
然后查看这个镜像的信息
发现数据卷的位置默认在/var/lib这个里面
14.1.2 加-v启动
加-v会多一组数据卷,不会影响之前的
如果容器内的挂载点相同就会覆盖(不测了,一般不会这么干)
14.2 直接写
比如还是在容器中创建 /home/A与/home/B 这两个挂载点
之后创建镜像,创建容器,发现里面有A和B两个挂载点
宿主机的挂载点在 /var/lib/docker/volumes 中,里面长名字的文件夹,代表不同的挂载点。可以根据创建的时间大概推测出来新创建的容器挂载点是哪个。这里面有两个文件夹分别代表A和B,我们先选一个看一下
进入这个文件需要输入密码(包括之前进入 /var/lib/docker 这一级也需要密码)
现在这个里面是空的
这个时候我在A里面创建一个文件夹
刷新宿主机发现能同步
那么后面再找B的对应文件夹,估计是这个
在这里创建一个名为1的文件夹
发现可以同步