Dockerfile指令详解

Dockerfile指令详解

FROM指定基础镜像

  • 定制镜像一定是以一个镜像为基础的,FROM就是指定基础镜像的指令,它是必备的第一条指令。
  • 基础镜像可以是
    • 服务类镜像:nginx、redis、mongo、mysql、httpd、tomcat
    • 也可以是语言运行环境镜像:node、openjdk、python、ruby、golang
    • 还可以是更基础操作系统镜像:ubuntu、debian、centos、fedora、alpine
    • 还可以是scratch,表示空白镜像

RUN执行命令

  • RUN是用来执行命令行命令的,其有两种格式:

    • shell格式:RUN <命令>,如RUN apt-get install -y gcc

    • exec格式:RUN ["可执行文件或命令", "参数1", "参数2", ...],如RUN ["apt-get", "install", "-y", "gcc"]

    • 每写一个RUN,镜像就会多一层,

      建议写成

      RUN 命令1&&命令2&&命令3
      

      而不是

      RUN 命令1
      RUN 命令2
      RUN 命令3
      

COPY复制文件

  • COPY有两种格式:

    • shell格式:RUN [--chown=:] <源路径>... <目标路径>

    • exec格式:COPY [--chown=:] ["<源路径1>",... "<目标路径>"]

    • 其中--chown=:是可选参数,会改变复制到镜像内的拥有者和所属组

    • 源文件可以是通配符表达式,规则要满足于 Gofilepath.Match

      COPY hom* /mydir/
      COPY hom?.txt /mydir/
      
    • 目标路径可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用WORKDIR指令来指定)。目标路径不需要事先创建,如果目标目录不存在会在复制文件前先行创建

ADD更高级的复制文件

  • ADD和COPY的格式和性质基本一致,但是在COPY基础上增加了一些功能,既如果源路径为一个tar压缩文件,压缩格式为gzipbzip2以及xz时,ADD指令会将其解压到目标路径,如

    FROM scratch
    ADD ubuntu-xenial-core-clouding-amd64-root.tar.gz /
    

CMD容器启动命令

  • 类似于RUN指令,但二者运行的时间点不同
    • CMDdocker run时运行(docker start不运行也会运行,即容器启动就会运行)
    • RUN是在docker build时运行
  • CMDdocker run启动的容器指定默认要运行的程序,程序运行结束,容器也就结束了
  • CMD指令指定的程序可被docker run命令行参数中指定要运行的程序所覆盖
  • 如果Dockerfile中存在多个CMD指令,仅最后一个生效
  • CMD有三种格式,分别是:
    • shell格式:CMD
    • exec格式:CMD ["可执行文件或命令", "参数1", "参数2", ...]
    • 参数列表格式:CMD ["参数1", "参数2", ...] #该写法是为ENTRYPOINT指令指定的程序提供默认参数

ENTRYPOINT

  • 类似于CMD指令,但不会被docker run的命令行参数指定的指令所覆盖,而且这些命令行参数会被当做参数送给ENTRYPOINT指令指定的程序。只有使用了--entrypoint才可以覆盖ENTRYPOINT指令指定的程序
  • Dockerfile中有多个ENTRYPOINT时,仅最后一个生效
  • 可以搭配CMD使用,此时CMD相当于是在给ENTRYPOINT传参,当在docker run中指定参数时,docker run中的参数会取代CMD的参数传递给ENTRYPOINT指定的命令

ENV设置环境变量

  • 用于设置环境变量,定义了环境变量,在后续的指令中就可以使用这个环境变量

  • 格式

    • ENV
    • ENV = =
  • 使用示例

    ENV NODE_VERSION 7.2.0
    RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-  x64.tar.xz" && curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc"
    

ARG构建参数

  • 构建参数,与ENV作用一致,但作用域不相同。ARG设置的环境变量仅在docker build的过程中有效,构建好的镜像内不存在此变量
  • ARG指定的参数值在docker build时可以用--build-arg <参数名>=<值>来覆盖
  • 格式:ARG <参数名>[=<默认值>]

VOLUMN定义匿名卷

  • 格式为:
    • VOLUMN ["<路径1>", "路径2"...]
    • VOLUMN <路径>
  • 为了防止容器内的重要数据因为容器重启而丢失,且避免容器不断变大,应该将容器内的某些目录挂载到宿主机上。
  • Dockerfile中,我们可以事先指定某些目录挂载为匿名卷,这样在docker run时若用户不用-v指定挂载,那么就会将Dockerfile中用VOLUMN指定的目录挂载为匿名卷,否则按-v的指定进行挂载

EXPOSE声明端口

  • 格式:EXPOSE <端口1> [<端口2> ...]

  • EXPOSE指定了该镜像生成容器时提供服务的端口,可以配合docker run -P(大写)使用,也可以配合docker run --net=host使用

    • 举例

      FROM nginx
      EXPOSE 80
      

      docker build -t nginx:test .

      • 以上指令指定了该镜像生成容器时,容器内提供服务的端口为80

      • 当执行命令docker run --name nginxtest -d -P nginx:test时,会随机将宿主机上某个端口映射到容器的80端口

      • 当执行命令docker run --name nginxtest -d --net=host nginx:test时,会将宿主机上的80端口映射到容器的80端口(可以暂时这么理解)

  • 要将EXPOSE和-p <宿主机端口>:<容器端口>区分开来,前者只是声明了容器将用什么端口提供服务,后者则是手动指定了宿主机和容器端口的映射

WORKDIR指定工作目录

  • 格式:WORKDIR <工作目录路径(必须是提前创建好的)>

  • 比较

    • # 示例1
      RUN cd /app
      RUN echo "hello" > hello.txt
      
    • # 示例2
      WORKDIR /app
      RUN echo "hello" > hello.txt
      
    • 示例1中因为两个RUN是不同层,所以hello.txt文件并不会出现在/app目录下

    • 示例2中用WORKDIR指定了工作路径,之后所有指令中的当前目录都是/app

USER指定当前用户

  • 格式:USER <用户名>[:<用户组>]

  • USER指令和WORKDIR相似,都是改变环境并影响以后的层。WORKDIR是改变工作目录,USER是改变之后各层中命令(由RUNCMDENTRYPOINT指令指定)的执行用户

    • 举例

      RUN groupadd -r redis && useradd -r -g redis redis
      USER redis
      RUN [ "redis-server" ]
      

      由于USER redis指令,所以redis-server这个命令由redis用户执行的

  • USER指令指定的用户必须是事先建立好的

HEALTHCHECK健康检查

  • 格式:

    • HEALTHCHECK [选项] CMD <命令>:设置检查容器健康状况的命令
    • HEALTHCHECK NONE:如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令
  • HEALTHCHECK指令是告诉Docker应该如何判断容器的状态是否正常,这是Docker1.12引入的新指令

  • 在没有HEALTHCHECK指令之前,Docker引擎只可以通过容器内主进程是否退出来判断容器状态是否异常。很多情况这没问题,但是当程序进入死锁或死循环状态,应用进程不退出,但已经无法提供服务了。在Docker1.12之前,Docker对此并无办法。

  • Docker1.12之后,Docker提供了HEALTHCHECK指令,通过该指令指定一行命令,用这行命令来判断容器主进程是否服务正常,可以更真实的反应容器实际状态

  • 当在Dockerfile中指定了HEALTHCHECK指令后,用其镜像启动的容器初始状态会为starting,在HEALTHCHECK指令检查成功后变为healthy,如果连续失败一定次数,则会变为unhealthy

  • HEALTHCHECK支持下列指令

    • --interval=<间隔>:两次健康检查的间隔,默认为30秒
    • --timeout=<时长>:健康检查命令执行后,等待响应的超时时间,如果超过这个时间,本次健康检查视为失败,默认为30秒
    • --retries=<次数>:当连续失败指定的次数后,则将容器状态视为unhealthy,默认3次
  • CMDENTRYPOINT一样,HEALTHCHECK只可以出现一次,如果写了多个,只有最后一个生效

  • HEALTHCHECK [选项] CMD后面的命令,格式和ENTRYPOINT一样,分为shellexec格式。命令的返回值决定了该次健康检查的成功与否:0-成功;1-失败;2-保留,不要使用2这个值。

  • 举例

    • 假设我们有个镜像是个最简单的Web服务,我们希望增加健康检查来判断其Web服务是否在正常工作,我们可以用curl来帮助判断,其DockerfileHEALTHCHECK可以这么写:

      FROM nginx
      RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/list*
      HEALTHCHECK --interval=5s --timeout=3s\
          CMD curl -fs http://localhost/ || exit 1
      

      这里设置了每5秒检查一次,如果健康检查命令超过3秒没响应就视为失败,并且使用curl -fs http://localhost/ || exit 1作为健康检查命令

      使用docker build来构建这个镜像

      $ docker build -t myweb:v1 .
      

      构建好之后,启动容器:

      $ docker run -d --name web -p 80:80 myweb:v1`
      

      当运行该镜像后,可以通过docker container ls看到最初的状态为(health: starting):

      $ docker container ls
      CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                            PORTS               NAMES
      03e28eb00bd0        myweb:v1            "nginx -g 'daemon off"   3 seconds ago       Up 2 seconds (health: starting)   80/tcp, 443/tcp     web
      

      再等待几秒钟后,再次docker container ls,就会看到健康状态变化为(healthy)

      $ docker container ls
      CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                    PORTS               NAMES
      03e28eb00bd0        myweb:v1            "nginx -g 'daemon off"   18 seconds ago      Up 16 seconds (healthy)   80/tcp, 443/tcp     web
      

      如果健康检查连续失败超过了重试次数,状态就会变为(unhealthy)

      为了帮助排障,健康检查命令的输出(包括stdoutstderr)都会被存储在健康状态里,可以用docker inspect查看

      $ docker inspect --format '{{json .State.Health}}' web | python -m json.tool
      {
          "FailingStreak": 0,
          "Log": [
              {
                  "End": "2016-11-25T14:35:37.940957051Z",
                  "ExitCode": 0,
                  "Output": "\n\n\nWelcome to nginx!\n\n\n\n

      Welcome to nginx!

      \n

      If you see this page, the nginx web server is successfully installed and\nworking. Further configuration is required.

      \n\n

      For online documentation and support please refer to\nnginx.org.
      \nCommercial support is available at\nnginx.com.

      \n\n

      Thank you for using nginx.

      \n\n\n", "Start": "2016-11-25T14:35:37.780192565Z" } ], "Status": "healthy" }

ONBUILD为他人做嫁衣

  • 格式:ONBUILD <其他指令>
  • ONBUILD是一个特殊的命令,它后面跟的是其他指令,比如RUN,COPY等,而这些指令,在当前镜像构建时并不会被执行。只有当以当前镜像为基础,去构建新镜像时才被执行

参考

  • https://yeasy.gitbooks.io/docker_practice/image/dockerfile/

你可能感兴趣的:(Dockerfile指令详解)