无法使用桥接网络模式从 docker 容器连接到远程 SQL 服务器

Posted

技术标签:

【中文标题】无法使用桥接网络模式从 docker 容器连接到远程 SQL 服务器【英文标题】:Unable to connect to remote SQL server from docker container with bridge network mode 【发布时间】:2021-06-13 00:45:36 【问题描述】:

我有一个由四台 Ubuntu 20.04 机器组成的 Docker Swarm。我想以复制的方式运行许多不同的应用程序。其中一个应用程序是从在命名实例上运行的远程 SQL Server 读取的 API。

如果我指定主机网络模式,我可以连接到 SQL Server,但这并不理想,因为我无法为将来要运行的其他服务映射端口。这也感觉像一个黑客。

这是我的 docker-compose 文件,名为 scapi-stack.yaml

version: '3.7'

services:
  sc-api:
    command: [ "--privileged" ]
    image: #private repo image
    deploy:
      replicas: 4
      restart_policy:
        condition: on-failure
    ports:
      - "51955:51955" #SQL Server instance port
      - "8443:443"
      - "8080:80"
    environment:
      - ASPNETCORE_URLS=http://+:80
      - ASPNETCORE_URLS=https://+:443

这是我运行服务的命令:

docker stack deploy --compose-file scapi-stack.yaml scapi

如果我随后对容器执行 docker attach 并导航到端口 8080,我会看到以下错误:

Microsoft.Data.SqlClient.SqlException (0x80131904): A network-related or instance-specific error occurred
 while establishing a connection to SQL Server. The server was not found or was not accessible. 
Verify that the instance name is correct and that SQL Server is configured to allow remote connections.
 (provider: TCP Provider, error: 40 - Could not open a connection to SQL Server)

我尝试指定--privileged 命令无济于事。 我还尝试将图像从主要图像更改为仿生、焦点和超薄图像。 我也尝试过使用 .Net Core 3.1,但没有任何区别。 我还运行了在这个answer 中找到的这两个命令:

sysctl net.ipv4.conf.all.forwarding=1
sudo iptables -P FORWARD ACCEPT

我可以让它工作的唯一方法是使用主机网络,但这违背了目的。

图像的 dockerfile 是这样的:

FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
EXPOSE 51955

FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /src
COPY ["SCAPI/SCAPI.csproj", "SCAPI/"]
COPY ["src/Infrastructure/Infrastructure.csproj", "src/Infrastructure/"]
COPY ["src/Application/Application.csproj", "src/Application/"]
COPY ["src/Domain/Domain.csproj", "src/Domain/"]
RUN dotnet restore "SCAPI/SCAPI.csproj"
COPY . .
WORKDIR "/src/SCAPI"
RUN dotnet build "SCAPI.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "SCAPI.csproj" -c Release -o /app/publish

FROM base AS final
#Enable connections with TLS 1.0
RUN sed -i 's/MinProtocol = TLSv1.2/MinProtocol = TLSv1/g' /etc/ssl/openssl.cnf
RUN sed -i 's/MinProtocol = TLSv1.2/MinProtocol = TLSv1/g' /usr/lib/ssl/openssl.cnf
RUN sed -i 's/DEFAULT@SECLEVEL=2/DEFAULT@SECLEVEL=1/g' /etc/ssl/openssl.cnf
RUN sed -i 's/DEFAULT@SECLEVEL=2/DEFAULT@SECLEVEL=1/g' /usr/lib/ssl/openssl.cnf
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "SCAPI.dll"]

我错过了什么?如何在不使用主机网络的情况下指定端口以启用与 SQL Server 的通信?

更新

我尝试连接的 SQL 服务器位于 swarm 之外的 Windows 机器上。如果我删除端口映射并在我的撰写文件中指定“主机”网络,我可以连接到它:

version: '3.7'

services:
  sc-api:
    command: [ "--privileged" ]
    image: #private repo image
    deploy:
      replicas: 4
      restart_policy:
        condition: on-failure
    environment:
      - ASPNETCORE_URLS=http://+:80
      - ASPNETCORE_URLS=https://+:44
    networks:
      - host

networks:
  host:
    name: host
    external: true

但这有一些缺点,正如官方 Docker documentation 中所述:

如果您对容器使用主机网络模式,则该容器的网络堆栈不会与 Docker 主机隔离(容器共享主机的网络命名空间),并且容器不会获得自己的 IP 地址分配。例如,如果您运行一个绑定到端口 80 的容器并使用主机网络,则容器的应用程序可在主机 IP 地址的端口 80 上使用。

【问题讨论】:

HTTP端口一般是80,但也经常是8080。你叫什么“网络模式”? 我已将其配置为将容器内的端口 80 映射到主机上的端口 8080。这是 docker 网络模式的概述:docs.docker.com/network/#network-drivers 我可以使用主机模式而不是桥接模式使其工作 什么是桥接模式?你要穿过防火墙吗?防火墙可能只需要端口 80。 您能否说明您对 SQL Server 命名实例的使用? Windows 机器上的 Docker 群之外的那些?因为 AFAIK,您不能在 Docker 容器中命名实例,因为没有可用于 linux 的 SQL 浏览器服务 - 在 Windows 上,它侦听 udp/1434 以将实例名称映射到客户端可以连接的 TCP 端口号。 请看更新 【参考方案1】:

留给后代

因此,在花了近 60 个小时在互联网上搜寻线索并尝试了上百万种不同的事情之后,这个问题终于得到了解决。 我什至切换到在同一台机器上运行的 Kubernetes 集群,但无济于事 - 我遇到了同样的问题。

我的网络专家同事终于让我在其中一个 linux 机器上安装 Wireshark,他发现流量正从容器/pod 中流出,通过 linux 机器接口到达 SQL 服务器,但没有出现返回。

一段时间后,他发现来自 swarm/kubernetes 的 IP 地址没有被伪装,核心网络交换机不知道如何将流量返回到容器/pod。

这些 linux 机器是虚拟机。

快速sudo iptables --append POSTROUTING --table nat --out-interface ens160 --jump MASQUERADE 其中“ens160”是网络接口——瞧!都很好。

此命令将所有容器/pod IP 地址转换为盒子的 IP 地址,反之亦然,用于所有出站流量。

【讨论】:

以上是关于无法使用桥接网络模式从 docker 容器连接到远程 SQL 服务器的主要内容,如果未能解决你的问题,请参考以下文章

Docker初学乍练之单主机网络

docker 的四种网络模式

无法从 docker 容器内的 Spring Boot 连接到 Redis

无法从 docker 容器连接到 SQL Server

无法从节点连接到 docker 中的 mongo 副本集

docker与宿主通信突然断了