了解多阶段DockerFile构建

时间:2020-08-31 15:45:44

标签: docker asp.net-core docker-compose

我是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

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

    在COPY [“ Services / Orders / Orders.csproj”,“ Services / Orders /”]行中,我们指定的路径似乎与docker-compose.yml文件相关,而不是与DockerFile相关。进一步嵌套在文件夹中。这是否意味着Dockerfile中的路径需要与docker-compose.yml相关?我之所以这样问,是因为我在想如果我使用此Dockerfile运行docker build命令,则会由于复制命令中的路径无效而收到错误消息。

2 个答案:

答案 0 :(得分:1)

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

这定义了您当前的工作目录。默认情况下,将从该位置运行以下命令,并且相对路径将从该位置开始。

具有以下文件结构的示例:

/app/
  - README.md
  - folder1/
       - some.txt
WORKDIR /app
RUN ls

将打印

folder1
README.md

WORKDIR /app/folder1
RUN ls

将打印

some.txt

问题2:为什么要为SDK映像设置WORKDIR / src而不是WORKDIR / app?

请参阅问题1的答案。以下COPYdotnet restore命令在/src源目录中执行。 (构建是从/src目录完成的,稍后在/app/publish中创建的工件会被复制到最后一个阶段的/app目录中,然后从该/app目录执行)

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

复制采用两条路径。第一个引用源(来自构建Docker镜像的上下文中的文件或文件夹),第二个引用目标(生成的Docker镜像中的文件顺序文件夹)。因此,这些路径通常仅特定于Dockerfile并且独立于您的docker-compose项目。

但是,在您的情况下,docker镜像构建的上下文是在docker-compose.yml中定义的:

build:
      context: .
      dockerfile: Services/Orders/Dockerfile

,因此您的docker映像映像构建的上下文似乎是docker-compose.yml所在的目录。如果仅在该文件夹中运行docker build -f Services/Orders/Dockerfile .,则可以构建相同的映像。 (并非特定于docker-compose)

因此,您应该在docker-compose.yml所在目录的Orders.csproj中找到./Services/Orders/。然后在第二个构建阶段将此文件复制到/src/Services/Orders/Orders.csproj。 (/src可以在COPY语句中提交,因为它是从当前工作目录开始的相对路径,该目录在上一行中定义。-请参阅问题1)

答案 1 :(得分:0)

对于以后进入该线程并遇到类似问题的任何人,我将根据Leo的答案和其他来源分享我到目前为止所学到的知识。

Docker具有称为 multi-stage 的功能。这使我们可以创建多个图层并使用它们。

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

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

来到Dockerfile ...

“来自” 关键字开头的每个部分都定义了一个图层。在我最初的Dockerfile中,您可以看到我正在创建4个基础,构建,发布最终层。我们可以命名一个图层,然后使用该名称在第一个图层的基础上创建一个新图层。

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映像,该映像将用于构建应用程序,并将此层称为“ build” 。 我们设置了以下“ 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

嵌套,我们根据上一层“ build” 创建一个名为“ publish” 的新层,并在“中将发布配置与发布配置一起发布应用程序/发布” 目录。

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

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

因此,我们正在使用2张单独的图像。 SDK 生成我们的应用程序,并 aspnet 图像将我们的应用程序托管在容器中,但要注意的重要一点是,将要生成的最终容器将仅包含aspnet运行时,并且尺寸较小。

所有这些东西都已记录在案,可以进行搜索,但是我苦苦挣扎,因为信息分散了。希望它将为将来对ASP.Net Core和Docker陌生的任何人节省一些时间。