在“Dockerfile最佳实践(一)”中,我们已经了解到Dockerfile中常用指令的使用,并给出了演示示例,这一篇将再补充和巩固Dockerfile中的常用知识点。
Dockerfile context(上下文)
在执行docker image build时,CLI首先会告知此次构建将向Docker守护进程发送生成上下文的大小,例如。
# cat > Dockerfile <<EOF FROM centos:7 EOF # docker image build -t demo .
Sending build context to Docker daemon 2.048kB
前面我们已经讲到镜像的构建是由Docker守护进程(Docker daemon)完成的,那么上述执行的"docker build -t demo . "其实是经历了两步:
首先,Docker CLI将上下文下包含的所有文件(递归地)发送给Docker守护进程Docker daemon。
然后,Docker Demon会收到Dcoker CLI发送的内容,通过读取Dockerfile里面的指令进行镜像的分层构建。
注意:若是使用"/"根作为上下文,则可能会导致主机异常重启(在AWS上验证是会导致主机自动重启),因为它将会将宿主机"/"根目录下所有文件传输到Docker的守护进程。
COPY指令
COPY指令有2种书写格式
COPY [--chown=user:group] 本地源文件 容器目标目录
COPY [--chown=user:group] ["本地源文件", "容器目标目录"]
默认所有拷贝到容器的文件属主(UID)和属组(GID)都是 0(root用户),除非可选参数--chown指定用户名、组名或UID/GID。--chown允许是username和groupname字符串,或者使用UID和GID。
--chown 特性仅支持用于构建Linux容器的Dockerfiles,在Windows容器上无效。这是因为Linux和Windows的用户和组的概念是有差异并且不能相互转换的,因此使用/etc/passwd和/etc/group将用户和组名称转换为UID或GID此功也仅适用于基于Linux操作系统的容器。
COPY指令的本地源文件支持使用模糊匹配和正则匹配。
COPY指令中源文件的路径是以上下文(context)作为起始点,而不是宿主机上某个绝对路径下的文件。也因此推荐用户在构建镜像时使用"."作为上下文,并且事先将Dockerfile中所需文件拷贝到指定的上下文路径下。
注意:使用COPY指令拷贝的源文件是多个文件(不是一个文件)时,则目标目录必须以"/"结尾,如“/test/",不能写成“/test”。
演示示例1
# mkdir demo2 # cd demo2 # mkdir dir{a..z} # touch arr{a..z}.txt # cat >Dockerfile <<EOF FROM centos:latest MAINTAINER firefly@demo.com #RUN yum -y install coreutils procps-ng bash RUN useradd -d /home/test -m test -s /bin/bash RUN mkdir /test WORKDIR /test COPY dir* /test/ COPY arr*.txt /home/test/ COPY --chown=test:test arr*.txt /test/ EOF # docker image build -t demo:v0.1 . # docker run -idt --name demo01 demo:v0.1 /bin/bash # docker exec -it demo01 ls -l /test/
RUN指令
RUN指令有2种书写格式
RUN 命令 #RUN指令后面所接的命令是在shell中运行的,在Linux上就好比执行了 /bin/sh -c command (例如在终端执行:/bin/sh -c ls),在Windows上就好比是执行 cmd /S /C command(例如按win+r键在“运行”窗口执行: cmd /S /C mstsc)
RUN ["命令", "参数1", "参数2"] #以exec的方式运行
RUN指令执行的任何命令都发生在当前镜像之上的一个新层中(即一个中间容器intermediate container),命令执行结束后会将结果提交到新的镜像(如同docker commit),并删除中间容器,所以我们在执行RUN指令时会经常看到类似如下日志。
Step 9/19 : RUN chmod 755 /root/start.sh
---> Running in a2e6b9ce0940
Removing intermediate container a2e6b9ce0940
---> 6b03e9b0ce70
新提交镜像将用于Dockerfile中的下一个指令。分层运行指令和提交符合Docker的核心概念,在Docker中,提交开销很小,我们可以从镜像历史记录的任何一点来创建容器,就像源代码管理一样。
CMD指令
CMD指令有3种书写格式
CMD 命令 #以shell方式运行
CMD ["可执行程序","参数1","参数2"] #exec方式,推荐运行方式
CMD ["参数1", "参数2"] #用于给ENTRYPOINT指令提供默认参数
Dockerfile中只能有一条CMD指令。如果出现多条,则只有最后一条指令生效。
注意: CMD指令会被 docker run 后的参数所覆盖。例如:
docker run -idt --name demo01 demo:v0.1 /bin/bash # "/bin/bash"将会覆盖CMD指令
ENTRYPOINT指令
ENTRYPOINT指令有2种书写格式
ENTRYPOINT [“命令”,“参数1”,“参数2”] #执行效果同docker exec
ENTRYPOINT 命令 参数1 参数2 #执行效果同shell命令
ENTRYPOINT指令不会被 docker run 后的参数所覆盖,而是附加在ENTRYPOINT指令之后。而且CMD指令中的参数会传递给ENTRYPOINT。
同CMD指令一样,Dockerfile中也只能有一条ENTRYPOINT指令,并且若是多条则最后一条生效。
CMD与ENTRYPOINT指令存在的目的就是在容器启动时就运行必要的应用程序。
演示示例2
验证CMD指令
# mkdir demo2 # cd demo2 # cat >Dockerfile <<EOF FROM ubuntu RUN apt-get -y update && apt-get install -y iputils-ping CMD ["ping", "114.114.114.114"] EOF
构建镜像
# docker image build -t demo02:v0.1 .
根据上述镜像创建容器demo02,并且不指定类似"/bin/bash" 命令
# docker run -idt --name demo02 demo02:v0.1
查看容器demo02日志输出
# docker logs demo02
PING 114.114.114.114 (114.114.114.114) 56(84) bytes of data.
64 bytes from 114.114.114.114: icmp_seq=1 ttl=61 time=36.7 ms
64 bytes from 114.114.114.114: icmp_seq=2 ttl=61 time=36.5 ms
登录到容器,并执行top命令查看,如图1.1所示。
# docker exec -it demo02 /bin/bash
图1.1
如果,我们在创建容器demo02_2时指定运行参数 "/bin/bash",如
# docker run -idt --name demo02_2 demo02:v0.1 /bin/bash
查看容器demo02_2运行状态
# docker ps -a |grep demo02_2
9b76b8af295d demo02:v0.1 "/bin/bash" 7 seconds ago Up 7 seconds demo02_2
登录到容器,使用top命令查看容器进程,可以看到CMD指令被docker run 后的"/bin/bash"参数覆盖了,如图1.2所示。
# docker exec -it demo02_2 /bin/bash
图1.2
演示示例3
验证ENTRYPOINT指令
# mkdir demo3 # cd demo3 # cat >Dockerfile <<EOF FROM ubuntu RUN apt-get -y update && apt-get install -y iputils-ping ENTRYPOINT ["ping", "114.114.114.114"] EOF # docker image build -t demo03:v0.1 .
注意,创建容器时不指定类似"/bin/bash"
# docker run -idt --name demo03 demo03:v0.1
查看容器日志
# docker logs demo03
PING 114.114.114.114 (114.114.114.114) 56(84) bytes of data.
64 bytes from 114.114.114.114: icmp_seq=1 ttl=61 time=36.6 ms
64 bytes from 114.114.114.114: icmp_seq=2 ttl=78 time=36.5 ms
登录到容器,并执行top命令查看,如图1.3所示。
# docker exec -it demo03 /bin/bash
图1.3
创建容器demo05时指定运行参数 ”127.0.0.1”
# docker run -idt --name demo05 demo03:v0.1 127.0.0.1
进入容器demo05并执行top查看,确认docker run 后的参数127.0.0.1是传递给了ENTRYPOINT指令,如图1.4所示。
图1.4
演示示例4
ENTRYPOINT指令和CMD指令结合使用
我们将可能会调整的参数写到CMD指令。然后在docker run 里指定参数,这样CMD指令后的参数就会被覆盖掉而ENTRYPOINT里的不被覆盖。
# cat >Dockerfile <<EOF FROM ubuntu RUN apt-get -y update && apt-get install -y iputils-ping ENTRYPOINT ["ping", "114.114.114.114"] CMD ["-a"] EOF # docker image build -t demo03:v0.2 .
创建容器demo03_2时指定运行参数为"-c 3"
# docker run -idt --name demo03_2 demo03:v0.2 -c 3
查看容器运行日志,确认是ping -c 3次后结束,即CMD指令后的参数被覆盖为CMD ["-c 3"]
# docker logs demo03_2
PING 114.114.114.114 (114.114.114.114) 56(84) bytes of data.
64 bytes from 114.114.114.114: icmp_seq=1 ttl=64 time=36.7 ms
64 bytes from 114.114.114.114: icmp_seq=2 ttl=59 time=36.5 ms
64 bytes from 114.114.114.114: icmp_seq=3 ttl=62 time=36.4 ms
--- 114.114.114.114 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 36.495/36.613/36.789/0.254 ms
Dockerfile使用管道符或标准输入方式来构建镜像
示例:
echo -e 'FROM busybox\nRUN echo "hello world"' | docker build -
或者
docker build -<<EOF FROM busybox RUN echo "hello world" EOF
上述2个示例是等价的
docker build [OPTIONS] - 中的"-"是连接符,用于获取路径的位置,并指示Docker从stdin读取构建上下文。
以下示例我们尝试使用COPY或ADD指令
# mkdir example1 # cd example1 # touch somefile.txt docker build -t myimage:v0.1 -<<EOF FROM busybox COPY somefile.txt . RUN cat /somefile.txt EOF
会提示如下错误,因为上述并没有指定从本地来构建上下文
Sending build context to Docker daemon 2.048kB
Step 1/3 : FROM busybox
---> b534869c81f0
Step 2/3 : COPY somefile.txt .
COPY failed: stat /var/lib/docker/tmp/docker-builder461920604/somefile.txt: no such file or directory
但使用以下语法就可以使用本地文件系统上的文件,并且使用stdin中的Dockerfile来构建镜像。语法使用-f(或--file)选项指定要使用的Dockerfile,使用连字符(-)作为文件名指示Docker从stdin读取Dockerfile
docker build [OPTIONS] -f- PATH
以下示例我们尝试使用COPY或ADD指令
# mkdir example2 # cd example2 # touch somefile.txt # docker build -t myimage:v0.2 -f- . <<EOF FROM busybox COPY somefile.txt . RUN cat /somefile.txt EOF
从远端仓库接取代码,然后再从标准输入读取Dockerfile构建镜像
docker build [OPTIONS] -f- PATH
如果要从不包含Dockerfile的git仓库中来构建镜像,或者是使用自定义Dockerfile构建镜像,则此语法非常有用。
# docker build -t myimage:latest -f- https://github.com/docker-library/hello-world.git <<EOF FROM busybox COPY hello.c . EOF
注意:上述示例执行成功前提是已安装好git客户端
总结
本节示例较多,但均是非常简单的示例,说话孰能生巧,需勤加练习。另Docker官方建议,为降低复杂性、依赖性、以及镜像大小和构建时间,要尽量避免安装额外的或不必要的包。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。