无法从 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的主要内容,如果未能解决你的问题,请参考以下文章