如何构建尽可能小的容器镜像?
关注我们获得更多内容
这是Google Developer Advocate Sandeep Dinesh 关于如何充分利用Kubernetes环境的七部分视频和博客系列的第一部分。 主要讲保持容器镜像尽可能小的理论和实用性。
Docker使构建容器镜像变得轻而易举。只需将标准 Dockerfile 放入您的文件夹,运行 docker build 命令,然后运行,芝麻开门!您的容器镜像已构建成功!
这种简单性的缺点是,很容易构建出大体积的容器镜像,其中包含您不需要的东西——包括潜在的安全漏洞。
在本期“Kubernetes最佳实践”中,让我们探讨如何使用Alpine Linux 和 Docker builder 模式创建生产就绪的容器镜像,再做一些基准测试,然后确定这些容器在Kubernetes集群中运行方式。
根据您使用的是解释型语言还是编译型语言,创建容器镜像的过程会有所不同。让我们一起来深入了解!
解释型语言的容器化
解释型语言,如Ruby,Python,Node.js,PHP和其他语言通过发送源代码到解释器来运行代码。 这样的好处是可以跳过编译步骤,但其缺点是要求您将解释器与代码一起丢进去。
幸运的是,大多数这些语言都提供了预构建的Docker容器,其中包含一个轻量级环境,允许您运行更小的容器。
我们来看一个Node.js应用程序并对其进行容器化。 首先,让我们使用node:onbuild镜像作为基础。 Docker容器的onbuild版本预先打包了您需要的所有内容,因此无需执行大量配置即可搞定。 这意味着Dockerfile非常简单(只有两行!)。 但是你要付出的磁盘大小代价——差不多700MB!
dockerfileFROM node:onbuildEXPOSE 8080
通过使用较小的基础镜像(如Alpine),您可以显著减少容器的大小。Alpine Linux是一款体积小,轻量级的Linux发行版,在Docker用户中非常受欢迎,因为它与许多应用程序兼容,同时仍然保持小体积。
幸运的是,Node.js(以及其他流行语言)有一个官方的Alpine图像,可以满足您的一切需求。与默认的node镜像不同,node:alpine会删除许多文件和程序,只留下足以运行您的应用程序的部分。
基于Alpine Linux的Dockerfile创建起来有点复杂,因为你必须运行一些针对onbuild的命令。
dockerfileFROM node:alpineWORKDIR /appCOPY package.json /app/package.jsonRUN npm install --productionCOPY server.js /app/server.jsEXPOSE 8080CMD npm start
但是,这是值得的,因为产生的镜像只有65MB!
编译型语言的容器化
诸如Go,C,C ++,Rust,Haskell等编译型语言可以创建在没有许多外部依赖性的情况下运行的二进制文件。 这意味着你可以提前构建二进制文件并将其投入生产,而无需把创建二进制文件(如编译器)放进去。
借助Docker对多阶段构建的支持,您可以轻松地打包二进制文件和最少量的脚手架。 让我们学习一下怎么做。
让我们采用Go应用程序并使用此模式对其进行容器化。 首先,让我们使用golang:onbuild镜像作为基础。 和以前一样,Dockerfile只有两行,但你再次付出磁盘大小超过700MB的代价!
dockerfileFROM golang:onbuildEXPOSE 8080
下一步是使用更小的基础镜像,也就是golang:alpine镜像。 到目前为止,这与我们针对解释型语言所遵循的过程相同。
同样,使用Alpine基础镜像创建Dockerfile有点复杂,因为您必须运行一些执行onbuild镜像相关的命令。
dockerfileFROM golang:alpineWORKDIR /appADD . /appRUN cd /app && go build -o goappEXPOSE 8080ENTRYPOINT ./goapp
但同样,由此产生的镜像要小得多,大小只有256MB!
但是,我们可以使镜像更小:您不需要Go附带的任何编译器或其他构建和调试工具,因此您可以从最终容器中删除它们。
让我们使用多阶段构建来获取由golang:alpine容器创建的二进制文件并将其自行打包。
dockerfileFROM golang:alpine AS build-envWORKDIR /appADD . /appRUN cd /app && go build -o goapp
FROM alpineRUN apk update && \ apk add ca-certificates && \ update-ca-certificates && \ rm -rf /var/cache/apk/*WORKDIR /appCOPY --from=build-env /app/goapp /appEXPOSE 8080ENTRYPOINT ./goapp
你看看现在! 这个容器镜像只有12MB大小!
在构建此容器时,您可能会注意到Dockerfile会执行一些奇怪的操作,例如手动将HTTPS证书安装到容器中。这是因为基础的Alpine Linux几乎没有预安装任何东西。 因此,即使您需要手动安装任何依赖项,最终结果也还是超小容器镜像!
注意:如果您想节省更多空间,可以静态编译应用程序并使用scratch容器镜像。使用scratch作为基础容器镜像意味着您从头开始,根本没有基础层。 但是,我建议使用Alpine作为基础镜像而不是scratch,因为Alpine镜像中的仅仅增加少量额外MB的大小却可以使得使用标准工具和安装依赖项变得更加容易。
在哪构建和存储您的容器镜像?
为了构建和存储镜像,我强烈推荐Google Container Builder和Google Container Registry的组合。 Google Container Builder非常快,并自动将镜像推送到Google Container Registry。 大多数开发人员应该可以轻松地在GCP的免费套餐中完成所有工作,而Google Container Registry与原始Google云端存储的价格相同(便宜!)。
Google Kubernetes Engine等平台可以安全地从Google Container Registry中拉取镜像而无需任何其他配置,让您轻松上手!
此外,Google Container Registry还为您提供漏洞扫描工具和开箱即用的IAM支持。 这些工具可以使您更轻松地保护和锁定容器。
评估小体积容器的性能
人们声称小体积容器的巨大优势是缩短了时间——建立时间和拉取时间。 让我们测试一下,使用onbuild创建的容器,对比在多阶段过程中使用Alpine创建的容器,看看真实情况如何。
TL;DR:对于功能强大的计算机或Container Builder没有显著差异,但对于性能没有那么强大的计算机和共享系统(如许多CI/CD系统)而言则有明显差异。 就绝对性能而言,小镜像总是更好。
在大型机器上构建镜像
对于第一次测试,我将使用笔记本电脑进行构建。 我正在使用我们的办公室WiFi,所以下载速度非常快!
对于每次构建,我都会删除缓存中的所有Docker镜像。
构建(Build):
Go Onbuild: 35 SecondsGo Multistage: 23 Seconds
对于较大的容器镜像,构建需要的时间大约是10秒左右。 虽然这个代价仅在初始构建时需要付出,但是如果您使用持续集成系统,则在每次构建时都要付出这个代价。
下一个测试是将容器镜像推送到远程镜像仓库。 对于此测试,我使用Google Container Registry来存储镜像。
推送(Push):
Go Onbuild: 15 SecondsGo Multistage: 14 Seconds
这很有趣! 为什么需要花费相同的时间来推送12MB镜像和700MB镜像? 事实证明,Google Container Registry在幕后使用了很多技巧,包括许多流行基础镜像的全局缓存。
最后,我想测试将镜像从远程镜像仓库库拉到本地计算机所需的时间。
拉取(Pull):
Go Onbuild: 26 SecondsGo Multistage: 6 Seconds
20秒的差距,这是使用两个不同容器镜像之间的最大差异。 您可以开始看到使用较小镜像的优势,尤其是在经常拉取镜像时。
您还可以使用Google Container Builder在云中构建容器镜像,这样可以自动将它们存储在Google Container Registry中。
构建(build)+ 推送(Push):
Go Onbuild: 25 SecondsGo Multistage: 20 Seconds
再次证明,使用较小的镜像有一点小优势,没有我想象的那么大。
在小型机器上构建镜像
如果是在小型机器上构建镜像,那么使用较小的容器镜像是否有优势呢? 如果你有一个功能强大的笔记本电脑与快速互联网连接和/或容器生成器,优势并不明显。 但是,如果您使用功能较弱的机器,故事就会发生变化。 为了模拟这一点,我使用了一个Google Compute Engine f1-micro VM来构建,推送和拉取这些镜像,结果非常惊人!
构建(build):
Go Onbuild: 52 secondsGo Multistage: 6 seconds
推送(Push):
Go Onbuild: 54 secondsGo Multistage: 28 seconds
拉取(Pull):
Go Onbuild: 48 SecondsGo Multistage: 16 seconds
在这种情况下,使用较小的容器镜像确实非常有帮助!
在Kubernetes上拉取镜像
虽然您可能不关心构建和推送容器镜像所需的时间,但您应该非常关心拉取容器镜像所需的时间。 对于Kubernetes,这可能是您的生产集群最重要的指标。
例如,假设您有一个三节点集群,其中一个节点崩溃。 如果您使用的是像Kubernetes Engine这样的托管系统,系统会自动生成一个新节点来代替它。
但是,这个新节点将是全新的,并且必须先拉取所有容器镜像才能开始工作。 拉取容器镜像所需的时间越长,集群执行的时间就越长!
当您增加群集大小(例如,使用Kubernetes Engine Autoscaling)或将节点升级到新版本的Kubernetes时,可能会发生这种情况(接下来的几集中会关注这个问题)。
我们可以看到来自多个部署的多个容器的拉取性能可以在这里真正体现出来,使用小容器可能会浪费几分钟的部署时间!
安全性和漏洞
除了性能之外,使用较小的容器镜像还有很大的安全性提升。 与使用大基础镜像的容器镜像相比,小容器镜像通常具有较小的攻击面。
几个月前,我构建了Go onbuild和multi-age容器镜像,因此它们可能包含一些已被发现的漏洞。 使用Google Container Registry的内置漏洞扫描,可以轻松扫描容器中的已知漏洞。 让我们看看我们能找到些什么。
哇,这次两者之间有很大的不同! 较小容器镜像中只有三个“中级”漏洞,而较大容器镜像中有16个严重漏洞和300多个其他漏洞。
让我们深入了解更大容器镜像里面存在的问题。
您可以看到大多数存在的问题与我们的应用程序无关,甚至没有我们的应用程序! 因为多阶段构建使用的是更小的基础镜像,所以可以产生漏洞的东西更少。
结论
使用小容器镜像的性能和安全优势不言而喻。使用小的基础镜像和builder pattern可以更容易地构建小镜像,并且还有许多其他技术可用于单个技术栈和编程语言,以最小化容器体积。 无论你做什么,你都可以确信你保持容器镜像最小化的努力是值得的!
原文链接:(复制打开或者打开阅读原文)https://cloudplatform.googleblog.com/2018/04/Kubernetes-best-practices-how-and-why-to-build-small-container-images.html
转载自:Docker。
投稿:有投稿意向技术人请在公众号对话框留言。
转载:意向文章下方留言。
更多精彩请关注 “数据和云” 公众号 。
招聘专栏
云和恩墨(北京)信息技术有限公司
Oracle 售前工程师(广州、深圳、上海、武汉、北京、石家庄)
Oracle 高级工程师(上海、深圳、北京、成都、昆明、贵州、西宁)
MySQL 技术经理(上海、南京、成都)
MySQL 工程师(上海、杭州)
超高待遇:丰厚的年终奖,五险一金,高额学习基金,团建旅游,法定节假日,福利假期等。
推荐他人成功入职有好礼(iPhone X)相送 。
投递简历至邮箱:hr@enmotech.com
资源下载
关注公众号:数据和云(OraNews)回复关键字获取
2018DTCC , 数据库大会PPT
2017DTC,2017 DTC 大会 PPT
DBALIFE ,“DBA 的一天”海报
DBA04 ,DBA 手记4 电子书
122ARCH ,Oracle 12.2体系结构图
2017OOW ,Oracle OpenWorld 资料
PRELECTION ,大讲堂讲师课程资料
近期文章
仅仅使用AWR做报告? 性能优化还未入门
实战课堂:一则CPU 100%的故障分析
杨廷琨:如何编写高效SQL(含PPT)
一份高达555页的技术PPT会是什么样子?
大象起舞:用PostgreSQL解海盗分金问题
ProxySQL!像C罗一样的强大
高手过招:用SQL解决环环相扣刑侦推理问题
如何构建尽可能小的容器镜像?相关推荐
- Docker多步构建更小的Java镜像
译者按: 最新版Docker将支持多步构建(Multi-stage build),这样使用单个Dockerfile就可以定义多个中间镜像用于构建,测试以及发布等多个步骤,并且有效减小最终镜像的大小. ...
- java镜像升级_Docker多步构建更小的Java镜像
译者按: 最新版Docker将支持多步构建(Multi-stage build),这样使用单个Dockerfile就可以定义多个中间镜像用于构建,测试以及发布等多个步骤,并且有效减小最终镜像的大小. ...
- docker java 最小镜像_Docker多步构建更小的Java镜像
[编者的话]最新版Docker将支持多步构建(Multi-stage build),这样使用单个Dockerfile就可以定义多个中间镜像用于构建,测试以及发布等多个步骤,并且有效减小最终镜像的大小. ...
- 手动构建自己的docker容器镜像实战
前言 之前的实战中,我们实战中,我们使用的镜像都是镜像仓库已有的镜像. 已有的镜像都是别人已经开发好上传的.今天我们一起来看看如何构建自己的镜像并上传到镜像仓库中.
- 创建尽可能小的 Docker 容器
当我们在使用 Docker 的时候,你会很快注意到你正在下载很多 MB 作为你的预先配置的容器.一个简单的 Ubuntu 容器很容易超过 200 MB,并且随着在上面安装软件,尺寸在逐渐增大.在某些情 ...
- 多平台容器镜像构建就看这一篇
前言 愿景与现实 早在1995年,就有"write once and run anywhere"(WORA,编写一次即可在任何地方运行)用于描述 Java 应用程序.时过20年,D ...
- 对容器镜像的思考和讨论
作者 | Liu,Bo 来源|阿里巴巴云原生公众号 前言 常言道,startup 有 startup 的好,大厂有大厂的好,那么大厂究竟好在哪呢?拿硅谷老牌大厂们 FLG 来说,如果要问最令人怀念的是 ...
- 云原生时代,如何保证容器镜像安全?
目录 遵从最佳实践,编写 Dockerfile 选择合适的基础镜像 以非 root 用户启动容器 采用多阶段构建 选择来源可靠且经常更新的镜像 用安全的方式构建容器镜像 使用容器镜像扫描 和极狐 Gi ...
- 腾讯云百万容器镜像安全治理运营实践
摘要 腾讯云容器服务TKE目前拥有国内超大规模的Kubernetes集群,运行了包括游戏.支付.直播.金融等多个应用场景,集群的稳定运行离不开安全能力的保驾护.腾讯云容器安全服务站在业内最前沿的云原生 ...
最新文章
- 平切分,分库,分表,主从,集群 数据库水平切分的实现原理解析
- Apache2 部署flask项目
- php 异常错误信息用处,关于PHP中异常错误的处理详细介绍
- 尚硅谷_springcloud(2020新版 思维导图_全网最火SpringCloud2020全家桶教程
- C++中public,protected,private的区别
- python爬虫网站简单_Python爬虫之简单爬虫框架实现
- 辞职前一定要三思的八个问题
- nuget.org 无法加载源 https://api.nuget.org/v3/index.json 的服务索引
- Exception:A generic error occurred in GDI+
- JavaScript数据结构与算法 - 散列表
- 聊聊Web 3为什么可以解决数据使用中存在的难题
- PDF转word的两种办法
- 振动数据 自相关计算时时延_振动诊断技术
- 加盟连锁如何降低风险?
- 超级计算机 人脑,世界第4超级计算机竟被人脑秒成渣,人类的大脑究竟多厉害?...
- leetcode 714. 买卖股票的最佳时机含手续费(java)
- 统计给定的n个数中,负数、零和正数的个数。
- c语言平台答案,C语言平台答案.docx
- 2008年网游发展热点:教育网游产业分析
- MacOS 的任务管理器
热门文章
- 前端:JS/25/DOM官方定义,DOM分类,HTML节点树(节点关系,节点类型,),核心DOM中公共的属性和方法(节点访问,查找DOM节点,节点属性,节点的创建,追加和删除)
- 个人博客网站文章添加目录导航
- Bootstrap翻页组件
- Math 对象的扩展
- 数值和布尔值的解构赋值
- CSS3否定伪类选择器
- android getresponsecode -1,getHttpResponseCode()在android 2.2中返回-1
- vb6.0企业版win7_比win7更快?官方精简win10:3G+不更新,旧电脑福音
- 爬虫python怎么下载_在网上下了一个 python 爬虫程序,怎么运行?
- linux线程同步互斥说法,linux线程间的同步与互斥知识点总结