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

news2024/9/21 12:35:37

如果您想减少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:16

COPY . .

RUN npm installEXPOSE 3000

CMD [ "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 build

WORKDIR /app
COPY package.json index.js env ./
RUN npm install

FROM node:alpine as main

COPY --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 build

WORKDIR /app

COPY package.json index.js env ./

RUN npm install

FROM gcr.io/distroless/nodejs

COPY --from=build /app /

EXPOSE 3000

CMD ["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:latest

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update -y

RUN apt-get upgrade -y

RUN apt-get install vim -y

RUN apt-get install net-tools -y

RUN 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:latest

ENV DEBIAN_FRONTEND=noninteractive

RUN 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:latest

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update -y && \
    apt-get upgrade -y && \
    apt-get install -y vim net-tools dnsutils

COPY . .

Dockerfile 6(次优示例)

FROM ubuntu:latest

ENV DEBIAN_FRONTEND=noninteractive

COPY . .

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.coloradmin.cn/o/2038110.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot快速入门(手动创建)

目录 案例:需求 步骤 1 创建Maven项目 2 导入SpringBoot起步依赖 3 定义Controller 4 编写引导类 案例:需求 搭建简单的SpringBoot工程,创建hello的类定义h1的方法,返回Hello SpringBoot! 步骤 1 创建Maven项目 大家&…

【Java】文件IO

文章目录 一、什么是文件二、硬盘1. 机械硬盘2. 固态硬盘 三、文件路径四、文件类型 文件 硬盘 系统管理文件,引入一个额专门的模块,“文件系统”,每个文件都有一个“路径”描述文件所在位置 一、什么是文件 此处谈到的文件,本…

SQL Zoo 8+.NSS Tutorial

以下数据来自SQL Zoo 1.at Edinburgh Napier University,studying (8) Computer Science,Show the the percentage who STRONGLY AGREE.(在爱丁堡纳皮尔大学,学习“计算机科学”,显示STRONGLY AGREE的百分比&#xff0…

解决navicat隔一段时间连接(操作,查询)就卡顿问题Navicat老是卡死,必须强制退出

解决方法: 打开编辑--->高级--->设置心跳时间30s(输入框设置为30 (建议30-240之间,不要超过240)– 点击确定!) 问题: 使用 Navicat 连接服务器上的数据库时,如果隔一段时间没有使用&…

餐厅管理系统

目录 一、 系统简介 1.1需求分析 餐厅管理系统是一个基于Javaweb的系统,旨在为音乐餐厅、情侣餐厅和中餐厅提供全面的管理和服务功能。系统主要包括餐厅详情管理、价格管理和加入购物车功能。 1.2 编程环境与工具 二、 系统总体设计 2.1 系统的功能模块图…

[log4cplus]: 快速搭建分布式日志系统

关键词: 日志系统 、日志分类、自动分文件夹、按时间(月/周/日/小时/分)轮替 一、引言 这里我默认看此文的我的朋友们都已经具备一定的基础,所以,我们本篇不打算讲关于log4cplus的基础内容,文中如果涉及到没有吃透的点,需要朋友们动动自己聪明的脑袋和发财的手指,进一…

C++进阶中多态的全部主要内容

今天小编和大家一起学习C中多态的全部主要内容,希望今天大家和小编一起学习之后,会对多态有一个初步的了解和使用,好啦,话不多说,开始学习!~~~ 一、多态的概念及满足条件 概念:指的就是不同的对…

NC单链表的排序

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 描述 给定一个节点…

九、OpenCVSharp 中的图像形态学操作

文章目录 简介一、腐蚀1. 腐蚀的原理和数学定义2. 结构元素的形状和大小选择3. 腐蚀操作的代码实现和效果展示二、膨胀1. 膨胀的概念和作用2. 与腐蚀的对比和组合使用(如开运算、闭运算)三、开运算1. 开运算的定义和用途(去除小的明亮区域)2. 开运算在去除噪声和分离物体方…

鸿蒙(API 12 Beta3版)【扩展屏投播开发指导】使用投播组件

通过本节开发指导,可在系统镜像投屏后,获取投屏设备信息,实现扩展屏模式的投播,实现双屏协作的能力。 运作机制 虚拟扩展屏 是在系统投屏启动过程中建立的,依据双端协商的投屏视频流的分辨率创建,支持1080…

OpenHarmony图像解码库—stb-image【GN编译】

简介 stb_image主要是C/C实现的图像解码库。 下载安装 直接在OpenHarmony-SIG仓中搜索stb-image并下载。 使用说明 以OpenHarmony 3.1 Beta的rk3568版本为例 库代码存放路径:./third_party/stb-image 修改添加依赖的编译脚本,路径:/devel…

哈希 | Java | (hot100) 力扣 49

我的想法:双重forhashset 方法一 由于互为字母异位词的两个字符串包含的字母相同,因此对两个字符串分别进行排序之后得到的字符串一定是相同的,故可以将排序之后的字符串作为哈希表的键。 力扣官方 values()方法返回一个包含HashMap中所有…

高性能日志系统 日志器模块

概述 作用:整合输出模块和格式化模块,创建日志器,通过该日志器对日志进行输出成员 格式化模块对象管理输出模块对象管理,数组管理(日志器可能会向多个位置进行日志输出)默认日志输出限制等级,只…

springboot智能城市交通管理系统-计算机毕业设计源码55174

目录 摘要 1 绪论 1.1 选题背景与意义 1.2国内外研究现状 1.3论文结构与章节安排 2系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1系统开发流程 2.2.2 用户登录流程 2.2.3 系统操作流程 2.2.4 添加信息流程 2.2.5 修改信息流程 2.2.6 删除信息流程 2.3 系统功能…

React + React-tsparticles + Tsparticles完成炫酷的登录特效

效果(动态) npm i react-tsparticles2.12.2 npm i tsparticles2.12.0 注意:最好和上面的版本一样,不然会出现一个报错,具体如何解决的话去官网吧,上面的版本是没有问题的 代码块 总计6个代码块, options里面是相关粒子的配置 完整代码 import ./index.sass import { Form, Inp…

5个人共享一台高配工作站流程运行SW UG等软件大装配设计

如何实现5个人共享一台高配工作站流程运行SW UG等软件大装配设计? 一、前期准备 硬件准备: 选购一台高性能的服务器作为云主机,确保服务器具备足够的计算能力、内存和存储空间以支持多用户并发使用。云主机需要结合企业具体的使用情况和人数…

基于vue框架的哀牢犁耙会助农系统r4347(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能:用户,商品分类,商品信息 开题报告内容 基于Vue框架的哀牢犁耙会助农系统 开题报告 一、研究背景与意义 1.1 研究背景 随着科技的飞速发展,农业现代化已成为全球农业发展的重要趋势。传统的农业生产方式已难以满足现代农…

windows使用vscode和cmake编译报错error C2001: 常量中有换行符

报错图: 解决方法:修改通过编码保存的格式为GBK

微信小程序--23(条件渲染)

一、wx&#xff1a;if 1.作用 来判断是否需要渲染 2.语句 wx&#xff1a;if “{{condition}}”来判断是否需要渲染该代码块 wx&#xff1a;elifwx&#xff1a;else 用来添加else判断 3.演示 二、<block> wx&#xff1a;if 1.作用 <block>标签&#xff1a;…

4章7节:用R做数据重塑,数据去重和数据的匹配

在数据科学的分析流程中&#xff0c;数据重塑是一项非常重要的操作。数据的重塑通常指将数据从一种形式转换为另一种形式&#xff0c;以满足后续分析的需求。R语言提供了丰富的工具和函数来帮助用户高效地进行数据重塑操作。本文中&#xff0c;我们将深入探讨数据重塑的概念及其…