Dockerfile最佳实践(二)巩固篇

在“Dockerfile最佳实践(一)”中,咱们已经了解到Dockerfile中经常使用指令的使用,并给出了演示示例,这一篇将再补充和巩固Dockerfile中的经常使用知识点。git

Dockerfile context(上下文)github

在执行docker image build时,CLI首先会告知这次构建将向Docker守护进程发送生成上下文的大小,例如。docker

# cat > Dockerfile <<EOF
FROM centos:7
EOF
# docker image build -t demo .

Sending build context to Docker daemon 2.048kBshell

前面咱们已经讲到镜像的构建是由Docker守护进程(Docker daemon)完成的,那么上述执行的"docker build -t demo . "实际上是经历了两步:ubuntu

首先,Docker CLI将上下文下包含的全部文件(递归地)发送给Docker守护进程Docker daemon。centos

而后,Docker Demon会收到Dcoker CLI发送的内容,经过读取Dockerfile里面的指令进行镜像的分层构建。bash

注意:如果使用"/"根做为上下文,则可能会致使主机异常重启(在AWS上验证是会致使主机自动重启),由于它将会将宿主机"/"根目录下全部文件传输到Docker的守护进程。ide

COPY指令ui

COPY指令有2种书写格式spa

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

clip_image001

图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


clip_image001[5]

图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


clip_image001[7]

图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所示。

clip_image001[9]

图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官方建议,为下降复杂性、依赖性、以及镜像大小和构建时间,要尽可能避免安装额外的或没必要要的包。

相关文章
相关标签/搜索