当前位置: 首页 > news >正文

优化 Docker 构建之方法(Methods for Optimizing Docker Construction)

优化 Docker 构建之方法

优化 Docker 构建不仅是为了提高效率,它还是降低部署成本、确保安全性和保持跨环境一致性的有效方法。每个层、依赖项和配置选择都会影响镜像的大小、安全性和可维护性。大型镜像部署速度较慢且消耗更多资源,这会增加成本,尤其是在规模较大的情况下。此外,未优化的镜像通常包含过时或不必要的软件包,从而引入潜在的漏洞。

Docker 镜像是现代 CI/CD 工作流程的基础,优化良好的镜像与臃肿的镜像之间的差异可能会影响从部署速度到运行时性能的一切。

本指南提供了 13 种高级技术,帮助工程师简化 Docker 镜像并构建工作流程,从多阶段构建到资源限制和漏洞扫描。

何时关注优化

每当 Docker 构建减慢部署管道速度或镜像大小影响性能和存储成本时,优化都应成为优先事项。当您注意到构建时间增加、资源使用量超出可接受限度或安全要求强制使用精简、强化的镜像时,就开始关注优化。使用微服务的团队将特别受益于优化,因为更小、更高效的镜像可以减少延迟和加载时间,从而实现更快的扩展和恢复。

Docker 构建优化面临的主要挑战

Docker 构建虽然灵活,但在优化方面也面临独特的挑战。Dockerfile 中的每条指令都会创建一个层,如果管理不当,可能会使镜像膨胀。随着时间的推移,镜像可能会充满冗余或过时的层,从而减慢构建和部署的速度。依赖管理是另一个挑战;镜像很容易被生产中不必要的库和工具弄得乱七八糟,这不仅会增加镜像大小,还会引入漏洞。最后,如果处理不当,缓存失效可能会通过强制不必要的重建而浪费资源,尤其是在迭代开发环境中。

选择正确的技术

每种优化技术都有其用例,选择正确的技术取决于您的特定需求。例如,多阶段构建对于具有复杂构建依赖项的应用程序至关重要,因为它们将构建时工具与运行时分开,从而产生更小、更干净的镜像。缓存管理在 CI/CD 管道中非常有用,重复构建节省的时间可以快速累积。同时,较小的基础镜像和谨慎的依赖项选择对于减少攻击面至关重要,这对于生产级安全性至关重要。本指南通过示例和指导来分解每种技术,说明何时以及如何应用它们。

您将学到什么

通过遵循这些优化技术,您将学习如何将 Docker 构建转变为部署管道中精简、高效的部分。每个部分都探讨了不同的策略 - 从管理 Dockerfile 层和利用构建缓存到设置资源约束和集成自动安全扫描。目标是提供可以立即应用的实用、实际技术,确保您构建快速、轻量且安全的 Docker 映像。

以下部分将深入介绍每种方法,解释最佳实践、用例和应避免的常见陷阱,为您提供完整的 Docker 构建优化工具包。

1. 使用多阶段构建

多阶段构建是一种高级 Docker 技术,通过将构建过程与运行时环境分离来创建优化的映像。其核心思想是利用FROM单个 Dockerfile 中的多个语句将映像构建过程分解为不同的阶段。每个阶段可以使用不同的基础映像,并且通过仅将最终可执行文件或所需工件从构建阶段转移到最小生产阶段,您可以获得针对部署进行了优化的精简最终映像。

如何使用多阶段构建

要实现多阶段构建,首先要用指令定义每个阶段FROM。例如,在 Golang 应用程序中,第一阶段可能使用功能齐全的基础映像(如)编译应用程序golang:alpine,而第二阶段可能只将可执行文件复制到较小的、可用于生产的映像(如)alpine。构建阶段包含编译应用程序所需的所有依赖项和工具,但只有最终的可执行文件会被转移到生产映像。

以下是简单多阶段构建的 Dockerfile 示例:

# Stage 1: BuildFROM golang:alpine AS builderWORKDIR /appCOPY . .RUN go build -o myapp .
# Stage 2: ProductionFROM alpine:latestWORKDIR /root/COPY --from=builder /app/myapp .CMD ["./myapp"]

在此 Dockerfile 中,builder阶段编译 Go 应用程序,然后生产阶段仅提取已编译的二进制文件。这种方法从最终映像中删除了不必要的构建文件、库和工具,使其更小且部署速度更快。

何时使用多阶段构建

当您需要不同的开发和生产环境时,多阶段构建特别有用。它们非常适合需要大量编译或依赖项管理的应用程序,例如使用 C++、Golang 或 Java 构建的应用程序。通过使用多阶段设置,您可以避免携带构建工具,减少镜像大小并最大限度地减少生产中的攻击面。对于具有多个依赖项的复杂应用程序,多阶段构建提供了一种结构化的方法将所有步骤合并到单个 Dockerfile 中,从而更易于维护和跨团队共享。

多阶段构建的最佳实践

  1. 使用明确的阶段名称AS:使用、AS builder或 等为阶段分配清晰的名称AS final,以便您可以在以后的阶段轻松引用它们。

  2. 保持构建工件最少:仅将必要的文件传输到生产阶段。避免复制不必要的库或工件,因为这可能会增加镜像大小或引入漏洞。

  3. 使用较小的生产基础镜像:在生产阶段,使用最小镜像,例如alpinedistroless(如果兼容)。较小的镜像可以减少构建时间并通过限制潜在攻击媒介来增强安全性。

  4. 根据更改频率分开构建步骤:根据组件更改的频率组织 Dockerfile 指令。经常修改的文件应出现在 Dockerfile 的后面,以最大化 Docker 的层缓存。例如,像这样的行COPY . .通常应出现在末尾附近,以避免因代码细微更改而导致缓存失效。

2. 尽量减少层数

在 Docker 中,每条指令(RUNCOPYADD)都会在镜像中创建一个新层。最小化这些层对于减小镜像大小、缩短构建时间和提高部署性能至关重要。通过组合多条指令,您可以限制层数并使镜像更精简、更易于管理。以下是如何充分利用此技术。

如何根据RUN说明最小化层数

为了减少层数,RUN可以使用&&链式命令将命令合并为一条指令。这不仅可以减少层数,还可以使 Dockerfile 更具可读性和效率。例如,在一行中安装依赖项和删除包管理器缓存可以通过减少冗余数据来显著降低最终镜像大小。

RUN apt-get update && \    apt-get install -y curl wget && \    rm -rf /var/lib/apt/lists/*

此示例在一个命令中安装curlwget,然后立即清除包缓存以保持镜像精简。通过在单个 中链接这些操作RUN,您可以为每个命令创建一个层而不是多个层,从而使最终镜像更小。

组合COPYADD说明

对于文件复制,请尝试以允许更少指令的方式构造源文件和文件夹COPYCOPY您可以将各个目录合并到一个根目录中,并使用一条COPY指令将所有文件放入容器中,而不是使用多行指令来表示各个目录。这可以减少总体层数。​​​​​​

COPY ./src /app/srcCOPY ./config /app/config

如果可能的话,请合并这些目录,以便您只需使用一行COPY ./ /app。此策略可防止构建上下文膨胀并减少潜在的冗余,从而提高构建效率。

图层管理的最佳实践

  1. 层排序以实现最佳缓存:将不常更改的指令(例如软件包安装)放在 Dockerfile 的顶部,将经常修改的指令(例如COPY . .)放在末尾。这种方法利用 Docker 的层缓存来避免不必要的重建,从而提高构建性能。

  2. 清理临时文件:安装依赖项或编译代码后,删除所有中间文件。清理同一RUN指令内的文件可防止这些文件保留在层历史记录中,从而使您的镜像更小且更易于维护

RUN curl -sL https://example.com/script.sh | bash && \    rm -rf /tmp/* /var/tmp/*

        3. 使用最小基础镜像:轻量级基础镜像(例如alpine)是减少镜像层数和整体大小的首选。但是,始终确保您的应用程序与此类镜像兼容,因为某些库或命令在轻量级系统上的行为可能有所不同。

3. 利用构建缓存

Docker 的构建缓存通过重用未更改的层来优化构建过程,从而显著缩短构建时间。当 Docker 构建映像时,它会根据缓存检查 Dockerfile 中的每条指令。如果 Docker 检测到命令或文件未发生更改,它会重用该指令的缓存层,而不是从头开始重新创建。高效使用缓存可减少冗余构建并加快开发周期。

如何使用 Docker 构建缓存

为了有效利用缓存,请在 Dockerfile 的末尾添加经常更改的命令。当指令发生变化时,Docker 会重建所有后续层,因此将依赖项安装等静态层放在顶部可最大限度地利用缓存。

例如,将依赖文件与应用程序代码分离可防止每次代码更改时缓存失效。以下是此设置对于 Node.js 应用程序可能的样子:​​​​​​​

# Base ImageFROM node:20
# Set Working DirectoryWORKDIR /app
# Copy package files and install dependenciesCOPY package.json package-lock.json /app/RUN npm install
# Copy application code and buildCOPY . .RUN npm build

在此设置中,只有更改才会package.json触发依赖项的重新安装。对源代码的更改将仅影响下面的层RUN npm install,从而通过最大限度地减少缓存失效来确保更快的构建。

缓存管理的最佳实践

  1. 用于.dockerignore排除文件:.dockerignore文件可防止不必要的文件(例如.git文件夹、本地配置文件)被包含在构建上下文中。这可保持构建上下文精简,并降低由于不相关的文件更改而导致缓存失效的可能性。

  2. 分离依赖层:将依赖项与应用程序代码分开安装,以避免不必要的重建。例如,在 Python 项目中,requirements.txt先仅复制,安装包,然后添加应用程序代码。

  3. 使用 BuildKit 的缓存挂载:BuildKit 的缓存挂载功能可让您在构建过程中维护持久的包缓存,从而加快涉及下载包的操作。例如,添加RUN --mount=type=cache,target=/var/cache/apt基于 Debian/Ubuntu 的映像以缓存包安装。

  4. # Enabling BuildKit with cache mount for apt packagesFROM ubuntuRUN --mount=type=cache,target=/var/cache/apt \    apt update && apt install -y gcc

        4. 谨慎管理基础镜像版本:如果基础镜像更新(例如,更新node:20node:21),Docker 会使所有依赖它的层失效。固定版本并选择稳定的基础镜像可最大限度地减少跨环境的意外重建。

4.清理临时文件

在构建过程中删除临时文件和缓存有助于创建更精简的 Docker 镜像并最大限度地减少不必要的数据积累。Docker 中的每个命令都会创建一个层,因此在RUN生成临时文件的同一命令中删除临时文件非常重要,以避免将它们保留在中间层中。这种方法可以最大限度地减少最终镜像的大小,从而提高部署中的容器性能。

如何有效清理临时文件

要减小映像大小,请在安装或配置软件后立即删除包管理器缓存和其他临时文件。例如,在基于 Debian 的映像中,/var/lib/apt/lists/*可以在安装包后立即删除包缓存 ( ),这样可以使最终映像不包含缓存数据:​​​​​​​

RUN apt-get update && apt-get install -y \    curl wget && \    rm -rf /var/lib/apt/lists/*

类似地,对于 Alpine Linux,使用--no-cache带有选项apk可防止包索引文件存储在本地,从而无需进行额外的清理:

RUN apk add --no-cache curl wget

结合清理和构建步骤

对于创建仅临时需要的文件的命令,请确保在不再需要这些文件时立即删除它们。例如,如果您正在编译软件,请在同一命令中清理构建依赖项:​​​​​​​

RUN ./configure && \    make && \    make install && \    rm -rf /path/to/source/files /var/tmp/*

这种方法可以防止这些文件存储在中间层,从而保持镜像干净、高效。

最佳实践

  1. 使用链式命令&&:链式命令可让您在单个RUN指令内安装依赖项并进行清理,从而将所有操作保留在一层中。这样可以减小镜像大小并避免在中间层中保留临时数据。

  2. 限制中间文件的使用:如果需要中间工件(如下载的文件),请在一个命令中下载并使用它们,然后在同一层内删除它们。

  3. 尽可能使用轻量级镜像:诸如alpine或之类distroless的镜像本质上很小,通过遵循这些镜像中的清理实践,您可以进一步减少不必要的数据。

5.使用.dockerignore

文件.dockerignore会从 Docker 构建上下文中排除不必要的文件和目录,从而通过减少构建时间、最小化映像大小以及避免由于不相关的文件更改而导致缓存失效来帮助简化映像创建。它的工作原理类似于.gitignoreGit,会过滤掉指定的文件或文件夹,这样它们就不会在构建期间发送到 Docker 守护程序。

如何使用.dockerignore

.dockerignore文件放在构建上下文的根目录中(通常与 Dockerfile 位于同一目录),并定义要排除的文件和目录的模式。例如,您可以排除本地配置文件、文档或与最终容器无关的其他文件:​​​​​​​

# Exclude node_modules and Git filesnode_modules/.git/*.md

此配置会排除node_modules目录、所有与 Git 相关的文件和 markdown 文件,从而阻止它们进入构建上下文。这使构建专注于重要文件,从而加快构建速度并缩小镜像大小。

最佳实践.dockerignore

  1. 排除敏感文件或大型文件.git:排除容器中不需要的大型文件或目录(例如日志、目录、临时文件)。这可以减少构建上下文的大小,从而加快构建过程。

  2. 保持应用程序代码独立:定义排除任何可能频繁更改或仅与开发相关的本地文件或资产(例如环境文件)。例如,排除.env文件可防止敏感信息意外进入构建上下文和镜像。

  3. 优化缓存:减少构建上下文中的文件数量可提高缓存效率,因为文件数量越少,缓存失效的可能性就越小。这在 CI/CD 管道中尤其有用,因为缓存可以显著加快构建时间。

使用配置良好的.dockerignore文件可以使您的 Docker 镜像保持精简、安全且快速构建,并且每个构建上下文中仅包含必要的组件。

6. 固定基础镜像版本

在 Docker 中固定基础映像版本有助于确保构建的一致性,并避免由于基础映像更新而导致的意外更改。使用特定版本而不是浮动标签(如latest或 )stable有助于保持可预测的构建,因为这些浮动标签可能会引入新的层和依赖项,从而可能导致错误、增加映像大小或引入安全漏洞。

如何固定基础镜像版本

要固定映像,请在FROM指令中指定确切的版本标记。例如,FROM node:latest使用而不是FROM node:14.17.0来明确定义版本。这种方法可确保每次构建都使用完全相同的基础层,从而防止底层操作系统、软件包或库发生意外变化:

FROM node:14.17.0

在需要绝对一致性的情况下,尤其是出于安全性和合规性考虑,您可以使用摘要(引用特定的不可变映像版本)进一步锁定基础映像。例如

FROM node@sha256:13b7e62e8df80264dbb747995705a986aa530415763a6c58f84a3ca8af9a5bcd

使用摘要可确保即使node:14.17.0标签更新,您的构建也将继续使用完全相同的层。

固定基础镜像的最佳实践

  1. 使用固定标签来保证稳定性:使用主要和次要标签指定版本(例如)。避免使用诸如或 之类的14.17.0宽泛标签,因为它们会随着新版本更新,可能会影响依赖关系或破坏功能。14latest

  2. 定期审核和更新:虽然固定版本会将构建锁定到特定映像,但定期更新这些固定版本以从安全补丁和性能改进中受益非常重要。在集成新版本之前,请设置一个流程来审核和测试新版本。

  3. 在 CI/CD 中使用摘要实现可重复性:在需要绝对一致性的环境中(如 CI/CD),使用摘要锁定跨环境的精确版本。这样可以确保无论在何时何地构建映像,基础映像版本都是相同的。

通过遵循这些实践,固定基础镜像版本可增强 Docker 构建的稳定性、可预测性和安全性,使其成为可靠生产部署的重要策略。

7. 优化依赖管理

Docker 中高效的依赖项管理可减少镜像大小、加快构建速度并最大限度地减少安全漏洞。通过仅安装必要的软件包并避免不必要的库,您可以确保 Docker 镜像中的每一层仅包含应用程序有效运行所需的内容。

如何优化 Docker 中的依赖关系

要限制映像中包含的软件包,请在软件包管理命令中使用最少的标志。例如,在基于 Debian 的映像中,--no-install-recommends带有的标志apt-get可确保仅安装明确指定的软件包,而不安装可能不需要的其他推荐软件包:

RUN apt-get update && apt-get install -y --no-install-recommends curl

在基于 Alpine 的镜像中,使用apk add --no-cache有助于避免在本地缓存索引文件,从而使镜像更小:

RUN apk add --no-cache curl

对于需要特定语言包管理器的应用程序(例如npmNode.js 或pipPython),请避免在构建生产映像时安装开发依赖项。例如,npm install --only=production仅安装运行时所需的依赖项。

管理依赖关系的最佳实践

  1. 分离开发和生产依赖项:使用单独的依赖项文件或配置选项在最终映像中仅安装生产依赖项。例如,Node.js 项目可以在 中同时拥有devDependencies和,从而允许生产构建排除开发依赖项。dependenciespackage.json

  2. 安装后删除构建依赖项:在多阶段构建中,在构建阶段编译代码,然后仅将最终可执行文件传输到生产阶段。对于从源代码编译的应用程序,此方法可避免将整个工具链转移到生产映像中。

  3. 使用最少的基础镜像:尽可能从精简的基础镜像开始alpine。虽然它们可能需要调整兼容性,但此类镜像本身包含的库和依赖项较少,因此最终镜像更轻便。

  4. 定期更新和审核依赖项:监控依赖项是否存在安全漏洞,删除过时或未使用的软件包,以保持镜像精简和安全。自动化工具可以帮助识别和更新易受攻击的软件包,尤其是对于经常受到安全补丁影响的库。

优化依赖管理确保Docker镜像精简、高效、安全,在保持镜像轻量的同时提升应用程序性能。

8. 分层组织 Dockerfile(倒金字塔

通过策略性地构建 Dockerfile 层,可以最大限度地利用缓存并减少重建时间,从而显著提高构建效率。通过将不经常更改的层放在开头,将经常更新的指令(如源代码更改)放在结尾,您可以让 Docker 尽可能地重复使用缓存的层。

如何组织 Dockerfile 层

Dockerfile 组织中的“倒金字塔”方法的理念是从最稳定且更改频率最低的指令开始,例如基本映像选择和依赖项安装。例如,软件包安装应在 Dockerfile 的早期进行,因为与应用程序代码相比,这些依赖项不太可能发生变化。当指令以这种方式排序时,源代码中的更改不会使早期层的缓存失效,从而使 Docker 可以跳过重建映像的某些部分。

请考虑以下 Node.js 应用程序示例:​​​​​​​

# Use a stable base imageFROM node:16-alpine
# Set up working directoryWORKDIR /app
# Copy only package files and install dependenciesCOPY package.json package-lock.json ./RUN npm install
# Copy the application source codeCOPY . .
# Build the applicationRUN npm run build

这里,开头仅复制package.json和,然后复制。此顺序可确保应用程序代码单独更改不会影响缓存的依赖层。package-lock.jsonRUN npm install

图层组织的最佳实践

  1. 将稳定层置于顶部:从很少更改的层开始,例如基础映像和环境设置。此设置利用 Docker 的缓存来加快构建速度,因为稳定层仍处于缓存状态。

  2. 最小化缓存失效:通过将经常更新的代码放在末尾(例如COPY . .),Docker 在代码发生更改时只需要重建这一层和后续层,而不是重新处理依赖项安装或配置层。

  3. 尽早使用依赖特定指令:对于依赖性强的应用程序,请在 Dockerfile 中尽早执行依赖于包管理器(例如npm install, )的安装步骤,以避免在应用程序代码更改时进行不必要的重新安装。pip install

使用具有倒金字塔方法的有组织的层结构可以最大限度地减少重建时间,从而实现更快的构建和在 Docker 中高效的映像创建。

9. 使用 BuildKit 的缓存挂载

BuildKit 的缓存挂载功能提供了一种高级缓存机制,通过在构建过程中为包管理器和其他经常访问的资源维护持久缓存来加快构建速度。与基于层且每次更改都会失效的标准 Docker 缓存不同,BuildKit 缓存挂载可实现更细粒度和持久的缓存,对于需要重复下载但不经常更改的依赖项特别有用。

如何启用和使用缓存挂载

要利用缓存挂载,请确保通过设置DOCKER_BUILDKIT=1您的环境来启用 BuildKit。命令--mount=type=cache中的选项RUN指定容器中用于缓存的目标路径,BuildKit 会在构建过程中保留该路径。例如,apt在基于 Debian 的映像中缓存软件包:​​​​​​​

# Enable BuildKit# syntax=docker/dockerfile:1.2
FROM ubuntu:20.04# Use cache mount for apt cacheRUN --mount=type=cache,target=/var/cache/apt \    apt update && apt install -y curl

在此设置中,/var/cache/apt充当包管理器的缓存装载apt。此缓存在构建过程中保持不变,这意味着后续构建将跳过重新下载未更改的包,从而加快构建过程。

使用 BuildKit 缓存挂载的最佳实践

  1. 指定与包管理器相关的路径:仅为包管理器存储下载的目录定义缓存挂载,例如Ubuntu 中/var/cache/aptapt或Python 中/root/.cache/pippip。这可确保仅缓存相关的依赖项,从而优化构建效率。

  2. 对大型依赖项使用缓存挂载:对于涉及大量依赖项安装的构建,例如 Python 或 Node.js 包,请使用 BuildKit--mount=type=cache来避免重复下载。例如,pip在 Python 中使用缓存

RUN --mount=type=cache,target=/root/.cache/pip \    pip install -r requirements.txt

        3. 限制生产中的缓存挂载使用:缓存挂载在开发和测试阶段有利于快速迭代,但对于生产构建来说可能不是必需的,因为在生产构建中,一致性和可重复性优先于构建时间。

10. 扫描镜像以查找漏洞

扫描 Docker 镜像中的漏洞对于保护您的应用程序环境至关重要,可确保您的镜像中不存在系统库、依赖项和配置文件中的已知安全漏洞。通过扫描镜像,您可以在漏洞进入生产环境之前识别它们,从而降低漏洞被利用的风险并确保容器安全。

如何扫描 Docker 镜像

Docker 提供了docker scan由 Snyk 提供支持的命令,该命令可检查镜像中所有层和依赖项中是否存在已知漏洞。要扫描镜像,只需使用:

docker scan myimage

此命令会输出漏洞的详细报告,包括严重程度、受影响的软件包和补救建议。例如,库中的严重漏洞会被标记,并且您会收到有关更新或配置更改的建议,以缓解漏洞。

另外,其他开源工具(如Trivy或Anchore)也常用于 Docker 镜像扫描。例如,Trivy 易于集成到 CI/CD 管道中,并支持各种漏洞数据库,使其成为持续安全监控的理想选择。

漏洞管理的最佳实践

  1. 在 CI/CD 中自动扫描:将、Trivy 或 Anchore 等扫描工具集成docker scan到 CI/CD 管道中,以确保在部署之前检查每个镜像。这种方法可防止易受攻击的镜像进入生产环境,并为开发人员及时提供有关安全问题的反馈。

  2. 定期更新基础镜像:即使您的应用程序代码没有更改,底层基础镜像也可能有新的安全补丁。定期使用更新的基础版本重建镜像以纳入这些补丁。

  3. 使用最少且受信任的镜像:使用来自受信任来源的经过验证的基础镜像,例如 Docker 的官方镜像或 distroless 镜像,它们包含较少的不必要的库并降低了出现漏洞的可能性。

11. 使用较小的基础镜像

使用轻量级基础镜像是减少 Docker 镜像大小、提高构建速度和最大程度减少安全漏洞的有效方法。较小的镜像(例如alpine或distroless)仅包含运行应用程序所需的基本组件,使其成为优先考虑简约性和安全性的生产环境的理想选择。

如何选择和使用最小基础镜像

要减小映像大小,请先选择符合应用程序要求的轻量级基础映像。例如,Alpine Linux因其占用空间小且与各种库兼容而成为许多语言的热门选择。您可以使用以下命令将 Alpine 指定为基础映像:

FROM node:alpine

虽然 Alpine 与许多应用程序兼容,但由于 Alpine 使用许多其他发行版中的标准 C 库musl(而不是) ,因此某些软件可能需要额外设置。对于需要更高兼容性的应用程序, distroless 映像是一种替代方案。这些映像通常比 Alpine 更小,并且仅包含执行应用程序所需的运行时依赖项。glibc

例如,Java 应用程序的 distroless 镜像如下所示:

FROM gcr.io/distroless/java

使用最小基础镜像的最佳实践

  1. 为应用程序选择正确的基础:虽然alpine对于大多数应用程序来说效果很好,但具有广泛库的复杂应用程序可能需要更标准的基础debian-slim以实现兼容性。

  2. 删除不必要的软件包:避免添加运行时不必要的软件包。较小的镜像可减少攻击面和需要维护的依赖项数量。

  3. 定期重新评估基础映像选项:定期检查可用的基础映像,因为轻量级、安全的选项(如更新的无发行版)会不断发展。

12.设置资源限制

在 Docker 中设置资源限制有助于控制每个容器可以使用的 CPU 和内存量,确保容器不会耗尽主机系统的资源。这种方法对于生产环境至关重要,因为容器共享资源,不受控制的消耗会降低整体系统性能。

如何设置资源限制

Docker 允许使用标志docker run在 Kubernetes 等编排器中限制资源。CPU 和内存限制可以定义如下:

要限制 CPU 使用率,请使用该--cpus标志分配特定的 CPU 份额。例如:

docker run --cpus="1.5" myimage

此命令将容器限制为 1.5 个 CPU 核心。Docker 根据工作负载动态调整此分配,确保容器不会超出指定的限制。

对于内存限制,请使用--memory标志来限制容器的内存使用量。例如:

docker run --memory="512m" myimage

这会将容器的 RAM 限制为 512 MB。当容器超出此限制时,Docker 会限制内存访问,或者如果使用--memory-swapwith 0,则强制容器保持在设置的内存限制内而不进行交换。

资源限制的最佳实践

  1. 根据工作负载设置限制:分析每个应用程序的资源需求并相应地分配限制。例如,轻量级服务可能只需要 256 MB 内存,而数据密集型应用程序可能需要多个 CPU 核心和更多内存。

  2. 同时使用 CPU 和内存限制:同时设置内存和 CPU 限制可确保资源使用平衡,并防止一个容器独占单一资源类型。这在多个容器在同一主机上运行的多租户或高密度环境中尤其有用。

  3. 按需监控和调整:持续监控容器性能并根据实际使用模式调整资源分配。Prometheus 或 Dockerstats命令等工具可提供有关容器资源利用率的见解。

13. 使用基础镜像重用公共层

在多容器和微服务环境中,通常会有多个容器共享类似的依赖项、基础库或配置设置。通过创建包含这些共享组件的自定义基础映像,您可以缩短构建时间、提高缓存重用率并确保服务之间的一致性。这种方法在 CI/CD 管道中特别有用,因为容器经常被重建,因为它可以最大限度地减少重复工作并加快构建过程。

如何设置和使用共享基础镜像

要创建共享基础映像,请先使用仅包含多个服务所需的通用依赖项、库或配置设置的 Dockerfile。构建并标记此映像,然后将其引用为其他 Dockerfile 的基础映像。

例如,假设您有多个共享通用实用程序和配置的 Node.js 微服务。创建如下基础映像:​​​​​​​

# Dockerfile for shared base imageFROM node:14.17-alpineWORKDIR /app
# Install common utilities and dependenciesRUN apk add --no-cache bash curlRUN npm install -g nodemon
# Save this as an image
# docker build -t my-shared-base:1.0 .

现在,每个微服务都可以使用 Dockerfilemy-shared-base:1.0作为其基础,跳过重复安装并实现一致性:​​​​​​​

# Dockerfile for a specific microserviceFROM my-shared-base:1.0WORKDIR /app
# Copy only microservice-specific filesCOPY ./service1 /appCMD ["node", "service1.js"]

通过此设置,对常见依赖项的任何更新只需要在基础映像中进行更改,所有服务在重建时都会继承该基础映像。

重用公共层的最佳实践

  1. 分离共享层和服务特定层:将公共依赖项保留在共享基础映像中,并将每个服务 Dockerfile 限制为仅特定于应用程序的层。这种分离可最大限度地提高缓存效率并最大限度地减少重建。

  2. 对基础镜像进行版本控制和标记:使用标记版本(例如my-shared-base:1.0)可确保跨服务的构建一致性。在对共享依赖项进行更改时,系统地更新标记。

  3. 定期重建安全补丁:尽管共享基础映像减少了冗余,但定期重建它以包含更新和安全补丁非常重要,确保所有服务保持安全和最新。

结论

优化 Docker 构建远不止节省几秒钟的构建时间 — — 它还可以构建一个强大、安全且高效的部署管道,该管道可扩展且性能一致。从多阶段构建和缓存管理到漏洞扫描和资源限制,讨论的每种技术都提供了一种独特的方式来增强您的 Docker 工作流程。通过实施这些实践,您不仅可以简化 CI/CD 流程,还可以减小映像大小、降低运营成本并提高安全性。

优化的 Docker 镜像部署速度更快、管理更简单、生产环境更安全。随着应用程序的增长和基础设施的扩展,这些优化将在保持部署高效和弹性方面发挥关键作用。虽然重新编写 Dockerfile 和设置自动化的前期工作似乎很繁重,但长期来看,它是一项非常值得的投资。

归根结底,容器化关乎灵活性、可扩展性和控制力。通过这些优化,您的 Docker 构建将更贴近这些目标,使您的团队能够专注于通过应用程序交付价值,减少瓶颈,实现更可靠、更安全的容器部署。

相关文章:

优化 Docker 构建之方法(Methods for Optimizing Docker Construction)

优化 Docker 构建之方法 优化 Docker 构建不仅是为了提高效率,它还是降低部署成本、确保安全性和保持跨环境一致性的有效方法。每个层、依赖项和配置选择都会影响镜像的大小、安全性和可维护性。大型镜像部署速度较慢且消耗更多资源,这会增加成本&#…...

谈谈空间复杂度考量,特别是递归调用栈空间消耗?

空间复杂度考量是算法设计的核心要素之一,递归调用栈的消耗问题在前端领域尤为突出。 以下结合真实开发场景进行深度解析: 一、递归调用栈的典型问题 1. 深层次DOM遍历的陷阱 // 危险操作:递归遍历未知层级的DOM树 function countDOMNode…...

四川省汽车加气站操作工备考题库及答案分享

1.按压力容器的设计压力分为( )个压力等级。 A. 三 B. 四 C. 五 D. 六 答案:B。解析:按压力容器的设计压力分为低压、中压、高压、超高压四个压力等级。 2.缓冲罐的安装位置在天然气压缩机( )。 A. 出口处 …...

《探秘SQL的BETWEEN:解锁数据范围查询的深度奥秘》

在数据的广袤宇宙中,结构化查询语言(SQL)宛如一座精密的导航系统,引导我们穿越数据的浩瀚星河,精准定位所需信息。其中,BETWEEN作为SQL的关键工具之一,以其独特的能力,在数据的海洋里…...

AppArmor 使用说明

目录 一:AppArmor 功能介绍二:AppArmor 配置介绍1、AppArmor 配置文件存放路径2、AppArmor 配置文件命名规则 三: AppArmor 工作模式1、AppArmor 两种工作模式2、查看当前进程的工作模式3、给指定进程切换工作模式4、重新加载配置文件生效 四…...

Linux的一些常见指令

一、ls指令 语法: ls (选项) 功能: ls可以查看当前目录下的所有文件和目录。 常用选项: -a:列出目录下的所有文件,包括以点(.)开头的隐含文件 。-d:将目录像文件一样显示,不显示其下的文件。…...

MATLAB中getfield函数用法

目录 语法 说明 示例 访问标量结构体的字段 嵌套结构体的字段 结构体数组元素的字段 嵌套结构体数组的索引 字段的元素 getfield函数的功能是结构体数组字段。 语法 value getfield(S,field) value getfield(S,field1,...,fieldN) value getfield(S,idx,field1,..…...

算法 | 河马优化算法原理,公式,应用,算法改进及研究综述,matlab代码

以下是关于河马优化算法(Hippopotamus Optimization Algorithm, HO)的完整综述,包含原理、公式、应用场景、改进方向及可直接运行的 Matlab 完整代码。一、算法原理 河马优化算法(HO)由Amiri等人于2024年提出,是受河马群体行为启发的元启发式算法,其核心基于以下三阶段行…...

Oracle到MySQL实时数据互通:透明网关跨库查询终极方案

技术架构概述 节点类型IP示例Oracle数据库172.18.0.11透明网关节点192.168.5.20MySQL数据库10.10.8.100 提示:透明网关支持部署在Oracle服务器实现集中式管理 一、MySQL环境准备 1. ODBC驱动部署 从MySQL官网获取对应版本的ODBC驱动: # 企业版推荐使…...

BAPLIE船图文件:EDI 核心字段与应用场景解析

BAPLIE(Bay Plan/Stowage Plan Occupied and Empty Locations Message)作为EDIFACT国际报文标准(D96A版本)的核心成员,是集装箱海运领域实现船舶配载数字化的关键工具。其通过结构化数据精确描述集装箱在船舶上的物理位…...

C#中获取字节数据的高字节和低字节

字节顺序(Endianness): 1.小端序(Little-Endian):低字节在前(x86架构) 2.大端序(Big-Endian):高字节在前(网络字节序) 特性小端序(Little-Endian)大端序(B…...

26考研——查找_树形查找_二叉排序树(BST)(7)

408答疑 文章目录 三、树形查找二叉排序树(BST)二叉排序树中结点值之间的关系二叉树形查找二叉排序树的查找过程示例 向二叉排序树中插入结点插入过程示例 构造二叉排序树的过程构造示例 二叉排序树中删除结点的操作情况一:被删除结点是叶结点…...

OpenCV图像拼接(5)图像拼接模块的用于创建权重图函数createWeightMap()

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 cv::detail::createWeightMap 是 OpenCV 库中用于图像拼接模块的一个函数,主要用于创建权重图。这个权重图在图像拼接过程中扮演着重…...

【Unity】 鼠标拖动物体移动速度跟不上鼠标,会掉落

错误示范: 一开始把移动的代码写到update里去了,发现物体老是掉(总之移动非常不流畅,体验感很差) void Update(){Ray ray Camera.main.ScreenPointToRay(Input.mousePosition);if (Physics.Raycast(ray, out RaycastHit hit, M…...

docker网桥问题导致ldap组件安装失败分析解决

使用pass_install_x86_64_0124版部署k8s底座、kem; 问题:一台kem节点部署ldap组件失败 解决:恢复问题主机的docker0网卡,重新部署kem相关组件 二、问题详情 现象描述 ansible部署kem组件 TASK [kem : start ldap] **********…...

如何保障kafka的数据不会重复消费呢,如何防止漏掉呢

在 Kafka 中保障数据不重复消费且不丢失,需要从生产者、消费者和 Kafka 自身配置三个层面综合设计。以下是具体实现方案: 一、防止数据重复消费 1. 消费者端控制 手动提交 Offset 禁用自动提交(enable.auto.commitfalse)&#x…...

mac vim命令快捷键

目录 移动光标插入模式复制/粘贴删除搜索/替换退出 移动光标 快捷键说明0 / ^跳到行首,移动到光标所在行的"行首"$跳到行末,移动到光标所在行的"行尾"gg跳到文件第一行G移动到文章的最后[n]G跳到第n行w光标跳到下个字的开头e光标跳…...

【01】噩梦终结flutter配安卓android鸿蒙harmonyOS 以及next调试环境配鸿蒙和ios真机调试环境-flutter项目安卓环境配置

噩梦终结:Flutter 配安卓、鸿蒙、iOS 真机调试环境 问题背景 很多开发者在配置 Flutter 项目环境时遇到困难,尤其是在处理 Android、鸿蒙和 iOS 真机调试环境时。卓伊凡最近接手了一个项目,发现很多“专业程序员”在环境搭建上花费了大量时…...

React 中React.memo的作用,如何利用它进行组件性能优化?

大白话React 中React.memo的作用,如何利用它进行组件性能优化? React.memo 是啥玩意儿 在 React 里,组件渲染是很频繁的事儿。每次父组件状态变了,子组件就可能会重新渲染,哪怕子组件的 props 压根儿没变化。这就好比…...

Python实现图片文字识别-OCR

PaddleOCR是由百度飞桨(PaddlePaddle)团队开发的OCR工具库,它包含超轻量级的中文OCR模型,支持多种语言和复杂情况下的文字识别。 pip install paddlepaddle paddleocr使用:已知我的图片是 img.png from paddleocr imp…...

MCP Server 实现一个 天气查询

​ Step1. 环境配置 安装 uv curl -LsSf https://astral.sh/uv/install.sh | shQuestion: 什么是 uv 呢和 conda 比有什么区别? Answer: 一个用 Rust 编写的超快速 (100x) Python 包管理器和环境管理工具,由 Astral 开发。定位为 pip 和 venv 的替代品…...

第38周:文献阅读

目录 摘要 Abstract 文献阅读 问题引入 研究背景 研究意义 研究目的 相关工作 自回归循环网络 基于 GAN 的序列生成方法 时间序列表示学习 创新点 提出模型 基本原理 代码实现 实验研究 实验1——基准和评估 实验2——不同类型的时间序列数据 实验3——…...

CD19.【C++ Dev】类和对象(10) 日期类对象的成员函数(日期+天数)

目录 日期天数 需要考虑的几个问题 1.天数加在日上,有可能会溢出,需要进位 2.对月进位,也有可能导致月会溢出,需要进位 3.对年进位,需要考虑是否为闰年 代码设计 取得指定月的天数GetMonthDay函数 方法1:if判断或switch/case 方法2:查表 版本1 版本2 operator 初始…...

Linux安装MySQL数据库并使用C语言进行数据库开发

目录 一、前言 二、安装VMware运行Ubuntu 1.安装VMware 2.使用VMware打开Ubuntu 三、配置VMware使用网卡 1.添加NAT网卡 四、Linux下安装MySQL数据库 五、安装MySQL开发库 六、演示代码 sql_connect.c sql_connect.h main.c中数据库相关代码 结尾 一、前言 由于最…...

GStreamer —— 3.1、Qt+GStreamer制作多功能播放器,支持本地mp4文件、rtsp流、usb摄像头等(可跨平台,附源码)

🔔 GStreamer 相关音视频技术、疑难杂症文章合集(掌握后可自封大侠 ⓿_⓿)(记得收藏,持续更新中…) 运行效果...

如何在根据名称或id找到json里的节点以及对应的所有的父节点?

函数如下: 数据如下: [{ "name": "数据看板", "id": "data", "pageName": "tableeauData", "list": [] }, { "name": "审计模块", "id": &quo…...

IP第一次笔记

一、TCP协议 第0步:如果浏览器和host文件存在域名对应的P地址记录关系 则直接封装HTTP数据报文,如果没有记录则触发DNS解析获 取目标域名对应的P地址 第一步:终端主机想服务器发起TCP三次握手 1.TCP的三次握手 2.传输网页数据 HTTP --应用层…...

手机硬件检测详解:从版本信息到相机功能的全面指南

手机硬件检测概述 接下来,我们将为您提供一份详尽的手机硬件检测指南,涵盖三十项关键内容,助您轻松掌握手机硬件的检测技巧。对于开发者而言,在测试手机硬件性能时,可以结合使用克魔开发助手(Keymob&#…...

大数据学习(86)-Zookeeper去中心化调度

🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言📝支持一…...

05-SpringBoot3入门-整合SpringMVC(配置静态资源、拦截器)

1、说明 在01-SpringBoot3入门-第一个项目-CSDN博客中,其实就已经整合了SpringMVC。下面讲解怎么配置静态资源和拦截器 2、配置静态资源 命名:static(文件夹) 位置:src/main/resources 编写一个html文件 访问 http:/…...

大数据学习(88)-zookeeper实现的高可用(HA)

🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言📝支持一…...

自然语言处理(5)—— 中文分词

中文分词的基本原理及实现 1. 什么是词2. 基本原理3. 发展趋势:多数场景无需显式分词 信息处理的目标是使用计算机能够理解和产生自然语言。而自然语言理解和产生的前提是对语言能够做出全面的解析。 汉语词汇是语言中能够独立运用的最小的语言单位,是语…...

MP4音视频格式

1.MP4 MP4是一种用于封装音视频/字幕/图片/章节信息等数据的多媒体容器格式,是MPEG-4系列的成员之一 2.文件结构 MP4由一层层的嵌套Box(atom)组成 [ size (4 bytes) ][ type (4 bytes)][ payload (嵌套box或者数据) ] 3.常见Box 类型名称…...

NO.59十六届蓝桥杯备战|基础算法-前缀和|一维前缀和|最大子段和|二维前缀和|激光炸弹(C++)

前缀和与差分的核⼼思想是预处理,可以在暴⼒枚举的过程中,快速给出查询的结果,从⽽优化时间复杂度。 是经典的⽤空间替换时间的做法 前缀和 解法1:暴力模拟,q有几次,就套几次for循环 解法2:前…...

Node.js 模块加载机制--详解

目录 Node.js 模块加载机制详解 1. 什么是模块? 2. 模块加载顺序 3. 核心模块加载 4. 自定义模块加载 5. 目录作为模块(index.js) 6. require.resolve() 查找模块路径 7. module 对象解析 8. require.cache 机制(避免重复…...

Live555+Windows+MSys2 编译Androidso库和运行使用(三,实战篇)

文章目录 先上效果图VLC中打开日志权限cmakelist 和gradle建议直接去git查看源代码ui下一篇视频预览和采集 先上效果图 VLC中打开 日志 com.mq.qrtspclient E mystream stream, from the file /storage/emulated/0/Android/data/com.mq.qrtspclient/files/…...

验证码通过“Canvas 绘制”与“Base64 图片”渲染两种不同的实现方式显示

文章目录 🎉验证码通过“Canvas 绘制”与“Base64 图片”渲染两种不同的实现方式显示一、Canvas 绘制:接口回调“字符串”通过绘制的方式显示到前端✨1、前端代码(Canvas 绘制):包含验证码区域、绘制验证码方法&#x…...

FALL靶场通关攻略

1,下载好靶机后打开,通过kali扫描靶机ip和端口,得到靶机ip为192.168.50.144 2,扫描目录 3,访问靶机 4,访问扫描到的test.php,得到缺少GET请求参数的提示 5,使用FUZZ来扫出参数为file 6&#xff…...

(学习总结31)Linux 进程地址空间与进程控制

Linux 进程地址空间与进程控制 进程地址空间Linux 进程地址空间分布虚拟地址空间和页表创建子进程与写时拷贝权限访问缺页中断 Linux 虚拟内存管理mm_struct 内存描述符区域划分vm_area_struct 独立虚拟内存区域 虚拟地址空间的作用 进程控制进程创建fork 函数介绍写时拷贝作用…...

基于Springboot的网上订餐系统 【源码】+【PPT】+【开题报告】+【论文】

网上订餐系统是一个基于Java语言和Spring Boot框架开发的Web应用,旨在为用户和管理员提供一个便捷的订餐平台。该系统通过简化餐饮订购和管理流程,为用户提供快速、高效的在线订餐体验,同时也为管理员提供完善的后台管理功能,帮助…...

Axure设计之中继器表格——拖动行排序教程(中继器)

一、原理介绍 在Axure中实现表格行的拖动排序,主要依赖于中继器的排序事件。然而要实现拖动效果,就必须结合动态面板,因为动态面板可以设置拖动事件,之所以使用动态面板或许是因为它可以更灵活地处理位置变化。用户拖动行时&…...

GAUSSDB 分布式存储机制深度解析

GAUSSDB 分布式存储机制深度解析 一、核心机制概览 GAUSSDB 的分布式存储通过 数据分片、多副本管理 和 全局事务控制 实现高可用与水平扩展。以下为关键机制详解: 二、数据分片(Sharding) 1. 分片策略 GAUSSDB 支持多种分片规则&#xff…...

【NLP 46、大模型技术发展】

目录 一、ELMo 2018 训练目标 二、GPT-1 2018 训练目标 三、BERT 2018 训练目标 四、Ernie —— baidu 2019 五、Ernie —— Tsinghua 2019 六、GPT-2 2019 七、UNILM 2019 八、Transformer - XL & XLNet 2019 1.模型结构 Ⅰ、循环机制 Recurrence Mechanism Ⅱ、相对位置…...

汽车加气站操作工证书报考条件是什么?

关于汽车加气站操作工的资格证书: 一、核心证书要求 CNG充装人员上岗证 这是加气站加气工的核心资质证书,需通过专业培训并考核。该证书由相关部门颁发,证明持证人具备从事CNG(压缩天然气)充装操作的专业技能&#xf…...

材质及制作笔记

基本流程: 建中模——zb雕刻高模——maya拓扑低模——拆uv——sp烘焙贴图——sp绘制材质——渲染 1 材质贴图: diffuse/albedo/basecolor:漫反射 reflection/specular:反射 metalness:金属度 glossiness&#xf…...

Opencv 图像读取与保存问题

本文仅对 Opencv图像读取与保存进行阐述,重在探讨图像读取与保存过程中应注意的细节问题。 1 图像读取 首先看一下,imread函数的声明: // C: Mat based Mat imread(const string& filename, int flags1 );// C: IplImage based IplImage*…...

Flutter 2025生态全景:从跨端到嵌入式开发的新机遇

一、技术演进:从"一次编写多端运行"到"全场景覆盖" 1.1 渲染引擎革命:Impeller 2.0的性能突破 // 启用Impeller的配置示例(android/app/build.gradle) def enableImpeller true android {defaultConfig {…...

Idea中诡异的文件编码问题: 设置fileCodeing为UTF8但不生效

在fileCoding配置了编码utf-8,右下角的文件里编码格式却是ISO-8859-1,git后到远程仓库里却是ISO-8859-1的乱码,怎么修改都不生效,重启也不行,恶心的不行。 最后发现修复方案是: 1. 先随便做个变更&#x…...

Linux中逻辑卷的使用、扩容与磁盘配额

目录 一.逻辑卷概述 1.普通分区的优缺点 2.逻辑卷的优点 3.逻辑卷概述 二.逻辑卷的创建 1.逻辑卷相关命令 2.创建步骤 三.逻辑卷的扩容 1.扩容步骤 四.磁盘配额 1.概念及注意点 2.使用条件 3.相关命令 一.逻辑卷概述 1.普通分区的优缺点 普通分区优点&#xff1a…...

Java版Manus实现来了,Spring AI Alibaba发布开源OpenManus实现

此次官方发布的 Spring AI Alibaba OpenManus 实现,包含完整的多智能体任务规划、思考与执行流程,可以让开发者体验 Java 版本的多智能体效果。它能够根据用户的问题进行分析,操作浏览器,执行代码等来完成复杂任务等。 项目源码及…...