使用Nginx在双栈环境下反向代理Http流量
最近,团队在进行微服务在双栈环境下提供服务的调研和改造工作。
工作一开始就遇到了瓶颈,我们的微服务目前均部署在K8s环境中,但我们用的K8s版本比较老,不支持IPv6。
想要实现微服务之间通过IPv6通信是不可能了。只能退而求其次,研究下用户从互联网上发起的访问能否支持双栈。
在典型的微服务使用场景中,一切外部流量均通过网关模块流到系统内部,似乎只需要想办法实现网关的双栈访问即可。
网关
我们用的微服务网关是基于Spring Cloud Gateway定制的,其底层使用了Spring WebFlux,再底层是Netty。
过去,我们通过Deployment+NodePort Service的方式暴露Gateway。
经测试,在低版本的K8s集群中,通过NodePort方式暴露的服务,无法通过IPv6的地址访问。(原因没有深究)
看来,目前NodePort是不能用了。还有一种比NodePort更粗暴的暴露方式,即HostNetwork,理论上应该是可以的。
本应直接验证下Spring Cloud Gateway使用HostNetwork暴露是否支持IPv6,但另一种方案突然浮现脑海。
第二种方案
第二种方案就是不直接暴露Gateway,而是在Gateway前面引入一个支持双栈的反向代理,即Nginx。
之所以会想到这种方案,是因为前段时间,前端有提过希望Gateway支持http2协议,但Netty现在还不支持。当时就有想过是否在Gateway前面再放一个nginx。
从上面的分析可以推断,Nginx同样不能使用NodePort暴露,需要使用HostNetwork。
要让Nginx在IPv6和IPv4两个地址上监听,需要配置一下,下面是一个最简单的配置文件。
default.conf
server {
listen [::]:16980 ipv6only=off;
server_name nginx;
location / {
proxy_pass http://gateway-server.ezci.svc.cluster.local:18080;
}
}
其中端口号16980可按实际情况替换,但必须是一个在HostNetwork上未被占用的端口号。
ipv6only=off是告诉Nginx,该Socket会同时在IPv4和IPv6网络上监听。
proxy_pass属性的值,请按需配置。
按照上面的配置,普通Http请求的流量是可以顺利转发的,但WebSocket会报错。
下面是一个支持代理WebSocket的配置文件,在上一个配置文件的基础上添加了部分内容。
default.conf
map $http_upgrade $connection_upgrade {
default upgrade;
‘’ close;
}
server {
listen [::]:16980 ipv6only=off;
server_name nginx;
location / {
proxy_pass http://gateway-server.ezci.svc.cluster.local:18080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
All in K8s
最后,看下如何在K8s中实现通过Nginx在双栈网络上监听,将流量转发至Gateway。
首先将Nginx的配置文件创建为ConfigMap。
apiVersion: v1
kind: ConfigMap
metadata:
namespace: jinzhenj
name: 5ee19740a4ed290001afcfab
data:
default.conf: |-
map $http_upgrade $connection_upgrade {
default upgrade;
‘’ close;
}
server {
listen [::]:16980 ipv6only=off;
server_name nginx;
location / {
proxy_pass http://gateway-server.ezci.svc.cluster.local:18080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
然后,以Deployment(或DaemonSet,此处以Deployment做示例)的方式启动Nginx。
apiVersion: v1
kind: Deployment
metadata:
labels:
service: nginx
name: nginx
namespace: jinzhenj
spec:
replicas: 1
selector:
matchLabels:
service: nginx
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
type: RollingUpdate
template:
metadata:
labels:
service: nginx
spec:
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
volumes:
- name: volume-0
configMap:
name: 5ee19740a4ed290001afcfab
containers:
- name: nginx
image: nexus.cmss.com:8086/nginx:1.19
volumeMounts:
- mountPath: /etc/nginx/conf.d/default.conf
name: volume-0
subPath: default.conf
env:
- name: POD_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.podIP
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
- name: SERVICE_NAME
value: nginx
- name: CONTROLLER_NAME
value: nginx
- name: CONTROLLER_REPLICAS
value: “1”
在PodTemplateSpec部分,设置hostNetwork为true,如果gateway在k8s集群内,想要通过域名做转发,则不要忘记将dnsPolicy设置为ClusterFirstWithHostNet。
还有,将上面定义的ConfigMap中的default.conf挂载在/etc/nginx/conf.d/default.conf。
经过上述配置后,就能通过nginx在双栈环境下将请求转发至K8s集群内的网关中了。
测试
下面附上一些测试截图。
下面使用wscat工具测试一下WebSocket。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?