Dockerfile 最佳实践
之前 一篇文章介绍 docker 的镜像基本原理和概念 ,主要介绍在编写 docker 镜像的时候一些需要注意的事项和推荐的做法。
虽然 Dockerfile 简化了镜像构建的过程,并且把这个过程可以进行版本控制,但是不正当的 Dockerfile 使用也会导致很多问题:
- docker 镜像太大。如果你经常使用镜像或者构建镜像,一定会遇到那种很大的镜像,甚至有些能达到 2G 以上
- docker 镜像的构建时间过长。每个 build 都会耗费很长时间,对于需要经常构建镜像(比如单元测试)的地方这可能是个大问题
- 重复劳动。多次镜像构建之间大部分内容都是完全一样而且重复的,但是每次都要做一遍,浪费时间和资源
这篇文章会讲述一些做法,希望能解决这些问题。
希望读者能够对 docker 镜像有一定的了解,阅读这篇文章至少需要一下前提知识:
- 了解 docker 的基础概念,运行过容器
- 熟悉 docker 镜像的基础知识,知道镜像的分层结构
- 最好是负责过某个 docker 镜像的构建(使用 docker build 命令创建过自己的镜像)
Dockerfile 和镜像构建
Dockerfile 是由一个个指令组成的,每个指令都对应着最终镜像的一层。每行的第一个单词就是命令,后面所有的字符串是这个命令的参数,关于 Dockerfile 支持的命令以及它们的用法,可以参考 官方文档 ,这里不再赘述。
当运行 docker build 命令的时候,整个的构建过程是这样的:
- 读取 Dockerfile 文件发送到 docker daemon
- 读取当前目录的所有文件(context),发送到 docker daemon
- 对 Dockerfile 进行解析,处理成命令加上对应参数的结构
- 按照顺序循环遍历所有的命令,对每个命令调用对应的处理函数进行处理
- 每个命令(除了 FROM)都会在一个容器执行,执行的结果会生成一个新的镜像
- 为最后生成的镜像打上标签
编写 Dockerfile 的一些最佳实践
1. 使用统一的 base 镜像
有些文章讲优化镜像会提倡使用尽量小的基础镜像,比如 busybox 或者 alpine 等。我更推荐使用统一的大家比较熟悉的基础镜像,比如 ubuntu,centos 等,因为基础镜像只需要下载一次可以共享,并不会造成太多的存储空间浪费。它的好处是这些镜像的生态比较完整,方便我们安装软件,除了问题进行调试。
2. 动静分离
经常变化的内容和基本不会变化的内容要分开,把不怎么变化的内容放在下层,创建出来不同基础镜像供上层使用。比如可以创建各种语言的基础镜像,python2.7、python3.4、go1.7、java7等等,这些镜像包含了最基本的语言库,每个组可以在上面继续构建应用级别的镜像。
3. 最小原则:只安装必需的东西
很多人构建镜像的时候,都有一种冲动——把可能用到的东西都打包到镜像中。要遏制这种想法,镜像中应该 只包含必需的东西 ,任何可以有也可以没有的东西都不要放到里面。因为镜像的扩展很容易,而且运行容器的时候也很方便地对其进行修改。这样可以保证镜像尽可能小,构建的时候尽可能快,也保证未来的更快传输、更省网络资源。
4. 一个原则:每个镜像只有一个功能
不要在容器里运行多个不同功能的进程,每个镜像中只安装一个应用的软件包和文件,需要交互的程序通过 pod(kubernetes 提供的特性) 或者容器之间的网络进行交流。这样可以保证模块化,不同的应用可以分开维护和升级,也能减小单个镜像的大小。
5. 使用更少的层
虽然看起来把不同的命令尽量分开来,写在多个命令中容易阅读和理解。但是这样会导致出现太多的镜像层,而不好管理和分析镜像,而且镜像的层是有限的。尽量把相关的内容放到同一个层,使用换行符进行分割,这样可以进一步减小镜像大小,并且方便查看镜像历史。
6. 减少每层的内容
尽管只安装必须的内容,在这个过程中也可能会产生额外的内容或者临时文件,我们要尽量让每层安装的东西保持最小。
- 比如使用
--no-install-recommends参数告诉apt-get不要安装推荐的软件包 - 安装完软件包,清楚
/var/lib/apt/list/缓存 - 删除中间文件:比如下载的压缩包
- 删除临时文件:如果命令产生了临时文件,也要及时删除
7. 不要在 Dockerfile 中修改文件的权限
因为 docker 镜像是分层的,任何修改都会新增一个层,修改文件或者目录权限也是如此。如果修改大文件或者目录的权限,会把这些文件复制一份,这样很容易导致镜像很大。
解决方案也很简单,要么在添加到 Dockerfile 之前就把文件的权限和用户设置好,要么在容器启动脚本(entrypoint)做这些修改。
8. 利用 cache 来加快构建速度
如果 Docker 发现某个层已经存在了,它会直接使用已经存在的层,而不会重新运行一次。如果你连续运行 docker build 多次,会发现第二次运行很快就结束了。
不过从 1.10 版本开始,Content Addressable Storage 的引入导致缓存功能的实效,目前引入了 --cache-from 参数可以手动指定一个镜像来使用它的缓存。
9. 版本控制和自动构建
最好把 Dockerfile 和对应的应用代码一起放到版本控制中,然后能够自动构建镜像。这样的好处是可以追踪各个版本镜像的内容,方便了解不同镜像有什么区别,对于调试和回滚都有好处。
另外,如果运行镜像的参数或者环境变量很多,也要有对应的文档给予说明,并且文档要随着 Dockerfile 变化而更新,这样任何人都能参考着文档很容易地使用镜像,而不是下载了镜像不知道怎么用。
参考资料
- Best Practices for writing Dockerfiles
- Refactoring a Dockerfile for Image Size
- How to Not Be the Engineer Running 3.5GB Docker Images
Dockerfile 最佳实践的更多相关文章
- 【原创】Docker实战 Dockerfile最佳实践&&容器之间通信
官方最佳实践文档 https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#from Docker实战(三十) ...
- dockerfile 最佳实践及示例
Dockerfile 最佳实践已经出现在官方文档中,地址在 Best practices for writing Dockerfiles.如果再写一份最佳实践,倒有点关公门前耍大刀之意.因此本篇文章是 ...
- 《容器高手实战: Dockerfile最佳实践》
Dockerfile最佳实践一个容器对应一个进程一个Docker容器应该只对应一个进程,也就是一个Docker 镜像一般只包含一个应用的制品包(比如.jar). 在需要组合多个进程的场景,使用容器组( ...
- go项目dockerfile最佳实践
1. 前言 2. 不需要cgo情况下的最佳实践 3. 依赖cgo情况下的最佳实践 1. 前言 这几天在构建golang编写的web项目中,关于dockerfile编写的一些总结 可能是单纯我比较菜(大 ...
- Dockerfile最佳实践(一)
1.使用缓存 Dockerfile的每条指令都会将结果提交为新的镜像,下一跳指令将会基于上一步指令的镜像的基础上构建,如果一个镜像存在相同的父镜像和指令(除了ADD),Docker将会使用镜像而不是 ...
- 编写 Dockerfile 最佳实践
官方仓库虽然有数十万计的免费镜像,但大多数无法直接满足公司业务需求,这就需要我们自己去定制镜像了. Docker通过Dockerfile自动构建镜像,Dockerfile是一个包含用于组建镜像的文本文 ...
- 8、Dockerfile介绍和最佳实践
一.Dockerfile 概念 1.Dockerfile是什么 Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序.库.资源.配置等文件外,还包含了一些为运行时准备的一些配置参数(如 ...
- Dockerfile 命令详解及最佳实践
Dockerfile 命令详解 FROM 指定基础镜像(必选) 所谓定制镜像,那一定是以一个镜像为基础,在其上进行定制.就像我们之前运行了一个 nginx 镜像的容器,再进行修改一样,基础镜像是必须指 ...
- 8.云原生之Docker容器镜像构建最佳实践浅析
转载自:https://www.bilibili.com/read/cv15220861/?from=readlist 本章目录 0x02 Docker 镜像构建最佳实践浅析 1.Dockerfile ...
随机推荐
- JavaWeb总结(三)—JSP
一.JSP简介 1.基本认识 (1)JSP页面是由HTML语句和嵌套在其中的Java代码组成的一个普通文本文件,JSP 页面的文件扩展名必须为.jsp. (2)在JSP页面中编写的Java代码需要嵌套 ...
- 前端jquery validate表单验证框架的使用
一.框架本身校验方法的扩展 建议写在页内用于扩展框架本身的一些校验方法, 使用频繁也可以直接在源码上修改 例如扩展手机号码的校验: /*手机号码验证扩展 最新的号码 mobile: class的表示 ...
- SQL模糊查询条件的四种匹配模式
执行数据库查询时,有完整查询和模糊查询之分. 一般模糊语句格式如下: SELECT 字段 FROM 表 WHERE 某字段 LIKE 条件 其中关于条件,SQL提供了四种匹配模式: 1.% :表示任意 ...
- ios 获取当前ViewController
- (UIViewController*)getpresentVC:(UIWindow *)window{ if ([window.rootViewController class]==[UITabB ...
- Node.js 原理简介
Node.js 的官方文档中有一段对 Node.js 的简介,如下. Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript ...
- yii框架数据库操作数据访问对象(DAO)简单总结
Yii提供了强大的数据库编程支持.Yii数据访问对象(DAO)建立在PHP的数据对象(PDO)extension上,使得在一个单一的统一的接口可以访问不同的数据库管理系统(DBMS).使用Yii的DA ...
- 在mysql 5.6的环境下修改生产环境的表结构(在线ddl) ----工具pt-osc
随着需求的变化越来越快,在线修改表结构变得越来越需要. 在mysql5.6以前,mysql的修改表结构操作会锁表,这样就会造成开发人员或者DBA修改表结构必须要等到凌晨流量谷值或者停服修改.这样必定会 ...
- css系列(布局):实现一个元素在浏览器中水平、垂直居中的几个方案
在开发中偶遇需要一个元素垂直居中的需求,之前都是水平居中,垂直居中使用的比较少,经过一通研究,选择了几种相对比较实用的方案分享,抛砖引玉,如有遗漏不足,还望不吝指正. 方案一(IE7下该方案无法实现垂 ...
- framework7+node+mongo项目
Famework7还是一个不错的前端框架 不过这个小项目做下来确实踩了不少的坑 废话不多说上干货 项目代码:https://github.com/tsxylhs/framework7
- CF #244 D. Match & Catch 后缀数组
题目链接:http://codeforces.com/problemset/problem/427/D 大意是寻找两个字符串中最短的公共子串,要求子串在两个串中都是唯一的. 造一个S#T的串,做后缀数 ...