docker - 了解多阶段 DockerFile 构建

我是 Docker 的新手。我的项目有以下目录结构

docker-compose.yml
|-services
  |-orders
    |-DockerFile

我正在使用具有以下内容的标准 ASP.Net Core DockerFile:

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 AS base
WORKDIR /app
EXPOSE 80

FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
WORKDIR /src
COPY ["Services/Orders/Orders.csproj", "Services/Orders/"]
RUN dotnet restore "Services/Orders/Orders.csproj"
COPY . .
WORKDIR "/src/Services/Orders"
RUN dotnet build "Orders.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "Orders.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Orders.dll"]

我的 docker-compose.yml 文件有

# Please refer https://aka.ms/HTTPSinContainer on how to setup an https developer certificate for your ASP .NET Core service.
version: "3.4"

services:
  orders-api:
    image: orders-api
    build:
      context: .
      dockerfile: Services/Orders/Dockerfile
    ports:
      - "2222:80"

我对这两个文件有些困惑

  • 问题 1:第 2 行的 WORKDIR/app 有什么用?

    我的理解是我们使用的是可以扩展的基础图像 当我们在第 1 行导入基本图像然后设置 第 2 行和第 3 行中的 WORKDIR 和端口,它们将被以下命令使用 哪些人使用这张图片?

  • 问题 2:为什么我们要为 SDK 镜像设置 WORKDIR/src 而不是 WORKDIR/app

  • 问题三:复制命令中的路径是否与Dockerfile或Docker-compose.yml文件相关?

    在 COPY ["Services/Orders/Orders.csproj", "Services/Orders/"] 行中,我们指定的路径似乎与 docker-compose.yml 文件相关,而不是 DockerFile进一步嵌套在文件夹中。这是否意味着 Dockerfile 中的路径需要与 docker-compose.yml 相关?我问这个是因为我认为如果我使用这个 Dockerfile 运行 docker build 命令那么我会得到一个错误,因为复制命令中的路径将无效。

最佳答案

对于后来来到这个话题并遇到类似问题的任何人,我将根据 Leo 的回答和其他来源分享我到目前为止所学到的知识。

Docker 有一个名为 multi-stage 的特性 构建。这使我们能够创建多个图层并使用它们。

可以使用不同的图像构建 ASP.Net Core 应用程序。 SDK 图像更大,但为我们提供了在开发环境中构建和调试代码的额外工具。

但是,在生产环境中,我们不需要 SDK。我们只想利用 PROD 中的轻量级运行时。我们可以利用 Docker 的多阶段构建功能来创建我们的最终容器并保持它的轻量级。

来到 Dockerfile 中......

“From”关键字开头的每个部分都定义了一个层。在我原来的 Dockerfile 中,您可以看到我正在创建 4 层 base、build、publishfinal。我们可以命名一个图层,然后使用该名称在第一个图层的基础上创建一个新图层。

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 AS base
WORKDIR /app
EXPOSE 80

以上代码将基于包含轻量级运行时的 aspnet:3.1 镜像创建一个名为 “base” 的层,并将用于托管我们的最终应用程序。 然后我们将工作目录设置为/app文件夹,这样以后我们使用这一层的时候,我们的COPY等命令都会在这个文件夹中运行。 接下来,我们只公开端口 80,这是 Web 服务器的默认端口。

FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
WORKDIR /src
COPY ["Services/Orders/Orders.csproj", "Services/Orders/"]
RUN dotnet restore "Services/Orders/Orders.csproj"
COPY . .
WORKDIR "/src/Services/Orders"
RUN dotnet build "Orders.csproj" -c Release -o /app/build

接下来,我们下载将用于构建应用程序的 SDK 镜像,并将此层称为“构建”。 我们设置了以下“COPY”命令将使用的工作目录。

Copy 命令将文件从本地文件系统复制到容器中的指定位置。所以基本上我是将我的 Orders.csproj 文件复制到容器中,然后运行 ​​“dotnet restore” 来恢复该项目所需的所有 Nuget 包。

仅复制 .csproj.sln 文件,然后在不复制整个代码的情况下恢复 NuGet 包会更有效,因为它利用缓存,如前所述 here这是一种我不知道的广泛采用的做法,我想知道为什么我们不能简单地复制所有内容并运行 “dotnet restore” 命令?

一旦我们恢复了 NuGet 包,我们就可以复制所有文件,然后运行 ​​“dotnet build” 命令来构建我们的项目。

FROM build AS publish
RUN dotnet publish "Orders.csproj" -c Release -o /app/publish

Nest,我们基于我们之前的层 “build” 创建一个名为 “publish” 的新层,并在 中使用发布配置简单地发布我们的项目app/publish” 目录。

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Orders.dll"]

最后一层使用“base”(我们的轻量级运行时图像)。将文件从publish 层的/app/publish 位置复制到设置的工作目录,然后设置应用程序的入口点。

所以我们使用了 2 个单独的图像。 SDK 用于构建我们的应用程序,aspnet 图像用于在容器中托管我们的应用程序,但这里需要注意的重要一点是,将生成的最终容器将仅包含aspnet 运行时,并且尺寸会更小。

所有这些都记录在案并且可以搜索,但我很挣扎,因为信息是分散的。希望它能为 future ASP.Net Core 和 Docker 的新手节省一些时间。

https://stackoverflow.com/questions/63673987/

相关文章:

r - 使用 dplyr::mutate(across()) 将多列应用于自定义函数

javascript - 使用 react-konva 捕捉网格,拖放

python - 在列表字典中查找最大列表范围的更好(更简洁)方法是什么

docker - 如何查看在我的 Google Cloud Platform Cloud Run 服

python - 类型错误 : request() missing 1 required posit

r - 如何通过提取将列拆分为两列?

python - 模块未找到错误 : No module named 'setuptools._di

python - 如何在 Python 中部分拆分并获取字符串的第一部分?

vue.js - 将插槽从 Vue 2 迁移到 Vue 3

c# - 使用 ConcurrentBag 的并行 ForEach 未按预期工作