.NET API容器部署异常:Azure DevOps构建镜像抛CultureNotFoundException
问题
我有一个.NET API,本地运行完全正常,本地构建的容器在目标机器运行也正常。但通过Azure DevOps CI构建并推送至Azure容器仓库的容器,启动后Swagger能正常运行,但调用任何接口都会抛出System.Globalization.CultureNotFoundException异常,本地构建无此问题,请问可能的问题出在哪里?
相关配置文件
Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:7.0-alpine AS base WORKDIR /app EXPOSE 80 EXPOSE 443 EXPOSE 5002 EXPOSE 5206 EXPOSE 7208 FROM mcr.microsoft.com/dotnet/sdk:7.0-alpine AS build WORKDIR /src COPY ["HealthCheck.API/HealthCheck.API.csproj", "HealthCheck.API/"] RUN dotnet restore "HealthCheck.API/HealthCheck.API.csproj" COPY . . WORKDIR "/src/HealthCheck.API" RUN dotnet build "HealthCheck.API.csproj" -c Release -o /app/build FROM build AS publish RUN dotnet publish "HealthCheck.API.csproj" -c Release -o /app/publish /p:UseAppHost=false FROM base AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "HealthCheck.API.dll"]
azure-pipeline.yml
trigger: - master resources: - repo: self variables: solution: '**/*.sln' buildPlatform: 'Any CPU' buildConfiguration: 'Release' # Container registry service connection established during pipeline creation dockerRegistryServiceConnection: 'xxxxxxxx' imageRepository: 'healthcheck' containerRegistry: 'xxxxxx' dockerfilePath: '$(Build.SourcesDirectory)/HealthCheck/Dockerfile' tag: '$(Build.BuildId)' # Agent VM image name vmImageName: 'ubuntu-latest' stages: - stage: BuildDocker displayName: Build and push stage jobs: - job: Build displayName: Build pool: vmImage: 'ubuntu-latest' steps: - task: Docker@2 displayName: Build and push an image to container registry inputs: command: buildAndPush repository: $(imageRepository) dockerfile: $(dockerfilePath) containerRegistry: $(dockerRegistryServiceConnection) tags: | $(tag) latest
控制台错误信息
info: Microsoft.AspNetCore.Hosting.Diagnostics[1] Request starting HTTP/1.1 GET http://192.168.10.17:5216/api/Zdarzenie/?typZdarzenia=-1 - - info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4] CORS policy execution successful. info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0] Executing endpoint 'HTTP: GET api/Zdarzenie' fail: Microsoft.EntityFrameworkCore.Database.Connection[20004] An error occurred using the connection to database 'HealthCheck' on server 'xxxxxxxxxxx'. fail: Microsoft.EntityFrameworkCore.Query[10100] An exception occurred while iterating over the results of a query for context type 'HealthCheck.Core.Data.ApplicationDbContext'. System.Globalization.CultureNotFoundException: Only the invariant culture is supported in globalization-invariant mode. See https://aka.ms/GlobalizationInvariantMode for more information. (Parameter 'name') en-us is an invalid culture identifier. at System.Globalization.CultureInfo.GetCultureInfo(String name) at Microsoft.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry, SqlConnectionOverrides overrides) at Microsoft.Data.SqlClient.SqlConnection.Open(SqlConnectionOverrides overrides) at Microsoft.Data.SqlClient.SqlConnection.Open() at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerConnection.OpenDbConnection(Boolean errorsExpected) at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenInternal(Boolean errorsExpected) at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.Open(Boolean errorsExpected) at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReader(RelationalCommandParameterObject parameterObject) at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.InitializeReader(Enumerator enumerator) at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.<>c.<MoveNext>b__21_0(DbContext _, Enumerator enumerator) at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded) at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.MoveNext() System.Globalization.CultureNotFoundException: Only the invariant culture is supported in globalization-invariant mode. See https://aka.ms/GlobalizationInvariantMode for more information. (Parameter 'name') en-us is an invalid culture identifier. at System.Globalization.CultureInfo.GetCultureInfo(String name) at Microsoft.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry, SqlConnectionOverrides overrides) at Microsoft.Data.SqlClient.SqlConnection.Open(SqlConnectionOverrides overrides) at Microsoft.Data.SqlClient.SqlConnection.Open() at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerConnection.OpenDbConnection(Boolean errorsExpected) at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenInternal(Boolean errorsExpected) at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.Open(Boolean errorsExpected) at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReader(RelationalCommandParameterObject parameterObject) at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.InitializeReader(Enumerator enumerator) at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.<>c.<MoveNext>b__21_0(DbContext _, Enumerator enumerator) at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded) at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.MoveNext() info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1] Executed endpoint 'HTTP: GET api/Zdarzenie' fail: Microsoft.AspNetCore.Server.Kestrel[13] Connection id "0HMVLD3R0RJ88", Request id "0HMVLD3R0RJ88:00000002": An unhandled exception was thrown by the application. System.Globalization.CultureNotFoundException: Only the invariant culture is supported in globalization-invariant mode. See https://aka.ms/GlobalizationInvariantMode for more information. (Parameter 'name') en-us is an invalid culture identifier. at System.Globalization.CultureInfo.GetCultureInfo(String name) at Microsoft.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry, SqlConnectionOverrides overrides) at Microsoft.Data.SqlClient.SqlConnection.Open(SqlConnectionOverrides overrides) at Microsoft.Data.SqlClient.SqlConnection.Open() at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerConnection.OpenDbConnection(Boolean errorsExpected) at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenInternal(Boolean errorsExpected) at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.Open(Boolean errorsExpected) at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReader(RelationalCommandParameterObject parameterObject) at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.InitializeReader(Enumerator enumerator) at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.<>c.<MoveNext>b__21_0(DbContext _, Enumerator enumerator) at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded) at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.MoveNext() at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source) at HealthCheck.Core.Data.Repositories.ZdarzenieRepository.GetAllWithTyp(Int32 typZdarzenia, Int32 count) in /src/HealthCheck.Core/Data/Repositories/ZdarzenieRepository.cs:line 35 at HealthCheck.API.Endpoints.ZdarzenieEndpoint.<>c.<MapZdarzenieEndpoint>b__0_0(Int32 typZdarzenia, Nullable`1 idAplikacja, IUnitOfWork unitOfWork, IMapper mapper) in /src/HealthCheck.API/Endpoints/ZdarzenieEndpoint.cs:line 20 at lambda_method10(Closure, Object, HttpContext) at Microsoft.AspNetCore.Routing.EndpointMiddleware.Invoke(HttpContext httpContext) --- End of stack trace from previous location --- at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext) at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider) at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application) info: Microsoft.AspNetCore.Hosting.Diagnostics[2] Request finished HTTP/1.1 GET http://192.168.10.17:5216/api/Zdarzenie/?typZdarzenia=-1 - - - 500 0 - 2.3359ms
原因分析
异常核心指向全球化不变模式(Globalization-Invariant Mode):
- Alpine版本的.NET镜像默认启用该模式,目的是减小镜像体积,此时仅支持固定区域性(InvariantCulture),无法加载
en-us等特定区域性 - 本地构建时,开发环境可能隐式禁用了该模式,或系统包含完整的区域性数据,因此无异常
- CI构建流程中,发布环节继承了Alpine镜像的全球化不变设置,且未显式关闭,导致连接数据库时加载
en-us区域性失败
解决方案
1. 修改Dockerfile禁用全球化不变模式(推荐)
在base阶段添加环境变量并安装ICU区域性依赖:
FROM mcr.microsoft.com/dotnet/aspnet:7.0-alpine AS base # 关闭全球化不变模式 ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false # 安装Alpine缺失的ICU区域性数据 RUN apk add --no-cache icu-libs icu-data-full WORKDIR /app EXPOSE 80 EXPOSE 443 EXPOSE 5002 EXPOSE 5206 EXPOSE 7208
2. 在项目文件中禁用全球化不变模式
在API项目的.csproj文件中添加配置,强制发布时关闭该模式:
<PropertyGroup> <InvariantGlobalization>false</InvariantGlobalization> </PropertyGroup>
注:若使用Alpine镜像,仍需确认是否需要额外安装ICU包,部分场景下dotnet会自动处理,但保险起见建议配合Dockerfile的依赖安装步骤。
3. 避免代码依赖特定区域性
检查EF Core/SqlClient配置,改用固定区域性:
- 在DbContext配置中指定:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer(connectionString, options => options.UseInvariantCulture()); }
- 或在应用启动时设置全局默认区域性:
CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture; CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.InvariantCulture;
内容的提问来源于stack exchange,提问作者Kamil D
相关产品推荐
相关产品推荐

