nginx 反向流代理具有多个端口到同一服务器

nginx 反向流代理具有多个端口到同一服务器

我正在尝试使用 nginx 作为两个不同服务器的反向代理。服务器需要使用客户端证书进行身份验证,这意味着 nginx 配置为流代理,利用map $ssl_preread_server_nameSNI 检查将数据发送到正确的服务器。

这对于它现在托管的两台服务器来说效果很好。两台服务器都监听 443,但提供完全不同的服务,但通过 SNI 进行的重定向效果很好。

问题在于,其中一台服务器还使用端口 9997 进行通信(TLS),我们需要将更多这样的服务器添加到组合中。目前,我们只是将 nginx 中的流量硬编码到使用 9997 的一台服务器。随着我们继续前进,并在 9997 上托管内容的其他服务器,这种方法将行不通

我如何配置 nginx 以将 443 和 9997 传输到需要这些通信的盒子,同时在需要时继续将 443 发送到另一台服务器?

它需要是动态的,以便将流量发送到正确的服务器。

这是现在可以运行的配置(一些信息被删除):

#user  nobody;
worker_processes  1;

error_log   /var/log/nginx/error.log;
#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}

stream {

    map $ssl_preread_server_name $upstream {
        server1.domain.com server1;
        server2.domain.com server2;
    }

    server {
        listen 443;
        proxy_pass $upstream;

        ssl_preread on;
    }

    server {
        listen 9997;
        proxy_pass 1.2.3.4:9997;
    }


    upstream server1 {
        server 1.2.3.4:443;
    }

    upstream server2 {
        server 1.2.3.5:443;
    }

}

答案1

以下配置应该适合你

stream {

    map $ssl_preread_server_name:$server_port $upstream {
      server1.domain.com:443 server1;
      server2.domain.com:443 server2;
      server1.domain.com:9997 server3;
    }

    server {
      listen 443;
      proxy_pass $upstream;

      ssl_preread on;
    }

    server {
      listen 9997;
      proxy_pass $upstream;
      ssl_preread on;
    }


    upstream server1 {
      server 1.2.3.4:443;
    }

    upstream server2 {
      server 1.2.3.5:443;
    }
    upstream server3 {
      server 1.2.3.4:9997;
    }
}

答案2

这是一个使用 nginx 作为路由器而不进行upstream定义的可能解决方案。upstream当服务器定义包括命名主机时,这可能是不可取的,因为如果无法解析命名主机,nginx 将无法启动(docker 网络可能就是这种情况)。以下配置假定仅路由 HTTPS 流量,并将所有 HTTP 流量重定向到 HTTPS。

此配置专为在 Docker 实例中运行并与其他 Docker 服务共享网络的 nginx 而设计。dockerservice1dockerservice2预计会监听端口 443。此resolver 127.0.0.11 ipv6=off valid=1s;行启用 Docker 本地 DNS 解析器 ( 127.0.0.11) 的主机名解析,以便dockerservice1dockerservice2可以解析为来自 Docker 内部网络的 ips。使用此配置,无论引用的 Docker 服务/主机的启动状态如何,nginx 都将始终启动。

可以省略日志记录配置,但对于诊断目的很有用。

# /etc/nginx/nginx.conf from the nginx docker instance

user nginx;
worker_processes auto;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log;
include /etc/nginx/modules-enabled/*.conf;

events {
        worker_connections 1024;
}

http {
        server {
                listen 80 default_server;
                server_name _;
                return 301 https://$host$request_uri;
        }

        log_format basic
                '$time_local router:RD $status $request_time client: $remote_addr '
                'http://$host$request_uri -> https://$host$request_uri';

        access_log /var/log/nginx/access.log basic;
        error_log /var/log/nginx/error.log;
}

stream {
        map $ssl_preread_server_name:$server_port $upstream {

                hostnames;

                mydomain.com:443 dockerservice1;
                www.mydomain.com:443 dockerservice1;
                test.mydomain.com:443 dockerservice2;
                
                default dockerservice1;
        }

        server {
                listen 443;
                resolver 127.0.0.11 ipv6=off valid=1s;
                proxy_pass $upstream:443;
                ssl_preread on;
        }
        log_format basic
                '$time_local router:RT $status $session_time client: $remote_addr '
                '$ssl_preread_server_name:$server_port -> $upstream ($upstream_addr) '
                'bytes from/to client $protocol $bytes_sent $bytes_received '
                'bytes from/to upstream $upstream_bytes_sent/$upstream_bytes_received '
                'upstream time: $upstream_connect_time';

        access_log /var/log/nginx/access.log basic;
        error_log /var/log/nginx/error.log;
}

注意:如果在不同的docker项目/docker-compose.yml文件上(并且可能在同一个网络上),存在同名的docker服务和docker主机名,则Docker version 20.10.12, build e91ed57DNS解析器似乎工作不可靠,例如127.0.0.11

# project1/docker-compose.yml
version: "3"
services:
  sharedName:
    container_name: sharedName
    hostname: sharedName
    networks:
      - default
      - external
    ...

networks:
  external:
    name: someNetwork
# project2/docker-compose.yml
version: "3"
services:
  sharedName:
    container_name: OTHERContainerName
    hostname: OTHERContainerName
    networks:
      - default
      - external
    ...

networks:
  external:
    name: someNetwork

上述情况下的预期行为是主机名 sharedName始终解析为从 创建的容器的 IP project1/docker-compose.yml。观察到的行为是随机解析为容器或sharedName的 IP 。这对于大量项目/容器也有效。sharedNameOTHERContainerName

一个可能的解决方案似乎是强制使用唯一的主机名,且服务名称中没有重复的主机名。

相关内容