无法从 NET Core 2.2 Web API 连接到 docker sql server

Posted

技术标签:

【中文标题】无法从 NET Core 2.2 Web API 连接到 docker sql server【英文标题】:Can't connect to docker sql server from NET Core 2.2 Web API 【发布时间】:2020-01-11 08:42:25 【问题描述】:

在收集结合 docker 编写 .NET Core Web API 的知识时,我遇到了一个我无法解决的错误。我已经在互联网上进行了研究,但不知何故,提供的解决方案都不适合我。

我有一个名为 Catalog.API 的 Web 服务(类似于 Microsoft 的 eShopOnContainers 项目),其中我有一个名为 CatalogController 的控制器。

这是来自控制器的代码:

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Catalog.API.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace Catalog.API.Controllers

    [ApiController]
    [Route("api/[controller]")]
    public class CatalogController : ControllerBase
    
        private readonly CatalogItemContext _context;

        public CatalogController(CatalogItemContext context)
        
            _context = context;
        

        public async Task<List<CatalogItem>> GetItemsAsync()
        
            var items = await _context.CatalogItems.Where(model => model.Id < 10).ToListAsync();
            return items;
        
    

如您所见,我只有一种方法应该从外部数据库返回所有 id 小于 10 的项目。

数据库添加在Startup.cs:

namespace Catalog.API

    public class Startup
    
        public Startup(IConfiguration configuration)
        
            Configuration = configuration;
        

        public IConfiguration Configuration  get; 

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        
            services.AddDbContext<CatalogItemContext>(builder =>
            
                builder.UseSqlServer(Configuration.GetConnectionString("Default"));
            );

        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        
            if (env.IsDevelopment())
            
                app.UseDeveloperExceptionPage();
            
            else
            
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            

            app.UseHttpsRedirection();
            app.UseMvc();
        
    

appsettings.json 看起来像这样:


  "Logging": 
    "LogLevel": 
      "Default": "Warning"
    
  ,
  "AllowedHosts": "*",
  "ConnectionStrings": 
    "Default": "Server=localhost,1433;Initial Catalog=Test.Services.CatalogDb;User Id=sa;Password=Pass@word" 
  

为了构建这个 Web 服务,我创建了一个从 docker-compose.yml 调用的 docker 文件:

FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS base
WORKDIR /app
EXPOSE 80

FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build
WORKDIR /src

COPY src/Services/Catalog/Catalog.API/Catalog.API.csproj /src/csproj-files/

WORKDIR ./csproj-files
RUN dotnet restore

WORKDIR /src

COPY . .
WORKDIR /src/src/Services/Catalog/Catalog.API/
RUN dotnet publish -c Release -o /app

FROM build AS publish

FROM base as final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "Catalog.API.dll"]

docker-compose.yml 看起来像这样:

version: '3.4'

services:

  sqldata:
    ports:
      - 1433:1433
    image: mcr.microsoft.com/mssql/server:2017-latest-ubuntu
    environment:
      - ACCEPT_EULA=Y
      - SA_PASSWORD=Pass@word

  catalog.api:
    ports:
      - 80:80
    build:
      context: .
      dockerfile: src/Services/Catalog/Catalog.API/Dockerfile
    depends_on:
      - sqldata

如前所述,除了 Catalog.API Web 服务之外,我还在 docker 容器中运行 SQL Server 数据库。出于开发目的,我使用 localhost 或我的机器 ip 作为主机地址(localhost192.168.2.101)从 DataGrip 连接到此 SQL Server。两种方式都可以正常工作,我可以毫无问题地在 DataGrip 中建立连接。

我的问题是我无法从我的CatalogController 中的 docker 容器内部连接到 SQL Server,该容器在 localhost:80 上运行。通常,当我调用localhost:80/api/catalog 时,我应该从数据库中取出所有项目(请参阅开头的控制器),但不知何故,我得到了这个错误:

失败:Microsoft.EntityFrameworkCore.Database.Connection[20004] 使用与服务器“localhost,1433”上的数据库“Test.Services.CatalogDb”的连接时出错。 System.Data.SqlClient.SqlException (0x80131904):建立与 SQL Server 的连接时发生与网络相关或特定于实例的错误。服务器未找到或无法访问。验证实例名称是否正确以及 SQL Server 是否配置为允许远程连接。 (提供者:TCP 提供者,错误:40 - 无法打开与 SQL Server 的连接)

在 System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity 标识、SqlConnectionString connectionOptions、SqlCredential 凭据、对象 providerInfo、String newPassword、SecureString newSecurePassword、Boolean redirectedUserInstance、SqlConnectionString userConnectionOptions、SessionData reconnectSessionData、Boolean applyTransientFaultHandling、String accessToken) 在 System.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions 选项,DbConnectionPoolKey poolKey,对象 poolGroupProviderInfo,DbConnectionPool 池,DbConnection owningConnection,DbConnectionOptions userOptions) 在 System.Data.ProviderBase.DbConnectionFactory.CreatePooledConnection(DbConnectionPool 池,DbConnection owningObject,DbConnectionOptions 选项,DbConnectionPoolKey poolKey,DbConnectionOptions userOptions) 在 System.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection owningObject,DbConnectionOptions userOptions,DbConnectionInternal oldConnection) 在 System.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection owningObject,DbConnectionOptions userOptions,DbConnectionInternal oldConnection) 在 System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject,UInt32 waitForMultipleObjectsTimeout,布尔 allowCreate,布尔 onlyOneCheckConnection,DbConnectionOptions userOptions,DbConnectionInternal& 连接) 在 System.Data.ProviderBase.DbConnectionPool.WaitForPendingOpen() --- 从之前抛出异常的位置结束堆栈跟踪 ---

在 Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenDbConnectionAsync(布尔错误预期,CancellationToken cancelToken) ClientConnectionId:00000000-0000-0000-0000-000000000000

我已经尝试使用多个 ips(本地主机,我的机器 ip + docker 检查的 ip),但没有一个工作。

Server=192.168.2.101,1433;Initial Catalog=Test.Services.CatalogDb;User Id=sa;Password=Pass@word

我愿意接受所有可以解决我问题的帮助。 提前致谢。

【问题讨论】:

两个docker服务需要在同一个network 问题在于您的连接字符串An error occurred using the connection to database 'Test.Services.CatalogDb' on server 'localhost,1433'。添加:在服务器名称中,您添加了逗号。 Server=localhost:1433; 一切看起来都很好。 Docker 容器可以在同一个网络中通过服务名互相访问。您可以将 'localhost,1433' 更改为 'sqldata' 在深入研究 .net 代码之前,您可以从命令行连接吗?例如sqlcmd -S localhost,1433 ? @JinnaBalu:逗号没问题。它必须在那里而不是 :. 【参考方案1】:

SQL Server 实例不在本地主机上。在 Web 应用的上下文中,localhost 是运行 Web 应用的容器,与 SQL Server 所在的容器不同。

使用 Docker Compose 时,服务名称以主机名结尾,这意味着您可以简单地连接到 sqldata,1433

添加服务名称而不是 IP 可以,但每当我使用“dotnet ef migrations add xyz”和“dotnet ef database update”迁移数据库时,都会出现错误。它说,找不到服务器或无法访问。所以现在我可以连接,但不能使用 dotnet ef。

主机名仅在您的 docker 容器运行所在的 vlan 中有效,这与您的桌面计算机所在的 vlan 不同。要访问 SQL Server 容器,您必须将docker container exec 放入其中一个容器中,然后从那里运行命令(sqldata 主机名将存在),或者您必须使用容器的 IP 地址,您可以通过运行 docker container ls 来获取容器 id (guid),然后运行 ​​docker inspect container id。 IP 地址将在生成的 JSON 的 NetworkSettings 部分中。

【讨论】:

感谢您的评论,我在研究此错误时也遇到了这个问题,但我无法解决这个问题。使用服务名称作为主机名有效。但是现在,每当我使用 dotnet ef 迁移添加 xyz 和 dotnet ef 数据库更新时,我都会收到一条错误消息,提示找不到数据库或无法访问。所以,现在连接是可能的,但我不能再使用 dotnet ef.. 也感谢您对 dotnet ef 的编辑。我确实按照您关于 docker inspect 的第二部分来获取容器的 IP 地址。再次检索 docker 容器的 IP 后,我将其粘贴到连接字符串中,它似乎可以工作。我以前已经这样做了,但不知何故,它没有用。无论如何,现在它正在工作。谢谢 好吧,IP 不是恒定的,所以你不应该真正坚持像appsettings.json 这样的东西。不过,您可以将连接字符串与dotnet ef 命令一起传递。 好点。将连接字符串与 dotnet ef 命令一起传递的最佳方式是什么? 对不起。我在想scaffold 命令。其他人不支持。这里的主要问题是这真的不是你应该处理的事情(一半进/一半出容器)。理想情况下,映像构建应输出 SQL 脚本或 DACPAC,然后您可以在容器启动时使用自定义入口点将其应用于 SQL Server 容器。网上有指南,但是这里太复杂了。

以上是关于无法从 NET Core 2.2 Web API 连接到 docker sql server的主要内容,如果未能解决你的问题,请参考以下文章

本地 Javascript Fetch Post 请求失败,调用 ASP.NET Core 2.2 Web API。 CORS 已启用

如何将 AzureAD 和 AzureADBearer 添加到 asp.net core 2.2 web api

从 ASP.NET MVC 项目调用 .NET CORE 5 Web API 微服务时无法检索 BadRequest 错误

Azure 静态 Web 应用 Blazor WASM .Net Core API 和标识 - 无法从“_configuration”加载设置

如何测试返回 Ok(object) 的 ASP.NET Core 2.2 Web API GET IActionResult?

使用Http-Repl工具测试ASP.NET Core 2.2中的Web Api项目