• 指导方针
 
创建短暂的容器
 
  意思是 container 可以停止和销毁,接着以最小化启动和配置进行重新构建和替换。
 
理解构建的上下文
 
  使用 docker build ,当前工作环境称为 构建的上下文,默认 Dockerfile 是在同级目录找,可通过 -f 指定 Dockerfile。
 
  无论 Dockerfile 实际在哪里,当前目录的所有递归的文件和目录的内容被发送到 docker daemon 作为构建的上下文。
 
  (无意中包含的不必要文件会增加 image 大小,增加 build/pull/push 时间和 container 运行时大小)
 
  上下文内容支持本地 PATH 和远程 URL,docker build [OPTIONS] PATH | URL | -
 
17.05开始支持用 stdin 管道化 Dockerfile 的内容
 
docker build -t foo:v1  .  -f-<<<EOF
FROM ubuntu:16.04
RUN echo "hello"
COPY . /copy-files
EOF
   
使用 .dockerignore 排除内容进入构建上下文
 
 
使用多级构建
 
  不需要努力去减少中间层数量和文件,从变化少的层到经常变化的层来排序(这样可以保证复用到构建历史缓存)
 
  不同层的顺序安排:安装工具 -> 安装库依赖 -> 生成应用
 
不安装不必要的包
 
解耦应用
 
  每一个 container 应该只关心一件事。解耦应用到多个容器可以让水平扩展更容易和重复使用容器。比如 web 技术栈的分为 应用/数据库/缓存 三个不同的容器。
 
最小化层的数量
 
  在 17.05 及更高版本中,通过 multi-stage 多级构建减少了这个限制。
 
给多行参数排序
 
  帮助避免包重复和更容易修改,更易读。
 
Run apt-get update && apt-get install -y \
bzr \
cvs \
git \
mercurial
利用构建缓存
 
  如果不想在构建中使用缓存的,给 docker build 命令加 --no-cache=true 选项。
 
    
  • Dockerfile 指令
        
    FROM - 只要有可能,使用当前官方仓库的作为基础 image。https://docs.docker.com/engine/reference/builder/#from
 
 
    LABEL - 通过object帮助组织image,每行以LABEL开头,有一个或多个 key-value 对。https://docs.docker.com/config/labels-custom-metadata/
 
 
    RUN - 把长且复杂的 RUN 语句用反斜线分割成多行,保持 Dockerfile 更可读,可理解,可维护。https://docs.docker.com/engine/reference/builder/#run
 
               把 update 和 install 放在一行,保证 Dockerfile 安装最近的包版本,如下:
 
RUN apt-get update && apt-get install -y \
package-foo \
package-bar
    APT-GET - 在 apt-get 应用中可能是最常使用的用于 RUN 的命令。因为它可以安装包,RUN apt-get 有几个陷阱需要提防。
 
                       不要使用 RUN apt-get dist-upgrade 和 dist-upgrade。因为许多来自父级 image 的基础的包不能在没有权限的 container 中升级。
 
                       如果父级 image 中的一个 package 过时了,联系它的维护者。
 
                       如果你知道有一个特定的包 foo 需要升级,使用 apt-get install -y foo 来自动更新。
 
                       总是把 RUN apt-get update 和 apt-get install 合并为一条 RUN 语句,确保无干涉的安装最新的包版本。
     
RUN apt-get update && apt-get install -y \
package-foo \
package-bar \
package-baz=1.3.*
&& rm -rf /var/lib/apt/lists/* # 通过移除 apt cache 减小 image 尺寸
                      单独在一行 RUN 语句中使用 apt-get update 会引起缓存问题,随后的 apt-get install 指令失败,这叫做’ cache busting ’;同样可以通过指定包版本获取 cache-busting,这叫做版本固定,这可以避免由包变化而引起的意外失败。
 
                      官方 debian 和 ubuntu 的 image 会自动运行 *apt-get clean*,所以明确调用不是必需的。
     
 
    使用管道 - 如果想让管道连接的命令在任何阶段遇到错误时就失败,在命令前追加  set -o pipefail &&  来保证遇到未期的错误时阻止构建。
 
                      例如:RUN set -o pipefail && wget -O - https://some.site | wc -l > /number
 
                      不是所有的 shell 都支持 -o pipefail , 基于 debian 的 image 需要指定 bash
 
                      例如:RUN [ “/bin/bash”, “-c”, “set -o pipefail && wget -O - https://some.site | wc -l > /number" ]
 
 
    CMD - 该指令用于运行 image 内的软件,随同任何参数。https://docs.docker.com/engine/reference/builder/#cmd
 
                格式形式为 CMD ["command", "param1", "param2"],这个形式的指令推荐用在任何基于服务的 image。
 
                在大多数其他案例中,CMD 应该给一个交互式的shell,比如 CMD ["php", "-a"],意味着执行 docker run -it php,你会有一个可用的shell。
 
                CMD 很少以 CMD ["param", "param"] 的方式与 ENTRYPOINT 协同,除非你和用户已经非常熟悉 ENTRYPOINT 是如何工作的。
 
 
    EXPOSE - 该指令指示 container 在哪一个端口监听用于连接。https://docs.docker.com/engine/reference/builder/#expose
 
                      因此,应该使用常见传统的端口用于应用软件。例如 包含 Apache 的 image 将使用 EXPOSE 80,而包含 MongoDB 的 image 将使用 EXPOSE 27017 等等。
 
                      用于外部访问,使用者可以执行 docker run 带上一个标记来标识映射指定端口到他们选择的端口。
 
                      对于容器连接,Docker 为从接收容器返回到源的路径提供环境变量。(如,MYSQL_PORT_3306_TCP)
    
 
    ENV - 为了使新软件更容易运行,可以使用 ENV 来更新容器中安装软件的 PATH 环境变量。https://docs.docker.com/engine/reference/builder/#env
 
               例如:ENV PATH /usr/local/nginx/bin:$PATH 保证 CMD ["nginx"] 能运行。
 
               ENV 指令 在 为你希望容器化的服务提供所需的环境变量 上同样有用,例如 Postgres 的 PGDATA。
 
               每个 ENV 行创建一个新的中间层,就像 RUN 命令。这意味着即使在之后的层 unset 这个环境变量,它仍然存在于这个层,并且它的值可以被打印。测试如下:
FROM alpine
ENV ADMIN_USER="mark"
RUN echo $ADMIN_USER > ./mark
RUN unset ADMIN_USER
CMD sh
$ docker run --rm -it test sh echo $ADMIN_USER
                要阻止这种情况,真正 unset 环境变量,使用 RUN 指令运行 shell 命令,在一个独立的层中 set, use, unset 变量。
 
                使用 ; 或 && 来分割命令,使用 && 只要一个命令失败,docker build 也失败。
FROM alpine
ENV export ADMIN_USER="mark" \
&& echo $ADMIN_USER > ./mark \
&& unset ADMIN_USER
CMD sh $ docker run --rm -it test sh echo $ADMIN_USER
    ADD 或 COPY - 尽管两者功能相似,一般来讲,首选 COPY。https://docs.docker.com/engine/reference/builder/#add | https://docs.docker.com/engine/reference/builder/#copy
 
                              因为 COPY 比 ADD 更易懂。COPY 只支持从本地文件到 container 的基本拷贝,而 ADD 有一些不明显的特性(如,本地 tar包 自动解压和支持远程 URL)
 
                              因此 ADD 的最佳使用是本地 tar 文件在 image 中的自动解压,如,ADD rootfs.tar.xz / .
 
                              如果 Dockerfile 有多个步骤使用了上下文中的不同文件,单独的拷贝它们,而不是一次拷贝所有。这确保每一步的构建缓存在文件发生改变时是失效的,如:                           
COPY requirements.txt /tmp
RUN pip install --requirement /tmp/requirements.txt
COPY . /tmp/
                              要让 RUN 步骤导致更少的缓存失效,那么把 COPY . /tmp/ 放到其前面。
 
                              因为 image 大小问题,使用 ADD 从远程地址拉取包是极不鼓励的;你应该使用 curl 或 wget 代替。这种方式你可以在解压后把不需要的文件删除,不需要把其它层加到你的 image 中。
 
                              避免做的是:
ADD http://example.com/big.tar.xz /usr/src/things/
RUN tar -zJf /usr/src/things/big.tar.xz -C /usr/src/things
RUN make -C /usr/src/things all
                              代替的做法是:
RUN mkdir -p /usr/src/things \
&& curl -SL http://example.com/big.tar.xz \
| tar -xJC /usr/src/things \
&& make -C /usr/src/things all
                              对于不需要 ADD 自动解压能力的文件和目录,你应该总是使用 COPY。
 
 
    ENTRYPOINT - 最好的使用点是设置 image 的主命令,允许 image 像命令一样运行(接着使用 CMD 做为默认标记)。https://docs.docker.com/engine/reference/builder/#entrypoint
ENTRYPOINT ["s3cmd"]
CMD ["--help"]
 
                             现在 image 可以像这样显示命令的帮助信息:$ docker run s3cmd
 
                             或者使用正确的参数来执行一个命令:$ docker run s3cmd ls s3://mybucket
 
                             这是有用的,因为 image 名称可以两次作为到如上命令二进制的引用。
 
                             ENTRYPOINT 指令同样可以用来和一个帮助脚本结合,允许它做和命令方式一样的事,尽管需要多进行一步 - 写脚本,以下是 Postgres 官方 image 的 ENTRYPOINT 使用的脚本:
#!/bin/bash
set -e
if [ "$1" = 'postgres' ]; then
chown -R postgres "$PGDATA"
if [ -z "$(ls -A "$PGDATA")" ]; then
gosu postgres initdb
fi
fi
exec "$@"
                             把帮助脚本拷贝到 container 中,并在 container 启动时通过 ENTRYPOINT 运行:          
COPY ./docker-entrypoint.sh /
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["postgres"]
                             帮助脚本允许命令有多个参数可以交互,比如:
$ docker run postgres
$ docker run postgres postgres --help
$ docker run --rm -it postgres bash
    VOLUME - 指令应该用于暴露任何数据库存储区域,配置区域,或 docker 容器创建的文件/目录。https://docs.docker.com/engine/reference/builder/#volume
 
                       强烈建议你把 VOLUMN 使用在 image 中易变的或用户维护的部分。
 
 
    USER - 如果一个服务不需要权限可以运行,使用 USER 切换为非 root 用户。https://docs.docker.com/engine/reference/builder/#user
 
                 通过在 Dockerfile 中创建用户和组开始:RUN groupadd -r postgres && useradd --no-log-init -r -g postgres postgres
 
                 image 中的用户和组被分配一个非确定的 UID/GID,下次 image 重建时重新分配。所以如果 UID/GID 至关重要,你应该分配一个准确的。
 
                (由于Go archive/tar 包中未解决的bug,在docker 容器中创建相当长的UID会耗尽磁盘,因为容器层中的 /var/log/faillog 填充了NULL字符,权宜措施是传递 --no-log-init 给 useradd)
 
                 避免使用 sudo,如果你确实需要像 sudo 一样的功能,例如像 root 一样初始化守护进程但以非 root 用户运行,考虑使用 gosu。https://github.com/tianon/gosu
 
                 为了减少层次和复杂性,避免频繁的切换用户。
 
 
    WORKDIR - 为了清晰和可靠,你应该总是为 WORKDIR 使用绝对路径。https://docs.docker.com/engine/reference/builder/#workdir
 
                         同样,你应该使用 WORKDIR 而不是 RUN cd ... && do-something ,更难度阅读和排除问题以及维护。
 
 
    ONBUILD - 命令在当前 Dockerfile 构建完毕执行。https://docs.docker.com/engine/reference/builder/#onbuild
 
                        ONBUILD 在当前 FROM image 派生的任何子 image 中执行。可以认为 ONBUILD 命令是父 Dockerfile 给子 Dockerfile 的指令。
 
                        Docker 构建时会在任何子 Dockerfile 中的命令前执行 ONBUILD。
 
                        ONBUILD 对于从给定的 image 构建 image 时有用。例如,你想为一个语言栈的 包含该语言写的任意用户软件到 Dockerfile 中的 image 使用 ONBUILD。
 
                        从 ONBUILD 构建的 image 应该有一个分割 tag,例如,ruby:1.9-onbuild 或 ruby-2.0-onbuild
 
                        当把 ADD 或 COPY 放到 ONBUILD 时要小心。如果新构建的上下文缺少被添加的资源,onbuild 构建 image 会灾难性失败。通过如上添加分割的 tag 来减轻这种影响。
 
 

[Docker] 写 Dockerfile 的最佳实践理论的更多相关文章

  1. [Docker] 容器开发环境最佳实践理论

      保持 image 小       选择合适的 base image.       使用 multi-stage 构建. https://docs.docker.com/develop/develo ...

  2. 8.云原生之Docker容器镜像构建最佳实践浅析

    转载自:https://www.bilibili.com/read/cv15220861/?from=readlist 本章目录 0x02 Docker 镜像构建最佳实践浅析 1.Dockerfile ...

  3. 新IT运维时代 | Docker运维之最佳实践-下篇

    上篇针对操作系统.主机配置.容器镜像.容器运行时四大方面分享一些Docker的运维经验,本篇将着重在Docker Daemon参数和权限两个方面进一步分享.(阅读上篇请点击右侧:新IT运维时代 | D ...

  4. Docker容器日志管理最佳实践

    目录 一 .Docker 引擎日志 二.容器日志 2.1.常用查看日志命令--docker logs 2.2 .Docker 日志 驱动 三. 生产环境中该如何储存容器中的日志 一.当是完全是标准输出 ...

  5. dockerfile 的最佳实践

    Dockerfile 编写nginx容器 [root@mast nginx]# cat Dockerfile FROM centos MAINTAINER zhaoruidong RUN yum -y ...

  6. Docker多主机互联最佳实践

    在公司使用docker多主机互联时碰到了各种坑.搞清楚后才发现如此简单,以下是根据实际经验的总结. 版本信息 Client: Version: 18.09.0 API version: 1.39 Go ...

  7. Docker镜像原理和最佳实践

    https://yq.aliyun.com/articles/68477 https://yq.aliyun.com/articles/57126  DockerCon 2016 深度解读: Dock ...

  8. 新IT运维时代 | Docker运维之最佳实践-上篇

    容器技术的发展可以分为两个阶段,第一个阶段聚焦在IaaS层,仅仅把容器当做更轻量级虚拟机来使用,解决了应用运行时进程级资源隔离的问题:随着Docker的出现,容器虚拟化才有了统一的平台,由此容器技术发 ...

  9. DOCKER学习_008:Docker容器的运行最佳实践

    一 容器分类 容器按用途大致可分为两类: 服务类容器,如 web server. database等 工具类容器,如cur容器, Iredis-cli容器 通常而言,服务类容器需要长期运行,所以使用 ...

随机推荐

  1. python 基础之python的六大标准数据类型

    一:Number 数字类型(int  float  bool  complex) 1.整型: (正整数 0 负整数)#(1)二进制用0b表示intvar = 0b1010print(intvar)pr ...

  2. C# System.Collections

    System.collection类: 动态数组ArrayList 代表了可被单独索引的对象的有序集合.也就是说他是一个动态的数组,你可以通过索引来进行增删改等操作,数组会自动调整数组的大小.允许在列 ...

  3. elasticsearch -- Logstash实现mysql同步数据到elasticsearch

    配置 安装插件由于这里是从mysql同步数据到elasticsearch,所以需要安装jdbc的入插件和elasticsearch的出插件:logstash-input-jdbc.logstash-o ...

  4. windows下使用caffe测试mnist数据集

    在win10机子上装了caffe,感谢大神们的帖子,要入坑caffe-windows的朋友们看这里,还有这里,安装下来基本没什么问题. 好了,本博文写一下使用caffe测试mnist数据集的步骤. 1 ...

  5. Ubuntu 18.04 启用 rc.local 设置开机启动

    ubuntu18.04 不再使用initd管理系统,改用systemd. 然而systemd很难用,改变太大,跟之前的完全不同. 使用systemd设置开机启动为了像以前一样,在/etc/rc.loc ...

  6. SQL SERVER2008 数据库日志文件的收缩方法

    最近公司的数据库随着业务量的增多,日志文件巨大(超过300G),造成磁盘空间不够用,进而后来的访问数据库请求无法访问. 网上类似的方法也很多,但不可行,如下是我实践过,可行的,将日志文件收缩至任意指定 ...

  7. maven项目自动创建src/main/resources等四个资源文件夹

    如何使maven项目自动创建这四个文件夹:src/main/resources.src/main/java.src/test/java.src/test/resources 网传甚广的在Config ...

  8. C#和.Net的关系

    1..net(dot net) .net是一个平台,抽象的平台概念. 实现形式是库:①定义了基本的类型(通用类型系统CTS,common type system).   ②包含.net公共语言运行库( ...

  9. Centos 7环境下安装配置Hadoop 3.0 Beta1简记

    前言 由于以前已经写过一篇Centos 7环境下安装配置2.8的随笔,因此这篇写得精简些,只挑选一些重要环节记录一下. 安装环境为:两台主机均为Centos 7.*操作系统,两台机器配置分别为: 主机 ...

  10. Linq to SQL -- Join

    Join操作 适用场景:在我们表关系中有一对一关系,一对多关系,多对多关系等.对各个表之间的关系,就用这些实现对多个表的操作. 说明:在Join操作中,分别为Join(Join查询), SelectM ...