c# - 作为 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 创建它。
解决方案
推荐阅读
- android - 正确处理禁用/只读文本框以实现可访问性
- ios - 动态大小嵌套的 UIStackView
- c# - 有没有一种在 Visual Studio 之外编辑代码分析规则集的好方法
- java - Impala 单个插入语句创建多个文件
- ios - iOS 12 - oneTimeCode OTP 委托
- javascript - 淘汰赛点击绑定不会在点击时触发
- java - DataVirt 错误“MSC000001:无法启动服务 jboss.web.deployment.default-host 服务 jboss.web.development.default-host 中的启动异常。/”
- c# - DDD、CQRS 和 Mediatr 查询过滤
- css - React-Bootstrap 居中选项卡
- bash - 如何使用 Mac OS X 上的命令行从另一个用户的私人 Github 存储库下载(仅文件)?