快速入门:将 Docker 与 Visual Studio 中的 React 单页面应用程序结合使用

借助 Visual Studio,可以轻松地生成、调试和运行容器化的 ASP.NET Core 应用(包括使用客户端 JavaScript 的应用,如 React.js 单页应用程序 (SPA))并将其发布到 Azure 容器注册表、Docker 中心、Azure 应用程序服务或你自己的容器注册表。 在本文中,我们发布到 Azure 容器注册表。

先决条件

  • Docker Desktop
  • 安装了“Web 开发”、“Azure 工具”工作负载和/或“.NET Core 跨平台开发”工作负载的 Visual Studio 2022
  • 若要发布到 Azure 容器注册表,需要 Azure 订阅。 注册免费试用版
  • Node.js
  • 对于 Windows 容器、Windows 10 版本 1809 或更高版本,使用本文中引用的 Docker 映像。

安装和设置

要安装 Docker,请先查看用于 Windows 的 Docker Desktop:安装须知了解相关信息。 然后安装用于 Windows 的 Docker Desktop

创建项目并添加 Docker 支持

  1. 使用“带 React.js 的 ASP.NET Core”模板创建新项目。

    创建新 React.js 项目的屏幕截图。

  2. 在“其他信息”屏幕上,你无法选择“启用 Docker 支持”,但不要担心,可以稍后添加该支持。

    “创建新 React.js 项目 - 其他信息”屏幕的屏幕截图。

  3. 右键单击项目节点,然后选择“添加”>“Docker 支持”,将 Dockerfile 添加到你的项目中。

    添加 Docker 支持菜单项的屏幕截图。

  4. 选择容器类型。

根据你使用的是 Linux 容器还是 Windows 容器,下一步会有所不同。

注意

如果在 Visual Studio 2022 或更高版本中使用最新项目模板,则无需修改 Dockerfile。

修改 Dockerfile(Linux 容器)

Dockerfile,用于创建最终 Docker 映像的方案,已在项目中创建。 请参阅 Dockerfile 引用,了解其中的命令。

默认的 Dockerfile 使用基础映像来运行容器,但是当你希望还能够在其上运行 Node.js 应用程序时,你需要安装 Node.js,这意味着在 Dockerfile 中的几个位置添加一些安装命令。 安装命令需要提升的权限,因为更改会影响容器的特权系统文件和文件夹。

如果选中了新建项目对话框的“为 HTTPS 配置”复选框,则 Dockerfile 公开两个端口。 一个端口用于 HTTP 流量;另一个端口用于 HTTPS。 如果未选中该复选框,则为 HTTP 流量公开单个端口 (80)。

如果你的目标是 .NET 8 或更高版本,则使用 Visual Studio 通过普通用户帐户创建的默认 Dockerfile(查找 USER app 行),但该帐户不具有安装 Node.js 所需的提升的权限。 考虑到这种情况,请执行以下操作:

  1. 在 Dockerfile 中删除 USER app 行。
  2. 将 Dockerfile 第一节中公开的端口更改为用于 HTTP 请求的端口 80 和用于 HTTPS 请求的 443(如果在创建项目时选择了支持 HTTPS)。
  3. 编辑 launchSettings.json,将其中的端口引用更改为 80 和 443;对于 HTTP,请将 8080 替换为 80;对于 HTTPS,请将 8081 替换为 443。

对于所有 .NET 版本,请使用以下步骤更新 Dockerfile 以安装 Node.js:

  1. 添加以下代码行以将 curl、Node.js 14.x 和某些必需的 Node 库安装到容器中。 请务必在第一部分中添加这些行,以将 Node 包管理器 npm.exe 的安装添加到基础映像以及 build 部分中。
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y libpng-dev libjpeg-dev curl libxi6 build-essential libgl1-mesa-glx
RUN curl -sL https://deb.nodesource.com/setup_lts.x | bash -
RUN apt-get install -y nodejs

“Dockerfile”现在看起来如下所示:

#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.

FROM mcr.microsoft.com/dotnet/aspnet:3.1 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y libpng-dev libjpeg-dev curl libxi6 build-essential libgl1-mesa-glx
RUN curl -sL https://deb.nodesource.com/setup_lts.x | bash -
RUN apt-get install -y nodejs

FROM mcr.microsoft.com/dotnet/sdk:3.1 AS build
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y libpng-dev libjpeg-dev curl libxi6 build-essential libgl1-mesa-glx
RUN curl -sL https://deb.nodesource.com/setup_lts.x | bash -
RUN apt-get install -y nodejs
WORKDIR /src
COPY ["ProjectSPA1/ProjectSPA1.csproj", "ProjectSPA1/"]
RUN dotnet restore "ProjectSPA1/ProjectSPA1.csproj"
COPY . .
WORKDIR "/src/ProjectSPA1"
RUN dotnet build "ProjectSPA1.csproj" -c Release -o /app/build

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

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

前面的 Dockerfile 基于 mcr.microsoft.com/dotnet/aspnet 映像,并包括通过构建项目并将其添加到容器中修改基本映像的说明。

修改 Dockerfile(Windows 容器)

通过双击项目节点来打开项目文件,然后通过将以下属性添加为 <PropertyGroup> 元素的子元素来更新项目文件 (*.csproj):

 <DockerfileFastModeStage>base</DockerfileFastModeStage>

注意

需要对 DockerfileFastModeStage 进行更改,因为此处的 Dockerfile 在 Dockerfile 的开头添加了一个阶段。 为了优化性能,Visual Studio 使用“快速”模式,但该模式仅在使用正确阶段的情况下才有效。 默认值是 Dockerfile 中的第一阶段,在本例中,它从 base 更改为其他内容,目的是下载 Node.js。 有关“快速”模式的更多说明,请参阅在 Visual Studio 中自定义 Docker 容器

通过添加以下行来更新 Dockerfile。 这些行会将 Node 和“npm”复制到容器中。

  1. # escape=` 添加到 Dockerfile 的第一行

  2. FROM ... base 之前添加以下行

    FROM mcr.microsoft.com/powershell AS downloadnodejs
    ENV NODE_VERSION=14.16.0
    SHELL ["pwsh", "-Command", "$ErrorActionPreference = 'Stop';$ProgressPreference='silentlyContinue';"]
    RUN Invoke-WebRequest -OutFile nodejs.zip -UseBasicParsing "https://nodejs.org/dist/v$($env:NODE_VERSION)/node-v$($env:NODE_VERSION)-win-x64.zip"; `
        Expand-Archive nodejs.zip -DestinationPath C:\; `
        Rename-Item "C:\node-v$($env:NODE_VERSION)-win-x64" c:\nodejs
    
  3. FROM ... build 之前和之后添加以下行

    COPY --from=downloadnodejs C:\nodejs\ C:\Windows\system32\
    
  4. 完整的 Dockerfile 现在看起来如下所示:

    # escape=`
    #Depending on the operating system of the host machines(s) that will build or run the containers, the image specified in the FROM statement may need to be changed.
    #For more information, please see https://aka.ms/containercompat
    FROM mcr.microsoft.com/powershell AS downloadnodejs
    ENV NODE_VERSION=14.16.0
    SHELL ["pwsh", "-Command", "$ErrorActionPreference = 'Stop';$ProgressPreference='silentlyContinue';"]
    RUN Invoke-WebRequest -OutFile nodejs.zip -UseBasicParsing "https://nodejs.org/dist/v$($env:NODE_VERSION)/node-v$($env:NODE_VERSION)-win-x64.zip"; \
        Expand-Archive nodejs.zip -DestinationPath C:\; \
        Rename-Item "C:\node-v$($env:NODE_VERSION)-win-x64" c:\nodejs
    
    FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 AS base
    WORKDIR /app
    EXPOSE 80
    EXPOSE 443
    COPY --from=downloadnodejs C:\\nodejs C:\\Windows\\system32
    
    FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
    COPY --from=downloadnodejs C:\\nodejs C:\\Windows\\system32
    WORKDIR /src
    COPY ["ProjectSPA1/ProjectSPA1.csproj", "ProjectSPA1/"]
    RUN dotnet restore "ProjectSPA1/ProjectSPA1.csproj"
    COPY . .
    WORKDIR "/src/ProjectSPA1"
    RUN dotnet build "ProjectSPA1.csproj" -c Release -o /app/build
    
    FROM build AS publish
    RUN dotnet publish "ProjectSPA1.csproj" -c Release -o /app/publish
    
    FROM base AS final
    WORKDIR /app
    COPY --from=publish /app/publish .
    ENTRYPOINT ["dotnet", "ProjectSPA1.dll"]
    
  5. 通过删除 **/bin 来更新 .dockerignore 文件。

如果你使用的是 Visual Studio 2022 版本 17.8 或更高版本,请按照以下步骤操作:

  1. 使用“React 和 ASP.NET Core”模板创建一个新项目。

    新建 React 和 ASP.NET Core 项目的屏幕截图。

  2. 在“其他信息”屏幕上,选择“启用容器支持”。 请务必选择“Dockerfile”选项,因为需要手动更改该文件。

    “创建 React 和 ASP.NET Core 项目 - 其他信息”屏幕的屏幕截图。

    注意

    在 Visual Studio 2022 的某些版本中,此选项未启用,但可以稍后添加该支持。

    Visual Studio 创建两个项目 - 一个用于 React JavaScript 客户端代码,另一个用于 ASP.NET Core C# 服务器代码。

  3. 如果在创建项目期间未添加 Docker 容器支持,请右键单击服务器项目节点,然后选择“添加”>“Docker 支持”,并确保选择“Dockerfile”选项来创建 Dockerfile。

    添加 Docker 支持菜单项的屏幕截图。

  4. 选择容器类型。

对于 Visual Studio 2022 版本 17.0 到 17.7,请使用以下步骤:

  1. 使用“带 React.js 的 ASP.NET Core”模板创建新项目。

    创建新 React.js 项目的屏幕截图。

  2. 在“其他信息”屏幕上,你无法选择“启用 Docker 支持”,但不要担心,可以稍后添加该支持。

    “创建新 React.js 项目 - 其他信息”屏幕的屏幕截图。

  3. 右键单击项目节点,然后选择“添加”>“Docker 支持”,将 Dockerfile 添加到你的项目中。

    添加 Docker 支持菜单项的屏幕截图。

  4. 选择容器类型。

调试

使用 Visual Studio 2022 版本 17.9 或更高版本以及使用 vite.jsReact 和 ASP.NET Core 模板,项目已配置为启动具有调试支持的客户端和服务器项目,但你需要为单页应用程序 (SPA) 代理设置正确的端口,以用于访问容器中运行的 ASP.NET Core 服务器。 可以从 Visual Studio 中的“容器”窗口获取主机端口,并在 React 项目中设置该端口,如创建 React 应用 - Docker中所述。

还可以在浏览器中禁用服务器的启动,该服务器设置为使用 Swagger 打开,这在这种情况下是不需要的。 若要禁用浏览器启动,请打开“属性”(Alt+Enter),转到“调试”选项卡,然后单击“打开调试启动配置文件 UI”链接,然后清除“启动浏览器”复选框。

如果使用的是早期版本的 Visual Studio 2022,请继续阅读以使用单页应用程序 (SPA) 代理服务器设置调试。

项目在调试期间使用 SPA 代理。 请参阅改进了单页应用 (SPA) 模板。 调试时,JavaScript 客户端在主机上运行,但 ASP.NET Core 服务器代码在容器中运行。 发布后,代理不运行,客户端代码在 ASP.NET Core 代码所在的服务器上运行。 你已具有可用于调试服务器代码的调试配置文件 *Docker。 若要调试 JavaScript 客户端代码,可以创建一个额外的调试配置文件。 调试 JavaScript 时,还需要从命令提示符处手动启动代理。 可以让它在多个调试会话中保持运行。

  1. 生成项目(如果尚未生成)。

  2. 打开 Visual Studio 开发命令提示符,转到项目中的 ClientApp 文件夹,然后提供命令 npm run start。 你应看到与下面类似的内容:

    Compiled successfully!
    
    You can now view project3_spa in the browser.
    
      Local:            https://localhost:44407
      On Your Network:  https://192.168.1.5:44407
    
    Note that the development build isn't optimized.
    To create a production build, use npm run build.
    
    webpack compiled successfully
    

    注意

    记下本地 URL。 需要在调试启动配置文件中提供此信息,该配置文件存储在 launchSettings.json 文件中。

  3. 打开包含调试配置文件的下拉列表(在绿色三角形按钮或“启动”按钮旁边),选择“{ProjectName} 调试属性”,然后选择“Docker”配置文件。

  4. 查看“环境变量”部分,并添加以下环境变量(如果尚不存在):

    ASPNETCORE_ENVIRONMENT=Development,ASPNETCORE_HOSTINGSTARTUPASSEMBLIES=Microsoft.AspNetCore.SpaProxy

  5. 将 URL 设置为 https://localhost:{proxy-port},其中 {proxy-port} 是来自代理服务器的端口(来自步骤 1)。

    用于客户端调试的调试启动配置文件设置的屏幕截图。

    此操作更改 launchSettings.json 文件中的 Docker 条目,并为主机上运行的本地代理启动正确的 URL。 在“解决方案资源管理器”的“属性”下,找到 launchSettings.json 文件。

  6. 你应看到如下代码:

    "profiles": {
       "Docker": {
          "commandName": "Docker",
          "launchBrowser": true,
          "environmentVariables": {
            "ASPNETCORE_ENVIRONMENT": "Development",
            "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy"
          },
          "launchUrl": "https://localhost:44407",
          "useSSL": true
       }
    }
    

    重要

    如果使用代理,请不要将启动设置选项 publishAllPorts 设置为 true。 此选项将所有公开的端口发布到一个随机端口,当你在 SPA 代理中设置特定端口时,该随机端口将不起作用。

  7. 打开文件 ClientApp/src/setupProxy.js 并更改用于设置目标的行,以使用容器上的 localhost 地址和端口。 可以在“容器”窗口的“端口”选项卡上找到端口。

    const target =  'https://localhost:{container-port}';
    

    如果使用 HTTPS,请务必为 HTTPS 选择正确的端口(在本教程中为 443)。

  8. 对应用启动调试 (F5)。

    正在运行的应用的屏幕截图。

    如果在遇到尝试写入输出程序集时遇到生成错误,可能需要停止之前运行的容器来解锁文件。

  9. 通过在 incrementCounter 函数的 ClientApp/src/components/Counter.js 中设置断点来验证你是否可以在客户端 JavaScript 代码中命中断点,然后通过单击“计数器”页上的“增量”按钮来尝试命中断点。

    显示调试客户端 JavaScript 的屏幕截图。

  10. 接下来,尝试在服务器端 ASP.NET Core 代码中命中断点。 在 Get 方法的 WeatherController.cs 中设置断点,然后尝试将 /weatherforecast 追加到基础 localhost 和端口 URL 以激活该代码。

    显示调试服务器端 ASP.NET Core 代码的屏幕截图。

  11. 如果容器端口发生更改(当你做出重大更改时可能出现此情况,例如更新 launchSettings.json 或更新 IDE 中的调试启动配置文件时),你需要更新 setupProxy.js 中的端口并重启代理。 终止当前代理(在它所运行的命令窗口中按 Ctrl+C),然后使用相同的命令 npm run start 重启它。

在工具栏的调试下拉列表中选择“Docker”,然后开始调试应用。 你可能会看到提示信任证书的消息;选择信任证书以继续。 第一次生成时,Docker 会下载基础映像,因此可能需要更长的时间。

“输出”窗口中的“容器工具”选项显示正在进行的操作。 你将看到与 npm.exe 关联的安装步骤。

浏览器将显示应用的主页。

正在运行的应用的屏幕截图。

容器窗口

打开“容器”工具窗口。 可以在“查看”>“其他 Windows”>“容器”下找到它,或按 Ctrl+Q 并开始在搜索框中键入 containers,然后从结果中选择“容器”窗口。 窗口打开时,将其停靠在编辑器窗格的底部。

“容器”窗口显示正在运行的容器,并可查看相关信息。 可以查看环境变量、标签、端口、卷、文件系统和日志。 使用工具栏按钮,可以在容器内创建一个终端(shell 提示)、附加调试器或删除未使用的容器。 请参阅使用“容器”窗口

“容器”窗口的屏幕截图。

单击“文件”选项卡,然后展开 app 文件夹以查看已发布的应用程序文件。

还可以查看图像并检查相关信息。 选择“图像”选项卡,找到项目对应的选项卡,然后选择“详细信息”选项卡以查看包含图像相关信息的 json 文件。

显示图像和详细信息的“容器”窗口的屏幕截图。

注意

“开发”映像不包含应用程序二进制文件和其他内容,因为“调试”配置使用卷装载提供迭代编辑和调试体验 。 若要创建包含所有内容的生产映像,请使用“版本”配置。

发布 Docker 映像

完成应用程序的开发和调试循环后,可以创建应用程序的生产映像。

  1. 将配置下拉列表更改为“发布”,然后生成应用。

  2. 在解决方案资源管理器中右键单击项目,并选择“发布” 。

  3. 在“发布目标”对话框中,选择“Docker 容器注册表”。

    选择“Docker 容器注册表”。

  4. 接下来,选择“Azure 容器注册表”。

    选择“Azure 容器注册表”。

  5. 选择“新建 Azure 容器注册表”。

  6. 在“新建 Azure 容器注册表”屏幕中填写所需的值。

    设置 建议值 描述
    DNS 前缀 全局唯一名称 用于唯一标识容器注册表的名称。
    订阅 选择订阅 要使用的 Azure 订阅。
    资源组 myResourceGroup 要在其中创建容器注册表的资源组的名称。 选择“新建” 创建新的资源组。
    SKU 标准 容器注册表的服务层
    注册表位置 靠近你的位置 在你附近或使用容器注册表的其他服务附近的区域中,选择一个“位置”。

    Visual Studio 的创建 Azure 容器注册表对话框的屏幕截图。

  7. 选择“创建”,然后选择“完成” 。

    显示选择或创建新 Azure 容器注册表的屏幕截图。

    发布过程结束时,你可查看发布设置并在需要时对其进行编辑,也可使用“发布”按钮再次发布该图像。

    显示成功发布的屏幕截图。

    若要使用“发布”对话框重新开始,请使用此页上的“删除”链接删除发布配置文件,然后再次选择“发布” 。

  1. 将配置下拉列表更改为“发布”,然后生成应用。

  2. 在解决方案资源管理器中右键单击项目,并选择“发布” 。

  3. 在“发布目标”对话框中,选择“Docker 容器注册表”。

    显示选择 Docker 容器注册表的屏幕截图。

  4. 接下来,选择“Azure 容器注册表”。

    显示选择 Azure 容器注册表的屏幕截图。

  5. 选择“新建 Azure 容器注册表”。

  6. 在“新建 Azure 容器注册表”屏幕中填写所需的值。

    设置 建议值 描述
    DNS 前缀 全局唯一名称 用于唯一标识容器注册表的名称。
    订阅 选择订阅 要使用的 Azure 订阅。
    资源组 myResourceGroup 要在其中创建容器注册表的资源组的名称。 选择“新建” 创建新的资源组。
    SKU 标准 容器注册表的服务层
    注册表位置 靠近你的位置 在你附近或使用容器注册表的其他服务附近的区域中,选择一个“位置”。

    Visual Studio 的创建 Azure 容器注册表对话框的屏幕截图。

  7. 选择“创建”,然后选择“完成” 。

    显示选择或创建新 Azure 容器注册表的屏幕截图。

    发布过程结束时,你可查看发布设置并在需要时对其进行编辑,也可使用“发布”按钮再次发布该图像。

    显示成功发布的屏幕截图

    若要使用“发布”对话框重新开始,请使用此页上的“删除”链接删除发布配置文件,然后再次选择“发布” 。

后续步骤

现在可以将容器从注册表中拖放到任何能够运行 Docker 映像的主机上,例如Azure 容器实例

其他资源