Dockerfile
1、第一步首先编写dockerfile(本例在~/my_docker目录下创建的dockerfile)
#version 0.0.1
FROM ubuntu
MAINTAINER lzj "leezhongjian@163.com"
RUN touch ~/hello.txt
RUN echo "hello lzj" >> ~/hello.txt
RUN cat ~/hello.txt
- ROM指定一个已经存在的镜像,后续指令都将基于该镜像进行,称为基础镜像。在运行容器时,必须指定基于哪个容器进行构建的。
- MAINTAINER指定镜像的作者以及联系方式
- RUN会在当前镜像中运行指定命令,每条RUN指定创建一个新的镜像层,指令成功会将镜像提交,依次类推,镜像层层提交。执行RUN的时候其实就是在执行/bin/sh -c来执行后面的touch、echo、cat命令
2、基于dockerfile构建镜像
docker build -t="softwarebird/mydocker" .
构建镜像时,-t指定了仓库名softwarebird,镜像名称mydocker,"."表示构建镜像用的dockerfile去当前目录下去找。运行命令,会生成需要的镜像。
也可以构建镜像时指定镜像的版本号,如下:
docker build -t="softerwarebird/my_hello:v0.1" .
如果不指定版本号,默认生成的是latest版本,如下所示:
构建镜像时也可以指定dockerfile的位置,如下(文件的名字不一定需要命名dockerfile):
docker build -t="softerwarebird/my_hello:v0.1" -f /home/demo/my_dockerfile
在指定目录先构建镜像时,可以在该目录下创建".dockerignore"文件,凡是做镜像时不需要把指定的文件做入镜像的,可以在".dockerignore"设置需要过滤的文件。与git的".gitignore"类似。
3、dockerfile与缓存
dockerfile的每一步构建过程都会生成一个镜像,并将镜像缓存起来,在下次构建镜像时,如果缓存中已经有了,直接用缓存中的镜像进行建构,如下所示:
如果强制不适用缓存的镜像,命令如下:
4、基于构建缓存的dockerfile模板
在用dockerfile构建镜像是,每次都会执行一遍dockerfile中命令,但是如果在dockerfile中通过ENV命令设置一个刷新时间,只要这个时间不变,除第一次构建后,后续每次构建都会利用缓存中的镜像;如果时间变动,就会把ENV命令后的所有命令重新构建一遍镜像。dockerfile示例如下:
FROM ubuntu
MAINTAINER lzj "leezhongjian@163.com"
ENV REFRESHED_AT 2019-03-18
RUN apt-get update
基于此dockerfile第一次构建镜像时,命令如下:
docker build -t="softwarebird/mydocker" .
运行结果如下:
第二次基于此dockerfile构建镜像时,执行docker build -t="softwarebird/mydocker" .
,运行结果如下:
从运行结果可以看出,每一步都应用了缓存中的镜像进行构建。
下面修改dockerfile文件中ENV制定的缓存时间
ENV REFRESHED_AT 2019-03-17
再次构建镜像,运行docker build -t="softwarebird/mydocker" .
,运行结果如下:
从运行结果可以看, ENV命令前的 MAINTAINER还是用的缓存中的镜像,ENV后面的apt-aget update没有用缓存中的,而是重新执行的。
5、查询创建的镜像
上面构建完镜像后,下面查询创建的镜像,执行
docker images softwarebird/mydocker
显示结果如下:
如果想查询镜像的每一步的构建情况,即构成最终镜像的每一层镜像的叠加,执行命令如下:
docker history softwarebird/mydocker
显示结果如下:
6、用构建的镜像启动容器
利用最开始的dockerfile构建的镜像启动容器
#version 0.0.1
FROM ubuntu
MAINTAINER lzj "leezhongjian@163.com"
RUN touch ~/hello.txt
RUN echo "hello lzj" >> ~/hello.txt
RUN cat ~/hello.txt
启动容器
docker run -i -t softwarebird/my_hello /bin/bash
进入启动后的容器查询创建的hello.txt文件
二、Dockerfile指令
在编写dockerfile文件过程中,除了上面用到的FROM、MAINTAINER、RUN命令,还可以用如下命令。
1、CMD
cmd命令用于指定容器启动时,默认执行的命令。dockerfile示例如下:
#version 0.0.1
FROM ubuntu
MAINTAINER lzj "leezhongjian@163.com"
RUN touch ~/hello.txt
RUN echo "hello lzj" >> ~/hello.txt
CMD ["/bin/bash"]
用该dockerfile制作镜像,docker build -t="softwarebird/my_hello" .
然后运行该制作的镜像docker run -i -t softwarebird/my_hello
,发现启动容器时,没有指定执行的/bin/bash命令,但在容器启动时却默认执行/bin/bash,就是因为dockerfile中指定了CMD ["/bin/bash"]。当然也可以在启动容器时指定执行的命令,docker build -t="softwarebird/my_hello" .
这样就会覆盖CMD ["/bin/bash"]中命令。
注意:dockerfile中只能指定一条CMD命令,即使指定了多条,也只有最后一条生效。
2、ENTRYPOINT
ENTRYPOINT命令与CMD命令类似,都可表示容器启动时默认执行的命令,但是CMD执行的命令可以被容器启动时传入的命令覆盖,而ENTRYPOINT执行的命令却不可以覆盖,容器启动时传入的命令可以作为ENTRYPOINT后的命令的参数。例如,基于如下dockerfile构建镜像
#version 0.0.1
FROM ubuntu
MAINTAINER lzj "leezhongjian@163.com"
RUN touch ~/hello.txt
RUN echo "hello lzj" >> ~/hello.txt
ENTRYPOINT ["ls", "-l"]
基于此dockerfile制作镜像,执行命令docker build -t="softwarebird/my_hello" .
,然后执行利用该镜像启动一个容器,执行命令如下:
docker run -i softwarebird/my_hello /root/hello.txt
输出结果如下:
可见启动容器时指定的“/root/hello.txt”传给了ENTRYPOINT 命令后的“ls -l”命令。
3、WORKDIR
WORKDIR为下面的RUN、CMD、ENTRYPOINT等提供工作目录。dockerfile示例如下:
#version 0.0.1
FROM ubuntu
MAINTAINER lzj "leezhongjian@163.com"
WORKDIR /root
RUN touch hello.txt
RUN echo "hello lzj" >> hello.txt
WORKDIR /home
RUN touch test.txt
RUN echo "hello test" >> test.txt
上面制作镜像时,在/root/下面创建了hello.txt文件,在/home/下面创建了test.txt文件。利用此dockerfile构建镜像docker build -t="softwarebird/my_hello" .
,然后运行该镜像docker run -i -t softwarebird/my_hello /bin/bash
。查看制作镜像时创建的文件结果如下:
4、USER
USER指令用来指定该镜像以什么用户进行,比如USER lzj
镜像以lzj用户运行。可以指定用户名或用户名的UID以及用户组以及组的GID进行组合,如下所示
USER user
USER user:group
USER uid
USER uid:gid
USER user:gid
USER uid:group
例如dockerfile内容如下,指定root用户:
#version 0.0.1
FROM ubuntu
MAINTAINER lzj "leezhongjian@163.com"
USER root
ENV home_path /home
WORKDIR $home_path
RUN touch test2.txt
RUN echo "hello test2" >> test2.txt
通过该dockerfile构建镜像 docker build -u root -t="softwarebird/my_hello" .
,用root用户运行该镜像,结果如下:
用lzj用户运行该镜像,结果如下:
可见用lzj用户是失败的。
5、VOLUME
VOLUME指令用来向基于镜像创建的容器添加卷,一个卷可以存在于一个或多个容器内的特定目录,提供数据共享或者对数据进行持久化的功能。卷有如下功能:
-
卷可以在容器间共享和重用
-
一个容器可以不是必须和容器共享卷
-
对卷的修改时立即生效的
-
对卷的修改不会对更新镜像产生影响
-
卷会一直存在直到没有任何容器再使用它
下面举一个容器与宿主机共享文件的例子,也即容器可以把数据持久化到宿主机上,dockerfile如下:#version 0.0.1 FROM ubuntu MAINTAINER lzj "leezhongjian@163.com" VOLUME ["/home/my_volume"]
表示在容器的/home的目录下添加了一个my_volume目录。
下面利用该dockerfile构建镜像,运行 docker build -t="softwarebird/my_hello" .
,然后运行该镜像docker run -i -t --name hello_container softwarebird/my_hello /bin/bash
,启动容器后,进行容器的/home/my_volume目录,发现该目录下为空。下面在宿主机上找到对应该容器共享的目录,另外打开一个终端,执行docker inspect hello_container
,查找到"Source": "/var/lib/docker/volumes/6afa1de9d514a765dd8f7a503270a199fb12d2b9c6e683423155b370472d2e0f/_data",
,/var/lib/docker/volumes/6afa1de9d514a765dd8f7a503270a199fb12d2b9c6e683423155b370472d2e0f/_data
即为宿主机上对应容器上/home/my_volume的目录,在宿主机的该目录下创建一个hello.txt,那么在容器的/home/my_volume目录下可以立即看到hello.txt文件,示意图如下:
6、ADD
ADD指令用来将构建环境下的文件和目录复制到镜像中。格式:ADD src dest
,示例如下,采用如下dockerfile构建镜像
#version 0.0.1
FROM ubuntu
MAINTAINER lzj "leezhongjian@163.com"
WORKDIR /home
ADD hello.txt .
然后在dockerfile所在的目录下新建一个hello.txt文件,然后执行命令构建镜像:docker build -t="softwarebird/my_hello" .
,然后执行构建的镜像: docker run -i -t softwarebird/my_hello /bin/bash
,最后可以在容器的home目录下看到一个hello.txt文件,即为从宿主机上copy过来的。
在ADD文件时,docker通过目的地址的末尾是否有“/”来判断目的源是文件还是目录,如果目的地址以“/”结尾,docker默认源位置是目录,如果目的地址不是以“/”结尾,那么源地址为文件。
ADD还可以把URL加入到容器中,例如ADD http://xxx/file.txx /home/
ADD还可以把压缩文件加入到容器目录中,并自动实现解压功能,例如ADD test.zip /home/test/
7、COPY
COPY功能与ADD相似,但COPY没有提取压缩文件的功能。格式COPY src dest
src为宿主机上的文件或目录,必须是相对于存放dockerfile的文件或目录,不能copy其它位置的目录或文件。
8、LABEL
LABEL指令用于为docker镜像添加元数据,元数据以键值对的形式出现,格式docker key="value"
,例如,以下面dockerfile为例
#version 0.0.1
FROM ubuntu
MAINTAINER lzj "leezhongjian@163.com"
LABEL version="0.1" author="lzj" date="20190323"
利用该dockerfile构建镜像,docker build -t="softwarebird/my_hello" .
,下面可以利用inspect查看生成的softwarebird/my_hello镜像元数据信息,执行docker inspect softwarebird/my_hello
,结果如下所示
9、STOPSIGNAL
STOPSIGNAL指令用来设置stop容器时,宿主机发送什么信号量给容器,这个信号量必须是内核系统调用表中合法的信号量,如SIGTERM、SIGKILL等。
格式:
STOPSIGNAL 信号量
默认情况下,当要stop一个容器,执行docker stop 容器名/容器id
,宿主机向容器发送的是SIGTERM信号,容器接受到SIGTERM信号后,会执行一系列的软件等关闭操作,然后执行退出关闭容器操作,如果短暂的时间内容器没有关闭,宿主机继续向容器发送一个SIGKILL信号,强制容器关闭。
-
下面示例默认情况,采用dockerfile如下:
#version 0.0.1 FROM ubuntu MAINTAINER lzj "leezhongjian@163.com" RUN touch ~/hello.txt RUN echo "hello world" >> ~/hello.txt
然后构建镜像,利用该镜像启动容器,过程如下:
进入容器后就可以执行一系列操作了,下面执行stop操作,观看容器的反应,启动另外一个终端,然后在另外一个终端上执行docker stop my_container1
关闭容器操作,容器退出过程如下:
可见,由于容器中目前没有执行耗时的操作,所以容器接受到SIGTERM信号后,自动执行了exit退出容器的操作。
-
下面定义容器关闭是,不响应默认的SIGTERM信号,而是直接响应SIGKILL信号
采用dockerfile如下,添加了指定响应的SIGKILL信号#version 0.0.1 FROM ubuntu MAINTAINER lzj "leezhongjian@163.com" RUN touch ~/hello.txt RUN echo "hello world" >> ~/hello.txt STOPSIGNAL SIGKILL
然后构建镜像,启动容器,过程如下:
下面启动另外一个终端,在另外一个终端上执行stop容器操作docker stop my_container2
,容器响应如下
可见当宿主机执行stop操作后,宿主机向容器发送SIGTERM信号后,容器没有反应,随之发送SIGKILL信号后,容器立即退出。从上图可以看出容器并没有执行exit操作,会立即退出容器,导致未执行完的东西也立即退出,这就是与默认时的区别所在。
10、ARG
ARG指令用来定义可以在docker build命令运行时传递给构建运行时的变量,在运行docker build时只需用–build-arg标志即可,在dockerfile中格式如下 ARG key=value
,下面实例显示,所用dockerfile如下:
#version 0.0.1
FROM ubuntu
MAINTAINER lzj "leezhongjian@163.com"
ARG arg1=docker_arg1
ARG arg2
RUN touch ~/hello.txt
RUN echo $arg1 >> ~/hello.txt
RUN echo $arg2 >> ~/hello.txt
下面利用该dockerfile构建镜像,构建镜像时,只需要加入–build-arg标志即可:
docker build --build-arg arg2=docker_arg2 -t="softwarebird/my_hello" .
构建镜像时,没有为arg1传参数,则arg1取默认值docker_arg1,为arg2传入的参数为docker_arg2,那么利用该镜像启动容器,查看是否正确
11、ONBUILD
ONBUILD指令能为镜像添加触发器,当一个镜像被用作其它镜像的基础 镜像时,该镜像中的触发器就会执行。触发器会在构建的过程中插入新的指令,这些指令紧跟在FROM之后执行。示例如下
首先要新构建一个基础镜像,该镜像所用的dockerfile如下:
#version 0.0.1
FROM ubuntu
MAINTAINER lzj "leezhongjian@163.com"
ONBUILD ADD hello.txt /home/
该dockerfile中添加一个ONBUILD 触发器,但是利用此dockerfile构建镜像时没有影响,只是在它的子镜像构建过程中才会发挥作用。构建基础镜像过程如下:
下面在上面的基础镜像softwarebird/my_hello上构建镜像,所用my_docker2/dockerfile如下:
FROM softwarebird/my_hello
MAINTAINER lzj "leezhongjian@163.com"
在my_docker2目录下新建hello.txt文件,为后面测试用,内容如下:
hello dockerfile2
it's based on dockerfile
下面利用该dockerfile构建镜像,构建的镜像是基于softwarebird/my_hello镜像的,那么在基础镜像softwarebird/my_hello所用的dockerfile中的触发器就会发挥作用(ONBUILD ADD hello.txt /home/
),会在构建子镜像执行FROM softwarebird/my_hello
后执行ADD hello.txt /home/
命令,过程如下:
可见在基于基础镜像softwarebird/my_hello构建softwarebird/my_hello2镜像时,触发了ONBUILD触发器,因此在构建softwarebird/my_hello2时执行了ADD hello.txt /home/