首页 > 解决方案 > 作为 docker 容器运行时,无法从程序集“Hangfire.AspNetCore”加载类型“Hangfire.HangfireEndpointRouteBuilderExtensions”

问题描述

我无法使用使用 Hangfire 的 .NET 5 应用程序运行 docker 容器。在当地,一切都很好。但随后我使用 Azure 管道构建映像,保存在 Azure 容器注册表中并尝试在本地运行它。我得到的例外是:

Starting app for environment Production
crit: Microsoft.AspNetCore.Hosting.Diagnostics[6]
      Application startup exception
      System.TypeLoadException: Could not load type 'Hangfire.HangfireEndpointRouteBuilderExtensions' from assembly 'Hangfire.AspNetCore, Version=1.7.18.0, Culture=neutral, PublicKeyToken=null'.
         at MyCompany.Platform.Scheduler.WebApi.Startup.<>c.<Configure>b__5_1(IEndpointRouteBuilder endpoints)
         at Microsoft.AspNetCore.Builder.EndpointRoutingApplicationBuilderExtensions.UseEndpoints(IApplicationBuilder builder, Action`1 configure)
         at MyCompany.Platform.Scheduler.WebApi.Startup.Configure(IApplicationBuilder app, IWebHostEnvironment env) in /home/vsts/work/1/s/src/WebApi/Startup.cs:line 56
         at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
         at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
         at Microsoft.AspNetCore.Hosting.ConfigureBuilder.Invoke(Object instance, IApplicationBuilder builder)
         at Microsoft.AspNetCore.Hosting.ConfigureBuilder.<>c__DisplayClass4_0.<Build>b__0(IApplicationBuilder builder)
         at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.<>c__DisplayClass15_0.<UseStartup>b__1(IApplicationBuilder app)
         at Microsoft.AspNetCore.Mvc.Filters.MiddlewareFilterBuilderStartupFilter.<>c__DisplayClass0_0.<Configure>g__MiddlewareFilterBuilder|0(IApplicationBuilder builder)
         at Microsoft.AspNetCore.HostFilteringStartupFilter.<>c__DisplayClass0_0.<Configure>b__0(IApplicationBuilder app)
         at Microsoft.AspNetCore.Hosting.GenericWebHostService.StartAsync(CancellationToken cancellationToken)
Unhandled exception. System.TypeLoadException: Could not load type 'Hangfire.HangfireEndpointRouteBuilderExtensions' from assembly 'Hangfire.AspNetCore, Version=1.7.18.0, Culture=neutral, PublicKeyToken=null'.
   at MyCompany.Platform.Scheduler.WebApi.Startup.<>c.<Configure>b__5_1(IEndpointRouteBuilder endpoints)
   at Microsoft.AspNetCore.Builder.EndpointRoutingApplicationBuilderExtensions.UseEndpoints(IApplicationBuilder builder, Action`1 configure)
   at MyCompany.Platform.Scheduler.WebApi.Startup.Configure(IApplicationBuilder app, IWebHostEnvironment env) in /home/vsts/work/1/s/src/WebApi/Startup.cs:line 56
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Microsoft.AspNetCore.Hosting.ConfigureBuilder.Invoke(Object instance, IApplicationBuilder builder)
   at Microsoft.AspNetCore.Hosting.ConfigureBuilder.<>c__DisplayClass4_0.<Build>b__0(IApplicationBuilder builder)
   at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.<>c__DisplayClass15_0.<UseStartup>b__1(IApplicationBuilder app)
   at Microsoft.AspNetCore.Mvc.Filters.MiddlewareFilterBuilderStartupFilter.<>c__DisplayClass0_0.<Configure>g__MiddlewareFilterBuilder|0(IApplicationBuilder builder)
   at Microsoft.AspNetCore.HostFilteringStartupFilter.<>c__DisplayClass0_0.<Configure>b__0(IApplicationBuilder app)
   at Microsoft.AspNetCore.Hosting.GenericWebHostService.StartAsync(CancellationToken cancellationToken)
   at Microsoft.Extensions.Hosting.Internal.Host.StartAsync(CancellationToken cancellationToken)
   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)
   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)
   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.Run(IHost host)
   at MyCompany.Platform.Scheduler.WebApi.Program.Main(String[] args) in /home/vsts/work/1/s/src/WebApi/Program.cs:line 10

这是我的 Dockerfile

FROM mcr.microsoft.com/dotnet/aspnet:5.0
COPY out .
EXPOSE 80
ENTRYPOINT ["dotnet", "WebApi.dll"]

这是我的天蓝色管道

trigger:
- master

pool:
  vmImage: ubuntu-20.04

variables:
  imageName: 'my-scheduler'

stages:
  - stage:
    displayName: 'Build'
    jobs:
      - job: 'Build'
        displayName: 'Restore and compile'
        steps:
          - task: UseDotNet@2
            displayName: 'Use .NET Core sdk 5.0.x'
            inputs:
              version: '5.0.x'
              includePreviewVersions: true
          - script: dotnet restore --no-cache --force
            displayName: 'Restore dependencies'
          - script: dotnet build --configuration Release --no-restore
            displayName: 'Build with Release Configuration'
          - script: dotnet vstest test/*FunctionalTests/bin/Release/**/*FunctionalTests.dll
            displayName: 'Run functional tests'
          - script: dotnet publish -c Release -o out
            displayName: 'Publish to folder'
          - task: Docker@2
            inputs:
              containerRegistry: 'saswcr-service-connection'
              command: 'login'
          - task: Docker@2
            inputs:
              containerRegistry: 'saswcr-service-connection'
              repository: $(imageName)
              command: 'buildAndPush'
              dockerfile: './Dockerfile'
              tags: |
                latest
                $(Build.BuildNumber)

这就是我登录 ACR 后在本地运行容器的方式

docker run --name my-scheduler -p 8080:80 saswcr.azurecr.io/my-scheduler:20201124.4

我的想法不多了。我使用的hangfire版本:

<PackageReference Include="Hangfire.AspNetCore" Version="1.7.18" />
<PackageReference Include="Hangfire.Core" Version="1.7.18" />
<PackageReference Include="Hangfire.MemoryStorage" Version="1.7.0" />

也许图像创建有问题,但我想不出究竟是什么。

如果我执行以下操作来查看容器的内容,我会docker export $(docker ps -lq) | tar tf - 看到:

$ docker export $(docker ps -lq) | tar tf -
.dockerenv
Application.deps.json
Application.dll
Application.pdb
Azure.Core.dll
Azure.Identity.dll
Azure.Security.KeyVault.Certificates.dll
Azure.Security.KeyVault.Secrets.dll
Castle.Core.dll
CodeCoverage/
CodeCoverage/CodeCoverage.config
CodeCoverage/CodeCoverage.exe
CodeCoverage/amd64/
CodeCoverage/amd64/covrun64.dll
CodeCoverage/amd64/msdia140.dll
CodeCoverage/codecoveragemessages.dll
CodeCoverage/covrun32.dll
CodeCoverage/msdia140.dll
FluentAssertions.dll
Hangfire.AspNetCore.dll
Hangfire.Core.dll
Hangfire.MemoryStorage.dll
Hangfire.SqlServer.dll
Infra.Hangfire.deps.json
Infra.Hangfire.dll
Infra.Hangfire.pdb
Microsoft.AspNetCore.Antiforgery.dll
Microsoft.AspNetCore.Authentication.JwtBearer.dll
Microsoft.AspNetCore.Authentication.OpenIdConnect.dll
Microsoft.AspNetCore.Cryptography.Internal.dll
Microsoft.AspNetCore.DataProtection.Abstractions.dll
Microsoft.AspNetCore.DataProtection.dll
Microsoft.AspNetCore.Hosting.Abstractions.dll
Microsoft.AspNetCore.Hosting.Server.Abstractions.dll
Microsoft.AspNetCore.Http.Abstractions.dll
Microsoft.AspNetCore.Http.Extensions.dll
Microsoft.AspNetCore.Http.Features.dll
Microsoft.AspNetCore.Mvc.Testing.dll
Microsoft.AspNetCore.TestHost.dll
Microsoft.AspNetCore.WebUtilities.dll
Microsoft.Bcl.AsyncInterfaces.dll
Microsoft.CodeCoverage.props
Microsoft.CodeCoverage.targets
Microsoft.DotNet.PlatformAbstractions.dll
Microsoft.Extensions.Configuration.Abstractions.dll
Microsoft.Extensions.DependencyInjection.Abstractions.dll
Microsoft.Extensions.DependencyInjection.dll
Microsoft.Extensions.DependencyModel.dll
Microsoft.Extensions.FileProviders.Abstractions.dll
Microsoft.Extensions.FileSystemGlobbing.dll
Microsoft.Extensions.Hosting.Abstractions.dll
Microsoft.Extensions.Logging.Abstractions.dll
Microsoft.Extensions.ObjectPool.dll
Microsoft.Extensions.Options.dll
Microsoft.Extensions.Primitives.dll
Microsoft.Identity.Client.Extensions.Msal.dll
Microsoft.Identity.Client.dll
Microsoft.Identity.Web.UI.Views.dll
Microsoft.Identity.Web.UI.dll
Microsoft.Identity.Web.dll
Microsoft.IdentityModel.JsonWebTokens.dll
Microsoft.IdentityModel.Logging.dll
Microsoft.IdentityModel.Protocols.OpenIdConnect.dll
Microsoft.IdentityModel.Protocols.dll
Microsoft.IdentityModel.Tokens.dll
Microsoft.Net.Http.Headers.dll
Microsoft.OpenApi.dll
Microsoft.TestPlatform.CommunicationUtilities.dll
Microsoft.TestPlatform.CoreUtilities.dll
Microsoft.TestPlatform.CrossPlatEngine.dll
Microsoft.TestPlatform.PlatformAbstractions.dll
Microsoft.TestPlatform.Utilities.dll
Microsoft.VisualStudio.CodeCoverage.Shim.dll
Microsoft.VisualStudio.TestPlatform.Common.dll
Microsoft.VisualStudio.TestPlatform.ObjectModel.dll
Microsoft.VisualStudio.TraceDataCollector.dll
Microsoft.Win32.Registry.dll
Mono.Cecil.Mdb.dll
Mono.Cecil.Pdb.dll
Mono.Cecil.Rocks.dll
Mono.Cecil.dll
Moq.dll
Newtonsoft.Json.dll
NuGet.Frameworks.dll
Swashbuckle.AspNetCore.Swagger.dll
Swashbuckle.AspNetCore.SwaggerGen.dll
Swashbuckle.AspNetCore.SwaggerUI.dll
System.Buffers.dll
System.Configuration.ConfigurationManager.dll
System.Data.SqlClient.dll
System.Diagnostics.DiagnosticSource.dll
System.IdentityModel.Tokens.Jwt.dll
System.Runtime.CompilerServices.Unsafe.dll
System.Security.AccessControl.dll
System.Security.Cryptography.ProtectedData.dll
System.Security.Cryptography.Xml.dll
System.Security.Principal.Windows.dll
System.Text.Encoding.CodePages.dll
System.Text.Encodings.Web.dll
System.Threading.dll
UseCases/
UseCases/AddJob/
UseCases/AddJob/simple_job.json
UseCases/AddJob/simple_job_with_url_parameters.json
WebApi
WebApi.FunctionalTests.deps.json
WebApi.FunctionalTests.dll
WebApi.FunctionalTests.pdb
WebApi.FunctionalTests.runtimeconfig.json
WebApi.deps.json
WebApi.dll
WebApi.pdb
WebApi.runtimeconfig.json
appsettings.Development.json
appsettings.Local.json
appsettings.Production.json
appsettings.Qa.json
appsettings.Test.json
appsettings.json
bin/
etc.

似乎导致问题的行是:

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
    endpoints
      .MapHangfireDashboard(HangfireConstants.DashboardUrl)
      .RequireAuthorization(PolicyConstants.DashboardPolicy);
});

可能是什么原因?为什么它发生在 docker 容器中而不是外部?

更新 1 2020-11-24: 有趣的发现。我已经为相同的代码生成了一个 docker 映像,只是这次使用 GitLab CI、Gitlab 容器存储库和 kaniko(我比 Azure DevOps 更熟悉这种方法),如下所示:

default:
  image: mcr.microsoft.com/dotnet/sdk:5.0

variables:
  PUBLISH_OUTPUT_DIR: publish
  ENTRYPOINT_DLL: WebApi.dll
  WEB_APP_HOOK: $WEB_APP_HOOK

stages:
  - build
  - test
  - publish
  - delivery
  - release

build:
  stage: build
  script:
    - dotnet restore --no-cache --force
    - dotnet build -c Release --no-restore
  artifacts:
    paths:
    - test
    expire_in: 1 hour

unit_tests:
  stage: test
  script: dotnet vstest test/*UnitTests/bin/Release/**/*UnitTests.dll --Blame
  rules:
    - exists:
      - test/*UnitTests/*UnitTests.csproj

functional_tests:
  stage: test
  script: dotnet vstest test/*FunctionalTests/bin/Release/**/*FunctionalTests.dll --Blame
  rules:
    - exists:
      - test/*FunctionalTests/*FunctionalTests.csproj

publish:
  stage: publish
  script:
    - dotnet publish -c Release -o $PUBLISH_OUTPUT_DIR
  artifacts:
    paths:
    - publish/
    expire_in: 1 hour

build_image:
  stage: delivery
  image:
    name: gcr.io/kaniko-project/executor:debug
    entrypoint: [""]
  before_script:
    - echo "Generating $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG"
    - echo "FROM mcr.microsoft.com/dotnet/aspnet:5.0" > $CI_PROJECT_DIR/Dockerfile
    - echo "COPY $PUBLISH_OUTPUT_DIR ." >> $CI_PROJECT_DIR/Dockerfile
    - echo "EXPOSE 80" >> $CI_PROJECT_DIR/Dockerfile
    - echo "ENTRYPOINT [\"dotnet\", \"$ENTRYPOINT_DLL\"]" >> $CI_PROJECT_DIR/Dockerfile
    # @see https://github.com/GoogleContainerTools/kaniko/issues/1227
    - mkdir -p /kaniko/.docker
    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
    - echo "Displaying files.."
    - cat $CI_PROJECT_DIR/Dockerfile
    - cat /kaniko/.docker/config.json
  script:
    - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG

然后我将容器运行为:

docker run --name cubic-scheduler -p 8080:80 registry.gitlab.com/cubictelecom/my-scheduler

这一次,通过这种创建 docker 镜像的方式,效果很好。因此,问题似乎确实与如何创建 docker 映像有关。但究竟是什么问题,为什么它对hangfire有如此大的影响?

更新 2 2020-11-24: 依赖项如下:

WebAPI

<ItemGroup>
  <PackageReference Include="Microsoft.Identity.Web" Version="1.3.0" />
  <PackageReference Include="Microsoft.Identity.Web.UI" Version="1.3.0" />
  <PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
</ItemGroup>

<ItemGroup>
  <ProjectReference Include="..\Application\Application.csproj" />
  <ProjectReference Include="..\Infra.Hangfire\Infra.Hangfire.csproj" />
</ItemGroup>

应用

no dependencies.

红外线吊火

<ItemGroup>
  <PackageReference Include="Hangfire.AspNetCore" Version="1.7.18" />
  <PackageReference Include="Hangfire.Core" Version="1.7.18" />
  <PackageReference Include="Hangfire.MemoryStorage" Version="1.7.0" />
  <PackageReference Include="Hangfire.SqlServer" Version="1.7.18" />
</ItemGroup>

<ItemGroup>
  <ProjectReference Include="..\Application\Application.csproj" />
</ItemGroup>

如您所见,版本没有冲突,因为它使用干净的架构方法,其中所有第三方依赖项都在基础层(和 Web api)中。无论如何,它在本地运行良好,并且在使用 kaniko 和 gitlab 创建它时作为 docker 容器运行良好。问题一定在于我如何为 Azure DevOps 创建它。

标签: c#dockercontainershangfire

解决方案


推荐阅读