使用 docker-compose 时,为什么我的 .NET Web API 应用程序无法连接到 docker 上的 MySQL?

ari*_*efs 0 .net c# mysql asp.net docker

我有一个在 docker 中运行的 .NET Core 应用程序,它连接到 docker 上的 MySQL 数据库,

\n

当 MySQL docker 容器使用 docker run 命令运行时:

\n
C:\\dev>docker run -p 3310:3306 --name=mysql1 -e MYSQL_ROOT_PASSWORD=pw -d mysql:5.6\nC:\\dev>docker run -p 3311:3306 --name=mysql2 -e MYSQL_ROOT_PASSWORD=pw -d mysql:5.6\n
Run Code Online (Sandbox Code Playgroud)\n

我没有收到错误,但是当我尝试使用 docker-compose 时,出现此错误:

\n
ConnectionString: server=database0; port=3312; database=post; user=root; password=pw; Persist Security Info=False; Connect Timeout=300\nConnectionString: server=database1; port=3313; database=post; user=root; password=pw; Persist Security Info=False; Connect Timeout=300\nfail: Microsoft.AspNetCore.Server.Kestrel[13]\n      Connection id "0HMA1R75ALK39", Request id "0HMA1R75ALK39:00000002": An unhandled exception was thrown by the application.\n      System.TypeInitializationException: The type initializer for \'MySql.Data.MySqlClient.Replication.ReplicationManager\' threw an exception.\n       ---> System.TypeInitializationException: The type initializer for \'MySql.Data.MySqlClient.MySqlConfiguration\' threw an exception.\n       ---> System.Configuration.ConfigurationErrorsException: Configuration system failed to initialize\n       ---> System.NotSupportedException: CodeBase is not supported on assemblies loaded from a single-file bundle.\n         at System.Reflection.RuntimeAssembly.get_CodeBase()\n         at System.Configuration.ClientConfigPaths..ctor(String exePath, Boolean includeUserConfig)\n         at System.Configuration.ClientConfigPaths.GetPaths(String exePath, Boolean includeUserConfig)\n         at System.Configuration.ClientConfigurationHost.get_ConfigPaths()\n         at System.Configuration.ClientConfigurationHost.GetStreamName(String configPath)\n         at System.Configuration.ClientConfigurationHost.get_IsAppConfigHttp()\n         at System.Configuration.Internal.DelegatingConfigHost.get_IsAppConfigHttp()\n         at System.Configuration.ClientConfigurationSystem..ctor()\n         at System.Configuration.ConfigurationManager.EnsureConfigurationSystem()\n         --- End of inner exception stack trace ---\n         at System.Configuration.ConfigurationManager.EnsureConfigurationSystem()\n         at System.Configuration.ConfigurationManager.PrepareConfigSystem()\n         at System.Configuration.ConfigurationManager.GetSection(String sectionName)\n         at MySql.Data.MySqlClient.MySqlConfiguration..cctor()\n         --- End of inner exception stack trace ---\n         at MySql.Data.MySqlClient.MySqlConfiguration.get_Settings()\n         at MySql.Data.MySqlClient.Replication.ReplicationManager..cctor()\n         --- End of inner exception stack trace ---\n         at MySql.Data.MySqlClient.Replication.ReplicationManager.IsReplicationGroup(String groupName)\n         at MySql.Data.MySqlClient.MySqlConnection.Open()\n         at System.Data.Common.DbConnection.OpenAsync(CancellationToken cancellationToken)\n      --- End of stack trace from previous location ---\n         at MySql.EntityFrameworkCore.MySQLDatabaseCreator.<>c__DisplayClass20_0.<<ExistsAsync>b__0>d.MoveNext()\n      --- End of stack trace from previous location ---\n         at MySql.EntityFrameworkCore.Storage.Internal.MySQLExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)\n         at Microsoft.EntityFrameworkCore.Storage.RelationalDatabaseCreator.EnsureDeletedAsync(CancellationToken cancellationToken)\n         at PostService.Data.DataAccess.InitDatabase(Int32 countUsers, Int32 countCategories)\n         at PostService.Controllers.PostsController.InitDatabase(Int32 countUsers, Int32 countCategories)\n         at lambda_method4(Closure , Object )\n         at Microsoft.Extensions.Internal.ObjectMethodExecutorAwaitable.Awaiter.GetResult()\n         at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)\n         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)\n         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)\n         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)\n         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)\n         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()\n      --- End of stack trace from previous location ---\n         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)\n         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)\n         at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)\n         at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)\n         at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)\n
Run Code Online (Sandbox Code Playgroud)\n

经过几个小时的尝试不同的事情后,我发现谷歌搜索仍然无法连接到 MySQL。

\n

这是我的 docker-compose.yaml

\n
version: \'3.8\'\nservices:\n  webservice:\n    image: \'ariefs/postservice:v1.0.0\'   \n    cpus: 0.5\n    scale: 4\n    environment:\n      - PostDbConnectionStrings__Shard0=server=database0; port=3312; database=post; user=root; password=pw; Persist Security Info=False; Connect Timeout=300      \n      - PostDbConnectionStrings__Shard1=server=database1; port=3313; database=post; user=root; password=pw; Persist Security Info=False; Connect Timeout=300   \n    #  - PostDbConnectionStrings__Shard2=server=database2; port=3306; database=post; user=root; password=pw; Persist Security Info=False; Connect Timeout=300   \n  loadbalancer:\n    image: dockercloud/haproxy\n    links:\n      - webservice\n    volumes:\n      - /var/run/docker.sock:/var/run/docker.sock\n    ports:\n      - 5001:80\n  database0:\n    image: \'mysql:5.6\'\n    cpus: 0.5\n    ports:\n      - 3312:3306\n    environment:\n      - MYSQL_ROOT_PASSWORD=pw\n  database1:\n    image: \'mysql:5.6\'\n    cpus: 0.5\n    ports:\n      - 3313:3306\n    environment:\n      - MYSQL_ROOT_PASSWORD=pw\n  database2:\n    image: \'mysql:5.6\'\n    cpus: 0.5\n    ports:\n      - 3314:3306\n    environment:\n      - MYSQL_ROOT_PASSWORD=pw\n
Run Code Online (Sandbox Code Playgroud)\n

这是我的 docker ps :

\n
\xe2\x9d\xaf docker ps\nCONTAINER ID   IMAGE                       COMMAND                  CREATED         STATUS         PORTS\n          NAMES\n668b32b68d0f   dockercloud/haproxy         "/sbin/tini -- docke\xe2\x80\xa6"   3 minutes ago   Up 3 minutes   443/tcp, 1936/tcp, 0.0.0.0:5001->80/tcp, :::5001->80/tcp   postservice_loadbalancer_1\na2f3be18868c   ariefs/postservice:v1.0.0   "./PostService --url\xe2\x80\xa6"   3 minutes ago   Up 3 minutes   80/tcp\n          postservice_webservice_3\nac8c9d791cc5   ariefs/postservice:v1.0.0   "./PostService --url\xe2\x80\xa6"   3 minutes ago   Up 3 minutes   80/tcp\n          postservice_webservice_2\n7fe86c655063   ariefs/postservice:v1.0.0   "./PostService --url\xe2\x80\xa6"   3 minutes ago   Up 3 minutes   80/tcp\n          postservice_webservice_4\n5105d0472f78   ariefs/postservice:v1.0.0   "./PostService --url\xe2\x80\xa6"   3 minutes ago   Up 3 minutes   80/tcp\n\n          postservice_webservice_1\nf8f8e5a1ed86   mysql:5.6                   "docker-entrypoint.s\xe2\x80\xa6"   3 minutes ago   Up 3 minutes   0.0.0.0:3313->3306/tcp, :::3313->3306/tcp                  postservice_database1_1\nc4aafd2b2182   mysql:5.6                   "docker-entrypoint.s\xe2\x80\xa6"   3 minutes ago   Up 3 minutes   0.0.0.0:3312->3306/tcp, :::3312->3306/tcp                  postservice_database0_1\n
Run Code Online (Sandbox Code Playgroud)\n

和我的 DataAccess 类

\n
using Microsoft.AspNetCore.Mvc;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.Extensions.Configuration;\nusing PostService.Entities;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Security.Cryptography;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace PostService.Data\n{\n    public class DataAccess\n    {\n        private readonly List<string> _connectionStrings = new List<string>();\n\n        public DataAccess(IConfiguration configuration)\n        {\n            var connectionStrings = configuration.GetSection("PostDbConnectionStrings");\n            foreach (var connectionString in connectionStrings.GetChildren())\n            {\n                Console.WriteLine("ConnectionString: " + connectionString.Value);\n                _connectionStrings.Add(connectionString.Value);\n            }\n        }\n\n        public async Task<ActionResult<IEnumerable<Post>>> ReadLatestPosts(string category, int count)\n        {\n            using var dbContext = new PostServiceContext(GetConnectionString(category));\n            return await dbContext.Post.OrderByDescending(p => p.PostId)\n                                        .Take(count)\n                                        .Include(x => x.User)\n                                        .Where(p => p.CategoryId == category)\n                                        .ToListAsync();\n        }\n\n        public async Task<int> CreatePost(Post post)\n        {\n            using var dbContext = new PostServiceContext(GetConnectionString(post.CategoryId));\n            dbContext.Post.Add(post);\n            return await dbContext.SaveChangesAsync();\n        }\n\n        public async Task InitDatabase(int countUsers, int countCategories)\n        {\n            foreach (var connectionString in _connectionStrings)\n            {\n                using var dbContext = new PostServiceContext(connectionString);\n                await dbContext.Database.EnsureDeletedAsync();\n                await dbContext.Database.EnsureDeletedAsync();\n                for (int i = 1; i <= countUsers; i++)\n                {\n                    await dbContext.User.AddAsync(new User { Name = "User" + i, Version = 1 });\n                    await dbContext.SaveChangesAsync();\n                }\n                for (int i = 1; i <= countCategories; i++)\n                {\n                    await dbContext.Category.AddAsync(new Category { CategoryId = "Category" + i });\n                    await dbContext.SaveChangesAsync();\n                }\n            }\n        }\n\n        private string GetConnectionString(string category)\n        {\n            using var md5 = MD5.Create();\n            var hash = md5.ComputeHash(Encoding.ASCII.GetBytes(category));\n            var x = BitConverter.ToUInt16(hash, 0) % _connectionStrings.Count;\n            return _connectionStrings[x];\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

更新

\n

我尝试更改环境中的端口 = 3360

\n
\n

环境:\n- PostDbConnectionStrings__Shard0=server=database0; 端口=3312;数据库=帖子;用户=root;密码=pw;保留安全信息=False;连接超时=300
\n- PostDbConnectionStrings__Shard1=server=database1; 端口=3313;数据库=帖子;用户=root;密码=pw;保留安全信息=False;连接超时=300
\n# - PostDbConnectionStrings__Shard2=server=database2; 端口=3306;数据库=帖子;用户=root;密码=pw;保留安全信息=False;连接超时=300\n并得到此异常:

\n
\n
ConnectionString: server=database1; port=3306; database=post; user=root; password=pw; Persist Security Info=False; Connect Timeout=300\nfail: Microsoft.AspNetCore.Server.Kestrel[13]\n      Connection id "0HMA1U8GBSBGK", Request id "0HMA1U8GBSBGK:00000002": An unhandled exception was thrown by the application.\n      System.TypeInitializationException: The type initializer for \'MySql.Data.MySqlClient.Replication.ReplicationManager\' threw an exception.\n       ---> System.TypeInitializationException: The type initializer for \'MySql.Data.MySqlClient.MySqlConfiguration\' threw an exception.\n       ---> System.Configuration.ConfigurationErrorsException: Configuration system failed to initialize\n       ---> System.NotSupportedException: CodeBase is not supported on assemblies loaded from a single-file bundle.\n         at System.Reflection.RuntimeAssembly.get_CodeBase()\n         at System.Configuration.ClientConfigPaths..ctor(String exePath, Boolean includeUserConfig)\n         at System.Configuration.ClientConfigPaths.GetPaths(String exePath, Boolean includeUserConfig)\n         at System.Configuration.ClientConfigurationHost.get_ConfigPaths()\n         at System.Configuration.ClientConfigurationHost.GetStreamName(String configPath)\n         at System.Configuration.ClientConfigurationHost.get_IsAppConfigHttp()\n         at System.Configuration.Internal.DelegatingConfigHost.get_IsAppConfigHttp()\n         at System.Configuration.ClientConfigurationSystem..ctor()\n         at System.Configuration.ConfigurationManager.EnsureConfigurationSystem()\n         --- End of inner exception stack trace ---\n         at System.Configuration.ConfigurationManager.EnsureConfigurationSystem()\n         at System.Configuration.ConfigurationManager.PrepareConfigSystem()\n         at System.Configuration.ConfigurationManager.GetSection(String sectionName)\n         at MySql.Data.MySqlClient.MySqlConfiguration..cctor()\n         --- End of inner exception stack trace ---\n         at MySql.Data.MySqlClient.MySqlConfiguration.get_Settings()\n         at MySql.Data.MySqlClient.Replication.ReplicationManager..cctor()\n         --- End of inner exception stack trace ---\n         at MySql.Data.MySqlClient.Replication.ReplicationManager.IsReplicationGroup(String groupName)\n         at MySql.Data.MySqlClient.MySqlConnection.Open()\n         at System.Data.Common.DbConnection.OpenAsync(CancellationToken cancellationToken)\n      --- End of stack trace from previous location ---\n         at MySql.EntityFrameworkCore.MySQLDatabaseCreator.<>c__DisplayClass20_0.<<ExistsAsync>b__0>d.MoveNext()\n      --- End of stack trace from previous location ---\n         at MySql.EntityFrameworkCore.Storage.Internal.MySQLExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)\n         at Microsoft.EntityFrameworkCore.Storage.RelationalDatabaseCreator.EnsureDeletedAsync(CancellationToken cancellationToken)\n         at PostService.Data.DataAccess.InitDatabase(Int32 countUsers, Int32 countCategories)\n         at PostService.Controllers.PostsController.InitDatabase(Int32 countUsers, Int32 countCategories)\n         at lambda_method4(Closure , Object )\n         at Microsoft.Extensions.Internal.ObjectMethodExecutorAwaitable.Awaiter.GetResult()\n         at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)\n         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)\n         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)\n         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)\n         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)\n         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()\n      --- End of stack trace from previous location ---\n         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)\n         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)\n         at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)\n         at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)\n         at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)\n
Run Code Online (Sandbox Code Playgroud)\n

这是我的 dockerfile

\n
\nFROM mcr.microsoft.com/dotnet/sdk:5.0-alpine AS publish\nWORKDIR /src\nCOPY ["PostService.csproj", "./"]\n\nRUN dotnet restore "PostService.csproj" --runtime alpine-x64\nCOPY . .\nRUN dotnet publish "PostService.csproj" -c Release -o /app/publish \\\n    --no-restore \\\n    --runtime alpine-x64 \\\n    --self-contained true \\\n    /p:PublishTrimmed=true \\\n    /p:PublishSingleFile=true\n\nFROM mcr.microsoft.com/dotnet/runtime-deps:5.0-alpine AS final\n\nRUN adduser --disabled-password \\\n    --home /app \\\n    --gecos \'\' dotnetuser && chown -R dotnetuser /app\n\nRUN apk upgrade musl\n\nUSER dotnetuser\nWORKDIR /app\nEXPOSE 80\n\nCOPY --from=publish /app/publish .\nENTRYPOINT ["./PostService", "--urls", "http://0.0.0.0:80"]\n\n
Run Code Online (Sandbox Code Playgroud)\n

这是我的 .csproj 文件

\n
<Project Sdk="Microsoft.NET.Sdk.Web">\n\n  <PropertyGroup>\n    <TargetFramework>net5.0</TargetFramework>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.7">\n      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n      <PrivateAssets>all</PrivateAssets>\n    </PackageReference>\n    <PackageReference Include="MySql.EntityFrameworkCore" Version="5.0.3.1" />\n    <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />\n    <PackageReference Include="Swashbuckle.AspNetCore" Version="6.1.4" />\n  </ItemGroup>\n\n</Project>\n\n
Run Code Online (Sandbox Code Playgroud)\n

我只是关注这篇文章

\n

Bra*_*ger 5

您尚未显示应用程序的 Dockerfile,但我猜您正在使用它dotnet publish来创建单文件应用程序。

正如异常消息所说:

System.Configuration.ConfigurationErrorsException:配置系统初始化失败

System.NotSupportedException:从单文件包加载的程序集不支持 CodeBase。

您正在使用的 .NET MySQL 库 (MySql.Data) 依赖于ConfigurationManager,这导致了此异常。您有两个选择:

  1. 停止将您的应用程序发布为单个文件。
  2. 切换到MySqlConnector,这是一个更现代的 MySQL .NET 库,完全支持 .NET Core。

要停止作为单个文件发布,请将RUN dotnet publish您的块更改Dockerfile为:

RUN dotnet publish "PostService.csproj" -c Release -o /app/publish \
    --no-restore \
    --runtime alpine-x64 \
    --self-contained true
Run Code Online (Sandbox Code Playgroud)