计算机系统应用教程网站

网站首页 > 技术文章 正文

Docker — 编写 Dockerfile 的最佳实践和专业技巧

btikc 2024-09-27 01:14:29 技术文章 6 ℃ 0 评论

构建尽可能小的镜像

构建更小的镜像具有诸如更快的上传和下载时间等优势。对于 Kubernetes 中 Pod 的冷启动时间很重要:镜像越小,节点下载速度越快。

使用多阶段 Dockerfile

多阶段构建允许在构建过程中使用多个临时镜像,但仅保留最新的镜像作为最终构件。换句话说,在镜像中排除构建依赖项,同时在构建镜像时仍然可用。多阶段构建可以通过在构建镜像和最终输出之间创建更清晰的分离,从而减小最终镜像的大小。减少依赖关系和减小镜像大小。

例如,对于运行 .NET/Java 应用程序,可以使用一个阶段进行编译和构建,然后使用另一个阶段将二进制构件和依赖项复制到镜像中,并且丢弃所有非必需的构件。

另一个例子是,对于 Angular/React 应用程序,可以在一个阶段中运行 npm install 和构建,然后在下一个阶段复制构建的构件。

### ASP.NET Core 应用程序的 Dockerfile ###
# 以基础镜像为基础构建镜像
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /source

# 复制 csproj 并作为独立层进行还原
COPY *.sln .
COPY aspnetapp/*.csproj ./aspnetapp/
RUN dotnet restore

# 复制其他所有内容并构建应用程序
COPY aspnetapp/. ./aspnetapp/
WORKDIR /source/aspnetapp
RUN dotnet publish -c release -o /app --no-restore

# 最终阶段/镜像
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY --from=build /app ./
ENTRYPOINT ["dotnet", "aspnetapp.dll"]
### Angular 应用程序的 Dockerfile ###
# 以 alpine 镜像为基础
FROM node:16-alpine AS builder
ARG CONFIGURATION='dev'

# 将 /app 设为工作目录
WORKDIR /app

# 复制 package.json 文件
COPY package.json .

# 安装依赖项
RUN npm install --legacy-peer-deps

# 将源代码复制到 /app 目录
COPY . .

# 构建应用程序
RUN npm run build --  --output-path=dist --configuration=$CONFIGURATION --output-hashing=all


# 最终阶段/镜像
FROM nginx:stable-alpine

# 删除默认的 nginx 网站
RUN rm -rf /usr/share/nginx/html/*

# 复制 nginx 配置文件
COPY ./nginx/nginx.conf /etc/nginx/nginx.conf

# 将构建阶段的 dist 文件夹复制到 nginx 的公共文件夹
COPY --from=builder /app/dist /usr/share/nginx/html

# 启动 Nginx 服务
CMD ["nginx", "-g", "daemon off;"]

避免安装不必要的软件包

安装不必要的软件包只会增加构建时间和镜像大小,从而降低性能。建议只包括那些最重要的软件包,并尽量避免多次安装相同的软件包。

当您避免安装额外或不必要的软件包时,您的镜像具有较低的复杂性、较少的依赖项、较小的文件大小和较短的构建时间。

使用尽可能小的基础镜像

基础镜像是 Dockerfile 中 FROM 指令引用的镜像。Dockerfile 中的每个其他指令都构建在此镜像之上。使用安装了更多软件包和库的较大基础镜像可能会增加最终 Docker 镜像的大小,并可能降低性能。

基础镜像越小,最终镜像就越小,下载速度就越快,从而提高性能和构建速度。此外,使用最小的基础镜像还可以通过减少可能存在于最终镜像中的潜在漏洞数量来提高安全性。

建议使用最小的基础镜像(例如 Alpine Linux)作为构建 Docker 镜像的起点。Alpine 具有启动容器应用所需的一切,但更轻量级。

减少层的数量

通常情况下,应尽量减少 Dockerfile 中使用的层数。每个 RUNCOPYFROM Dockerfile 指令都会添加一个新层,每个层都会增加构建执行时间并增加镜像的存储需求。

建议将相同类型的命令捆绑在一起。例如,不要编写多个 pip installnpm install 命令以安装许多软件包。

使用 Docker 忽略文件

通常,在构建镜像时,我们不需要项目中的所有内容来运行容器中的应用程序。例如,我们不需要自动生成的文件夹,如 targetsbuild 文件夹,或 readmelicense 文件等。

如果在 **.**dockerignore 文件中配置了 Docker 忽略文件,则 Docker 会忽略工作目录中存在的文件,类似于 .gitignore 文件。创建一个 .dockerignore 文件,并列出所有要忽略的文件和文件夹,当构建镜像时,Docker 将查看内容并忽略内部指定的任何内容。这将导致从 Docker 容器中删除不必要的文件,减小 Docker 镜像的大小,并提高构建性能。

将应用程序数据存储在其他位置

将应用程序数据存储在镜像中将不必要地增加镜像的大小。强烈建议使用容器运行时的卷功能将镜像与数据分开。

安全

使用官方和经过验证的 Docker 镜像

尽可能使用官方和经过验证的 Docker 镜像作为基础镜像。

使用最新发布的上游基础镜像

使用基础镜像的最新发布版本。最新发布版本应在构建基础镜像时包含最新的安全补丁。

扫描镜像以检测安全漏洞

扫描 Docker 镜像以检测已知的安全漏洞。扫描基础镜像或应用程序镜像,以确认它不包含任何已知的安全漏洞。使用开源扫描工具,如 Synk 或 Trivy。

构建镜像后,使用 docker scan 命令扫描其安全漏洞。实际上,Docker 在后台使用一个名为 snyk 的服务来扫描镜像的漏洞。该扫描使用一个不断更新的漏洞数据库。

自动化扫描

应在 CI 流水线和企业注册表中实现自动化扫描工具。此外,建议在未来发现漏洞时,在应用程序上部署运行时扫描。

慎重考虑是否使用公共镜像

Docker 的一个巨大优势是公共可用镜像的数量,涵盖各种软件和应用程序。这些镜像可以让您快速入门。在某些情况下,不建议或不可能使用公共镜像,例如:

  • 您不希望依赖外部存储库。
  • 您希望每个镜像具有相同的基础操作系统。
  • 您希望严格控制生产环境中的漏洞。
  • 您希望精确控制镜像内的内容。

使用最低特权用户

默认情况下,当 Dockerfile 没有指定用户时,它使用 root 用户。一般情况下,没有理由以 root 权限运行容器。这会引入安全问题,因为攻击者更容易在主机上提升权限。

建议在 Docker 镜像中创建一个专用用户和一个专用组来运行应用程序,并使用该用户在容器中运行应用程序。

不要暴露秘密信息

建议不要在 Dockerfile 中共享或复制应用程序凭据或任何敏感信息。使用 .dockerignore 文件防止复制可能包含敏感信息的文件。

注意要暴露哪些端口

在设计 Dockerfile 时,请确保知道暴露了哪些端口。默认情况下,Docker 将所有容器暴露到一系列随机内部端口。这是有问题的,因为它可能会将关键服务暴露给外部世界,并使其容易受到攻击。

如果使用必须暴露到公共互联网的服务,则必须在 Dockerfile 中创建一个条目。这可以通过在 Dockerfile 中添加 'EXPOSE' 来实现。

通用

正确为您的镜像打标签

Docker 镜像通常由两个组成部分标识:它们的名称和它们的标签。名称和标签配对在任何给定时间是唯一的。构建镜像时,请遵循一致和一致的标记策略。容器镜像是打包和发布软件的一种方式。为镜像打标签让开发人员能够识别特定版本的软件以便下载。

使用特定的镜像标签或版本

为您的镜像使用特定的标签或版本,而不是 latest。这样可以使您的镜像具有可追溯性。在排除运行容器问题时,确切的镜像将是明显的。

latest 标签是不可预测的,并且会导致意外行为,例如可能会得到与上一次构建中不同的镜像版本,或者新的镜像版本可能会破坏一些功能。

因此,与其使用随机的 latest 镜像标签,不如像应用程序版本一样固定镜像的版本。

// 这样做
nginx:1.23.1
myapp:1.2.0

// 不要这样做: 
nginx:latest
myapp:latest

优化 Docker 构建缓存

Docker 镜像是逐层构建的,在 Dockerfile 中,每个指令都会创建结果镜像中的一个层。在构建过程中,如果可能,Docker 会重用先前构建的层并跳过潜在的昂贵步骤。只有所有先前的构建步骤都使用了构建缓存,Docker 才能使用它的构建缓存。虽然这种行为通常是使构建速度更快的好方法,但您需要考虑一些情况。

建议按照从最不经常更改的命令到最经常更改的命令的顺序排列 Dockerfile 中的命令,以利用缓存并优化镜像构建速度。

使用最佳语句顺序:在您的 Dockerfile 中,将最经常更改的语句放在最后。

使用 ENV 定义环境变量

设置环境变量将使您的容器更易于移植。这是因为您的环境变量是从一次执行到下一次执行唯一会发生变化的内容。

不要将您的 Dockerfile 用作构建脚本

Dockerfile 是一组指令,可用于创建自定义镜像。它不应该被用作构建脚本,因为这会使您的构建变得不必要的长。

将 Dockerfile 提交到仓库

建议将 Dockerfile 与应用程序源代码一起保存在仓库中。

对多行参数进行排序

在可能的情况下,对多行参数进行按字母数字顺序排序,以便更易于维护。这有助于避免软件包的重复,并使列表更易于更新。

总结

记住的最终最佳实践是尽可能保持您的 Dockerfile 简单,不要试图添加不必要的复杂性。上述提示应该帮助您构建优化的 Docker 镜像和编写更好的 Dockerfile。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表