devops的世界|docker如何构建尽可能小的容器镜像?

概述

Docker使构建容器镜像变得轻而易举。只需将标准 Dockerfile 放入您的文件夹,运行 docker build 命令,然后运行,芝麻开门!您的容器镜像已构建成功!

这种简单性的缺点是,很容易构建出大体积的容器镜像,其中包含很多不需要的东西——包括潜在的安全漏洞。

因为使用的是解释型语言还是编译型语言,创建容器镜像的过程会有所不同。下面一起深入了解(下面通过dockerfile模拟):


解释型语言的容器化

解释型语言,如Ruby,Python,Node.js,PHP和其他语言通过发送源代码到解释器来运行代码。 这样的好处是可以跳过编译步骤,但其缺点是要求将解释器与代码一起丢进去。

幸运的是,大多数这些语言都提供了预构建的Docker容器,其中包含一个轻量级环境,允许运行更小的容器。

我们来看一个Node.js应用程序并对其进行容器化。

首先,使用node:onbuild镜像作为基础。 Docker容器的onbuild版本预先打包了您需要的所有内容,因此无需执行大量配置即可搞定。 这意味着Dockerfile非常简单(只有两行!)。 但是要付出的磁盘大小代价——差不多700MB!

devops的世界|docker如何构建尽可能小的容器镜像?

devops的世界|docker如何构建尽可能小的容器镜像?

通过使用较小的基础镜像(如Alpine),您可以显著减少容器的大小。Alpine Linux是一款体积小,轻量级的Linux发行版,在Docker用户中非常受欢迎,因为它与许多应用程序兼容,同时仍然保持小体积。

幸运的是,Node.js(以及其他流行语言)有一个官方的Alpine图像,可以满足您的一切需求。与默认的node镜像不同,node:alpine会删除许多文件和程序,只留下足以运行应用程序的部分。

基于Alpine Linux的Dockerfile创建起来有点复杂,因为必须运行一些针对onbuild的命令。

devops的世界|docker如何构建尽可能小的容器镜像?

但是,产生的镜像只有65MB!

devops的世界|docker如何构建尽可能小的容器镜像?


编译型语言的容器化

诸如Go,C,C ++,Rust,Haskell等编译型语言可以创建在没有许多外部依赖性的情况下运行的二进制文件。 这意味着你可以提前构建二进制文件并将其投入生产,而无需把创建二进制文件(如编译器)放进去。

下面采用Go应用程序并使用此模式对其进行容器化。

首先,使用golang:onbuild镜像作为基础。 和以前一样,Dockerfile只有两行,但需付出磁盘大小超过700MB的代价!

devops的世界|docker如何构建尽可能小的容器镜像?

devops的世界|docker如何构建尽可能小的容器镜像?

下一步是使用更小的基础镜像,也就是golang:alpine镜像。

同样,使用Alpine基础镜像创建Dockerfile有点复杂,因为必须运行一些执行onbuild镜像相关的命令。

devops的世界|docker如何构建尽可能小的容器镜像?

但同样,由此产生的镜像要小得多,大小只有256MB!

devops的世界|docker如何构建尽可能小的容器镜像?

但是,我们可以使镜像更小:假设不需要Go附带的任何编译器或其他构建和调试工具,可以从最终容器中删除它们。

下面使用多阶段构建来获取由golang:alpine容器创建的二进制文件并将其自行打包。

devops的世界|docker如何构建尽可能小的容器镜像?

下面的这个容器镜像只有12MB大小!

devops的世界|docker如何构建尽可能小的容器镜像?


在Kubernetes上拉取镜像

虽然你可能不关心构建和推送容器镜像所需的时间,但应该非常关心拉取容器镜像所需的时间。 对于Kubernetes,这可能是您的生产集群最重要的指标。

例如,假设有一个三节点集群,其中一个节点崩溃。 如果使用的是像Kubernetes Engine这样的托管系统,系统会自动生成一个新节点来代替它。

但是,这个新节点将是全新的,并且必须先拉取所有容器镜像才能开始工作。 拉取容器镜像所需的时间越长,集群执行的时间就越长!

当增加群集大小(例如,使用Kubernetes Engine Autoscaling)或将节点升级到新版本的Kubernetes时,可能会发生这种情况(接下来的几集中会关注这个问题)。

我们可以看到来自多个部署的多个容器的拉取性能可以在这里真正体现出来,使用小容器可能会浪费几分钟的部署时间!


安全性和漏洞

除了性能之外,使用较小的容器镜像还有很大的安全性提升。 与使用大基础镜像的容器镜像相比,小容器镜像通常具有较小的攻击面。

几个月前,我构建了Go onbuild和multi-age容器镜像,因此它们可能包含一些已被发现的漏洞。 使用Google Container Registry的内置漏洞扫描,可以轻松扫描容器中的已知漏洞。

devops的世界|docker如何构建尽可能小的容器镜像?

小容器镜像

较小容器镜像中只有三个“中级”漏洞,而较大容器镜像中有16个严重漏洞和300多个其他漏洞。

devops的世界|docker如何构建尽可能小的容器镜像?

大容器镜像

大多数存在的问题与应用程序无关,甚至没有应用程序! 因为多阶段构建使用的是更小的基础镜像,所以可以产生漏洞的东西更少。


总结

使用小容器镜像的性能和安全优势不言而喻。使用小的基础镜像和builder pattern可以更容易地构建小镜像,并且还有许多其他技术可用于单个技术栈和编程语言,以最小化容器体积。 无论做什么,都可以确信保持容器镜像最小化的努力是值得的!

后期内容会更多分享DBA和DEVOPS内容,感兴趣的朋友可以关注下!

devops的世界|docker如何构建尽可能小的容器镜像?

相关推荐