简体   繁体   English

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

[英]Unable to connect to remote SQL server from docker container with bridge network mode

I have a Docker Swarm of four Ubuntu 20.04 machines.我有一个由四台 Ubuntu 20.04 机器组成的 Docker Swarm。 I want to run many different apps in a replicated fashion.我想以复制的方式运行许多不同的应用程序。 One of these apps is an API that reads from a remote SQL Server running on a named instance.其中一个应用程序是 API,它从在命名实例上运行的远程 SQL 服务器读取。

I am able to connect to SQL Server if I specify the host network mode but this is not ideal since I won't be able to map ports for other services that I want to run in the future.如果我指定主机网络模式,我可以连接到 SQL 服务器,但这并不理想,因为我将无法连接 map 端口以用于将来要运行的其他服务。 It also feels like a hack.它也感觉像一个黑客。

This is my docker-compose file named scapi-stack.yaml这是我的 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

This is the command with which I run the service:这是我运行服务的命令:

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

If I then do docker attach to a container and navigate to port 8080 I see the following error:如果我然后将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)

I've tried specifying the --privileged command to no avail.我试过指定--privileged命令无济于事。 I've also tried changing images from the main ones to bionic, focal and buster-slim.我还尝试将图像从主要图像更改为仿生、焦点和超薄图像。 I've tried going down to.Net Core 3.1 as well and it made no difference.我也尝试过使用.Net Core 3.1,但没有任何区别。 I have also run these two commands found in this answer :我还运行了在这个答案中找到的这两个命令:

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

The only way I can get it to work is with the host network but that defeats the purpose.我可以让它工作的唯一方法是使用主机网络,但这违背了目的。

The dockerfile for the image is this:图像的 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"]

What am I missing?我错过了什么? How can I specify the ports to enable comms to SQL Server without using the host network?如何在不使用主机网络的情况下指定端口以启用与 SQL 服务器的通信?

Update更新

The SQL server I am trying to connect to is on a Windows machine outside of the swarm.我尝试连接的 SQL 服务器位于集群外的 Windows 机器上。 I can connect to it if I remove port mapping and specify 'host' networking in my compose file:如果我删除端口映射并在我的撰写文件中指定“主机”网络,我可以连接到它:

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

But that has drawbacks as described in the official Docker documentation :但这有官方 Docker文档中描述的缺点:

If you use the host network mode for a container, that container's network stack is not isolated from the Docker host (the container shares the host's networking namespace), and the container does not get its own IP-address allocated.如果您对容器使用主机网络模式,则该容器的网络堆栈不会与 Docker 主机隔离(容器共享主机的网络命名空间),并且容器不会获得自己的 IP 地址分配。 For instance, if you run a container which binds to port 80 and you use host networking, the container's application is available on port 80 on the host's IP address.例如,如果您运行绑定到端口 80 的容器并使用主机网络,则容器的应用程序可在主机 IP 地址的端口 80 上使用。

Leaving this for posterity留给后代

So after close to 60 hours spent trawling the internet looking for clues and trying a million different things, this has finally been resolved.因此,在花了近 60 个小时在互联网上寻找线索并尝试了一百万种不同的事情之后,这个问题终于得到了解决。 I even switched to a Kubernetes cluster spun up on the same machines to no avail - I had the same issue.我什至切换到在同一台机器上旋转的 Kubernetes 集群无济于事 - 我遇到了同样的问题。

My network-guru colleague finally got me to install Wireshark on one of the linux boxes and he discovered that the traffic was going out of the containers/pods, through the linux box interface and to the SQL server, but was not coming back.我的网络专家同事终于让我在其中一个 linux 盒子上安装 Wireshark,他发现流量正在通过 linux 盒子接口流出容器/pod,并到达 Z9778840A0100CB30C5228 服务器,但没有回来。

After some time he discovered that the IP addresses coming out of swarm/kubernetes weren't masqueraded and the core network switch didn't know how to return traffic back to the containers/pods.一段时间后,他发现来自 swarm/kubernetes 的 IP 地址没有被伪装,并且核心网络交换机不知道如何将流量返回到容器/pod。

These linux boxes are virtual machines.这些 linux 盒子是虚拟机。

A quick sudo iptables --append POSTROUTING --table nat --out-interface ens160 --jump MASQUERADE where 'ens160' is the network interface - and voila.快速sudo iptables --append POSTROUTING --table nat --out-interface ens160 --jump MASQUERADE其中“ens160”是网络接口——瞧。 All good.都好。

This command translates all the container/pod IP addresses to the IP address of the box and vice-versa for all outbound traffic.此命令将所有容器/pod IP 地址转换为盒子的 IP 地址,反之亦然。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM