在 Docker Compose Stack 中通过 Npgsql 从 ASP.NET Core 6 连接到 PostgreSql

Connecting to PostgreSql via Npgsql from ASP.NET Core 6 in a Docker Compose Stack

我正在尝试创建一个 docker-compose 脚本来使用 PostgreSql 数据库和 ASP.NET Core 6 Web API 触发堆栈。为了测试这个场景,我使用默认模板创建了一个新的 ASP.NET Core 6 Web API。然后我添加了 NuGet 包 Npgsql (6.0.3),以及一个仅查询数据库引擎版本的示例控制器,例如:

[ApiController]
[Produces("application/json")]
public class TestController : Controller
{
    private readonly IConfiguration _config;

    public TestController(IConfiguration config)
    {
        _config = config;
    }

    [HttpGet("api/dbversion")]
    public IActionResult GetVersion()
    {
        using NpgsqlConnection conn = new(_config.GetConnectionString("Default"));

        conn.Open();

        var cmd = conn.CreateCommand();
        cmd.CommandText = "SELECT version();";

        return Ok($"Version: {cmd.ExecuteScalar() as string}");
    }
}

然后我为此 API 创建了 Docker 图像,并编写了一个 docker-compose 脚本,如下所示:

version: '3.7'

services:
  test-db:
    image: postgres
    # explicit container name
    # another approach using hostname: 
    container_name: test-db
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_DB=postgres
    ports:
      - 5433:5432
    networks:
      - test-network

  test-api:
    image: MYREPONAME/test-api:0.0.1
    ports:
      - 5154:80
    depends_on:
      - test-db
    environment:
      - CONNECTIONSTRINGS__DEFAULT=User ID=postgres;Password=postgres;Host=test-db;Port=5433;Database=postgres
    networks:
      - test-network

networks:
  test-network:
    driver: bridge

这里我使用的是官方postgres镜像,设置默认凭证,将服务重定向到compose栈中的5433端口,并从同一网络中的API使用它.我正在覆盖脚本中的连接字符串,使用容器名称作为主机名 (test-db); localhost 在 Docker 网络中不是一个选项(参见 and here)。

当我从 Ubuntu 主机启动此脚本时,我可以在端口 5433 访问数据库服务,并在 localhost:5154/swagger/index 访问 Web API。 html。然而,当我 运行 应该连接到数据库的操作时,我得到一个连接被拒绝的错误,即使我可以清楚地看到数据库服务 IP 和端口:

"Exception":"Npgsql.NpgsqlException (0x80004005): Failed to connect to 172.27.0.2:5433  
---\u003E System.Net.Sockets.SocketException (111): Connection refused    
at Npgsql.Internal.NpgsqlConnector.Connect(NpgsqlTimeout timeout)
at Npgsql.Internal.NpgsqlConnector.Connect(NpgsqlTimeout timeout)
at Npgsql.Internal.NpgsqlConnector.RawOpen(SslMode sslMode, NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken)
at Npgsql.Internal.NpgsqlConnector.\u003COpen\u003Eg__OpenCore|191_1(NpgsqlConnector conn, SslMode sslMode, NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken)
at Npgsql.Internal.NpgsqlConnector.Open(NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken)
at Npgsql.ConnectorPool.OpenNewConnector(NpgsqlConnection conn, NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken)
at Npgsql.ConnectorPool.\u003CGet\u003Eg__RentAsync|28_0(NpgsqlConnection conn, NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken)
at Npgsql.NpgsqlConnection.\u003COpen\u003Eg__OpenAsync|45_0(Boolean async, CancellationToken cancellationToken)
at Npgsql.NpgsqlConnection.Open()  
...

当一个容器连接到桥接网络上的另一个容器时,您使用容器端口。不是主机上的映射端口。所以你的连接字符串应该是

CONNECTIONSTRINGS__DEFAULT=User ID=postgres;Password=postgres;Host=test-db;Port=5432;Database=postgres