我正在尝试使用 nginx 作为两个不同服务器的反向代理。服务器需要使用客户端证书进行身份验证,这意味着 nginx 配置为流代理,利用map $ssl_preread_server_name
SNI 检查将数据发送到正确的服务器。
这对于它现在托管的两台服务器来说效果很好。两台服务器都监听 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 而设计。dockerservice1
和dockerservice2
预计会监听端口 443。此resolver 127.0.0.11 ipv6=off valid=1s;
行启用 Docker 本地 DNS 解析器 ( 127.0.0.11
) 的主机名解析,以便dockerservice1
和dockerservice2
可以解析为来自 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 e91ed57
DNS解析器似乎工作不可靠,例如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 。这对于大量项目/容器也有效。sharedName
OTHERContainerName
一个可能的解决方案似乎是强制使用唯一的主机名,且服务名称中没有重复的主机名。