【问题标题】:Why does my .NET web API application not connect to MySQL on docker when using docker-compose?为什么我的 .NET Web API 应用程序在使用 docker-compose 时无法连接到 docker 上的 MySQL?
【发布时间】:2021-09-18 17:01:49
【问题描述】:

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

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

C:\dev>docker run -p 3310:3306 --name=mysql1 -e MYSQL_ROOT_PASSWORD=pw -d mysql:5.6
C:\dev>docker run -p 3311:3306 --name=mysql2 -e MYSQL_ROOT_PASSWORD=pw -d mysql:5.6

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

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

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

这是我的 docker-compose.yaml

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

这是我的 docker ps:

❯ docker ps
CONTAINER ID   IMAGE                       COMMAND                  CREATED         STATUS         PORTS
          NAMES
668b32b68d0f   dockercloud/haproxy         "/sbin/tini -- docke…"   3 minutes ago   Up 3 minutes   443/tcp, 1936/tcp, 0.0.0.0:5001->80/tcp, :::5001->80/tcp   postservice_loadbalancer_1
a2f3be18868c   ariefs/postservice:v1.0.0   "./PostService --url…"   3 minutes ago   Up 3 minutes   80/tcp
          postservice_webservice_3
ac8c9d791cc5   ariefs/postservice:v1.0.0   "./PostService --url…"   3 minutes ago   Up 3 minutes   80/tcp
          postservice_webservice_2
7fe86c655063   ariefs/postservice:v1.0.0   "./PostService --url…"   3 minutes ago   Up 3 minutes   80/tcp
          postservice_webservice_4
5105d0472f78   ariefs/postservice:v1.0.0   "./PostService --url…"   3 minutes ago   Up 3 minutes   80/tcp

          postservice_webservice_1
f8f8e5a1ed86   mysql:5.6                   "docker-entrypoint.s…"   3 minutes ago   Up 3 minutes   0.0.0.0:3313->3306/tcp, :::3313->3306/tcp                  postservice_database1_1
c4aafd2b2182   mysql:5.6                   "docker-entrypoint.s…"   3 minutes ago   Up 3 minutes   0.0.0.0:3312->3306/tcp, :::3312->3306/tcp                  postservice_database0_1

还有我的 DataAccess 类

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using PostService.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace PostService.Data
{
    public class DataAccess
    {
        private readonly List<string> _connectionStrings = new List<string>();

        public DataAccess(IConfiguration configuration)
        {
            var connectionStrings = configuration.GetSection("PostDbConnectionStrings");
            foreach (var connectionString in connectionStrings.GetChildren())
            {
                Console.WriteLine("ConnectionString: " + connectionString.Value);
                _connectionStrings.Add(connectionString.Value);
            }
        }

        public async Task<ActionResult<IEnumerable<Post>>> ReadLatestPosts(string category, int count)
        {
            using var dbContext = new PostServiceContext(GetConnectionString(category));
            return await dbContext.Post.OrderByDescending(p => p.PostId)
                                        .Take(count)
                                        .Include(x => x.User)
                                        .Where(p => p.CategoryId == category)
                                        .ToListAsync();
        }

        public async Task<int> CreatePost(Post post)
        {
            using var dbContext = new PostServiceContext(GetConnectionString(post.CategoryId));
            dbContext.Post.Add(post);
            return await dbContext.SaveChangesAsync();
        }

        public async Task InitDatabase(int countUsers, int countCategories)
        {
            foreach (var connectionString in _connectionStrings)
            {
                using var dbContext = new PostServiceContext(connectionString);
                await dbContext.Database.EnsureDeletedAsync();
                await dbContext.Database.EnsureDeletedAsync();
                for (int i = 1; i <= countUsers; i++)
                {
                    await dbContext.User.AddAsync(new User { Name = "User" + i, Version = 1 });
                    await dbContext.SaveChangesAsync();
                }
                for (int i = 1; i <= countCategories; i++)
                {
                    await dbContext.Category.AddAsync(new Category { CategoryId = "Category" + i });
                    await dbContext.SaveChangesAsync();
                }
            }
        }

        private string GetConnectionString(string category)
        {
            using var md5 = MD5.Create();
            var hash = md5.ComputeHash(Encoding.ASCII.GetBytes(category));
            var x = BitConverter.ToUInt16(hash, 0) % _connectionStrings.Count;
            return _connectionStrings[x];
        }
    }
}

更新

我已经尝试在环境中更改端口 = 3360

环境: - PostDbConnectionStrings__Shard0=server=database0;端口=3312;数据库=发布;用户=根;密码=密码;持久安全信息=假;连接超时=300
- PostDbConnectionStrings__Shard1=server=database1;端口=3313;数据库=发布;用户=根;密码=密码;持久安全信息=假;连接超时=300
# - PostDbConnectionStrings__Shard2=server=database2;端口=3306;数据库=发布;用户=根;密码=密码;持久安全信息=假;连接超时=300 并得到了这个例外:

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

这是我的 dockerfile


FROM mcr.microsoft.com/dotnet/sdk:5.0-alpine AS publish
WORKDIR /src
COPY ["PostService.csproj", "./"]

RUN dotnet restore "PostService.csproj" --runtime alpine-x64
COPY . .
RUN dotnet publish "PostService.csproj" -c Release -o /app/publish \
    --no-restore \
    --runtime alpine-x64 \
    --self-contained true \
    /p:PublishTrimmed=true \
    /p:PublishSingleFile=true

FROM mcr.microsoft.com/dotnet/runtime-deps:5.0-alpine AS final

RUN adduser --disabled-password \
    --home /app \
    --gecos '' dotnetuser && chown -R dotnetuser /app

RUN apk upgrade musl

USER dotnetuser
WORKDIR /app
EXPOSE 80

COPY --from=publish /app/publish .
ENTRYPOINT ["./PostService", "--urls", "http://0.0.0.0:80"]

这里是我的 .csproj 文件

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.7">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
    <PackageReference Include="MySql.EntityFrameworkCore" Version="5.0.3.1" />
    <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
    <PackageReference Include="Swashbuckle.AspNetCore" Version="6.1.4" />
  </ItemGroup>

</Project>

我只是关注这个article

【问题讨论】:

  • database0database1 容器在 3306 端口上运行,webservice 容器应尝试仅在 3306 端口上连接它们......
  • 已经将此端口PostDbConnectionStrings__Shard0=server=database0; port=3312; database=post; user=root; password=pw; Persist Security Info=False; Connect Timeout=300 - PostDbConnectionStrings__Shard1=server=database1; port=3313; database=post; user=root; password=pw; Persist Security Info=False; Connect Timeout=300 更改为3306,但仍然出现相同的错误
  • 请尝试使用port=3306
  • 是的,收到此错误:ConnectionString: server=database0; port=3306; database=post; user=root; password=pw; Persist Security Info=False; Connect Timeout=300 ConnectionString: server=database1; port=3306; database=post; user=root; password=pw; Persist Security Info=False; Connect Timeout=300 fail: Microsoft.AspNetCore.Server.Kestrel[13] Connection id "0HMA1TRRU6KQC", Request id "0HMA1TRRU6KQC:00000002": An unhandled exception was thrown by the application. System.TypeInitializationException: The type initializer for 'MySql.Data.MySqlClient.Replication.
  • 您正在 Docker-compose 中设置 PostDbConnectionStrings__Shard0PostDbConnectionStrings__Shard1 环境变量。在代码中,您尝试从配置部分读取配置,我相信它来自 appSettings.json 文件。您如何将环境变量值推送到 appSettings.json 文件?

标签: c# mysql asp.net .net docker


【解决方案1】:

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

正如异常消息所说:

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

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

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

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

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

RUN dotnet publish "PostService.csproj" -c Release -o /app/publish \
    --no-restore \
    --runtime alpine-x64 \
    --self-contained true

【讨论】:

  • 我只是通过添加dockerfile和.csproj来更新我的问题,然后尝试选项2但似乎我必须更改我的代码,太费劲了,选项1怎么样,我在哪一行必须更改我的 docker 文件..
猜你喜欢
  • 1970-01-01
  • 2018-08-20
  • 1970-01-01
  • 1970-01-01
  • 2021-08-02
  • 2020-07-10
  • 2020-02-12
  • 1970-01-01
相关资源
最近更新 更多