亚马逊AWS官方博客
云原生游戏网关架构:EKS + APISIX + Graviton 构建高性能游戏服务网关
前言
在现代游戏运营环境中,随着游戏服务器规模的不断扩大,传统的服务器代理方案面临着诸多挑战。本文将介绍如何使用 API Six 这一高性能网关来解决大规模游戏服务器代理的问题,特别是针对需要使用多个 Network Load Balancer (NLB) 的场景,提供一个更加优雅和高效的解决方案。
在游戏服务架构中,我们经常遇到以下几个关键挑战:
- 服务器规模问题
- 随着游戏的成功运营,服务器数量可能从最初的几台扩展到成百上千台
- 传统的使用多个 NLB 进行代理的方案在管理和维护上变得越来越复杂
- 成本问题:每增加一个 NLB 都会带来额外的费用支出
- 安全性考虑
- 游戏服务器需要防护各种网络攻击
- 传统的 TCP 协议缺乏足够的安全保护机制
- 需要在不影响性能的前提下提供安全保障
- 运维复杂性
- 多个 NLB 的配置管理较为繁琐
- 服务器扩缩容时需要频繁调整负载均衡配置
- 监控和故障排查的难度随着规模增加而增加
面对这些挑战,我们需要一个更现代化的解决方案。API Six 作为一个高性能、可扩展的网关,结合 TLS 加密,能够很好地解决这些问题。在接下来的内容中,我们将详细介绍如何使用 API Six 构建一个高效、安全、易于管理的游戏服务网关系统。
架构介绍
1. 架构整体说明
APIsix核心组件运行于 Amazon EKS(Elastic Kubernetes Service)集群内部。整个系统主要分为两大访问入口:运维(Ops)和玩家(Players),分别通过独立的 ELB(Elastic Load Balancer)接入.(在此建议咱们在部署环境前可以先手动创建ELB, 在EKS中通过TargetGroupBinding的方式来进行绑定服务,这样可以保证后续服务变更时前端接入ELB为同一个.)
2. 流量入口
- Ops(运维)入口
运维人员通过 ELB 访问 EKS 集群中的 Admin API,实现对平台的管理和监控。
- Players(玩家)入口
玩家流量同样通过独立的 ELB 进入 EKS 集群,主要访问 API Gateway,进而路由到具体的游戏服务(Game Server)或平台服务(Platform Service)。
3. EKS 集群内部结构
- Admin API 层
提供管理接口,供运维人员操作和管理整个系统。
- etcd 层
作为分布式键值存储,负责服务发现、配置管理等核心功能。Admin API 会将变更写入 etcd,API Gateway 通过 watch 机制实时感知服务变化。
- API Gateway 层
这一层是玩家访问的主要入口,API Gateway 负责根据 etcd 的服务发现信息,将玩家请求路由到后端的具体服务(如 Platform Service 或 Game Server)。
- 业务服务层
包含平台服务(Platform Service)和多个游戏服(Game Server1、Game Server2 等),这些服务是最终处理玩家请求的核心业务组件。
方案部署
下面我们将逐步来验证整个方案, 方案中我们将采用模拟TCP协议的游戏服务,通过ELB来实现不同游戏服的路由功能.首先我们需要创建一个实验EKS集群, 参考 EKS文档 创建EKS.
创建好EKS后, 添加用户权限
然后创建Access Entry
使用Helm来安装部署APISix
本文采用的部署目标服务器为亚马逊云科技Graviton机型,可以帮助我们发挥APISix的最大性能. 参考步骤如下:
- 添加相关helm库
helm repo add apisix https://charts.apiseven.com
helm repo update
- 整理apisix-values.yaml
service:
type: LoadBalancer
annotations:
service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
service.beta.kubernetes.io/aws-load-balancer-type: nlb
service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip
service.beta.kubernetes.io/aws-load-balancer-backend-protocol: tcp
Stream proxy configuration (L4 proxy TCP/UDP)
stream:
enabled: true
only: true # Disable HTTP proxy and only enable stream proxy
tcp:
- addr: 8888
tls: true
udp: []
Enable APISIX Ingress Controller
ingress-controller:
enabled: false
Enable APISIX Dashboard
dashboard:
enabled: true
config:
conf:
etcd:
endpoints:
- apisix-etcd:2379
prefix: “/apisix”
authentication:
secret: Admin@2025
expire_time: 3600
users:
- username: admin # dashboard 用户名
password: Admin@2025 # dashboard 密码
ingress:
enabled: true
className: “alb”
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/listen-ports: ‘[{“HTTP”: 80}]’
alb.ingress.kubernetes.io/healthcheck-path: “/dashboard”
alb.ingress.kubernetes.io/healthcheck-protocol: HTTP
alb.ingress.kubernetes.io/healthcheck-port: traffic-port
alb.ingress.kubernetes.io/healthcheck-interval-seconds: ’10’
alb.ingress.kubernetes.io/healthcheck-timeout-seconds: ‘5’
alb.ingress.kubernetes.io/success-codes: ‘200’
alb.ingress.kubernetes.io/healthy-threshold-count: ‘2’
alb.ingress.kubernetes.io/unhealthy-threshold-count: ‘2’
hosts:
- host: “”
paths:
- “/*”
Basic APISIX configuration
apisix:
image:
repository: apache/apisix
tag: 3.7.0-debian
pullPolicy: IfNotPresent
replicaCount: 2
admin:
enabled: true
service:
type: ClusterIP
etcd configuration
etcd:
image:
repository: bitnami/etcd
tag: 3.5.9
pullPolicy: IfNotPresent
persistence:
storageClass: efs-sc
replicaCount: 3
service:
port: 2379
prefix: “/apisix”
timeout: 30
Resource settings
resources:
limits:
cpu: 1000m
memory: 2Gi
requests:
cpu: 500m
memory: 1Gi
Timezone setting
timezone: “UTC”
AWS EKS specific settings
nodeSelector:
kubernetes.io/os: linux
kubernetes.io/arch: arm64
Tolerations for Graviton nodes (if needed)
tolerations:
- key: “kubernetes.io/arch”
operator: “Equal”
value: “arm64”
effect: “NoSchedule”
Affinity to prefer Graviton nodes
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: kubernetes.io/arch
operator: In
values:
- arm64
- 执行命令更新服务
helm install apisix apisix/apisix –create-namespace –namespace ingress-apisix \
–values apisix-values.yaml
- 如果此处部署有问题,一定要关注一下当前的storageclass是否存在.
etcd:
persistence:
storageClass: efs-sc # 请格外注意此处,否则可能方案部署失败.
另推荐一个小技巧,如果部署出现问题,可以使用Amazon Q CLI来做诊断,整个过程完全自动化,下面是我的步骤截图.
部署 游戏服务
模拟游戏服代码
!/usr/bin/env python3
import socket
import sys
import threading
Get server name from command line argument
server_name = sys.argv[1] if len(sys.argv) > 1 else “Unknown Server”
def handle_client(client_socket, addr):
print(f”[{server_name}] Connection from {addr}”)
try:
Keep connection alive and echo back data
while True:
data = client_socket.recv(1024)
if not data:
break
print(f”[{server_name}] Received: {data}”)
Echo back the data with server name prefix
response = f”[{server_name}] {data.decode(‘utf-8′, errors=’ignore’)}”.encode()
client_socket.send(response)
except Exception as e:
print(f”[{server_name}] Error handling client: {e}”)
finally:
print(f”[{server_name}] Connection closed: {addr}”)
client_socket.close()
def start_server(port=9000):
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
Bind to all interfaces
server.bind((‘0.0.0.0’, port))
server.listen(5)
print(f”[*] {server_name} listening on 0.0.0.0:{port}”)
try:
while True:
client, addr = server.accept()
client_handler = threading.Thread(target=handle_client, args=(client, addr))
client_handler.daemon = True
client_handler.start()
except KeyboardInterrupt:
print(f”[{server_name}] Shutting down server”)
server.close()
if __name__ == “__main__”:
start_server(9000)
模拟EKS中的服务部署代码: test-server-1.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-server-1
namespace: game
labels:
app: test-server-1
spec:
replicas: 1
selector:
matchLabels:
app: test-server-1
template:
metadata:
labels:
app: test-server-1
spec:
containers:
- name: tcp-server
image: python:3.9-slim
command: [“python”]
args: [“-u”, “/app/tcp-echo-server.py”, “test-server-1”]
ports:
- containerPort: 9000
volumeMounts:
- name: script-volume
mountPath: /app
volumes:
- name: script-volume
configMap:
name: tcp-echo-server
defaultMode: 0777
—
apiVersion: v1
kind: Service
metadata:
name: gs-1
namespace: game
labels:
app: test-server-1
spec:
selector:
app: test-server-1
ports:
- port: 9000
targetPort: 9000
protocol: TCP
type: ClusterIP
test-server-2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-server-2
namespace: game
labels:
app: test-server-2
spec:
replicas: 1
selector:
matchLabels:
app: test-server-2
template:
metadata:
labels:
app: test-server-2
spec:
containers:
- name: tcp-server
image: python:3.9-slim
command: [“python”]
args: [“-u”, “/app/tcp-echo-server.py”, “test-server-2”]
ports:
- containerPort: 9000
volumeMounts:
- name: script-volume
mountPath: /app
volumes:
- name: script-volume
configMap:
name: tcp-echo-server
defaultMode: 0777
—
apiVersion: v1
kind: Service
metadata:
name: gs-2
namespace: game
labels:
app: test-server-2
spec:
selector:
app: test-server-2
ports:
- port: 9000
targetPort: 9000
protocol: TCP
type: ClusterIP
部署服务
kubectl create namespace game
kubectl create configmap tcp-echo-server –from-file=tcp-echo-server.py –namespace game
kubectl apply -f test-server-1.yaml
kubectl apply -f test-server-2.yaml
配置证书
当使用TLS的SNI功能时,每个你想要使用SNI的域名或主机名都需要一个有效的证书。这是因为SNI允许从同一个IP地址和端口提供多个主机名服务,而证书用于验证服务器的身份并与客户端建立加密连接。使用OpenSSL为2个Game Server服务生成证书文件和密钥文件。
- 生成证书
–
openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 -keyout gs-1.key -out gs-1.crt -subj “/CN=gs-1.com” openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 -keyout gs-2.key -out gs-2.crt -subj “/CN=gs-2.com”
–
- 上传证书到apisix
–
kubectl port-forward -n ingress-apisix svc/apisix-admin 9180:9180 & sleep 3 curl -X POST http://127.0.0.1:9180/apisix/admin/ssls -H ‘X-API-KEY: edd1c9f034335f136f87ad84b625c8f1’ -d ‘ { “cert”: “‘”$(cat gs-1.crt)”‘”, “key”: “‘”$(cat gs-1.key)”‘”, “snis”: [“gs-1.com”] }’ # Create SSL certificate for gs-2.com curl -X POST http://127.0.0.1:9180/apisix/admin/ssls -H ‘X-API-KEY: edd1c9f034335f136f87ad84b625c8f1’ -d ‘ { “cert”: “‘”$(cat gs-2.crt)”‘”, “key”: “‘”$(cat gs-2.key)”‘”, “snis”: [“gs-2.com”] }’ kill %1
–
- 验证证书上传
–
curl -X GET http://127.0.0.1:9180/apisix/admin/ssls -H ‘X-API-KEY: edd1c9f034335f136f87ad84b625c8f1’
–
配置路由
下面我们基于已经配置好的证书来配置相关的路由信息, 也就是通常我们在平台服配置好证书后,可以调用相关API来配置路由,命令信息如下:
kubectl port-forward -n ingress-apisix svc/apisix-admin 9180:9180 &
sleep 3
curl -i -X POST http://127.0.0.1:9180/apisix/admin/stream_routes -H ‘X-API-KEY: edd1c9f034335f136f87ad84b625c8f1’ -d ‘{
“upstream”: {
“nodes”: {
“gs-1.game.svc.cluster.local:9000”: 1
},
“type”: “roundrobin”
},
“sni”: “gs-1.com”
}’
curl -i -X POST http://127.0.0.1:9180/apisix/admin/stream_routes -H ‘X-API-KEY: edd1c9f034335f136f87ad84b625c8f1’ -d ‘{
“upstream”: {
“nodes”: {
“gs-2.game.svc.cluster.local:9000”: 1
},
“type”: “roundrobin”
},
“sni”: “gs-2.com”
}’
测试基于SNI的访问
首先获取对应APIsix服务的ALB地址
> kubectl get svc -n ingress-apisix apisix-gateway
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
apisix-gateway LoadBalancer 10.100.xxxx.12 k8s-ingressa-apisixga-xxxxxxx-xxx.elb.us-east-1.amazonaws.com 80:30496/TCP,8888:30694/TCP 3d2h
通过上面返回获取的ALB的地址
openssl s_client -connect k8s-ingressa-apisixga-xxxxx.xxxx.elb.ap-northeast-1.amazonaws.com:8888 \
-servername gs-1.com -quiet
openssl s_client -connect k8s-ingressa-apisixga-xxxx.xxxx.elb.ap-northeast-1.amazonaws.com:8888 \
-servername gs-2.com -quiet
至此可以看到通过不同的SNI我就可以访问到不同的游戏服了,也就解决了使用同一个NLB+APIsix的访问不同的游戏服了.
APISix dashboard访问 (Optional)
我们也可以通过Dashboard来访问当前的APIsix系统,查看相关的配置数据. 不过这里需要我们确认一下ALB的certificate ARN 是不是正确.
kubectl get ingress -n ingress-apisix
APISix 部署亚马逊云科技最佳实践
在生产环境中部署 Apache APISIX 时的关键最佳实践,帮助提升稳定性、性能与可维护性。
核心架构与组件分离
为了保证系统可扩展与高可用,推荐将 APISIX 各核心组件解耦部署:
- 控制平面(etcd):使用单独部署的 etcd 集群存储路由与插件配置,建议在部署APISix的时候直接指向预先部署好的etcd,至少部署 3 节点,开启数据持久化与快照备份,防止单点故障。
- 数据平面(APISIX 节点):外部请求由多个 APISIX 实例处理,按需水平扩容。每个实例仅负责流量转发与插件执行,配置从 etcd 动态拉取。
- 运维监控(Prometheus & Grafana):部署专用的监控系统,采集 APISIX 及 etcd 的指标与日志,实时告警与可视化。
- 无状态部署
部署模式与扩展策略
APISIX 实例本身应保持无状态,所有动态配置均存储在 etcd。容器化或虚拟机化均可,借助 Kubernetes 等平台实现自动伸缩与滚动升级。
- 水平扩展
根据 QPS 与响应延迟指标,动态扩缩容。建议在 Kubernetes 中配置 HPA(Horizontal Pod Autoscaler),结合自定义指标(如 CPU、内存或请求速率)自动调整实例数。
- 灰度发布与回滚
配合 Kubernetes Deployment 或其它发布工具,利用 canary 发布策略逐步下发新版本。在出现问题时,可快速回滚至稳定版本,且不中断大部分流量。
- 优雅退出, 需要保证apisix pod退出时请求都已经处理完毕
lifecycle:
preStop:
exec:
command: [“sh”, “-c”, “sleep 15 && apisix quit”]
网络与安全
- 高性能网络
采用 L4 负载均衡(如 Nginx Stream、LVS)将流量分发至 APISIX,避免在 L7 层引入过多额外延迟。
- TLS 终端
如需 HTTPS 支持,推荐在边缘层(L4)终端 TLS,再以 HTTP 通信至 APISIX;或直接在 APISIX 上使用 ssl 插件终端 TLS,并结合 Cert-Manager 自动续签证书。
- 访问控制与认证
启用 IP 黑白名单、ACL 插件,并根据业务需求接入 JWT、OAuth2 等认证插件,确保后端服务安全。
配置管理与版本控制
- 基础配置与热更新
把路由、上游服务、插件配置以 YAML/JSON 格式存储于代码仓库,结合 CI/CD 流水线自动同步至 etcd,实现配置即代码(Configuration as Code)。
- 版本管理
每次配置变更都需打 tag 并在流水线中校验(lint、单元测试、灰度发布),确保变更可追溯、可回滚。
- 选择稳定版本,并及时跟进社区的更新.
- 升级版本时需要进行完整的回归测试,保证新版本的兼容性问题.
- 实例选择
性能优化与插件治理
优先选择Graviton类型主机,经过多轮测试发现Graviton的机型相对于x86机型可以提供至少2两倍的性能提升,具体请参考社区 Benchmark 链接:.
- 插件开关粒度
仅在需要的路由上启用插件,避免全局加载过多插件导致请求路径冗余执行。
- 缓存与限流
利用 proxy-cache 插件对静态或可缓存响应进行本地缓存,减轻后端压力;结合 limit-req、limit-count 插件防止流量突发与恶意攻击。
- 日志与追踪
启用 skywalking、zipkin 或 opentelemetry 插件,将请求链路与指标上报至分布式追踪系统,快速定位性能瓶颈。
监控告警与健康检查
- 健康探针
在 Kubernetes 中配置 LivenessProbe 与 ReadinessProbe,APISIX 节点异常时可自动剔除。
- 关键指标
重点监控请求速率、响应延迟、错误率,以及 etcd 的延迟与 leader 选举状态。根据阈值配置告警规则,保证故障可被及时发现与响应
- 在实际生产中,如果service数量比较多以及并发大的情况下,需要对netfilter.nf_conntrack_max进行调整。建议结合prometheus和grafana进行告警,及时发现问题并优化相关参数。我们也可以通过采用类似C7gn的机型来提升网络吞吐。
- 跨可用区部署
灾备与高可用设计
将 etcd 和 APISIX 实例分布在多个可用区或机房,保证单区故障时仍有服务可用。
- 定期备份
对 etcd 数据进行周期性全量与增量备份,并在异地存储;同时验证备份可用性与恢复流程。
通过上述最佳实践,可以构建一套 高可用、可扩展、易运维 的 APISIX 服务部署体系,满足业务在复杂流量下的稳定运行与快速迭代需求。
总结
借助以上方案通过将所有玩家和运维流量先汇聚到单个NLB,再由部署在 EKS 集群内的 Apache APISIX 按 TLS SNI 把请求精准分发到各游戏服,从而用最少的负载均衡实例实现统一路由、动态服务发现和全链路加密,不仅显著降低 NLB 成本和配置复杂度,还能在服务器扩缩容时保持流量无感知切换,成为高并发游戏场景下经济、高效且易维护的网关架构,同时,借助Graviton,APISix能够实现极高的性价比。
参考内容
https://api7.ai/blog/api7-latency
https://apisix.apache.org/blog/2022/08/12/arm-performance-google-aws-azure-with-apisix/