• Dockerfile实例


    一、先看最简单的例子,定制nginx镜像,打印出 <h1>Hello, Docker!</h1>

     Dockerfile文件:

    FROM nginx
    RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html

     在该Dockerfile目录下运行

    docker build -t nginx:v1 .

     则将生成一个 nginx:v1 的新镜像,运行该镜像

    docker run -p 80:80 nginx:v1

     在浏览器直接访问地址 localhost,可以看到nginx的主页已被修改

     二、RUN 命令关键解析

    Dockerfile 中每一个指令都会建立一层, RUN 也不例外。每一个 RUN 的行为,新建立一层,在其上执行这些命令,执行结束后, commit 这一层的修改,构成新的镜像。

    FROM debian:jessie
    RUN apt-get update
    RUN apt-get install -y gcc libc6-dev make
    RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz"
    RUN mkdir -p /usr/src/redis
    RUN tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1
    RUN make -C /usr/src/redis
    RUN make -C /usr/src/redis install

    如上dockerfile所示,创建了 7 层镜像,这是完全没有意义的,而且很多运行时不需要的东西,都被装进了镜像里,比如编译环境、更新的软件包等等。结果就是产生非常臃肿、非常多层的镜像,不仅仅增加了构建部署的时间,也很容易出错。

    正确的写法:

    FROM debian:jessie
    RUN buildDeps='gcc libc6-dev make' 
    && apt-get update 
    && apt-get install -y $buildDeps 
    && wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz" 
    && mkdir -p /usr/src/redis 
    && tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 
    && make -C /usr/src/redis 
    && make -C /usr/src/redis install 
    && rm -rf /var/lib/apt/lists/* 
    && rm redis.tar.gz 
    && rm -r /usr/src/redis 
    && apt-get purge -y --auto-remove $buildDeps

    三、CMD 与 ENTRYPOINT对比

    场景1、让镜像变成像命令一样使用

    假设我们需要一个得知自己当前公网 IP 的镜像,那么可以先用 CMD 来实现:

    FROM ubuntu:16.04
    RUN apt-get update 
    && apt-get install -y curl 
    && rm -rf /var/lib/apt/lists/*
    CMD [ "curl", "-s", "http://ip.cn" ]

    使用docker build -t myip . 来构建镜像的话,如果我们需要查询当前公网 IP,只需要执行:

    docker run myip

    可以打印出当前的ip地址

    这么看起来好像可以直接把镜像当做命令使用了,不过命令总有参数,如果我们希望加参数呢?比如从上面的 CMD 中可以看到实质的命令是 curl ,那么如果我们希望显示 HTTP头信息,就需要加上 -i 参数。那么我们可以直接加 -i 参数给 docker run myip 么?

    $ docker run myip -i
    docker: Error response from daemon: invalid header field value "oci runtime error: con
    tainer_linux.go:247: starting container process caused "exec: \"-i\": executable
    file not found in $PATH"
    ".

    可以看到可执行文件找不到的报错, executable file not found 。之前我们说过,跟在镜像名后面的是 command ,运行时会替换 CMD 的默认值。因此这里的 -i 替换了远了的CMD ,而不是添加在原来的 curl -s http://ip.cn 后面。而 -i 根本不是命令,所以自然找不到。
    那么如果我们希望加入 -i 这参数,我们就必须重新完整的输入这个命令:

    $ docker run myip curl -s http://ip.cn -i

    这显然不是很好的解决方案,而使用 ENTRYPOINT 就可以解决这个问题。现在我们重新用ENTRYPOINT 来实现这个镜像:

    FROM ubuntu:16.04
    RUN apt-get update 
    && apt-get install -y curl 
    && rm -rf /var/lib/apt/lists/*
    ENTRYPOINT [ "curl", "-s", "http://ip.cn" ]

    这次我们再来尝试直接使用 docker run myip -i

    成功!这是因为当存在 ENTRYPOINT 后, CMD 的内容将会作为参数传给ENTRYPOINT ,而这里 -i 就是新的 CMD ,因此会作为参数传给 curl ,从而达到了我们预期的效果

    场景2:应用运行前的准备工作

    启动容器就是启动主进程,但有些时候,启动主进程前,需要一些准备工作。
    比如 mysql 类的数据库,可能需要一些数据库配置、初始化的工作,这些工作要在最终的mysql 服务器运行之前解决。
    此外,可能希望避免使用 root 用户去启动服务,从而提高安全性,而在启动服务前还需要以 root 身份执行一些必要的准备工作,最后切换到服务用户身份启动服务。或者除了服务外,其它命令依旧可以使用 root 身份执行,方便调试等。
    这些准备工作是和容器 CMD 无关的,无论 CMD 为什么,都需要事先进行一个预处理的工作。这种情况下,可以写一个脚本,然后放入 ENTRYPOINT 中去执行,而这个脚本会将接到的参数(也就是 <CMD> )作为命令,在脚本最后执行。

    四、HEALTHCHECK 健康检查

    HEALTHCHECK 指令是告诉 Docker 应该如何进行判断容器的状态是否正常,这是 Docker 1.12引入的新指令

    HEALTHCHECK 支持下列选项:
    --interval=<时长> :两次健康检查的间隔,默认为 30 秒;
    --timeout=<时长> :健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被
    视为失败,默认 30 秒;
    --retries=<次数> :当连续失败指定次数后,则将容器状态视为 unhealthy ,默认 3
    次。

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

    这里我们设置了每 5 秒检查一次(这里为了试验所以间隔非常短,实际应该相对较长),如果健康检查命令超过 3 秒没响应就视为失败,并且使用 curl -fs http://localhost/ || exit 1 作为健康检查命令。

    构建镜像,并且启动容器

    $ docker build -t myweb:v1 .

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

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

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

    如果健康检查连续失败超过了重试次数,状态就会变为 (unhealthy) 。
    为了帮助排障,健康检查命令的输出(包括 stdout 以及 stderr )都会被存储于健康状态里,可以用 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>
    <html>
    <head>
    <title>Welcome to nginx!</titl
    e>
    <style>
     body {
      35em;
     margin: 0 auto;
     font-f
    amily: Tahoma, Verdana, Arial, sans-serif;
     }
    </style>
    </head>
    <body>
    <h1>Welc
    ome to nginx!</h1>
    <p>If you see this page, the nginx web server is successfully inst
    alled and
    working. Further configuration is required.</p>
    
    <p>For online documentat
    ion and support please refer to
    <a href="http://nginx.org/">nginx.org</a>.<br/>
    Co
    mmercial support is available at
    <a href="http://nginx.com/">nginx.com</a>.</p>
    
    
    <p><em>Thank you for using nginx.</em></p>
    </body>
    </html>
    ",
    "Start": "2016-11-25T14:35:37.780192565Z"
    }
    ],
    "Status": "healthy"
    }

    五、COPY与ADD区别

    在 Docker 官方的最佳实践文档中要求,尽可能的使用 COPY ,因为 COPY 的语义很明确,就是复制文件而已,而 ADD 则包含了更复杂的功能,其行为也不一定很清晰。最适合使用ADD 的场合,就是所提及的需要自动解压缩的场合。
    另外需要注意的是, ADD 指令会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。
    因此在 COPY 和 ADD 指令中选择的时候,可以遵循这样的原则,所有的文件复制均使用COPY 指令,仅在需要自动解压缩的场合使用 ADD 。

  • 相关阅读:
    力扣算法:组合总和IV
    力扣算法:组合总和III
    逻辑回归(Logistic Regression)学习笔记
    力扣算法:组合总和II
    力扣算法:组合总和
    寒假作业(五)
    寒假作业(四)
    寒假作业(三)
    寒假作业(二)
    寒假学习(一)
  • 原文地址:https://www.cnblogs.com/UniqueColor/p/11133843.html
Copyright © 2020-2023  润新知