Dockerfile指令详解

wjj丶 · · 180 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

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=<user>:<group>] <源路径>... <目标路径>

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

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

    • 源文件可以是通配符表达式,规则要满足于 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 <shell命令>
    • 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 <key> <value>
    • ENV <key1>=<value1> <key2>=<value2>
  • 使用示例

    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": "<!DOCTYPE html>\n<html>\n<head>\n<title>Welcome to nginx!</title>\n<style>\n    body {\n        width: 35em;\n        margin: 0 auto;\n        font-family: Tahoma, Verdana, Arial, sans-serif;\n    }\n</style>\n</head>\n<body>\n<h1>Welcome to nginx!</h1>\n<p>If you see this page, the nginx web server is successfully installed and\nworking. Further configuration is required.</p>\n\n<p>For online documentation and support please refer to\n<a href=\"http://nginx.org/\">nginx.org</a>.<br/>\nCommercial support is available at\n<a href=\"http://nginx.com/\">nginx.com</a>.</p>\n\n<p><em>Thank you for using nginx.</em></p>\n</body>\n</html>\n",
                  "Start": "2016-11-25T14:35:37.780192565Z"
              }
          ],
          "Status": "healthy"
      }
      

ONBUILD为他人做嫁衣

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

参考


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:wjj丶

查看原文:Dockerfile指令详解

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:1006366459

180 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传