24条Dockerfile及指令最佳实践( 三 )


RUN为了保持Dockerfile文件的可读性以及可维护性 , 建议将过长的或复杂的RUN指令用反斜杠分割成多行,以提高可读性和可维护性 。
RUN指令最常见的用法是安装包用的apt-get 。因为RUN apt-get指令会安装包,所以有几个问题需要注意 。

  • 避免运行apt-get upgrade或dist-upgrade , 在无特权的容器中 , 很多必要的包不能正常升级 。如果基础镜像过时了,应当联系维护者 。如果你确定某个特定的包,比如foo,需要升级 , 使用apt-get install -y foo就行,该指令会自动升级foo包 。
  • 永远将apt-get update和apt-get install一起执行,否则apt-get install会出现异常 。
  • 推荐apt-get update && apt-get install -y package-a package-b这种方式,先更新,之后安装最新的软件包 。
RUN apt-get update && apt-get install -yaufs-toolsautomakebtrfs-toolsbuild-essentialcurldpkg-siggitiptableslibApparmor-devlibcap-devlibsqlite3-devlxc=1.0*mercurialparallelrepreproruby1.9.1ruby1.9.1-devs3cmd=1.1.0*将apt-get update放在一条单独的RUN声明中会导致缓存问题以及后续的apt-get install失败 。比如,假设有一个Dockerfile文件:
FROM ubuntu:14.04RUN apt-get updateRUN apt-get install -y curl构建镜像后,所有的层都在Docker的缓存中 。假设后来又修改了其中的apt-get install添加了一个包:
FROM ubuntu:14.04RUN apt-get updateRUN apt-get install -y curl NginxDocker发现修改后的RUN apt-get update指令和之前的完全一样 。所以 , apt-get update不会执行,而是使用之前的缓存镜像 。因为apt-get update没有运行,后面的apt-get install可能安装的是过时的curl和nginx版本 。
使用RUN apt-get update && apt-get install -y可以确保Dockerfiles每次安装的都是包的最新的版本,而且这个过程不需要进一步的编码或额外干预 。这项技术叫做cache busting(缓存破坏) 。
EXPOSE 指令EXPOSE指令用于指定容器将要监听的端口 。因此,要为应用程序使用常见的端口 。
例如 , 提供Apache web服务的镜像应该使用EXPOSE 80,而提供MongoDB服务的镜像使用EXPOSE 27017 。
对于外部访问,用户可以在执行docker run时使用一个-p参数来指示如何将指定的端口映射到所选择的端口 。
ENV 指令为了方便新程序运行,可以使用ENV指令来为容器中安装的程序更新PATH环境变量 。例如使用ENV PATH /usr/local/nginx/bin:$PATH来确保CMD ["nginx"]能正确运行 。
ENV指令也可用于为容器化的服务提供必要的环境变量 , 比如Postgres需要的PGDATA 。最后,ENV也能用于设置常见的版本号 , 比如下面的示例:
ENV PG_MAJOR 9.3ENV PG_VERSION 9.3.4RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && …ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH类似于程序中的常量 , 这种方法可以只需改变ENV指令来自动的改变容器中的软件版本 。
CMDCMD指令是容器启动以后 , 默认的执行命令,需要重点理解下这个默认的含义,意思就是如果我们执行docker run没有指定任何的执行命令或者Dockerfile里面也没有指定ENTRYPOINT,那么就会使用CMD指定的执行命令执行了 。这也说明了ENTRYPOINT才是容器启动以后真正要执行的命令 。
所以经常遇到CMD会被覆盖的情况 。为什么会被覆盖呢?主要还是因为CMD的定位就是默认,如果不额外指定,那么才会执行CMD命令 , 但是如果我们指定了的话那就不会执行CMD命令了,也就是说CMD会被覆盖 。
CMD总共有三种用法:
CMD ["executable", "param1", "param2"]# exec 形式CMD ["param1", "param2"] # 作为 ENTRYPOINT 的默认参数CMD command param1 param2# shell 形式CMD推荐使用CMD ["executable","param1","param2"]这样的格式 。如果镜像是用来运行服务 , 需要使用CMD["apache2","-DFOREGROUND"],这种格式的指令适用于任何服务性质的镜像 。
ENTRYPOINT 指令根据官方定义来说ENTRYPOINT才是用于定义容器启动以后的执行程序的,允许将镜像当成命令本身来运行(用CMD提供默认选项),从名字也可以理解,是容器的入口 。
ENTRYPOINT 一共有两种用法:
ENTRYPOINT ["executable", "param1", "param2"] (exec 形式)ENTRYPOINT command param1 param2 (shell 形式)对应命令行exec模式,也就是带中括号的,和CMD的中括号形式是一致的 。但是这里貌似是在shell的环境下执行的,与cmd有区别 。


推荐阅读