跳到主要内容
版本:Dev

在 Kubernetes 上部署 AISIX 网关

使用 api7/aisix Helm Chart 在 Kubernetes 集群中部署、暴露和扩缩 AISIX 网关。网关在你的环境中承载实时 AI 流量,并连接现有 AISIX Cloud 控制面以接收配置。

该 Chart 需要数据面管理器端点,以及为目标环境签发的网关证书包。连接后,网关会从控制面接收模型、调用方 API Key 和策略。

前提条件​

  • 一个 Kubernetes 集群,已配置 kubectl 访问权限并安装 Helm 3。
  • 可以访问一个能够签发网关证书的 AISIX Cloud 环境。
  • 集群可以通过网络访问 AISIX Cloud 数据面管理器端点。如果使用 On-Premises,请先完成 On-Premises 安装。

安装 Chart​

在控制台中打开目标环境的 Data planes 视图并签发网关证书。Kubernetes (Helm) 标签页会提供下文使用的数据面管理器端点和证书包。本示例还将初始部署固定为一个副本。私钥只显示一次。

将证书包存储在 Secret 中,避免私钥出现在 values 文件中:

kubectl create namespace aisix-dp

kubectl -n aisix-dp create secret generic aisix-gateway-certificate \
--from-file=cert.pem=./cert.pem \
--from-file=key.pem=./key.pem \
--from-file=ca.pem=./ca.pem

使用同一视图中的数据面管理器端点安装 Chart。Dev 文档会将 Chart 的已发布网关镜像替换为 dev 镜像:

helm repo add api7 https://charts.api7.ai
helm repo update

helm install aisix-dp api7/aisix --namespace aisix-dp \
--set image.repository=ghcr.io/api7/aisix \
--set image.tag=dev \
--set controlPlane.baseURL=https://dp-manager.example.com:7944 \
--set controlPlane.certificate.existingSecret=aisix-gateway-certificate \
--set replicaCount=1

网关首次上报心跳后,会出现在环境的 Data planes 视图中。Chart 默认为两个副本,但此初始命令只启动一个副本,因为在配置共享 Redis 之前,限流计数器使用每个副本各自的内存。控制台 Helm 代码片段省略了 replicaCount,因此原样安装会启动两个副本。如上所示,请在配置 Redis 前添加 --set replicaCount=1。增加副本数或启用自动扩缩容器前,请先在副本间共享限流计数器。

每个副本会注册为独立实例,并共享同一个证书。

查看 Chart 接受的所有值:

helm show values api7/aisix

Chart 源码发布在 api7/api7-helm-chart 仓库中。

暴露网关​

Chart 默认为代理创建 ClusterIP 类型的 Service。如需通过云负载均衡器发布:

service:
type: LoadBalancer
port: 80
# 保留客户端源 IP,按模型配置的 IP 允许列表会使用该地址进行匹配。
externalTrafficPolicy: Local

Chart 默认只给网关一个明文 HTTP 代理监听器:它在容器内绑定 containerPorts.proxy(端口 3000),由 Service 以 service.port 发布。Service 可以暴露端口 80 或 443,无需让进程绑定特权容器端口。

同时提供 HTTPS 和明文 HTTP​

配置 listeners 即可同时提供多个代理端口,每个端口有各自的 TLS:

listeners:
- name: https # 端口名称,容器端口和 Service 端口共用。
containerPort: 3443
servicePort: 443
# nodePort: 30443 # 可选;仅适用于非 ClusterIP 类型的 Service。
tls:
secretName: aisix-proxy-tls
- name: http
containerPort: 3000
servicePort: 80

非空的 listeners 就是代理监听器的完整集合,并取代默认的那一个监听器。此时没有任何东西绑定 containerPorts.proxy,service.port 和 service.nodePort 也不会被读取,因为每个条目都带有自己的配置。代理 Service 仍然只有一个,它为每个条目发布一个端口,并按名称指向该条目的容器端口。

每个监听器提供相同的路由,包括 /livez 和 /readyz,因此 startup、readiness 和 liveness 探针都指向第一个条目——当该条目终止 TLS 时探针使用 HTTPS,且 kubelet 不会校验证书。Chart 会把这组监听器以 proxy.listeners 写入网关的配置文件。它仍然会写入 proxy.addr,网关会忽略该值并记录一行 INFO 日志,这行日志无需处理。此功能需要支持 proxy.listeners 的网关镜像。

这些 values 背后的网关侧规则,包括明文监听器为什么只能绑定到可信网络接口,参阅同时提供 HTTPS 和 HTTP。

网关从文件读取 TLS 证书材料,因此每个启用 TLS 的监听器都需要各自的 kubernetes.io/tls Secret,键名为 tls.crt 和 tls.key。Chart 会把它以只读方式挂载到 /etc/aisix/tls/<name>。使用你已有的证书和私钥创建:

kubectl -n aisix-dp create secret tls aisix-proxy-tls \
--cert=./tls.crt --key=./tls.key

或者由 cert-manager 签发该 Secret 并自动续期:

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: aisix-proxy-tls
namespace: aisix-dp
spec:
secretName: aisix-proxy-tls
dnsNames:
- gateway.example.com
issuerRef:
name: letsencrypt
kind: ClusterIssuer

轮换后的证书会以挂载点中文件变更的形式到达网关。请使用 Chart 安装后提示中的 Deployment 名称重启 Pod,以使其生效。包含 aisix 的 Release 名通常会原样使用;名称覆盖可能会改变结果:

kubectl rollout restart deployment/<deployment-name> -n <namespace>

绑定特权容器端口​

只有当网关必须直接监听某个低于 1024 的端口时,才在容器内绑定该端口。发布的镜像以非 root 用户 UID 10001 运行,网关二进制文件具有生效的 CAP_NET_BIND_SERVICE 文件能力:

containerPorts:
proxy: 80

配置了 listeners 时,请把特权端口写在应当绑定它的那个条目的 containerPort 上,此时 containerPorts.proxy 不会被读取。

如果你自定义渲染后的 Pod 并丢弃所有 capability,请为 AISIX 容器重新添加 NET_BIND_SERVICE:

spec:
containers:
- name: aisix
securityContext:
capabilities:
drop: ["ALL"]
add: ["NET_BIND_SERVICE"]

Kubernetes Restricted Pod Security Standard 允许此 capability。由于二进制文件的文件 capability 已设置 effective bit,如果运行时阻止授予该 capability,容器可能会因 exec: Operation not permitted 而失败。

只有当网关必须直接绑定节点且集群策略允许时,才使用 hostNetwork 或 hostPort。这些选项会引入节点端口冲突并降低网络隔离;Baseline 和 Restricted Pod Security Standard 也不允许使用它们。

完整的网络暴露和凭证模型请参阅网络与安全。

在 OpenShift 上运行​

该 chart 可以在 OpenShift 默认的 restricted-v2 security context constraint 下安装,既不需要自定义 SCC,也不需要改动 ServiceAccount。

chart 不再固定 UID。它的默认 Pod 安全上下文就是 runAsNonRoot: true 加 seccompProfile.type: RuntimeDefault,因此 restricted-v2 会从命名空间分配的区间中提供 runAsUser 和 fsGroup。网关镜像声明了数字用户,并可在分配到的任意 UID 搭配 GID 0 下运行;运行期唯一被写入的路径是一个 emptyDir 卷。容器级安全上下文本身已满足 restricted-v2:除 NET_BIND_SERVICE 外丢弃全部 capability、只读根文件系统、禁止提权。

如果需要改为固定 UID,把这些 values 设置回去即可:

podSecurityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
fsGroup: 10001
seccompProfile:
type: RuntimeDefault

控制面在 OpenShift 上的安装,请参见在 OpenShift 上安装。

在副本间共享限流计数器​

限流计数器默认存储在每个网关自己的内存中,因此 N 个副本实际会执行每项已配置请求、Token 和并发限制的 N 倍。在运行多个副本前——包括自动扩缩容器新增的任何副本——请让所有副本指向同一个 Redis:

rateLimit:
backend: redis
redis:
url: redis://redis.default.svc:6379

当连接 URL 包含密码时,请改用 rateLimit.redis.existingSecret。若要与 URL 分开提供 Redis 用户名和密码,请通过 configSecrets 从 Secret 读取,详见设置其他网关配置。共享 Redis 连接的其他设置,例如 mode、nodes 或 timeout_secs,写在 config.ratelimit.redis 下。

所有副本都指向同一个 Redis 部署后,再提高 replicaCount 或启用下面的一种自动扩缩容方式。如果部署不使用请求、Token 或并发限制,也可以有意识地接受每个副本独立的内存后端。

基于 CPU 或内存扩缩容​

autoscaling 会为网关 Deployment 创建 HorizontalPodAutoscaler:

autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 20
targetCPUUtilizationPercentage: 70

目标值是 Pod 资源 requests 的百分比,因此 Chart 默认设置 CPU request。它特意不设置 CPU limit:限流会增加代理的长尾延迟,并抑制自动扩缩容器读取的信号。基于 CPU 扩缩容要求集群中安装 metrics-server。

在 Linux 上,proxy.workers 默认使用网关进程可用的 CPU 并行度。Kubernetes CPU request 不会限制该值,但 CPU limit 会限制。如果每个副本需要稳定的 Worker 数量,由于 Chart 不设置 CPU limit,请显式设置 config.proxy.workers,并确保该值不超过为副本规划的 CPU 容量。有关 Worker 配置和容量规划注意事项,请参阅每核一线程 Worker。

启用自动扩缩容后,Deployment 会省略 spec.replicas,避免后续 helm upgrade 重置自动扩缩容器选择的副本数。此后会忽略 replicaCount 值。

所有 behavior 策略都会原样传递,例如让缩容比 Kubernetes 默认行为更平缓:

autoscaling:
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Pods
value: 1
periodSeconds: 60

对于 Pods、Object 或 External 指标(例如通过 Prometheus adapter 暴露的序列),请使用 autoscaling.extraMetrics。

使用 KEDA 基于请求负载扩缩容​

CPU 是负载的间接指标。如需改为根据网关自身流量扩缩容,请使用 KEDA,并针对网关指标执行 Prometheus 查询。

启用示例前,请准备以下集群组件:

  • 安装 KEDA,包括 ScaledObject CRD 和控制器。
  • 提供可查询网关指标的 Prometheus 服务器。
  • 如果需要由 Chart 创建下文所示的抓取配置,请安装 Prometheus Operator 或其他能够消费 ServiceMonitor 对象的控制器。仅安装 CRD 会让 Helm 能够创建对象,但不会有组件将其转换为抓取配置。当 Prometheus 实例按标签选择 ServiceMonitor 时,请设置 metrics.serviceMonitor.labels。如果 Prometheus 通过其他方式发现指标 Service,请将 metrics.serviceMonitor.enabled 保持为 false,并省略该配置块。

确认所启用值依赖的 CRD 已经存在:

kubectl get crd scaledobjects.keda.sh
# 仅当 metrics.serviceMonitor.enabled 为 true 时需要。
kubectl get crd servicemonitors.monitoring.coreos.com

这些前提条件就绪后再配置 Chart:

metrics:
serviceMonitor:
enabled: true

keda:
enabled: true
minReplicas: 2
maxReplicas: 20
pollingInterval: 15
cooldownPeriod: 300
triggers:
- type: prometheus
metadata:
serverAddress: http://prometheus.monitoring.svc:9090
query: sum(rate(aisix_llm_requests_total[2m]))
threshold: "100"

aisix_llm_requests_total 统计模型推理请求,例如 /v1/chat/completions,但不包括 MCP 或 A2A 调用。如果这类流量才是扩缩容依据,请使用 aisix_proxy_requests_total。

流式请求在流结束时计数,而不是在开始时。因此在一批长流进行期间,基于这两个计数器的 rate() 会比实际到达的负载滞后大约一个流的时长;稳态速率不变。选择 pollingInterval 和查询窗口时请考虑这一延迟。1.5.0 及更早版本的网关在响应开始时对流式请求计数。

autoscaling 和 keda 互斥。同时启用两者会使 Helm 渲染失败,避免两个控制器同时写入 spec.replicas。

扩缩容事件期间的行为​

自动扩缩容器新增的副本在能够提供服务前不会接收流量。网关应用控制面配置之前根本不会绑定代理监听器,因此 Chart 指向该监听器的所有探针得到的都是被拒绝的连接,而不是响应。

覆盖这段等待的是 Chart 的 startupProbe。在启动探针成功之前,Kubernetes 会挡住就绪和存活探针,因此等待期间就绪探针根本不会执行,Pod 只是一直不进入就绪状态——Kubernetes 会让它保持在 Service 端点之外,存活探针也不会重启仍在连接控制面的 Pod。启动探针的预算是 periodSeconds x failureThreshold,需要覆盖连接控制面并应用其中内容的时间。控制面连接持续不可达、超过该预算的 Pod,其容器会被杀掉并重启,反复重启会表现为 CrashLoopBackOff——对于从来没有可提供服务内容的实例,这是预期结果。就绪契约以及如何设置该预算,请参阅启动与第一个配置。

副本缩容时,Kubernetes 会同时开始将其从 Service 端点移除并终止 Pod。以下两个 Chart 配置会在此过程中保护进行中的请求。

preStopSleepSeconds 在容器收到 SIGTERM 前先暂停一段时间,让端点移除在网关开始关闭之前传播到每个节点。它覆盖的是监听 Kubernetes API 的负载均衡器;通过轮询健康检查感知的负载均衡器在整个暂停期间看到的仍是就绪的 Pod,由网关自身的排空窗口 shutdown.min_drain_secs 覆盖。

terminationGracePeriodSeconds 为整个终止流程设置上限——暂停、排空窗口、随后的在途请求排空,以及再之后最多 5 秒的快照缓存排空——因为网关排空本身没有截止时间。Kubernetes 对该值的默认是 30 秒,会中断流式响应,因此 Chart 采用了更长的取值。

两者都带有与该流程匹配的默认值,具体取值以 helm show values api7/aisix 的输出为准。如果工作负载的流式传输或 Realtime WebSocket 会话的持续时间超过暂停和排空窗口结束后剩余的预算,请提高 terminationGracePeriodSeconds。

观察扩缩容决策及其依据的指标:

kubectl -n aisix-dp get hpa aisix-dp --watch
kubectl -n aisix-dp describe hpa aisix-dp

应对节点中断​

PodDisruptionBudget 可防止节点排空、集群升级等主动中断一次性停止所有网关。分布约束可以避免副本集中在单个故障域:

podDisruptionBudget:
enabled: true
minAvailable: 50%

topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app.kubernetes.io/name: aisix

Chart 使用 emptyDir 卷挂载 /var/lib/aisix。同一 Pod 内的容器重启后该卷仍然存在,但 Pod 被替换时数据会丢失。Chart 会在每次启动时通过 Secret 提供证书包,因此替代 Pod 会从该证书包重建身份,并在接收流量前从控制面下载配置。请跨故障域保留多个副本,使现有副本可以在其他副本替换期间继续提供服务。

若要在控制面不可用时,让替代 Pod 从缓存配置恢复,需要同时以两种方式自定义工作负载:为每个副本提供独立的持久化状态目录,并将 config.managed.snapshot_cache_enabled 设置为 true。仅提供持久化存储不会启用缓存。完整的恢复与安全要求请参阅从缓存配置重启。

更完整的部署模式请参阅高可用。

设置其他网关配置​

控制面负责动态资源,Chart 负责 Pod。网关从一个配置文件读取启动配置,该文件由 Chart 渲染到 ConfigMap 中并以只读方式挂载。config value 与该文件逐键对应,节名和键名与启动配置参考相同,并列出了每项设置的网关默认值。只需设置要修改的键:

config:
observability:
log_level: debug
upstream:
pool_max_idle_per_host: 32
proxy:
real_ip:
trusted_proxies: ["10.0.0.0/8"]
recursive: true

设置为 null 的键不会写入文件,网关会使用自身的默认值。修改 config 后,helm upgrade 会滚动更新 Pod。运行 helm show values api7/aisix 可查看所有键。

部分键由 Chart 根据其他 value 设置,在 config 下会被拒绝。渲染会失败,并在报错中指明应改用的 value:

配置键Chart value
proxy.addr、observability.metrics.prometheus.addrcontainerPorts
proxy.listeners、proxy.tlslisteners
ratelimit.backend、ratelimit.redis.urlrateLimit
managed.enabled 以及 managed 下的控制面连接设置controlPlane
etcd.endpoints、etcd.prefix由控制面连接设置。
etcd.env_id、etcd.user、etcd.password_env、etcd.tls无法通过此 Chart 配置。
resources_filestandalone
adminadmin 和 containerPorts.admin,仅适用于独立模式。参见启用 Admin API。

包含凭据的设置永远不会写入 ConfigMap。请把值存放在 Secret 中,并在 configSecrets 下以配置路径为键引用它。Chart 会以环境变量的形式把它交给网关,环境变量会覆盖配置文件:

config:
cache:
backend: redis
redis:
mode: single
configSecrets:
cache.redis.url:
secretName: aisix-cache-redis
key: url
cache.redis.password:
secretName: aisix-cache-redis
key: password

configSecrets 接受 cache.redis.url、cache.redis.username、cache.redis.password、ratelimit.redis.username 和 ratelimit.redis.password。Chart 同样会拒绝在 config 下设置这些键。限流 Redis 的 URL 来自 rateLimit.redis。

extraEnvVars 只用于系统级环境变量(例如 TZ),以及独立模式资源文件所引用的变量。在其中设置的 AISIX_* 变量仍会覆盖配置文件。命名规则请参阅环境变量。

覆盖 Chart 自身设置的变量仍然有效,且 extraEnvVars 中的值仍然优先,但这只是为现有部署保留的兼容途径。对于 1.5.0 版 Chart 已渲染的变量名(例如 AISIX_MANAGED__CP_BASE_URL 或 AISIX_MANAGED__HEARTBEAT_INTERVAL_SECS),Chart 会把自己的条目保留在你的条目之前,因此容器中会出现两个同名条目,而 Helm 4 的 server-side apply 会拒绝这种情况。新安装请改为通过对应的 Chart value 或在 config 下设置该值,现有的覆盖也请迁移过去。参见将应用设置迁移到 Chart values,其中说明了为什么删除这类覆盖需要升级两次。