C# 和 Docker - 无法从容器化 .NET Core 3.1 Web API 连接到容器化 MySQL 服务器

Posted

技术标签:

【中文标题】C# 和 Docker - 无法从容器化 .NET Core 3.1 Web API 连接到容器化 MySQL 服务器【英文标题】:C# and Docker - Can't connect to containerized MySQL server from containerized .NET Core 3.1 Web API 【发布时间】:2020-12-11 04:21:42 【问题描述】:

作为参考,我尝试了以下链接中的想法无济于事:

Docker-Compose Unable to connect to any of the specified mysql hosts Connect to MySQL container from Web Api .Net Core Container? How to get Ip Address?

我有三个容器化应用程序:mysql@8.0“暴露”——因为没有更好的术语——在端口 9999 后面; .NET Core 3.1 WebAPI;和一个容器化的 Angular 应用程序。 Angular 应用程序可以成功地调用 5001 端口后面的 WebAPI。问题似乎是 Web API 与 MySQL 容器建立了连接。

所有应用程序都作为容器部署在我的本地开发工作站上。 Web API 和 MySQL 数据库正在使用一个 docker-compose.yml 进行部署,我在下面分享了它。我为前端应用构建了一个简单的镜像,并从 Docker 命令行进行部署。

这是我的docker-compose.yml API 和 DB:

version: "3.3"

services: # list of services composing your application
  db: # the service hosting your MySQL instance
    image: mysql:8.0 # the image and tag docker will pull from docker hub
    volumes: # this section allows you to configure persistence within multiple restarts
      - db_data:/var/lib/mysql
    restart: always # if the db crash somehow, restart it
    ports:
      - "9999:3306"
    environment: # env variables, you usually set this to override existing ones
      MYSQL_ROOT_PASSWORD: *****
    networks:
      - soar-network
  soar-api: # you application service
    build: ./ # this tells docker-compose to not pull from docker hub, but to build from the Dockerfile it will find in ./
    restart: always
    ports:
      - "5001:80"
    networks:
      - soar-network
    # set a dependency between your service and the database:
    # this means that your application will not run if the db service is not running,
    # but it doesn't assure you that the dabase will be ready to accept incoming connection
    # (so your application could crash untill the db initializes itself)
    depends_on:
      - db

volumes:
  db_data: # this tells docker-compose to save your data in a generic docker volume. You can see existing volumes typing 'docker volume ls'

networks:
  soar-network:
    driver: bridge

Web API 代码对我在代码中使用的DbContext 使用以下连接字符串:

  "ConnectionStrings": 
    "DefaultConnection": "server=localhost;port=9999;uid=root;pwd=*****;database=SoarDb"
  

请注意,连接字符串中的port 与我在docker-compose 中映射的内容相匹配。我已经尝试过使用330633060,也无济于事。

我也尝试过使用127.0.0.1 作为server 值,但没有成功。

Web API 容器中记录的错误如下:

soar-api_1  | fail: Microsoft.EntityFrameworkCore.Database.Connection[20004]
soar-api_1  |       An error occurred using the connection to database 'SoarDb' on server 'localhost'.
soar-api_1  | fail: Microsoft.EntityFrameworkCore.Query[10100]
soar-api_1  |       An exception occurred while iterating over the results of a query for context type 'DataAccess.SoarDataContext'.
soar-api_1  |       MySql.Data.MySqlClient.MySqlException (0x80004005): Unable to connect to any of the specified MySQL hosts.
soar-api_1  |          at MySqlConnector.Core.ServerSession.ConnectAsync(ConnectionSettings cs, ILoadBalancer loadBalancer, IOBehavior ioBehavior, CancellationToken cancellationToken) in C:\projects\mysqlconnector\src\MySqlConnector\Core\ServerSession.cs:line 442

另一个奇怪的事情是:我可以添加迁移并使用dotnet-ef add migrationdotnet-ef database update 将数据库更新到这个特定的容器化数据库。

非常感谢任何见解。我尝试了许多不同的设置排列和调整,但没有运气,不知道我在误解什么。

【问题讨论】:

【参考方案1】:

您的错误是,从soar-api 容器的角度来看,localhost 只是指回容器(soar-api 正在其中运行)...而不是 docker 正在运行的服务器(即下一层)。

相反,您应该能够将连接字符串设置为 server=db;port=3306;... 这是因为 docker 提供了一个 DNS 代理,允许您按名称访问同一网络上的容器(看起来您已经正确设置了 @987654326 @)

实际上容器db 获得一个IP(例如:A),而容器soar-api 获得另一个IP(B)。您来自 B 的连接需要指定 IP 地址 A,除非您将 docker-compose 配置为指定(您可以do this too,但正如您一样)已经写好了,docker会帮你处理的)

我想您是在主服务器外部运行迁移,而不是在任何一个容器内。

如果没有其他服务需要直接访问它(这是为了让外部计算机连接到 docker-server 并访问该服务),您可能不需要在 9999 上的 9999 上公开 MySQL。

注意127.0.0.1(实际上是127.0.0.0/8 空间中的任何地址)是localhost 的同义词。还有::1/128(IPv6,如果已启用)

【讨论】:

感谢您的回复。我在帖子中确实提到有 3 个容器化应用程序:其中 2 个可以在 docker-compose 中找到,还有 1 个(此处未共享)具有在 Dockerfile 中定义并从 docker run CLI 部署的图像.感谢您的想法;我一定会试一试的! 所以我发现在 conn 字符串中使用服务名称(在这种情况下为db)是必要的;谢谢。但是,连接字符串似乎还需要包含 3306 作为端口号——not 9999。你能解释一下为什么会这样吗?连接字符串现在是"DefaultConnection": "server=db;port=3306;uid=root;pwd=****;database=SoarDb" 哦,是的,所以 port:port 映射是将服务器(迄今为止未知的 IP)端口路由到 docker 容器端口。在 docker 网络中,任何容器(在该网络上)上的任何公开端口都可以直接访问。您通常不需要对外公开端口(如果 docker 之外没有任何东西需要访问 MySQL,则不需要公开 9999)。 啊哈!明白了;再次感谢。就我而言,我正在从我的 Docker 主机对容器运行迁移和查询,not 在任何容器内。但由于我的 Docker 主机上也有一个 MySQL 服务器实例——而不是在容器中——在默认端口 3306 后面,我需要在公开 MySQL 容器时选择不同的端口。现在一切都说得通了!

以上是关于C# 和 Docker - 无法从容器化 .NET Core 3.1 Web API 连接到容器化 MySQL 服务器的主要内容,如果未能解决你的问题,请参考以下文章

CentOS7下使用Docker容器化.net Core 2.2

Docker容器——Docker静态化IP

无法从 docker windows 容器中的 asp.net 核心应用程序连接到主机数据库

.NET Core容器化@Docker

.NET Core容器化开发系列——Docker里面跑个.NET Core

Docker最全教程——从理论到实战