如何减少 Docker 镜像大小:6 种优化方法

如果您想减少docker镜像的大小,您需要使用构建docker镜像的标准最佳实践。

本博客讨论了您可以快速实施的各种优化技术,以制作最小、最精简的 docker 镜像。我们还将介绍一些用于 Docker 镜像优化的最佳工具

Docker 作为一种容器引擎,可以轻松地获取一段代码并在容器内运行。它使工程师能够将所有代码依赖项和文件收集到一个位置,然后可以在任何地方快速轻松地运行。

“随处运行”镜像的整个概念始于一个名为 Dockerfile 的简单配置文件。首先,我们在 Dockerfile 中添加所有构建指令,例如代码依赖项、命令和基础镜像详细信息。

Docker 镜像优化需求

尽管Docker构建过程很简单,但许多组织都犯了一个错误,即构建 臃肿的 Docker 镜像 而没有优化容器镜像。

在典型的软件开发中,每个服务都会有多个版本/发布,每个版本都需要更多的依赖项、命令和配置。这给 Docker 镜像构建带来了挑战,因为现在——相同的代码需要更多的时间和资源来构建,然后才能作为容器运送。

我曾经见过这样的情况:初始应用程序映像大小为 350MB,随着时间的推移,其大小增长到 1.5 GB 以上。

此外,通过安装不需要的库,我们增加了攻击面,从而增加了潜在安全风险的可能性。

因此,DevOps 工程师必须优化 docker 镜像,以确保 docker 镜像在应用程序构建或未来发布后不会变得臃肿。不仅限于生产环境,在 CI/CD 流程的每个阶段,您都应该优化 docker 镜像。

此外,使用Kubernetes 等容器编排工具时,最好使用小尺寸的镜像,以减少镜像传输和部署时间

如何减少 Docker 镜像大小?

如果我们采用典型应用程序的容器镜像,它包含基本镜像、依赖项/文件/配置垃圾(不需要的软件)。

所以一切都归结为我们如何有效地管理容器镜像中的这些资源。

让我们看看优化 Docker 镜像的不同既定方法。此外,我们还提供了实际示例,以便实时了解 Docker 镜像优化。

您可以使用文章中给出的示例,也可以在现有的 Dockerfile 上尝试优化技术。

下面我们可以通过以下几种方法来实现docker镜像的优化。

  1. 使用 distroless/minimal 基础镜像
  2. 多阶段构建
  3. 最小化层数
  4. 了解缓存
  5. 使用 Dockerignore
  6. 将应用程序数据保存在其他地方

Docker 练习文件:本文中使用的所有应用程序代码、Dockerfile 和配置都托管在这个 Github 存储库中。您可以克隆它并按照本教程进行操作。

方法 1:使用最小基础镜像

您首先应该关注的是选择具有最小操作系统占用空间的正确基础映像。

一个这样的例子是 alpine 基础镜像。Alpine 镜像可以小到 5.59MB。它不仅小,而且非常安全。

alpine       latest    c059bfaa849c     5.59MB

Nginx alpine基础镜像仅有 22MB。

默认情况下,它带有 sh shell,可通过附加它来帮助调试容器。

您可以使用distroless images进一步减小基础镜像大小。它是操作系统的精简版。Distroless 基础镜像适用于 java、nodejs、python、Rust 等。

Distroless 镜像非常小,甚至没有 shell。那么,你可能会问,那么我们如何调试应用程序呢?他们有与 busybox 一起提供的相同镜像的调试版本,用于调试。

此外,现在大多数发行版都具有最少的基础图像。

注意:您不能直接在项目环境中使用公开可用的基础镜像。您需要获得企业安全团队的批准才能使用基础镜像。在某些组织中,安全团队本身会在测试和安全扫描后每月发布基础镜像。这些镜像将在通用组织 docker 私有存储库中提供。

方法 2:使用 Docker 多阶段构建

多阶段构建模式是从构建器模式的概念发展而来的,我们使用不同的 Dockerfile 来构建和打包应用程序代码。尽管这种模式有助于减小镜像大小,但在构建管道时,它几乎没有开销。

在多阶段构建中,我们获得了与构建器模式类似的优势。我们使用中间映像(构建阶段)来编译代码、安装依赖项并打包文件。其背后的想法是消除映像中不需要的层。

之后,仅将运行应用程序所需的必要应用文件复制到仅具有所需库的另一个映像中,即可更轻地运行应用程序。

让我们借助一个实际示例来看一下它的实际效果,其中我们创建了一个简单的 Nodejs 应用程序并优化了它的 Dockerfile。

首先,让我们创建代码。我们将拥有以下文件夹结构。

├── Dockerfile1
├── Dockerfile2
├── env
├── index.js
└── package.json

将以下内容保存为index.js

将以下内容保存为index.js

const dotenv=require('dotenv'); 
dotenv.config({ path: './env' });dotenv.config();const express=require("express");
const app=express();app.get('/',(req,res)=>{res.send(`Learning to Optimize Docker Images with DevOpsCube!`);
});app.listen(process.env.PORT,(err)=>{if(err){console.log(`Error: ${err.message}`);}else{console.log(`Listening on port ${process.env.PORT}`);}}
)

将以下内容保存为package.json

{"name": "nodejs","version": "1.0.0","description": "","main": "index.js","scripts": {"test": "echo \"Error: no test specified\" && exit 1"},"keywords": [],"author": "","license": "ISC","dependencies": {"dotenv": "^10.0.0","express": "^4.17.2"}
}

将以下端口变量保存在名为 的文件中env

PORT=8080

该应用程序的简单做法Dockerfile是这样的 – 将其另存为Dockerfile1

FROM node:16COPY . .RUN npm installEXPOSE 3000CMD [ "node", "index.js" ]

让我们看看构建它所需的存储空间。

docker build -t devopscube/node-app:1.0 --no-cache -f Dockerfile1 .

构建完成后。让我们使用以下方法检查其大小:

docker image ls

这就是我们所得到的。

devopscube/node-app   1.0       b15397d01cca   22 seconds ago   910MB

所以尺寸是910MBs

现在,让我们使用此方法来创建多阶段构建。

我们将使用基础镜像,即所有依赖项和模块安装的node:16镜像,之后,我们将内容移至最精简、更轻量的基于 ' ' 的镜像中。' ' 镜像具有最低限度的实用程序,因此非常轻量。alpinealpine

以下是 Docker 多阶段构建的图示。

此外,在单个 中Dockerfile,您可以拥有多个具有不同基础映像的阶段。例如,您可以拥有具有不同基础映像的构建、测试、静态分析和打包的不同阶段

让我们看看新的 Dockerfile 是什么样子。我们只是将必要的文件从基础镜像复制到主镜像。

将以下内容保存为Dockerfile2

FROM node:16 as buildWORKDIR /app
COPY package.json index.js env ./
RUN npm installFROM node:alpine as mainCOPY --from=build /app /
EXPOSE 8080
CMD ["index.js"]

让我们看看构建它所需的存储空间。

docker build -t devopscube/node-app:2.0 --no-cache -f Dockerfile2 .

构建完成后。让我们使用以下代码检查其大小

docker image ls

这就是我们所得到的。

devopscube/node-app   2.0       fa6ae75da252   32 seconds ago   171MB

因此,与具有所有依赖项的图像相比,新的缩小图像大小为 171MB 。

这优化了超过 80%!

但是,如果我们使用与构建阶段相同的基础图像,就不会看到太大的差异。

您可以使用 distroless images 进一步减小图像大小。以下是Dockerfile使用 Google nodeJS distroless 图像(而不是 alpine)的多阶段构建步骤。

FROM node:16 as buildWORKDIR /appCOPY package.json index.js env ./RUN npm installFROM gcr.io/distroless/nodejsCOPY --from=build /app /EXPOSE 3000CMD ["index.js"]

如果你构建上述 Dockerfile,你的镜像将有118MB

devopscube/distroless-node   1.0       302990bc5e76     118MB

方法 3:最小化层数

Docker 镜像的工作方式如下 - 每个RUN, COPY, FROMDockerfile 指令添加一个新层,每个层都会增加构建执行时间并增加镜像的存储要求。

让我们借助一个实际示例来看一下它的实际效果:让我们创建一个包含更新和升级的库的 ubuntu 映像,并安装一些必要的包,例如 vim、net-tools、dnsutils。

实现此目的的方法Dockerfile如下 - 将其另存为Dockerfile3

FROM ubuntu:latestENV DEBIAN_FRONTEND=noninteractiveRUN apt-get update -yRUN apt-get upgrade -yRUN apt-get install vim -yRUN apt-get install net-tools -yRUN apt-get install dnsutils -y

我们还希望了解该图像的构建时间。

Docker 守护进程具有内置功能,可以显示 Dockerfile 所需的总执行时间。

要启用此功能,请执行以下步骤 -

  1. 在以下位置创建daemon.json包含以下内容的文件/etc/docker/
{"experimental": true
}

2.执行以下命令来启用该功能。

export DOCKER_BUILDKIT=1

让我们构建它并查看存储和构建时间。

time docker build -t devopscube/optimize:3.0 --no-cache -f Dockerfile3 .

它会在终端显示执行时间。

time docker build -t devopscube/optimize:3.0 --no-cache -f Dockerfile3 .[+] Building 117.1s (10/10) FINISHED                                                                   => [internal] load build definition from Dockerfile                                              
.
.
.
.                                                                       => => writing image sha256:9601bcac010062c656dacacbc7c554b8ba552c7174f32fdcbd24ff9c7482a805      0.0s => => naming to docker.io/devopscube/optimize:3.0                                                0.0s real    1m57.219s                                                                                      
user	0m1.062s
sys	0m0.911s

构建完成后,执行时间为 117.1 秒

让我们使用以下方法检查其大小

docker image ls

这就是我们所得到的。

devopscube/optimize  3.0   9601bcac0100   About a minute ago   227MB

因此大小为 227MB

让我们将 RUN 命令合并到单个层中并将其保存为 Dockerfile4。

FROM ubuntu:latestENV DEBIAN_FRONTEND=noninteractiveRUN apt-get update -y && \apt-get upgrade -y && \apt-get install --no-install-recommends vim net-tools dnsutils -y

在上面的 RUN 命令中,我们使用了--no-install-recommends标志来禁用推荐的软件包。建议installDockerfiles

让我们看看构建它所需的存储和构建时间。

time docker build -t devopscube/optimize:4.0 --no-cache -f Dockerfile4 .

它会在终端显示执行时间。

time docker build -t devopscube/optimize:0.4 --no-cache -f Dockerfile4 .[+] Building 91.7s (6/6) FINISHED                                                                      => [internal] load build definition from Dockerfile2                                             0.4s
.
.
.  => => naming to docker.io/devopscube/optimize:4.0                                                0.0s real    1m31.874s                                                                                      
user	0m0.884s
sys	0m0.679s

构建完成后,执行时间为 91.7 秒。

让我们使用以下方法检查其大小

docker image ls

这就是我们所得到的。

devopscube/optimize  4.0   37d746b976e3   42 seconds ago      216MB

因此大小为 216MB。

使用这种优化技术,执行时间从 117.1 秒减少到 91.7 秒,存储大小从 227MB 减少到 216MB。

方法 4:了解缓存

通常,只需对代码进行少许修改即可一次又一次地重建相同的图像。

由于 Docker 使用分层文件系统,因此每条指令都会创建一个层。因此,Docker 会缓存该层,如果该层未发生更改,则可以重复使用它。

由于这个概念,建议在COPY 命令之前在–内添加用于安装依赖项和包的Dockerfile

这样做的原因是,docker 能够缓存具有所需依赖项的图像,然后在代码修改时可以在后续构建中使用此缓存。

此外, a 中的COPYADD指令Dockerfile会使后续层的缓存无效。这意味着,Docker 将在 COPY 和 ADD 之后重建所有层。

这意味着,建议在 Dockerfile 中尽早添加不太可能改变的指令。

例如,我们来看看下面两个Dockerfile。

Dockerfile 5(良好示例)

FROM ubuntu:latestENV DEBIAN_FRONTEND=noninteractiveRUN apt-get update -y && \apt-get upgrade -y && \apt-get install -y vim net-tools dnsutilsCOPY . .

Dockerfile 6(次优示例)

FROM ubuntu:latestENV DEBIAN_FRONTEND=noninteractiveCOPY . .RUN apt-get update -y && \apt-get upgrade -y && \apt-get install -y vim net-tools dnsutils

由于COPY 命令的位置更佳,Docker 能够更好地使用缓存功能。Dockerfile5Dockerfile6

方法 5:使用 Dockerignore

通常来说,只需要将必要的文件复制到 docker 镜像上。

如果在文件中配置了,Docker 可以忽略工作目录中存在的文件.dockerignore 

它还通过忽略不必要的文件来改进缓存,并防止不必要的缓存无效。

在优化docker镜像时应该牢记这个特性。

方法 6:将应用程序数据保存在其他地方

将应用程序数据存储在图像中会不必要地增加图像的大小。

强烈建议使用容器运行时的卷功能将图像与数据分开。

如果你正在使用 Kubernetes,请确保

Docker 镜像优化工具

以下是一些可帮助您优化 Docker 镜像的开源工具。您可以选择一个工具并将其作为 Docker 镜像管道的一部分,以确保仅为应用程序部署创建优化的镜像。

1. Dive

这是一个图像浏览器工具,可帮助您发现 Docker 和 OCI 容器图像中的层。使用 Dive,您可以找到优化 Docker 图像的方法。查看Dive Github repo了解更多详细信息。

2. SlimtoolKit

它可以帮助您优化 Docker 镜像的安全性和大小。查看Docker Slim Github repo了解更多详情。您可以使用 Slim 将 docker 镜像大小缩小至原来的 1/30。

3. Docker Squash

此实用程序可帮助您通过压缩图像层来减小图像大小。使用squash 标志,Docker CLI 中也提供压缩功能。

我会继续将工具添加到此列表中。

总结

上述方法应该可以帮助您构建优化的 Docker 镜像并编写更好的 Dockerfile。

此外,如果您遵循所有标准容器最佳实践,则可以减小docker 镜像大小以实现轻量级镜像部署。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/400296.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

k8s核心架构分析

k8s核心概念概述 Kubernetes入门:掌握集群核心,释放容器潜能 技术爱好者们,CD集群的核心概念是构建、部署和管理容器化应用的基石。掌握这些概念,不仅助你深入理解技术细节,更能在CD集群中自如操作,无论是…

2 C 语言开发工具选择、 MinGW 的安装与配置、VS Code 的安装与配置、插件推荐

目录 1 开发工具选择 1.1 Visual Studio 1.2 Code::Block 1.3 Clion 1.4 VS Code 1.5 在线编辑工具 2 开发工具安装 2.1 安装 MinGW-w64 2.1.1 MinGW-w64 介绍 2.1.2 解压 MinGW 2.1.3 将 MinGW 添加至环境变量 2.1.4 验证安装 2.2 安装 VS Code 2.2.1 下载安装包…

Avnet ZUBoard 1CG开发板上手—深度学习新选择

Avnet ZUBoard 1CG 开发板上手—深度学习新选择 摘要 本文主要介绍了 Avnet ZUBoard 1CG 开发板的特性、架构、硬件单元等概念,并对如何使用以太网接口和串口连接开发板进行基本介绍,同时辅以两个应用例程演示其功能。 原文链接: FreakSt…

如何编写一个CMakeLists.txt文件(由简到难,较详细)

在Linux系统下,经常使用CMakeLists.txt文件来链接、编译C工程,大部分人clone的代码里都是有CMakeLists.txt文件的,只需要cmake .. 和make就完事了,但在工作中,你必须要有从无到有编写CMakeLists.txt文件的能力。 一、…

【QGroundControl二次开发】十. QT添加GStreamer视频播放同时保存

上一章介绍使用QT播放GStreamer视频流 【QGroundControl二次开发】八. QT实现播放gstreamer视频。 这章介绍如何在原有基础上保存为视频,同时保存为一个个规定大小的小视频。 一. 思想 之前的文章展示了如何在QT中播放GST视频流,这章在原有的基础上增加…

金九银十,软件测试面试题合集(含答案)

前言 前面看到了一些面试题,总感觉会用得到,但是看一遍又记不住,所以我把面试题都整合在一起,都是来自各路大佬的分享,为了方便以后自己需要的时候刷一刷,不用再到处找题,今天把自己整理的这些…

常见的几种用例测试方法

等价类划分法 适用场景:需要有大量的测试数据输入,但是我们实际测试中不可能一一列举进行测试,所以讲数据进行分类,选出具有代表性的数据代表一类数据进行测试。 分类: 有效等价类:满足需求的数据无效等…

普元EOS-新项目不停提示登录信息已过期

1 问题 新创建的EOS精简应用, 项目端口为 28015 启动后,在浏览器输入地址 http://127.0.0.1:28015 。 页面不停提示 “登录信息已过期” 2 解决办法 EOS的项目对Login-Filter的配置错误, EOS的项目在Http安全过滤管理的时候,会…

【原创】java+swing+mysql商品信息管理系统设计与实现

个人主页:程序员杨工 个人简介:从事软件开发多年,前后端均有涉猎,具有丰富的开发经验 博客内容:全栈开发,分享Java、Python、Php、小程序、前后端、数据库经验和实战 开发背景: 使用javaswing技…

使用mybatis注解和xml映射执行javaWeb中增删改查等操作

Mapper接口 使用注解执行SQL语句操作和相应的Java抽象类(对于简单的增删改查使用注解) Mapper public interface EmpMapper {// 根据id删除员工信息Delete("delete from mybatis.emp where id#{id}")public int EmpDelete(Integer id);// 查…

【mysql 第一篇章】系统和数据库的交互方法

一、宏观的查看系统怎么和数据库交互 在我们刚刚接触系统和数据库的时候不明白其中的原理,只知道系统和数据库是需要交互的。所以我们会理解成上图的形式。 二、MYSQL 驱动 随着我们的学习时间的加长以及对程序的了解,发现链接数据库是需要有别的工具辅…

可乐机的设计验证

前言 状态机(State Machine)是一种数学模型,用于表示具有有限状态集合的系统。它通过定义状态、转移规则和事件,描述系统在不同条件下的行为。状态机的核心概念包括状态、事件、转移和动作。状态是系统的具体条件或配置&#xff0…

【Python】函数入门(下)

3))* ** ​​​​​​注意:也遵循位置传参在前面,按关键字传参在后面。 代码示例: def func(*args,**kwargs):print(args,kwargs) 该函数中的参数会自动根据传参的方式不同(即:按位置…

k8s 四种Service类型(ClusterIP、NodePort、LoadBalancer、ExternalName)详解

🐇明明跟你说过:个人主页 🏅个人专栏:《Kubernetes航线图:从船长到K8s掌舵者》 🏅 🔖行路有良友,便是天堂🔖 目录 一、引言 1、k8s概述 2、Service在Kubernetes中的…

【Redis】事务

目录 什么是事务 事务操作 MULTI EXEC DISCARD WATCH UNWATCH 什么是事务 Redis 的事务和 MySQL 的事务概念上是类似的. 都是把⼀系列操作绑定成⼀组. 让这⼀组能够批量执⾏. 但是注意体会 Redis 的事务和 MySQL 事务的区别: 弱化的原⼦性: redis 没有 "回滚机制…

Zabbix自动导出PDF报告

zabbix6提供了定时导出PDF报告功能。此功能可按照Dashboard维度,定时自动导出报告,并通过邮件发送。 1.安装 zabbix 提供了官方的rhel8版本的rpm包,可使用yum方式安装,zabbix自动导出PDF功能是基于go环境的zabbix web service程…

C语言 ——— 在杨氏矩阵中查找具体的某个数

目录 何为杨氏矩阵 题目要求 代码实现 何为杨氏矩阵 可以把杨氏矩阵理解为一个二维数组,这个二维数组中的每一行从左到右是递增的,每一列从上到下是递增的 题目要求 在杨氏矩阵中查找具体的某个数 要求:时间复杂度小于O(N) 代码实现…

如何定义和引用二维数组

一.二维数组 常称为矩阵,把二维数组写成行和列的排列形式。、 二.怎么定义二维数组 float pay[3][5]; 以上定义了一个float型的二维数组,第1维有3个元素,第2维有6个元素。每一维的长度分别用一对方括号括起来。 二维数组定义的一般形式为 …

基于STM32开发的智能家居照明控制系统

目录 引言环境准备工作 硬件准备软件安装与配置系统设计 系统架构硬件连接代码实现 初始化代码控制代码应用场景 家庭照明自动化节能照明管理常见问题及解决方案 常见问题解决方案结论 1. 引言 智能家居照明控制系统通过整合各种传感器和控制器,能够实现对家居照…

基于ICMP(Ping)的多线程网络通道监视程序(QT)开发

基于ICMP(Ping)的多线程网络通道监视程序(QT)开发 1、 ICMP原理简介 可参考 ICMP(Ping)功能原理及其C实现简介 。 2、 网络通道监视程序开发 设计原理: 通过PING 功能实现服务器、交换机、网闸等设备的网络检测,判断网络的否可达和TTL计算 。 具备功…