Wayfair的Python平台团队维护了一组基本Docker镜像,供所有已部署的Python应用程序使用。维护这些基础图像使我们能够:

  • 标准化我们的环境(我们的所有图像都基于CentOS)
  • 简化应用程序开发人员的部署(预先配置为连接到我们内部PYPI和RPM镜像的图像船舶)
  • 提高安全性(我们采取措施硬化将部署到生产的图像)。

我们最近重构了我们的图像,使它们更有效减少我们的图像大小超过50%。这些改进不仅仅是开发人员友好的优化:除了降低图像构建时间和存储要求之外,许多优化使图像更安全,因为降低图像复杂性减少了整个攻击表面区域。阅读,看看我们如何实现这一伟大的结果。

优化工具

我们使用和推荐潜水介绍图片。它提供了快速探索的图形界面图像层并计算“效率分数”,突出显示空间浪费的空间。

以下是图像的示例潜水报告centos: centos7,这类似于在Wayfair中使用的基础CentOS图像。

相比之下,这里是我们在CentOS 7.5上运行Python 3.8的未优化基础图像之一的潜水输出:一个高达931MB.(作为比较的基础,开源软件的总规模python: 3.8.5图像是882MB)。

继承自CentOS基础图像的层用黄色高亮显示,而在Python图像中引入的最大层用红色高亮显示。Dive显示生成所选层的命令(以白色突出显示)。

为了优化我们的图像,我们介绍了每个最大层,以识别可以消除或修剪的层。以下是图像优化版本的潜水报告。

优化后,最终图像是414MB,减少超过50%!我们通过以下策略实现了这一减少。

优化策略

当安装包时,清理在同一层

上述未优化图像中的最大层为324MB,并从命令产生yum -y安装libcurl。该图层非常大,因为yum命令生成高速缓存,引入了重要的cruft。

因此,在运行yum install时,最好在一个命令中安装所有包,并始终在同一步骤中删除缓存。

从中心:Centos7#避免这个运行yum安装-y foo运行yum安装-y bar#更好,但仍然糟糕的运行yum安装-y \ foo \ bar#更喜欢这个运行yum安装-y \ foo \ bar \ && yum clean所有\ && rm -rf / var / cache / yum

通过更新命令安装libcurl && yum clean all && rm -rf /var/cache/yum,层尺寸从324MB减小到23MB,减少93%

同样的原则也适用于其他语言的包管理器;大多数都针对尺寸敏感的Docker图像的本地开发进行了优化。始终尝试在一个步骤中安装所有包,并禁用包管理器的缓存。例如:

在构建或编译代码时使用多级构建

我们从源代码编译包含在基本映像中的Python发行版,这需要许多构建依赖项。总的来说,这些依赖项超过200MB,并且只在编译时使用。多级构建防止这些依赖性在最终图像中运输,提高图像尺寸和减少通过在部署图像中运送GNU工具链和其他二进制文件引入的攻击向量。

而不是这个:

来自Python-Base-Image:3.8.5#安装构建Python包所需的一些系统依赖性运行yum安装-y \ gcc \ && yum clean all && rm -rf / var / cache / yum copy要求.txt。运行pip install-r要求.txt复制应用程序代码/。entrypoint [“python”] cmd [“script.py”]

喜欢这个:

来自Python-Base-Image:3.8.5作为Builder#安装构建Python软件包所需的某些系统依赖项运行yum安装-y \ gcc \ && yum clean \ && rm -rf / var / cache / yum#创建virtureenv将依赖关系运行Python-M venv / opt / venv env路径=“/ opt / venv / bin:$ path”#将依赖项安装成虚拟环境副本要求。运行pip安装-r要求。从python-base-moder:3.8.5 #popy over virtualenv copy --from = builder / opt / venv / opt / venv Env path =“/ opt / venv / bin:$ path“复制应用程序代码/。entrypoint [“python”] cmd [“script.py”]

如上所述在Docker Image中使用VirtualEnv可能似乎是违反直接的,因为码头容器本质上是隔离的,但它们很容易复制构建阶段之间的所有Python依赖关系。小心这种方法pip安装具有动态链接依赖项的软件包(例如,程序包取决于未与包本身捆绑在一起的C库),因为这些依赖项也必须安装在最终图像中,而不仅仅是在中间构建步骤中。

避免运行chmod奔驰步骤

当执行更改文件权限或文件所有权的命令时,Docker会创建该文件或目录的副本。这可能会导致严重的膨胀。避免这种情况发生,乔恩在同一步骤中,内容被复制到图像中:

来自CentOS:Centos7#不执行此副本./ /应用程序运行chown -R 1001:100 / app#更喜欢此副本--chown = 1001:100 ./ / app

码头工人了最近实施一种修改文件权限选择复制,但它只可用Buildkit后端启用。在使用传统构建后端时,要么在文件复制到映像之前设置权限模式,要么使用多级构建,其中修改文件权限命令仅在中间图像中运行。

通过消除,我们能够从图像中消除185MB修改文件权限命令。

在将应用程序数据复制到图像之前,批判性思考

我们遇到的许多Dockerfiles包括一个复制。/。/将所有应用程序数据复制到映像中的最后步骤之一。虽然这样做很方便,但可能会导致生产映像(测试套件、文档等)中出现不必要的麻烦。

考虑包括.dockerignore文件在项目中,故意排除在最终图像中的不必要的文件。

结论

如果您发现了这个有趣的话,您可以欣赏Wayfair的阅读创新的方法构建一个Python平台团队,或者听这个播客.__ init__关于Python在Wayfair的剧集。如果您想了解更多,请伸出手!我们是目前招聘,并一直在寻找高级蟒蛇系加入我们的团队。