博客 / 詳情

返回

雲原生遊戲網關架構:EKS + APISIX + Graviton 構建高性能遊戲服務網關

前言

在現代遊戲運營環境中,隨着遊戲服務器規模的不斷擴大,傳統的服務器代理方案面臨着諸多挑戰。本文將介紹如何使用 API Six 這一高性能網關來解決大規模遊戲服務器代理的問題,特別是針對需要使用多個 Network Load Balancer (NLB) 的場景,提供一個更加優雅和高效的解決方案。
在遊戲服務架構中,我們經常遇到以下幾個關鍵挑戰:

1、 服務器規模問題

  • 隨着遊戲的成功運營,服務器數量可能從最初的幾台擴展到成百上千台
  • 傳統的使用多個 NLB 進行代理的方案在管理和維護上變得越來越複雜
  • 成本問題:每增加一個 NLB 都會帶來額外的費用支出

2、 安全性考慮

  • 遊戲服務器需要防護各種網絡攻擊
  • 傳統的 TCP 協議缺乏足夠的安全保護機制
  • 需要在不影響性能的前提下提供安全保障

3、運維複雜性

  • 多個 NLB 的配置管理較為繁瑣
  • 服務器擴縮容時需要頻繁調整負載均衡配置
  • 監控和故障排查的難度隨着規模增加而增加

面對這些挑戰,我們需要一個更現代化的解決方案。API Six 作為一個高性能、可擴展的網關,結合 TLS 加密,能夠很好地解決這些問題。在接下來的內容中,我們將詳細介紹如何使用 API Six 構建一個高效、安全、易於管理的遊戲服務網關係統。

📢限時插播:在本次實驗中,你可以在基於 Graviton 的 EC2 實例上輕鬆啓動 Milvus 向量數據庫,加速您的生成式 AI 應用。
⏩快快點擊進入《創新基石 —— 基於 Graviton 構建差異化生成式AI向量數據庫》實驗
📱 即刻在雲上探索實驗室,開啓構建開發者探索之旅吧!

架構介紹

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的最大性能. 參考步驟如下:

1、添加相關helm庫

helm repo add apisix https://charts.apiseven.com
helm repo update

2、整理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

3、執行命令更新服務

helm install apisix apisix/apisix --create-namespace --namespace ingress-apisix \
--values apisix-values.yaml

4、如果此處部署有問題,一定要關注一下當前的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服務生成證書文件和密鑰文件。

1、生成證書

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"

2、上傳證書到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

3、驗證證書上傳

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 發佈策略逐步下發新版本。在出現問題時,可快速回滾至穩定版本,且不中斷大部分流量。
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-goo...

本篇作者
image.png

本期最新實驗為《創新基石 —— 基於 Graviton 構建差異化生成式AI向量數據庫》
✨ 在本次實驗中,你可以在基於 Graviton 的 EC2 實例上輕鬆啓動 Milvus 向量數據庫,加速您的生成式 AI 應用。基於 Graviton 的 EC2 實例為您提供極佳性價比的向量數據庫部署選項。
📱 即刻在雲上探索實驗室,開啓構建開發者探索之旅吧!
⏩[點擊進入實驗] 構建無限, 探索啓程!
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.