Dockerfile编写的注意事项
一.Dockerfile合理分层
Dockerfile的写法不合理,有时候会导致镜像膨胀,由于Docker是分层设计,而在Dockerfile中,每一条指令都拥有自己的context,而执行到下一条指令时,则会将下一层的构建层叠加到上一层上。因此,假如你在上一层指令做了一些包下载操作安装操作,然后在下一层再做清理,其实没什么用,只能说明你下一层引入的diff是0而已。
可以实验一下:
对照组A:下载清理操作全在同一层
FROM centos MAINTAINER ** RUN yum -y update RUN yum -y install wget RUN wget --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" "http://download.oracle.com/otn-pub/java/jdk/8u121-b13/e9e7ea248e2c4826b92b3f075a80e441/jdk-8u121-linux-x64.tar.gz" -O /tmp/jdk8_x64.tar.gz && gunzip /tmp/jdk8_x64.tar.gz && tar -C /opt -xf /tmp/jdk8_x64.tar && ln -s /opt/jdk1.8.0_121 /opt/jdk && yum clean all && rm -fr /tmp/*
对照组B:下载清理操作放在不同层
FROM centos MAINTAINER ** RUN yum -y update RUN yum -y install wget RUN wget --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" "http://download.oracle.com/otn-pub/java/jdk/8u121-b13/e9e7ea248e2c4826b92b3f075a80e441/jdk-8u121-linux-x64.tar.gz" -O /tmp/jdk8_x64.tar.gz && gunzip /tmp/jdk8_x64.tar.gz && tar -C /opt -xf /tmp/jdk8_x64.tar && ln -s /opt/jdk1.8.0_121 /opt/jdk RUN yum clean all RUN rm -fr /tmp/*
分别执行构建操作,得到的镜像差异很大,其中test1为对照组A的构建结果,test2为对照组B的构建结果,两者相差400M.
alex@ubuntu:~/workspace/docker_project$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE test2 latest a65c6cced43b 13 minutes ago 1.1 GB test1 latest 67897397b053 14 minutes ago 729 MB
二.CMD跟ENTRYPOINT的合理使用
举个例子,假设你需要容器启动时,去执行一个后台常驻脚本,那我们一般想到的是在CMD中执行脚本即可。
假设对照组A的Dockerfile:
FROM centos MAINTAINER ** EXPOSE 8000 WORKDIR / CMD [ "/bin/bash","-c","/usr/bin/python2.7 -m SimpleHTTPServer" ]
那正常镜像构建成功后,启动容器
alex@ubuntu:~/test$ docker build -t test1 . Sending build context to Docker daemon 2.048 kB Step 1/5 : FROM centos ---> 98d35105a391 Step 2/5 : MAINTAINER shufeng, shufeng.lsf@alibaba-inc.com ---> Using cache ---> 054ed64f1b34 Step 3/5 : EXPOSE 8000 ---> Running in e706b62c3c16 ---> f1fd512940dc Removing intermediate container e706b62c3c16 Step 4/5 : WORKDIR / ---> daa58d7899df Removing intermediate container 5c51961e6e87 Step 5/5 : CMD /bin/bash -c /usr/bin/python2.7 -m SimpleHTTPServer ---> Running in 5dfa9742ccaf ---> 6ff71edb9bad Removing intermediate container 5dfa9742ccaf Successfully built 6ff71edb9bad
启动容器:
alex@ubuntu:~/test$ docker run -it -d -p 8001:8000 6ff71edb9bad a4318a775ec005473f6bc9342cd3c14b55981b334cc6160e7ea17e802f6eff15
查看一下映射情况
可以发现映射成功了。但这样启动有适合什么情况呢,只适合后台启动的情况,让我们试试另一种启动容器的方式
alex@ubuntu:~/test$ docker run -it -d -p 8001:8000 6ff71edb9bad /bin/bash 0fdaf33f00fb9307394e4b91577af026627801ab6364ef6729aaf9754eb84455 alex@ubuntu:~/test$ alex@ubuntu:~/test$ telnet 127.0.0.1 8001 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. Connection closed by foreign host.
可以发现当我需要在容器启动后进入命令行时,/bin/bash的操作直接将CMD的指令覆盖了。因此,这时候需要用ENTRYPOINT来代替CMD,对应的Dockerfile如下。
FROM centos MAINTAINER ** EXPOSE 8000 WORKDIR / ENTRYPOINT ["/bin/bash","-c","/usr/bin/python2.7 -m SimpleHTTPServer"]
在这里说下CMD跟ENTRYPOINT的区别。
docker run命令行可以覆盖CMD指令,而ENTRYPOINT则不会,此外,可以通过docker run在命令行中指定的参数,来传递给ENTRYPOINT指令中指定的命令,这样就可以达到动态执行的效果。