国内航空巨头如何从 NGINX 迁移至 APISIX?

作者 | 卞弘智

本文主要介绍了航空公司互联网能力持续提升的大背景下,国内某大型航空公司移动互联网基础架构团队针对南北向网关从 NGINX 升级到 APISIX 的历程。

深入解析 NGINX 网关的痛点

我们一直使用 NGINX 作为南北向网关。随着业务规模的发展和产品的增加,从流量的角度来说,NGINX 仍然能很好地满足我们的需求,然而,在其他方面我们遇到了越来越多的痛点:

  • 多节点配置管理:我们管理着多台 NGINX 和多套不同的域名,NGINX 的核心是其配置文件。随着 NGINX 数量的增加,单纯依靠 scp 复制文件的方式来管理 NGINX 配置文件已经变得越来越困难,尤其是在后端大量使用微服务、容器化部署后,对反向代理配置灵活度要求更高,配置一致性工作量也越来越大。
  • NGINX 及其插件的升级:由于历史原因,我们同时在使用多个版本的 NGINX ,同时还使用了很多 NGINX 的插件。每次需要升级时,NGINX 自身的升级并不困难,难点在于各种插件的升级、编译和适配,因为很多是非官方插件,编译时遇到各种问题排查起来非常困难,并且与 NGINX 版本的适配也得不到保障。
  • NGINX 配置的标准化:由于我们接手来自不同团队的系统,各自使用的 NGINX 配置习惯不尽相同,缺乏统一的配置标准。
  • 现代网关功能不足:虽然 NGINX 很好地满足了我们对南北向网关的基本需求,如反向代理和负载均衡等,但随着业务发展,我们对南北向网关提出了更多的需求,例如服务熔断、安全防控、灰度发布等,单纯依赖 NGINX 实现这些功能并不容易。

剖析网关选型,精准匹配业务需求

针对 NGINX 所遇到的各种痛点,我们认真梳理了对新网关产品的三个主要基本需求:

  • 易于管理和配置:我们需要在多个网关节点的场景下,方便而统一地管理和发布路由、上游服务等各种配置。
  • 满足现代 API 网关基础需求:新网关必须能实现现代 API 网关的业务需求,如服务熔断、安全防控、灰度发布等。
  • 易用性和上手门槛:鉴于我们团队成员人数有限,并且中间件领域没有专职的开发和运维人员,我们希望新网关产品能通过配置和低代码的方式,让我们轻松完成大部分基本需求。

在明确了对现有南北向网关的迭代升级和基本需求之后,我们对市面上流行的多款产品进行了调研,并最终选择了 APISIX 作为我们的新网关。

  • OpenResty: 在调研过程中,我们首先考虑了 OpenResty,它被很多大厂,例如 Bilibili,广泛采用作为网关。对于我们来说,OpenResty 最大的优点是其配置文件与 NGINX 完全兼容。由于我们有大量复杂的域名配置,而且有些配置相当复杂,部分网关从 NGINX 升级到 OpenResty 时,能实现无缝衔接,只需复制配置文件即可使用。然而,相较于我们后面调研的 Kong 和 APISIX,OpenResty 的开源版本默认自带的插件不够完善,也没有可视化的配置界面,我们需要自己进行 Lua 开发来满足部分基本功能。
  • Kong:其次,我们对 Kong 进行了初步的调研。Kong 的默认自带插件可以满足我们大部分需求。但是,它的开源版本的可视化界面,即 Dashboard,基本几年内没有更新。尤其是在了解了 APISIX 后,我们更希望能够拥有一个可视化界面,以简化配置过程。
  • Envoy:我们还留意到了 Envoy,尤其在网易和阿里相继发布基于 Envoy 的下一代网关之后。Envoy 基于 C++,对于我们这样规模的团队来说还存在较高的门槛,因此我们最终没有选择它。

最终,我们决定采用 APISIX 作为新的网关产品,因为它在功能和性能上均得到了市场的认可。根据测试结果,APISIX 在压力测试下的性能表现相当出色,在没有开启插件的情况下,其性能是 Kong 的 2 倍;而在开启限流和 prometheus 插件后,性能甚至高出 Kong 的 10 倍,延迟仅为 Kong 的十分之一。此外,APISIX 基于 OpenResty 实现,拥有出色的路由功能,进一步增加了我们的信心。另外,与 Kong 相比,APISIX 吸引我们的特点主要集中在以下两个方面:

  • APISIX Dashboard:基于 Dashboard,我们能更便捷地管理各种路由和插件的配置。特别值得一提的是,APISIX Dashboard 本身就是开源项目的一部分,我们相信未来会随着 APISIX 的发展而持续更新,为我们提供更好的管理体验。
  • Apache 开源项目:APISIX 作为 Apache 软件基金会的顶级项目,相较于 Kong,我们更容易在网络上搜索到相关的技术文档。在遇到各种问题时,我们能够得到 Apache 社区的支持,与其他开发者共同解决疑难问题,为我们的项目提供更加可靠的技术支持。

在前面提到的关于 NGINX 的痛点,也能够通过 APISIX 很好地解决:

  • 多节点配置管理:APISIX 将配置存储于 etcd 中,因此我们只需要部署一套 etcd 集群即可方便地管理多个不同域名的多个 APISIX 节点。
  • NGINX 及其插件的升级:APISIX 内置了常用的健康检查等插件,与 NGINX 中常用的插件相同,这使得我们无需再考虑升级和兼容性等问题。
  • 现代网关的功能:APISIX 自带多种安全性和流量控制插件,轻松实现服务熔断、安全防控、灰度发布等功能。总体来说, APISIX 是对我们团队现阶段最适合的产品。

NGINX 迁移 APISIX:探索先进解决方案

在 NGINX 中所有的域名管理以及其上的功能实现都是基于 NGINX 的配置文件来实现的。虽然 APISIX 仍然基于 NGINX 和 Openresty,但在 APISIX 中,我们采用了完全不同的方式,不再使用 NGINX 的配置文件来管理域名和实现功能。我们首先根据域名配置路由(route)和其上游(upstream),然后通过插件的形式实现路由上的各种附加功能。

从 NGINX 向 APISIX 的迁移的主要工作是将 NGINX 的各路由以及相关配置用 APISIX 的相关插件进行重构。实际迁移过程远比前面一句话更复杂,我们将这个迁移过程拆分为以下三个主要步骤:

  1. 提取独立功能配置:首先从 NGINX 配置中提取出一个独立的功能配置,并深入理解其配置的含义。
  2. 网络协议层面理解配置:其次,需要从网络协议层面理解相关配置,并借助各种网络工具进行验证测试。
  3. 寻找合适的插件实现功能:最后,在 APISIX 中找到适合的插件来实现与 NGINX 配置相同的功能,并再次通过各种网络工具进行验证测试,以确保 NGINX 和 APISIX 能够实现相同的效果。

这里我们以一个 CORS 跨域相关的配置来进行举例,我们先把 NGINX 中跨域相关配置提取出来。

代码语言:javascript
复制

    add_header 'Access-Control-Allow-Origin' $corsHost;
    add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS';
    add_header 'Access-Control-Allow-Credentials' 'true';
    add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Accept,Authorization,appver';
    if ($request_method = 'OPTIONS') {
            return 204;
     }

首先理解这段 NGINX 的含义,用 add_header 在 NGINX response 中添加了一些关于跨域配置 header,然后如果 request 方法为 OPTIONS 的话就直接返回 204。在赋值的时候,这里我们还使用了一个变量 corsHost,其定义如下:

代码语言:javascript
复制
    map $http_origin $corsHost {
        default 0;
        "~http://wap.test.com" http://wap.test.com;
        "~https://wap.test.com" https://wap.test.com;
    }

再从网络层面去理解下这段 NGINX 配置的含义,这一段配置其实是为了实现 CORS 跨域访问, 允许来自于 Access-Control-Allow-Origin 源的请求能够进行跨域访问,请求的方法由 Access-Control-Allow-Methods 定义,同时定义了允许的 Header 和请求可以包含 Cookie 等 credentials 信息。Options 返回 204 是 CORS 的预检要求的。更详细的信息可以在 mozilla 的网站上查看相关定义:https://developer.mozilla.org/。

要将这段 NGINX 的配置,在 APISIX 中进行实现,我们并不需要一行行的进行配置转换,而是可以基于 APISIX 的 cors 插件进行实现:

代码语言:javascript
复制
    "cors": {
      "allow_credential": true,
      "allow_headers": "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Accept,Authorization,appver",
      "allow_methods": "GET,POST,PUT,OPTIONS",
      "allow_origins": "https://wap.test.com,http://wap.test.com,",
    },

针对 OPTIONS 请求返回 204 这部分配置,我们使用了 response-rewrite 插件来实现:

代码语言:javascript
复制
"response-rewrite": {
    "status_code": 204,
    "vars": [
        [ "request_method",
            "==",
            "OPTIONS"
]
    ]
}

在 APISIX 中使用 cors 插件和 response-rewrite 插件实现了这段配置之后, 我们可以使用浏览器自带的网络工具进行迁移后的测试验证:

从截图可以看到,针对 Options 返回 204,在响应 Header 中也配置好了 Access-Control-Allow-Origin 等要求的值。

APISIX 和 NGINX 配置对比

这里我们直接对比下 NGINX 和 APISIX 的配置代码:

代码语言:javascript
复制
#   NGINX  conf
    add_header 'Access-Control-Allow-Origin' $corsHost;
    add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS';
    add_header 'Access-Control-Allow-Credentials' 'true';
    add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Accept,Authorization,appver';
    if ($request_method = 'OPTIONS') {
            return 204;
     }
代码语言:javascript
复制
#  APISIX  plugins config
    "cors": {
      "allow_credential": true,
      "allow_headers": "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Accept,Authorization,appver",
      "allow_methods": "GET,POST,PUT,OPTIONS",
      "allow_origins": "https://wap.test.com,http://wap.test.com,",
    },
    "response-rewrite": {
      "status_code": 204,
      "vars": [
        [
          "request_method",
          "==",
          "OPTIONS"
]
      ]
    }

NGINX 的配置看起来更加简洁,但对于不熟悉 NGINX 和跨域的人来说,理解其背后的含义可能并不容易。相比之下,APISIX 对不同的业务功能进行了插件封装,使得配置更加模块化,基本上一眼就可以看出其实现的业务功能和背后的含义。

类似的 NGINX 的配置向 APISIX 进行迁移的代码案例我们还有很多,例如在 NGINX 中 websocket 协议需要进行如下配置:

代码语言:javascript
复制
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection ""Upgrade"";
proxy_http_version 1.1;

而在 APISIX 就进行了封装,非常简单明了:

代码语言:javascript
复制
  "enable_websocket": true,

成功迁移后的回顾与展望

自从我们在 2023 年 4 月份首次接触 APISIX,到 7 月份在生产环境成功完成了从 NGINX 到 APISIX 的升级,整个迁移过程取得了令人满意的成果。在迁移初期,由于接手的生产 NGINX 存在各种历史遗留配置,有些甚至不清楚其真正意义,我们曾对 APISIX 的插件能否完全实现我们现有 NGINX 的所有功能感到担心。但最终的结果表明,APISIX 的插件完全胜任这一挑战。

NGINX 向 APISIX 迁移的核心是重新在 APISIX 中实现 NGINX 配置文件,这并不是一行 APISIX 配置对应一行 NGINX 的配置转换。我们需要深入理解相关 NGINX 配置模块背后的含义。在 APISIX 中,往往可以通过插件实现更加优雅的解决方案,例如跨域支持(cors)、WebSocket 等。

在整个升级过程中,我们发现在原有的 NGINX 中存在不少上古配置,其中很多地方甚至是毫无意义的复制粘贴配置。这次升级的过程也是对我们整个南北向网关的一次全面梳理,特别是基于 APISIX 的 plugin_config 等功能,我们在网关配置层面更容易实现模块化的管理和复用。

总体来说,APISIX 完美地解决了我们之前在 NGINX 中遇到的各种痛点,其丰富的插件使我们能够轻松应对客户端提出的各种新需求。在迁移过程中,社区小伙伴的大力帮助也为我们解决了一些故障,对此我们表示衷心的感谢。

作者简介

卞弘智:国内某大型航空公司研发工程师,拥有超过十年的 SRE 经验,工作经历涵盖 DevOps、监控和告警系统、日志处理系统、WAF 和网关等系统基础架构领域,致力于通过优秀的开源软件推动自动化和智能化基础架构平台的演进。