简介:Apache APISIX 是一个动态、实时、高性能的云原生 API 网关,面向微服务架构、Kubernetes 入口控制器及东西向/南北向流量管理,可解决接口统一入口、灰度发布、熔断鉴权、可观测等实际问题。资料包共收录 1129 个文件,大小仅 11.1MB,核心代码以 225 个 Lua 脚本为主,配合 237 个 Markdown 文档、334 个模板文件,以及 shell 脚本、Go 代码、证书密钥和 YAML 编排文件,构成一套完整的网关工程结构。Lua 脚本对应路由、插件与限流逻辑,Markdown 与模板文件辅助理解配置项和部署方式,预览中的核心配置文件则有助于梳理 APISIX 与 Nginx 的关系、启动参数及 TLS 细节。目前已有 520 人学习,适合云原生开发者、后端工程师或运维人员,借助这些源码与配置进行本地部署、二次开发或网关问题排查。
1. 云原生 API 网关选型:APISIX 凭什么被放进架构里
接手过一套跑在虚拟机上的老网关,Nginx 配置堆了几千行,上游节点要变更时靠 sed 脚本改文件再 reload,灰度发布只能在负载均衡层按 IP 切,每次上线都像拆炸弹。很多团队第一次接触 Apache APISIX 就是这种处境。APISIX 是 Apache 基金会下的云原生 API 网关,控制面和数据面分离,配置落在 etcd 里,改路由、开限流、挂鉴权全是调 API 的活,不需要 reload 进程,也不会断开存量连接。它适合正在迁 Kubernetes、拆微服务、想提高发布效率的后端和运维团队。这篇文章按「部署、插件、K8s、避坑、压测」的顺序,把能直接复现的命令和参数都写出来,新手能照做,熟手能拿去填坑。
2. 从零跑通 APISIX:Docker 部署与第一条路由
2.1 控制面与数据面分离意味着什么
APISIX 架构里,etcd 负责保存全部路由、上游、插件、SSL 证书配置,APISIX 节点本身不带持久化状态。管理员操作的 Admin API 监听在 9180 端口,数据面的流量入口是 9080(HTTP)和 9443(HTTPS)。修改一条路由,APISIX 通过 etcd 的 watch 机制拿到变更,新请求进来就按新配置转发,整个过程不 reload、不掉连接。这和 OpenResty 下改完 nginx.conf 必须 nginx -s reload 有本质区别,也是它被叫云原生网关的核心原因。
另一个容易被忽略的点:因为配置都集中在 etcd,多台 APISIX 节点天然共享同一份配置,扩容就是加节点,不需要逐台同步文件。做多可用区部署时,只要所有节点能连到同一个 etcd 集群,路由和插件配置就是一致的,这比维护多份 Nginx 配置省太多事了。
提示:Admin API 默认只监听 127.0.0.1,生产环境要绑定内网网卡并打开鉴权;演示环境用 Docker 映射出来方便调试。
2.2 用 Docker Compose 起一套最小环境
先准备 docker-compose.yml,这是最简可运行的组合:一个 etcd 加一个 APISIX。
services: etcd: image: bitnami/etcd:3.5 environment: - ALLOW_NONE_AUTHENTICATION=yes - ETCD_ADVERTISE_CLIENT_URLS=http://etcd:2379 - ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379 ports: - "2379:2379" apisix: image: apache/apisix:latest depends_on: - etcd ports: - "9080:9080" - "9180:9180" volumes: - ./config.yaml:/usr/local/apisix/conf/config.yaml:ro restart: alwaysETCD_ADVERTISE_CLIENT_URLS 必须写成服务名 etcd,而不是 localhost。容器里 localhost 指向容器自身,APISIX 容器访问 localhost:2379 会连到自己而不是 etcd。ALLOW_NONE_AUTHENTICATION=yes 只是演示用,etcd 不带鉴权,生产环境至少开 TLS 和账号体系。
APISIX 官方镜像的默认配置把 etcd 指向 127.0.0.1,不挂载配置文件无法在 Compose 环境里连上 etcd,所以这里必须挂一个最小 config.yaml:
apisix: node_listen: 9080 enable_admin: true admin_key: - name: admin key: edd1c9f034335f136f87ad84b625c8f1 role: admin etcd: host: - http://etcd:2379 nginx_config: error_log_level: warnadmin_key 是 Admin API 的访问凭证,这里用的是官方默认值,自己环境里要换掉。etcd.host 写成 http://etcd:2379,和 Compose 里的服务名对齐。保存后执行 docker compose up -d,等几秒后看日志确认两个容器都起来了。
2.3 创建第一条路由
环境起来后,通过 Admin API 创建一条路由,把 /hello 转发到两个后端节点。用 PUT 指定路由 id 是幂等操作,重复执行不会报错:
curl http://127.0.0.1:9180/apisix/admin/routes/1 \ -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" \ -X PUT \ -d '{ "name": "hello-route", "uri": "/hello", "upstream": { "type": "roundrobin", "nodes": { "192.168.1.10:8080": 1, "192.168.1.11:8080": 1 } } }'uri 字段定义匹配路径,这里做的是精确匹配,访问 http://网关/hello 才会命中这条路由。upstream 里 type 用 roundrobin 按权重轮询,nodes 是上游节点列表,值为权重,1 表示等权。实际部署时把 IP 换成你的后端服务地址。如果后端在 Docker 网络里,写容器服务名同样可行。
2.4 验证转发链路
路由建好后,用 curl 走数据面端口验证:
curl -i http://127.0.0.1:9080/hello看到上游返回的响应和响应头说明链路通了。如果返回 404,说明请求没匹配到任何路由,去 Admin API 查一下路由是否真的存在:
curl http://127.0.0.1:9180/apisix/admin/routes/1 \ -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1"返回 JSON 里能看到路由完整配置。这一步是最基础的链路验证,后面加插件、调权重都在这条路由上操作。
3. 插件体系实战:限流、鉴权、灰度发布
3.1 插件的执行时机与配置位置
APISIX 的插件机制是它区别于普通 Nginx 转发层的关键。插件挂在路由、服务、消费者或网关全局上,请求经过时按 OpenResty 的 phase 顺序执行:rewrite 阶段做改写,access 阶段做鉴权和限流,header_filter 和 body_filter 阶段处理响应。你只需要关心插件的配置项,不需要管底层 phase,APISIX 会按声明顺序调度。
配置插件的方式是在路由的 plugins 字段里写插件名和参数。同一个路由可以叠加多个插件,比如先 limit-count 限流,再 key-auth 鉴权,执行顺序由 APISIX 内部排序决定,大部分场景不用手动干预。下面三个例子都在第 2 章建好的路由上操作。
3.2 limit-count 限流:参数拆解与验证
接口防刷最常用的插件是 limit-count,固定窗口计数器。给 /hello 路由加一个每分钟 100 次的限制:
curl http://127.0.0.1:9180/apisix/admin/routes/1 \ -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" \ -X PATCH \ -d '{ "plugins": { "limit-count": { "count": 100, "time_window": 60, "rejected_code": 429, "rejected_msg": "too many requests", "key_type": "var", "key": "remote_addr" } } }'count 是窗口内允许的请求数,time_window 是窗口秒数,这里 100 次/60 秒。rejected_code 和 rejected_msg 定义超限后的响应。key_type 和 key 决定按什么维度计数,remote_addr 是按客户端 IP。用 PATCH 只更新 plugins 字段,不影响路由其他配置。
验证时直接用压测工具或者循环 curl 打 100 次以上,超过后返回 429。注意一条:如果所有请求从 Docker 宿主机的同一个出口 IP 过来,remote_addr 会全部相同,限流会按一个客户端算,这不是插件问题,是网络拓扑决定的。生产环境按消费者维度限流更合理。
3.3 key-auth 鉴权:Consumer 与路由两头配置
给内部服务加最简单的一把锁,用 key-auth 插件。它分两步:先创建 Consumer 定义调用方身份,再在路由上启用插件。
curl http://127.0.0.1:9180/apisix/admin/consumers \ -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" \ -X PUT \ -d '{ "username": "backend-service", "plugins": { "key-auth": { "key": "s3cr3t-7k9x" } } }'看这个请求:PUT 到 consumers 资源上,username 是消费者标识,plugins 里声明了它持有的 key。这个 key 就是调用方要带在请求头里的凭证。
然后给路由挂 key-auth 插件:
curl http://127.0.0.1:9180/apisix/admin/routes/1 \ -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" \ -X PATCH \ -d '{ "plugins": { "key-auth": {} } }'验证就很简单了:不带凭证访问会拿到 401,带上凭证才通:
curl -i http://127.0.0.1:9080/hello curl -i http://127.0.0.1:9080/hello -H "X-API-KEY: s3cr3t-7k9x"key-auth 默认从 X-API-KEY 这个 header 取凭证,在请求头里带上 Consumer 配置的 key 就能通过。如果返回 401,先检查 Consumer 是否创建成功,再确认路由上的插件字段是否真的写进去了。
3.4 灰度发布:权重分流和条件分流两种做法
APISIX 做灰度发布不需要动代码,也不需要改 DNS。做法一:给上游节点配权重,慢慢把流量切到新版本。先建一个 upstream:
curl http://127.0.0.1:9180/apisix/admin/upstreams/1 \ -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" \ -X PUT \ -d '{ "name": "v1-v2-canary", "type": "roundrobin", "nodes": { "192.168.1.10:8080": 90, "192.168.1.20:8080": 10 } }'90 和 10 表示新旧版本按 9:1 分流量。发布时逐步调整权重,比如从 1 调到 5 再调到 9,每次调整都是秒级生效,这就是云原生网关相对传统 reload 式网关最有体感的地方。
做法二:按请求条件分流,比如只让 iOS 客户端走新版本。创建一条带 vars 匹配的路由:
curl http://127.0.0.1:9180/apisix/admin/routes/2 \ -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" \ -X PUT \ -d '{ "name": "ios-canary", "uri": "/api/*", "vars": [ ["http_user_agent", "~~", ".*iOS.*"] ], "upstream": { "type": "roundrobin", "nodes": { "192.168.1.20:8080": 1 } } }'vars 是一个条件表达式数组,["http_user_agent", "~~", ".iOS."] 的含义是:取请求头 User-Agent,用正则匹配 iOS 关键字。命中条件的请求会优先走这条路由,其余流量走 /api/* 的默认路由。条件分流比权重分流更精细,适合按灰度人群验证功能。
4. 接入 Kubernetes:用 APISIX Ingress 管理南北向流量
4.1 为什么云原生集群里要一个独立的网关层
Kubernetes 的 Service 只解决集群内负载均衡,外部流量进来要么 NodePort 要么 LoadBalancer,这些层都不具备路由、鉴权、限流能力。传统做法是 Nginx Ingress Controller 把 Ingress 资源翻译成 Nginx 配置,但配置变更后 reload 模式在流量波动大的集群里很痛苦。APISIX Ingress 的思路是把 Ingress 和自定义 CRD 翻译成 APISIX 配置写入 etcd,让数据面做真正的流量治理。集群里的东西向流量继续走 Service,南北向统一收口到 APISIX。
这套架构里还有个隐性好处:网关规则和业务部署解耦。业务团队能通过 ApisixRoute 自己管理路由,平台团队只需要保证 APISIX 集群本身高可用,不需要为了某条新路由去 reload 网关,发布风险和权限边界都更清楚。
4.2 部署架构:Controller 与数据面各管一边
集群里要部署三块:etcd 集群、APISIX 数据面、apisix-ingress-controller 控制面。最常见的方式是用官方 Helm Chart 一次性拉起,Chart 会负责 etcd 连接配置和 RBAC。部署完成后,数据面以 Deployment 方式运行,外部流量通过 NodePort 或 LoadBalancer 进入。
Ingress Controller 做的事本质上是消息同步:它 watch Kubernetes 里的 Ingress、ApisixRoute、ApisixUpstream 等资源的变化,转成 APISIX 的配置写入 etcd。你在集群里 kubectl apply 一个新路由,Controller 几秒内把它同步到网关配置里,数据面自动生效,Pod 不用重启。控制面和数据面分开部署也让扩容更自由,数据面压力大就多拉几个 APISIX 副本,Controller 只做配置同步,负载很低。
4.3 用 ApisixRoute CRD 定义一条路由
CRD 方式比 Ingress 注解表达力强,限流、鉴权这些直接写在资源里。下面是一条完整的 ApisixRoute:
apiVersion: apisix.apache.org/v2beta3 kind: ApisixRoute metadata: name: demo-route namespace: default spec: rules: - host: demo.example.com http: - name: demo-http match: paths: - /api/* methods: - GET - POST backends: - serviceName: demo-service servicePort: 8080host 限定域名,paths 支持前缀匹配,/api/* 表示所有以 /api/ 开头的路径。methods 限制方法,不写表示全部允许。backends 对应 Kubernetes Service,serviceName 写 Service 名,servicePort 写端口。apply 这个文件后,访问 demo.example.com/api/ 下的 GET 和 POST 请求就会被转发到 demo-service。
如果还是习惯用原生 Ingress,APISIX Ingress Controller 也兼容标准 Ingress:
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: demo-ingress annotations: kubernetes.io/ingress.class: apisix spec: rules: - host: demo.example.com http: paths: - path: /api pathType: Prefix backend: service: name: demo-service port: number: 8080关键差别是注解里的 ingress.class 要写成 apisix,这样 Controller 才会接管这条 Ingress,否则会被 Nginx Ingress Controller 抢走。标准 Ingress 能覆盖大部分基础转发需求,但限流、灰度这种高级能力还是 ApisixRoute 更直接。
4.4 从 Nginx Ingress 迁移时最容易改错的点
迁移到 APISIX Ingress 时,第一件要改的是重写规则。Nginx Ingress 常用的 rewrite-target 注解在 APISIX 里不生效,路径改写需要在 ApisixRoute 里挂 filters 或者用正则路由的 capture 能力实现。同理,nginx.ingress.kubernetes.io/auth-type 这类注解也不能平移,鉴权要改成 APISIX 插件或 Consumer 配置。
第二件容易踩坑的是证书管理。Nginx Ingress 用 TLS secret 字段指定证书,APISIX Ingress 这边要么用 ApisixTls CRD 把证书配置到网关,要么让 Controller 自动从 Secret 同步。直接改 secret 内容不会自动触发网关重新加载,需要通过 Controller 的同步机制,这个在官方文档里写得很细,迁移前先把证书方案定下来。
第三件是上游策略。Nginx Ingress 默认轮询,APISIX 里可以通过 ApisixUpstream 定义健康检查、熔断、重试策略。如果原来依赖 Nginx 的 upsteam 长连接保持,迁移后要在 upstream 层显式配置 keepalive_pool,不然性能表现会和你预期的差一截。
5. 避坑指南:五个让新手翻车的 APISIX 现场
5.1 容器里的 etcd 连接失败
现象:docker compose 把 etcd 和 APISIX 都拉起来了,但 APISIX 日志不停刷 etcd 连接超时,Admin API 写路由也报错。
原因:官方镜像默认配置里 etcd 指向 127.0.0.1:2379,在容器里 127.0.0.1 是 APISIX 容器自己,根本不是 etcd。
解决:挂载自定义 config.yaml,把 etcd.host 改成 compose 服务名 http://etcd:2379,重新创建容器。这个坑几乎每个刚接触 APISIX 的人都会踩一次。
5.2 改完路由却不生效
现象:用 curl 调 Admin API 返回 200,但请求 9080 端口时转发结果还是老样子。
原因:把 9080 当成了 Admin API 端口,PUT 的请求打到数据面端口上,配置根本没写进 etcd。或者修改的是另一个节点的 Admin API。
解决:先确认 Admin API 地址是 9180 端口,再确认 docker 端口映射是否正确。改完用 GET /apisix/admin/routes/1 读一次配置,确认 plugins 和 upstream 字段都在,这个排查习惯能省很多时间。
5.3 限流范围比自己想的大
现象:配置 limit-count 后,压测一开始所有客户端请求全部 429,明明 count 设得够高。
原因:key 用了 remote_addr,而所有压测请求都从同一个出口 IP 过来,限流维度实际是整个网段,不是单个客户端。
解决:生产环境换 key_type 为 consumer 或 header 里的真实客户端标识。docker bridge 网络下要特别注意源 IP 聚合问题,这不是配置错误,是网络拓扑对限流维度的天然影响。
5.4 key-auth 配了还能直接访问
现象:路由上的 plugins 里已经加了 key-auth,但不带 header 请求照样打到上游。
原因:大概率是 PATCH 请求把插件写进了别的路由 id,或者修改的是一个不匹配该路径的路由。APISIX 配置同步是秒级的,基本不存在缓存延迟背锅的情况。
解决:用 GET 把这条路由完整读出来,确认 plugins 字段里 key-auth 确实在;再看请求的路径、host 是否命中了这条路由而非其他泛化路由。排查时把 Admin API 的返回和实际请求路径放一起看,别只盯修改动作本身。
5.5 证书更新后还是旧证书
现象:换了域名证书,HTTPS 请求返回的还是旧证书,客户端报警过期。
原因:APISIX 的 SSL 资源存在 etcd 里,通过 Admin API 的 /apisix/admin/ssl 管理。直接替换宿主机上挂载的 pem 文件不会生效,因为 APISIX 压根不读那个文件。
解决:用 PUT /apisix/admin/ssl/{id} 更新证书内容,或者通过 ApisixTls CRD 管理。更新后用 openssl s_client 或 curl -v 查看证书指纹,确认服务端确实换了新证书,这步验证别省。
6. 压测与调优:用 JMeter 验证网关能扛多少并发
写完配置只是开始,网关到底能扛多少并发、瓶颈在哪,都要靠压测说话。压测工具我常用 Apache JMeter,它有个优点是可以直接在聚合报告里看到 90% 线、95% 线和 99% 线,对网关这种对尾延迟敏感的场景正好够用。
测试计划不用建得太复杂:线程组里线程数设 200,Ramp-up 时间 10 秒,循环次数 100,HTTP 请求默认值写网关地址。跑完之后重点看三个指标:吞吐量、错误率、99% Line 响应时间。错误率不为 0 时,先别急着调优,去上游服务日志确认是不是后端被压垮了。很多时候瓶颈根本不在 APISIX,而在你的业务服务。
压测前先做一步配置调整,打开 APISIX config.yaml 里的 worker_processes:
nginx_config: worker_processes: auto events: worker_connections: 16384worker_processes 设 auto 会让每个 CPU 核跑一个 worker,避免单核瓶颈。worker_connections 决定每个 worker 最多同时承接多少连接,压测并发高的时候默认值 1024 不够用,我一般生产环境调到 16384 以上。改完重启 APISIX 再压,你会发现吞吐量和尾延迟都有明显变化。
上游连接复用也要记得配。APISIX 转发到后端服务时,如果每个请求都新建 TCP 连接,压测时你会看到大量 TIME_WAIT,延迟也飙升。在 upstream 上开 keepalive_pool:
curl http://127.0.0.1:9180/apisix/admin/upstreams/1 \ -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" \ -X PATCH \ -d '{ "keepalive_pool": { "size": 256, "idle_timeout": 60 } }'size 是连接池最多保持多少条到后端的复用连接,idle_timeout 是空闲回收时间。数值要看后端容量来定,池子开太大后端撑不住,开太小复用效果不明显。压测时观察后端连接数,如果一直打满说明池子开小了。
我自己调网关有个习惯:压测不只盯平均响应时间,更盯 99% 线。平均时间好看但尾延迟炸掉的网关,线上高峰期一定会出问题。有一次我把一个服务从 OpenResty 迁到 APISIX,压测平均响应时间只差几毫秒,但 99% 线从 80ms 飙到 300ms,最后排查下来就是 keepalive 没配,连接建造成本全堆到了尾请求上。调完再进行第二轮压测,数据才恢复正常。
压测这件事没有一次跑完就收工的说法:先把 worker_processes 调成 auto,再配好 upstream 连接池,跑一轮看基线,记录 p99 和错误率,每改一个参数就压一轮对比。APISIX 的好处是配置热更新,调参不用重启,一轮一轮验证的成本很低。希望这些参数和排查思路能帮你在自己的环境里少走几步弯路。
本文还有配套的精品资源,点击获取