一、引言

在现代微服务架构中,API 网关是所有外部流量的入口,承担着路由转发、鉴权、限流、监控等核心职责。网关的单点故障或性能瓶颈会直接影响整个系统的可用性,因此网关的高可用设计是运维工程中的重中之重。

本文聚焦于分布式高可用架构自动化建设两个核心方向,探讨如何构建一个能够在故障发生时自动恢复、持续提供服务的网关集群。

二、分布式高可用架构设计

2.1 无状态网关节点

高可用网关的首要设计原则是节点无状态化。网关本身不持久化任何会话数据或请求状态,所有状态外置到独立的存储系统中。这样做的优势在于:

  • 任意节点可随时增减:不需要考虑数据迁移或状态同步;
  • 故障恢复成本低:宕机节点重启后直接从配置中心拉取最新配置即可加入集群;
  • 水平扩展灵活:通过增加节点即可线性提升吞吐量。

无状态设计并不意味着完全没有本地缓存。为了提高性能,网关节点通常会缓存热点配置(如路由规则、鉴权密钥),但这些缓存必须支持失效后快速重建,且不能作为唯一数据源。

2.2 集群及自动化设计

针对于大型入口网关集群,主要有以下一些问题:

  1. 使用传统nginx方案代理,当新的路由发布时需要更新集群内所有节点(至少相关节点需要更新),需要依赖于自动化发布,可能会存在节点配置一致性问题。
  2. 使用云原生方案代理,如apisix、envoy等,这些方案支持自动发现等功能(本文章仅讨论通用化网关集群方案,这部分永不到)。针对于常规场景下配置下发也需要依赖于配置中心(部分场景下可能需要自研插件以实现配置落地)。

集群隔离设计

集群隔离在网关集群中的重要程度高于任何业务,原因如下:

  1. 所有流量全部接入到同一集群,当单集群出现故障时,可能会影响到所有业务。
  2. 超大型网关集群可能存在某些未知问题(如流量不均衡、配置下发延迟等),将集群进行小粒度分割能有效降低影响。
  3. 部分业务可能会有合规需求,不能与普通业务共享集群。

传统网关集群设计

集群搭建

集群流量链路通常如下:

1
2
3
4
5
6
7
8
    证书管理       配置下发        批量部署                
|-----------|--------------|

用户 --> L4网关 --> L7网关 --> 上游服务
|
|--> 日志 --> 日志中心/BD
|--> 镜像流量(按需)
|--> 监控 --> 监控中心

自动化方案设计

大集群模式

大集群模式顾名思义,直接把流量接入到所有节点中,所有节点接收流量,网关层均衡由L4网关负责。(当然不是所有业务都接入同一个集群中,可能企业内部也有多个集群,多个业务复用同一套网关集群。)

自动化部署

该场景下,最简单的自动化部署方案是直接使用puppet或者ansible等工具进行无差别部署。

证书管理

在小规模业务场景下,特别是个人业务场景下,证书通常是在节点上使用acme.sh等自动工具进行申请管理,但是在集群场景下会有问题:

  1. 所有节点都申请证书,每个节点的证书都不相同。
  2. 频繁申请证书还可能导致CA屏蔽节点IP。

所以在本场景下,最常用的方案是使用中心化证书管理。所有证书由中心化证书系统统一申请/采购,节点维护一个证书自动更新工具,定时更新证书。

路由自动化发布

路由自动化发布主要实现方式为被动式下发,其下发流程如下:

1
github/gitlab --> webhook --> airflow(根据变更标记定向操作相关节点) --> 节点更新配置并重启

分布式模式

nginx是无状态服务,所以为了降低部署和维护成本,以及提高服务的可用性,我们可以将网关集群整个搬到k8s集群上。

引入k8s集群后,因为其极低的部署以及横向扩缩容成本,理论上来说可以做到每个服务一套独立的逻辑流量链路(注意,物理流量链路是不变的,还是k8s),极大程度上降低了网关故障影响业务风险。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-proxy-conf
namespace: default
data:
nginx.conf: |
worker_processes auto;
events {
worker_connections 10240;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;

server {
listen 80;
server_name _;

location / {
# 固定代理后端 192.168.0.100
proxy_pass http://192.168.0.100;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}

---

apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-proxy
namespace: default
spec:
replicas: 3 # 多副本,由Service负载均衡
selector:
matchLabels:
app: nginx-proxy
template:
metadata:
labels:
app: nginx-proxy
spec:
containers:
- name: nginx
image: nginx:1.25-alpine
ports:
- containerPort: 80
volumeMounts:
- name: nginx-conf
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 3
periodSeconds: 5
volumes:
- name: nginx-conf
configMap:
name: nginx-proxy-conf

---

apiVersion: v1
kind: Service
metadata:
name: nginx-proxy-svc
namespace: default
spec:
selector:
app: nginx-proxy
ports:
- port: 80
targetPort: 80

如果需要做SaaS化平台,只需要在后端渲染出上面的配置(svc建议使用LB模式),然后导入到k8s集群中即可。

云原生网关集群设计

如apisix和kong等云原生网关,原本是支持集群模式部署的,并且都有直接提供管理API,可以通过API配置路由规则。所以在需要自研LB SaaS场景下,可以直接由后端对接网关。

但是需要注意一个点,该场景下可能对于配置版本化管理不太友好,因为云原生网关的配置是依赖于etcd等注册中心的。所以如果在使用过程中需要对配置进行版本化管理,建议在网关上层加一个版本化管理的功能。