Dockerfile构建指南

抛砖引玉

先说结论


  1. 以不变应万变
  • 一个相对固定的 build 环境
  • 善用 cache
  • 构建 自己的基础镜像

2 . 精简为美

  • 缓存清理
  • multi stage build
  • 使用 .dockerignore 保持 context 干净
  • 容器镜像环境清理

你需要的了解的参考资料

为什么要优化镜像


  • 一个小镜像有什么好处: 分发更快,存储更少,加载更快。
  • 镜像臃肿带来了什么问题: 存储过多,分发更慢且浪费带宽更多。

镜像的构成

Dockerfile构建指南-怎样构建一个适用企业级的镜像

  • 俯瞰镜像: 就是一个删减版的操作系统。
  • 侧看镜像: 由一层层的 layer 堆叠而成

那么问题来了


  1. 是否层数少的镜像, 就是一个好镜像?
  2. 在企业应用中, 要怎么去规划和建设 CI中的镜像和构建 ?
  3. 带集群足够大, 节点足够多的时候, 要怎么快速分发这些镜像 ?

举个例子 docker build


  • Dockerfile v1

    v1

    FROM nginx:1.15-alpine

    RUN echo "hello"

    RUN echo "demo best practise"

    ENTRYPOINT [ "/bin/sh" ]

  • Dockerfile v2

    v2

    FROM nginx:1.15-alpine

    RUN echo "hello"

    RUN echo "demo best practise 02"

    ENTRYPOINT [ "/bin/sh" ]

  • 1st build

全新构建

1# docker build -t demo:0.0.1 . Sending build context to Docker daemon 2.048kB 2Step 1/4 : FROM nginx:1.15-alpine 3 ---> 9a2868cac230 4Step 2/4 : RUN echo "hello" 5 ---> Running in d301b4b3ed55 6hello 7Removing intermediate container d301b4b3ed55 8 ---> 6dd2a7773bbc 9Step 3/4 : RUN echo "demo best practise" 10 ---> Running in e3084037668e 11demo best practise 12Removing intermediate container e3084037668e 13 ---> 4588ecf9837a 14Step 4/4 : ENTRYPOINT [ "/bin/sh" ] 15 ---> Running in d63f460347ff 16Removing intermediate container d63f460347ff 17 ---> 77b52d828f21 18Successfully built 77b52d828f21 19Successfully tagged demo:0.0.1
  • 2nd build

Dockerfile 与 1st build 完全一致, 命令仅修改 build tag , 从 0.0.1 到 0.0.2

1# docker build -t demo:0.0.2 .Sending build context to Docker daemon 4.096kB 2Step 1/4 : FROM nginx:1.15-alpine 3 ---> 9a2868cac230 4Step 2/4 : RUN echo "hello" 5 ---> Using cache 6 ---> 6dd2a7773bbc 7Step 3/4 : RUN echo "demo best practise" 8 ---> Using cache 9 ---> 4588ecf9837a 10Step 4/4 : ENTRYPOINT [ "/bin/sh" ] 11 ---> Using cache 12 ---> 77b52d828f21 13Successfully built 77b52d828f21 14Successfully tagged demo:0.0.2

可以看到,

  1. 每层 layer 都使用 cache (---> Using cache) ,并未重新构建。
  2. 我们可以通过 docker image ls |grep demo 看到, demo:0.0.1 与 demo:0.0.2 的 layer hash 是相同。
    所以从根本上来说, 这两个镜像就是同一个镜像,虽然都是 build 出来的。

3rd build

这次, 我们将第三层 RUN echo "demo best practise" 变更为 RUN echo "demo best practise 02"

1docker build -t demo:0.0.3 . 2Sending build context to Docker daemon 4.608kB 3Step 1/4 : FROM nginx:1.15-alpine 4 ---> 9a2868cac230 5Step 2/4 : RUN echo "hello" 6 ---> Using cache 7 ---> 6dd2a7773bbc 8Step 3/4 : RUN echo "demo best practise 02" 9 ---> Running in c55f94e217bd 10demo best practise 02Removing intermediate container c55f94e217bd 11 ---> 46992ea04f49 12Step 4/4 : ENTRYPOINT [ "/bin/sh" ] 13 ---> Running in f176830cf445 14Removing intermediate container f176830cf445 15 ---> 2e2043b7f3cb 16Successfully built 2e2043b7f3cb 17Successfully tagged demo:0.0.3

可以看到 ,

  1. 第二层仍然使用 cache

  2. 但是第三层已经生成了新的 hash 了

  3. 虽然第四层的操作没有变更,但是由于上层的镜像已经变化了,所以第四层本身也发生了变化。

    注意: 每层在 build 的时候都是依赖于上册 ---> Running in f176830cf445。

4th build

第四次构建, 这次使用 --no-cache 不使用缓存, 模拟在另一台电脑上进行 build 。

1# docker build -t demo:0.0.4 --no-cache . Sending build context to Docker daemon 5.632kB 2Step 1/4 : FROM nginx:1.15-alpine 3 ---> 9a2868cac230 4Step 2/4 : RUN echo "hello" 5 ---> Running in 7ecbed95c4cdhello 6Removing intermediate container 7ecbed95c4cd 7 ---> a1c998781f2e 8Step 3/4 : RUN echo "demo best practise 02" 9 ---> Running in e90dae9440c2 10demo best practise 02Removing intermediate container e90dae9440c2 11 ---> 09bf3b4238b8 12Step 4/4 : ENTRYPOINT [ "/bin/sh" ] 13 ---> Running in 2ec19670cb14 14Removing intermediate container 2ec19670cb14 15 ---> 9a552fa08f73 16Successfully built 9a552fa08f73 17Successfully tagged demo:0.0.4

可以看到,

  1. 虽然和 3rd build 使用的 Dockerfile 相同, 但由于没有缓存,每一层都是重新 build 的。
  2. 虽然 demo:0.0.3 和 demo:0.0.4 在功能上是一致的。
    但是 他们的 layer 不同, 从根本上来说,他们是不同的镜像。

总结

Dockerfile 以外的功夫

  1. 尽量不变的构建环境,保障 Dockerfile 在任意时间构建的时候,可以复用已生成的 缓存层
  2. 更好的组织架构规划,同一组或同一技术栈的同事可以用相同的 FROM 基础层, 以复用在生产场景中的基础镜像。

Dockerfile 以内的事项

  1. 需要保证, 在 Dockerfile 中,越上层的语句越稳定,越不容易变化。
  2. 在执行 ADDCOPY 时, 尽量不要以 文件夹 作为对象,因为文件夹中变化因子更多。尽可能的将变化频发的文件放在最下层。
  3. 使用 .dockerignore 忽略不需要的文件,以保证 context 的简洁。
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )

Dockerfile构建指南 - HelloWorld