SegmentFault Apache APISIX 技术专栏最新的文章
2023-04-18T17:56:03+08:00
https://segmentfault.com/feeds/blogs
https://creativecommons.org/licenses/by-nc-nd/4.0/
API7 助力头部券商实现数字化转型
https://segmentfault.com/a/1190000043691622
2023-04-18T17:56:03+08:00
2023-04-18T17:56:03+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<h2>背景</h2><p>随着中国经济步入高质量发展阶段,数字化转型正在被更多的企业提上议程。2021 年证监会出台的《证券期货行业科技发展“十四五”规划》中指出,需“提升证券期货业数据治理水平”,“深化数字化转型标准建设,推动行业数据接口的互联互通,防止技术垄断,降低信息壁垒”。</p><p>国内某头部券商为应对行业内外在业务层、数据层和流量层愈发激烈的竞争,迫切需要进行通过提升其对 API 的注册管理、认证授权,以及限流、熔断等公共功能的支持来实现数字化转型。API 管理是技术转型中的重中之重 ,API 管理的问题成为了亟待解决的首要难题。经过严格的技术选型,该头部券商最终选择了以新一代云原生 API 网关 APISIX 为基础的 API7 企业版,解决了业务侧、技术侧和管理侧一系列的难题。</p><h2>统一 API 网关,提升业务管理效率</h2><p>在该头部券商内部,不同业务线使用着众多复杂且分散的 NGINX/OpenResty/Kong 服务,管理成本、风险一直较高,维护起来比较困难。使用 NGINX 配置时需要手动填写配置文件,流程冗长,上下线管理复杂,人员维护方式和效率均不佳。尤其是在运维人员流动的情况下,经常出现问题难处理的情况。而且 NGINX 的配置无法动态生效,单点的修改对整个系统的影响很大。</p><p>总结来说,主要有以下痛点:</p><ul><li>众多底层逻辑不一的服务复杂且分散,管理成本和风险很高</li><li>各系统之间定制功能差异较大,无法进行统一维护</li><li>审批严格且环节多,且各个环节的操作对整体流程影响较大</li><li>无任何系统和数据隔离的能力,业务线之间存在相互影响的可能</li><li>无统一监控、告警、鉴权、灰度等方案</li><li>以开放证券为大目标,数据安全存在隐患</li></ul><p>因而该头部券商通过使用统一 API 网关平台,为其对内对外的信息系统提供统一 API 网关服务,包含的服务有公共的 API 管理、路由、负载均衡、访问控制、认证控制、流量控制、熔断、接口编排、协议转换、日志、统计、监控等功能。从而使 API 基础功能与业务功能分离实现,基础功能更强大,让业务开发更专注于业务,提升效能,促进相关信息系统建设完善。</p><h2>业务选型最佳实践 — API7 企业版</h2><p>为解决该券商的痛点,在新网关技术选型中,该证券严格审查了 API7 企业版在高可靠、高性能、低延时、部署简单、支持水平扩容等方面的要求。希望新的 API 网关:</p><ul><li>能逐步替换各类使用 NGINX/OpenResty/Kong 等开源 API 网关,作为互联网 API 网关</li><li>具备完善的管理后台,方便对 API 网关进行管理维护</li><li>具备可维护性及可扩展性,方便后续进行功能定制</li></ul><p>那么,API7 企业版到底是什么样的 API 网关?它又是如何轻松解决如此多且复杂的问题,满足该头部券商的需求,在众多的 API 产品中脱颖而出的呢?</p><p>APISIX 是全球性能最好、社区最活跃、最开放的云原生 API 网关。内置近百开源插件开箱即用,支持多语言方式满足企业二次扩展。API7 企业版是基于 Apache APISIX 构建的 API 网关产品,是能为用户提供生产可用的七层全流量处理平台,提供审计日志、SSL 证书管理、权限控制、统计报表等多项企业级 API 统一管理功能。</p><p>它有以下技术特性:</p><ul><li>云原生架构,强大的扩展能力</li><li>顶级性能、高稳定性,强大的流量治理能力</li><li>支持多语言开发,支持多协议</li><li>多集群、多租户</li><li>全动态加载</li><li>国密支持</li></ul><p>为了加速完成公司数字化转型的大目标,实现降本增效,该券商选择使用 API7 企业版,为其提供公共的 API 管理、路由、访问控制、认证控制、流量控制、接口编排、熔断、协议转换、日志、统计、监控等功能。</p><p>目前,该头部券商已在用户生产环境运行 API7 企业版,并且已将其接入最复杂业务。</p><p><img src="/img/remote/1460000043691624" alt="该券商技术架构图" title="该券商技术架构图"></p><h2>实践成果</h2><p>该头部券商在使用 API7 企业版后,取得了显著的成效。</p><p><strong>1. 搭建完善的管理后台,完成了开发、维护和管理上的统一。</strong></p><p>以前开发、维护和管理流程涉及人力多,流程繁杂,重复管理浪费资源等问题都得到了解决,使用 API7 企业版统一管理,节约了资源,简化了流程。同时,增加了统一的监控、告警、鉴权、灰度等方案,提高了系统的可用性。</p><p><strong>2. 优化管理制度,优化了用户权限、调度审批的制度与流程,加快业务开发进度,杜绝单点依赖。</strong></p><p>每次配置的更新均需要相关负责人审批才能执行,审批流程长,执行效率低,风险大,且人工出错概率更大,无形之中浪费了时间成本。API7 企业版的可视化特性让查看和确认配置变得方便了起来,界面操作不再需要审批,跳过了繁复的流程,提升了整个流程的效率,减少了出错的可能,也节约了很多人力成本。</p><p><strong>3. API7 企业版产品作为统一 API 网关平台后,在提升了运作效率的同时也能保证高可用性。</strong></p><p>具体体现在:</p><ul><li>系统具备可测试性</li><li>随系统提交的技术文件明确标识出所实现的功能</li><li>7×24 小时持续可靠的运行</li><li>系统支持动态负载均衡</li><li>系统提供报警功能,能对整个系统中出现的任何异常状况进行报警</li></ul><p><strong>4. 证券行业的加密信息得到更安全的保障。</strong></p><p>“大数据是工业社会的‘自由’资源,谁掌握了数据,谁就掌握了主动权。”随着企业数字化转型的浪潮,数据已成为金融行业的核心资产和创新要素。对于证券行业来说,数据安全成为了至关重要的一环。SSL 证书是一种服务器证书,支持向用户验证服务器,以及对服务器与用户之间所传输的数据进行加密。API7 企业版提供 SSL 证书有效保障了该券商的数据安全性。</p><p><strong>5. 具备可维护性及可扩展性,方便后续进行功能定制。</strong></p><p>API7 企业版在系统设计上支持插件功能,以方便对部分功能进行定制。同时有完善文档、SDK、样例等以支持插件自定义开发。除此之外,API7 企业版支持通过 HTTP 管理接口对功能进行查询或配置。</p><p><strong>6. 实现多“租户”隔离,让业务能够独立管理系统,进一步提升内部效率;同时也实现了集群的统一管理。</strong></p><p>API7 企业版帮助该券商实现了多租户隔离,加强了系统的稳定性和内部系统数据的安全性。同时实现了集群的统一管理,极大地提升了内部效率。</p><h2>总结</h2><p>立足思维开放、业务开放、技术开放三个“开放”,券商需要持续保持对新技术的敏感与关注,通过使用统一的 API 网关平台,提升业务间交互的敏捷性、稳定性、安全性,增加跨业务、跨公司乃至跨行业的合作潜力。</p><p>API7 企业版能帮助提升企业极大地提升 API 管理的效率,进而提升公司的效率,从而让企业在激烈的竞争中立于不败之地。该头部券商与 API7.ai 的合作充分体现了数字化大背景下技术增长的互惠互利,科技在服务金融的过程中,金融也反向驱动了科技,实现行业整体效率的提升,为终端用户带来无尽便利。</p>
2023 年十大 API 管理趋势
https://segmentfault.com/a/1190000043652716
2023-04-12T16:54:36+08:00
2023-04-12T16:54:36+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<p>本文探讨了 API 管理在数字化转型中的重要性,以及 API 管理面临的挑战和发展机遇。文章重点介绍了十大 API 管理发展趋势,包括 API 安全性、API 标准化、云端 API 管理解决方案、低代码 API 平台、API 市场、新兴 API 协议、人工智能与 API、开发者体验、API 分析和无服务器架构等。通过关注这些趋势,企业能更好地应对未来挑战,抓住发展机遇,实现业务的持续增长和创新。</p><blockquote>作者郑玩星,API7.ai 技术工程师。</blockquote><p><a href="https://link.segmentfault.com/?enc=AkIFRvrlrwMeINf4kajUeg%3D%3D.JyoF7eFv3nGt4PKuiW9hzAlc%2B4UVT7xXSB2MvXBMkrrdrZ66EsiNm1%2BMp%2BP09jAYAoCgepiTGxuk07UsEMkZgg%3D%3D" rel="nofollow">阅读原文</a></p><h2>什么是 API?什么是 API 管理?</h2><p>近期,AIGC(AI Generated Content,生成式人工智能)在各行业的应用日趋普及。AIGC 服务提供商通过 API 向外部提供其内容生成能力,使得用户能够便捷地获取 AIGC 应用相关内容。显然,API 成为 AIGC 应用的重要支柱。那么,究竟什么是 API 呢?</p><p>API(Application Programming Interface,应用程序接口)是一系列预先定义的规则和约定,目的是用于不同软件应用之间的通信。API 使得一个软件应用程序(即客户端)能够请求另一个软件应用程序(即服务器)的功能与数据,从而促成不同系统间的互动和数据共享。借助 API,开发者能够利用其他应用程序的功能,从而更快速地构建和发布新应用。</p><p>API 管理包括创建和发布 API、制定使用策略、控制访问权限、培养用户社区、搜集与分析使用统计数据以及报告性能等过程,通常包含 API 网关、开发者门户等组件。其中,API 网关作为关键组件,负责处理和转发请求,同时执行安全和性能策略,而开发者门户则是一个在线平台,为开发者提供 API 文档、密钥管理和其他相关资源。</p><p>随着企业日益依赖 API 推进数字化转型,API 管理的重要性空前提升。在简要介绍了相关概念之后,接下来我们将探讨 API 管理的十大发展趋势。</p><h2>一、API 安全性越来越重要</h2><p>API 安全性是指保护应用程序和系统之间通过 API 交换数据和功能的过程。API 安全性的主要目标是确保数据和功能的正确性、可靠性和私密性,防止未经授权的访问和潜在的恶意攻击。API 安全性对于现代应用和企业服务至关重要,因为它们大量依赖于 API 进行数据交换和集成。以下是 API 安全性重要的几点原因:</p><ol><li><strong>数据保护</strong>:API 通常用于传输敏感数据,如用户信息、交易细节和支付信息。确保 API 安全性可以防止数据泄露、篡改和丢失,保障用户和企业的信息安全。</li><li><strong>系统完整性</strong>:通过确保 API 只能被合法用户和合规应用访问,可以维护系统完整性。这有助于防止恶意攻击者通过 API 破坏或控制系统。</li><li><strong>信任和声誉</strong>:一个安全的 API 可以提高用户对企业服务的信任度,有助于建立良好的品牌声誉。相反,如果 API 安全性不足,可能导致企业声誉受损,用户流失。</li></ol><p>为了保障 API 的安全性,通常我们可以利用 API 网关来管理安全功能,例如身份验证和访问控制,以保护 API 免受未经授权的访问和攻击。市场上已有许多 API 网关提供这些功能,其中之一便是 Apache APISIX。<a href="https://link.segmentfault.com/?enc=fCX0jtXG0PnXibsi5W%2BNRQ%3D%3D.jIlcxMYkG%2BFIFplaNMgcuIPpQHn1hNT6laQORUSPF8Y%3D" rel="nofollow">Apache APISIX</a> 是 Apache 软件基金会下的云原生 API 网关,兼具动态、实时、高性能等特点。它提供了一系列安全功能,以确保 API 的安全性。例如,Apache APISIX 支持通过 <code>key-auth</code>、<code>jwt-auth</code> 等插件进行身份验证,以及通过 <code>consumer-restriction</code> 等插件进行访问控制。这些功能帮助企业防止数据泄露,保护用户隐私及企业利益。</p><h2>二、API 标准化日益重要</h2><p>随着 API 的广泛应用,API 标准化变得越来越重要。以下是 API 标准化的几大好处:</p><ol><li><strong>促进组织内部的协作和沟通</strong>:让不同团队和部门遵循统一的设计原则和规范,提高开发效率和质量。</li><li><strong>增强 API 的安全性和稳定性</strong>:通过定义清晰的接口、数据结构和协议,防止错误或滥用的情况发生。</li><li><strong>提升 API 的可扩展性和互操作性</strong>:通过遵循行业或社区认可的设计指南或最佳实践,使 API 能够适应不同的场景和需求。</li></ol><p>在 API 标准化过程中,常见的 API 标准规范有 <a href="https://link.segmentfault.com/?enc=tIMNiBdI%2BlonDcfhtVyulw%3D%3D.BDtRg9lq2PJD02IEqeYRWuoB8lJQQD8ndiMis45tecgt8GSS867NaTLkEJ0Gegxs" rel="nofollow">OpenAPI Specfication</a>。许多工具和平台都支持这种规范,以方便用户导入和管理 API。例如,<a href="https://link.segmentfault.com/?enc=vJROJxm%2BZADiLMIhXGf8OQ%3D%3D.7KgZzt0kaspxi%2B0CIHKGqqwt2NvmkCbZAcx%2FVE28P0kygu%2FjSIxOz7FsL25J5SBQ" rel="nofollow">Apache APISIX Dashboard</a> 就可以通过 OpenAPI 文档进行导入相关路由数据。</p><p><img src="/img/remote/1460000043652718" alt="import routes data" title="import routes data"></p><p>利用这些标准规范,团队可以轻松地在不同平台和工具之间共享和管理 API,进一步提高协作效率和 API 的可维护性。</p><h2>三、云端 API 管理解决方案的普及</h2><p>传统的 API 管理解决方案通常侧重于在本地部署和管理 API,这意味着企业需要购买、部署和维护硬件和软件资源,以支持 API 的开发、发布和监控。然而,随着业务的发展和云计算技术的普及,传统的 API 管理解决方案在可伸缩性、成本效益和跨平台集成方面面临一定的挑战。</p><p>与此同时,云端 API 管理解决方案应运而生。这类解决方案充分利用了云计算的弹性、按需付费和跨平台特性,为企业提供了一种更为灵活、高效和可靠的 API 管理方式。云端 API 管理解决方案通常包括 API 网关、安全功能、监控和分析等组件,以支持企业在混合云和多云环境中实现 API 的统一管理。</p><p>相比于传统的 API 管理解决方案,云端 API 管理解决方案拥有以下优势:</p><ol><li><strong>高可用性</strong>:得益于云端 API 管理解决方案提供弹性的负载均衡与自动扩展功能、以及自动化的故障切换和灾难恢复能力,使得云端 API 管理解决方案具备更高的可用性。</li><li><strong>降低成本</strong>:云端 API 管理解决方案可以降低 API 的开发、部署和维护成本,让企业专注于业务创新而无需担心基础设施的管理。</li><li><strong>跨平台支持</strong>:云端 API 管理解决方案支持混合云和多云环境,实现跨平台的 API 集成和管理,让企业可以轻松地在不同云服务提供商之间迁移和扩展其 API。</li></ol><p>值得一提的是,基于 Apache APISIX 的 <a href="https://link.segmentfault.com/?enc=vbP%2FQEbAH0mBgx5loJ6adw%3D%3D.ZsjnTTwFf2ymQK4LiOT970areHsRf5goRIe09ViP%2BqI%3D" rel="nofollow">API7 Cloud</a> 产品正是这样一种云端 API 管理解决方案。API7 Cloud 采用了现代化的云架构,可以帮助企业管理部署在混合云和多云上的 API,并高效、可靠地连接它们,相较于传统 API 解决方案,具有更多的优势和灵活性。</p><h2>四、使用低代码 API 平台方便创建发布 API</h2><p>低代码 API 平台是一种允许用户通过简单的图形界面和预构建的模块创建、发布和管理 API 的工具。这些平台旨在简化 API 开发过程,降低开发门槛,提高开发效率。</p><p>一个具体的例子是,<a href="https://link.segmentfault.com/?enc=SqehkW0xZX18G%2FUdtA8bRg%3D%3D.C0ZQfsUqjgocwYCD2dH2JZ28giMZ7OeOiNKT9xJIG9o0TTusmZKOUyjVS0ALQ4wf" rel="nofollow">Apache APISIX Dashboard</a> 创建路由的时候,无需手动编写代码,可以使用拖拽方式进行插件编排组合不同的插件。</p><p><img src="/img/remote/1460000043652719" alt="plugin-config" title="plugin-config"></p><h2>五、API 市场的发展</h2><p>随着 API 的普及,API 市场逐渐成为企业从各种供应商发现、评估和购买 API 的一种方式。API 市场可以帮助企业加速创新,降低开发成本。</p><p>1、对于 API 供应商,API 市场可以提高他们的 API 的可见性和吸引力,增加他们的收入和客户群,以及利用市场的分析功能来优化他们的 API 策略和设计。</p><p>2、对于 API 消费者,API 市场可以提供一个方便的一站式服务,让他们能够轻松地找到并使用各种高质量的 API 来满足他们的业务需求,并且节省了自己开发或维护这些 API 的时间和资源。</p><p>3、对于整个 API 生态系统,API 市场可以促进多方之间的协作和创新,激发新的用例和价值。</p><h2>六、更多 API 协议崛起</h2><p>随着下一代 API 协议,如 <a href="https://link.segmentfault.com/?enc=iZZ9KcAGgw2HmP685kG7Sg%3D%3D.EgjekiPbwUZh0y%2BLJQ3h2cAaBgoGtsZgwGcBteUP9ukfGu1kHoQ0CRZ2Y39l%2BjX0" rel="nofollow">GraphQL</a> 和 <a href="https://link.segmentfault.com/?enc=qoZBucpDHa7wwT2F%2FiU7BQ%3D%3D.U%2BmLmBOf%2B4oMrcfcB4%2F92Ey8EeGekttXuKmyIAF3HT8HHeEsdtKWBUDfPKblNdU95hfLLmsCQZhta4EtdpdTsQ%3D%3D" rel="nofollow">gRPC</a>,与当前主导但逐渐衰落的 <a href="https://link.segmentfault.com/?enc=Sq1aQ%2FHOJSNS%2BTZI27XvGA%3D%3D.2miL96hx1VU58j0RP6iaXafKzuKxot3pW%2B6a990i3B3YEHy2gkvl7dFESyairIMEaR2a5K2X%2FXv25KAOAq%2FtoQ%3D%3D" rel="nofollow">REST API</a> 展开竞争,越来越多的 API 协议得到了广泛应用。</p><p>GraphQL 是由 Facebook 开发的一种数据查询和操作语言。它允许客户端根据其需求明确请求所需数据,并在一个请求中获取多个资源。这有助于减少数据传输量和提高性能。与 REST API 相比,GraphQL 的优势包括:</p><ol><li><strong>灵活的数据请求</strong>:客户端可以指定所需的数据,避免了过度或不足的数据传输。</li><li><strong>更高效的请求处理</strong>:通过单个请求获取多个资源,有助于减少网络往返次数。</li><li><strong>实时数据更新</strong>:GraphQL 支持实时数据更新,可以及时响应客户端的数据变更需求。</li></ol><p>gRPC 是由 Google 开发的一种高性能、开源的远程过程调用(RPC)框架。它允许客户端像调用本地方法一样调用服务端的方法。gRPC 使用 Protocol Buffers(Protobuf)作为接口定义语言和数据序列化格式,以实现高效的数据传输。与 REST API 相比,gRPC 的优势包括:</p><ol><li><strong>gRPC 使用 Protobuf 进行数据序列化</strong>:相较于 JSON 格式,具有更高的性能和更小的数据体积。</li><li><strong>gRPC 基于 HTTP/2 协议,支持双向流式传输,多路复用和内置的 TLS 安全性</strong>:比 REST API 基于 HTTP/1.1 协议的单向请求响应模式更快,更灵活,更安全。</li><li><strong>gRPC 基于 Protobuf 定义 API</strong>:提供了原生的代码生成功能,可以自动生成客户端和服务器端的代码,支持多种编程语言,比 REST API 需要使用第三方工具如 Swagger 生成代码更方便,更一致。</li></ol><p>为了适应这些新兴协议的需求,Apache APISIX 提供了一系列相关插件,以支持不同协议的处理。</p><p>在 Apache APISIX 中,以下插件可以处理这些新兴 API 协议:</p><ul><li><a href="https://link.segmentfault.com/?enc=aB28bF5DJ3IEJ%2B1DyMKfPg%3D%3D.i%2FYy6VtYP9AFsd%2FpjmVTzdOsuXyMGby1xAQ912aBgese%2BaisJ5kptx4jpVvNckmelCGJr7X741lxjS6i3CjI7A%3D%3D" rel="nofollow">grpc-transcode</a>:<code>grpc-transcode</code> 用于在 HTTP 和 gRPC 请求之间进行转换。</li><li><a href="https://link.segmentfault.com/?enc=qMRTPM7A1ZIsi7Py2B%2FKHA%3D%3D.duCD6YolU2WGARVh4F6FDOuiDKHqzu8az%2FW7AhCkb7DVsbIbQ0bxZ1431gJLDyY0tmjBvCYVaJxhHksKnAanJA%3D%3D" rel="nofollow">grpc-web</a>:<code>grpc-web</code> 是一个代理插件,可以处理从 JavaScript 客户端到 gRPC Service 的 <a href="https://link.segmentfault.com/?enc=UyEYjuW%2F2Y5p4wkBvKsP1A%3D%3D.m4Lua9mjsAYVWNQxqVsH8MNzNeQnAIIdePyOZCkk8qnlMtoML9E1VTt%2BrHyjmon2" rel="nofollow">gRPC Web</a> 请求</li><li><a href="https://link.segmentfault.com/?enc=N162M1eohdcrFCMPfnZlsg%3D%3D.YkgRlDOOOtf7IBTg6EXIb5iFgenlEuei9BPCaEP1WBp%2Fso17kOnIgfSkwTDq6NEzqYhY8LrGNYih66gwCcZSNQ%3D%3D" rel="nofollow">degraphql</a>:<code>degraphql</code> 插件用于支持将 RESTful API 解码为 GraphQL。</li></ul><h2>七、人工智能与 API</h2><p>API 管理平台正在利用机器学习和人工智能自动化任务,例如 API 发现、安全威胁检测和异常检测。这可以帮助企业减轻其 IT 团队的负担,并提高其 API 管理流程的效率和准确性。</p><ol><li><strong>安全威胁检测</strong>:机器学习和人工智能可以帮助 API 管理平台实时监控并分析 API 流量,以便于及时发现并阻止任何恶意或异常的请求。</li><li><strong>异常检测</strong>:机器学习和人工智能可以帮助 API 管理平台预测并诊断任何可能影响 API 性能或可用性的问题,以便于及时修复并优化。</li></ol><h2>八、更加关注开发者体验</h2><p>随着 API 变得越来越集中于业务运营,开发者体验变得越来越重要。API 管理平台正在增加更多的开发者友好功能,例如文档、测试工具和 SDK,以使开发者更容易使用 API。</p><ol><li><strong>文档</strong>:文档是开发者了解和学习 API 的主要途径,因此文档应该清晰、完整、准确、及时地描述 API 的功能、参数、示例和错误码等信息。文档还应该提供交互式的控制台或沙盒,让开发者能够快速地测试和调试 API。</li><li><strong>测试工具</strong>:测试工具是开发者验证和优化 API 的重要手段,因此测试工具应该方便、可靠、灵活地支持各种测试场景和需求。测试工具还应该提供实时的反馈和报告,让开发者能够及时地发现并解决问题。</li><li><strong>SDK</strong>:SDK 是开发者集成和使用 API 的便捷方式,因此 SDK 应该覆盖各种主流的编程语言和平台,并且保持与 API 的同步更新。SDK 还应该遵循最佳实践和规范,让开发者能够轻松地理解和调用。</li></ol><h2>九、API 分析的兴起</h2><p>API 分析(API Analytics)是一种用于收集、分析和解释 API 使用情况数据的技术。随着 API 在软件和互联网行业的普及,API 分析应运而生,成为一种关键的管理和优化手段。以下是 API 分析兴起的几点原因:</p><ol><li>随着云计算、大数据、物联网等技术的发展,API 已成为企业和开发者之间交换数据和功能的重要工具。这导致了对 API 分析的需求不断增长,以便更好地了解和优化 API 的性能。</li><li>现代软件开发越来越多地采用微服务架构,将复杂的应用程序分解为多个独立的、可扩展的服务。这些服务通过 API 相互通信,因此对 API 分析的需求在这种架构下更加明显。</li><li>API 分析可以帮助检测潜在的安全漏洞和违反合规性的行为,从而降低风险。</li></ol><h2>十、更多的 API 通过无服务器(Serverless)架构提供服务</h2><p>无服务器架构是一种云计算模式,它允许开发者在不管理服务器的情况下,部署和运行应用程序。</p><p>要通过无服务器架构提供 API 服务,你只需要以下几个步骤:</p><ol><li>选择一个无服务器平台,编写你的 API 逻辑代码,使用无服务器平台提供的编程语言和框架。</li><li>在平台上配置你的 API 触发器,如 HTTP 请求,定时器,事件等。</li><li>使用平台提供的相关工具部署你的 API 代码到无服务器平台,并测试其功能和性能。</li></ol><p>使用无服务器架构拥有以下优势:</p><ol><li>无服务器架构可以让 API 开发者专注于业务逻辑,而不用担心基础设施、部署、缩放等问题。</li><li>无服务器架构可以根据 API 请求量自动调整资源,避免资源浪费或不足。</li><li>无服务器架构可以提高 API 的响应速度和可靠性,因为它可以利用分布式的边缘计算节点来处理请求</li></ol><p>Apache APISIX 在这方面也有所支持,包括 <a href="https://link.segmentfault.com/?enc=uHRnhcY4dBqZMs9ALxURhQ%3D%3D.wGituNE1j8K9omS0MoaHE536pSxOwNQ4G2OH6qTpdozbp5XI46Jm7oSKhBgG4KDVNlgYoGFIN%2Fr8tyDsNTE%2BlA%3D%3D" rel="nofollow">serverless</a>,<a href="https://link.segmentfault.com/?enc=Fmk%2F5xE65tymDACdjYRI%2Fg%3D%3D.cb8PDzr%2BMrtTwrpeTs%2BrnTD54CIF4EpSdEFGnY05I%2FbBv8TESgfD1XqJ0UAbsTAX6ZL8jIq9y3iiAZ65ht3hEg%3D%3D" rel="nofollow">openfunction</a> 等插件。</p><h2>总结</h2><p>API 管理作为数字化转型战略的重要组成部分,正面临着诸多挑战和发展机遇。通过关注 API 管理的这十大趋势,企业可以更好地应对未来的挑战,抓住发展机遇,并实现业务的持续增长和创新。</p><h2>关于 API7.ai 与 APISIX</h2><p>API7.ai(<a href="https://link.segmentfault.com/?enc=8OAy0Id%2FJhF0oxSCQBSJ3A%3D%3D.Sdp3EBZzNLvV3K2VurH%2B6qjNtyMr5fd8XMemqvpmiMw%3D" rel="nofollow">支流科技</a> )是一家提供 API 处理和分析的开源基础软件公司,于 2019 年开源了新一代云原生 API 网关 -- APISIX 并捐赠给 Apache 软件基金会。此后,API7.ai 一直积极投入支持 Apache APISIX 的开发、维护和社区运营。与千万贡献者、使用者、支持者一起做出世界级的开源项目,是 API7.ai 努力的目标。</p>
景顺长城基于 Apache APISIX 在金融云原生的生产实践
https://segmentfault.com/a/1190000043623880
2023-04-04T14:56:51+08:00
2023-04-04T14:56:51+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<p>本文介绍了景顺长城在金融云原生架构演进中选择 APISIX 作为网关工具的技术细节,同时分享了使用 APISIX 的实践细节,并对 APISIX 的未来展望进行了探讨。</p><blockquote>作者李奕浩,景顺长城信息技术部研发工程师,负责公司网关和业务系统上云等工作。</blockquote><h2>业务背景</h2><p>景顺长城基金管理有限公司成立于 2003 年,是一家专注于资产管理领域的企业。目前,公司主要业务涵盖量化投资、主动收益和固定收益,拥有超过 6200 亿的资金管理规模,为超过 6000 万的投资者提供服务。作者所在的信息技术部门为投资交易、产品运营、市场营销等服务系统提供技术支持和开发业务需求。</p><p>公司内众多业务都需要流量网关作为重要支撑,然而在采用 APISIX 之前,我们遇到了不少问题和痛点:</p><h3>痛点 1:网关不统一,维护成本高</h3><p>首先介绍景顺长城业务系统在架构上的演进,分为 3 大阶段,本文基于阶段 2 到 3 的转变背景:</p><ol><li>单体服务方式。服务应用部署在物理机上,但是随着服务应用和业务的不断增长,运维和开发成本开始激增;</li><li>虚拟机方式。在虚拟机阶段,各业务系统使用的网关不统一,业务各自为政,网关相关的机器服务也很多,维护成本一直很高。在基金证券行业,网络分区方面存在一定的监管要求,每个网络分区都需要按照信息安全级别划分为不同的逻辑安全区域,或者是物理隔离安全的区域,各个区域使用防火墙达到网络隔离的目的。</li><li>结合在金融科技云战略以及数字化转型的需求,启动了上云工程。</li></ol><p>当前业务系统大致分为了三个分区:交易区、生产区、管理区,每个分区部署的网关都不一样。原本景顺长城使用 NGINX 作为 Web 服务器和反向代理,在同网络分区中的业务,都是使用了同一个 NGINX,导致了每次服务变更或路由更新,都需要在这个 NGINX 上面手动更新和重载。</p><p><img src="/img/remote/1460000043623882" alt="系统架构图" title="系统架构图"></p><p>上图就是当前的系统架构,从上往下分别是用户接入层、负载均衡层、网关集群层以及业务系统层,其中网关安全集群层使用了 Zuul、Spring Cloud Gateway、Kong 还有 NGINX 等多个框架,架构管理不统一,管理起来比较繁琐。</p><h3>痛点 2:网关承载能力弱</h3><p>现存的网关只使用了很基本的能力,比如负载均衡、路由转发,鉴权、灰度发布、熔断能力都没有。</p><p>前面提到的单一的 NGINX 服务,每次服务变更和路由更新都需要在 NGINX 上面进行配置。由于业务前端的服务都集成在 NGINX 上面,没有对接 DevOps 平台生产环境,NGINX 的配置项数量如今已经相当庞大了,单个 Server 模块超过 700 行,维护成本相当高。</p><h3>痛点 3:限流熔断、灰度发布配置繁琐</h3><ul><li>限流熔断能力缺失</li><li>灰度能力配置繁琐:需同步改动多个应用代码和数据库变更。现在使用的网关需要单独做对应的开发,并且业务侧也要做对应的代码修改,才能实现不同服务的灰度能力(全链路灰度)。</li></ul><h3>痛点 4:新增服务涉及步骤繁多</h3><p><img src="/img/remote/1460000043623883" alt="服务调用" title="服务调用"></p><p>在新增服务的情况下,需要手动添加 NGINX、Gateway、业务系统等各个模块的配置步骤,无法配置自动化。这就意味着新增服务的一系列配置修改很是繁琐,导致在服务发布时面临很高的人力成本。</p><h3>痛点 5:接口调用存在安全隐患</h3><p>此外,当前使用的网关未涉及鉴权能力,各接口可直接调用,业务系统存在很高的安全风险。</p><h2>发现并选择 APISIX</h2><p>基于上述痛点,景顺长城在 API 网关产品选型过程中对以下几点更为关注。</p><p>由于金融领域对服务的稳定性相较其他行业更加重视,因此<strong>稳定性</strong>放在第一位,另外也看重网关的扩展性,对于在虚拟机时期野蛮生长的网关服务,都需要一一满足其中的业务需求,这里就是比较考验所选网关的<strong>扩展性</strong>;再有就是<strong>可观测性</strong>,对业务系统的日志、链路追踪、监控都有很强烈要求;最后就是<strong>热更新</strong>的能力。</p><p><img src="/img/remote/1460000043623884" alt="APISIX 特性" title="APISIX 特性"></p><p>由于 NGINX 无法动态变更配置,因此当面临配置变更场景时需要重载 NGINX 以载入新的配置,在长链接场景下,由于 NGINX 实现特性的原因,该过程将导致业务系统出现流量波动,进而影响用户体验。</p><p>所以基于这 4 个关注点,景顺长城技术团队对 APISIX 和 Kong 进行了横向对比:</p><table><thead><tr><th align="left">特点</th><th align="left">APISIX</th><th align="left">Kong</th><th align="left">备注</th></tr></thead><tbody><tr><td align="left">社区活跃度</td><td align="left">高</td><td align="left">高</td><td align="left">commit: APISIX&Kong 平均 20 次/天</td></tr><tr><td align="left">插件数量</td><td align="left">80+</td><td align="left">30+</td><td align="left">该数量表示网关安装包自带标准插件</td></tr><tr><td align="left">单插件文件数</td><td align="left">1</td><td align="left">4-5</td><td align="left"> </td></tr><tr><td align="left">单插件代码量</td><td align="left">100+</td><td align="left">300+</td><td align="left"> </td></tr><tr><td align="left">扩展性</td><td align="left">Java、Go、Python、Lua、Js</td><td align="left">Lua</td><td align="left"> </td></tr><tr><td align="left">热更新</td><td align="left">支持</td><td align="left">不支持</td><td align="left">1、插件的启用禁止都可以热部署;<br/> 2、新增插件(例如编写自定义插件),Kong 需要重启,APISIX 不需要;<br/> 3、修改网关自身参数</td></tr><tr><td align="left">可观测性</td><td align="left">OpenTelemetry</td><td align="left">OpenTelemetry</td><td align="left">APISIX 支持 OpenTelemetry 协议,可以接入 Elasticsearch、Prometheus 和 SkyWalking</td></tr></tbody></table><ul><li><strong>技术框架方面</strong>,APISIX 和 Kong 都是基于 OpenResty 的基础之上进行二次开发,配置中心方面 APISIX 选型了 etcd,Kong 选型了 PostgreSQL,由于 etcd 的云原生特性与 APISIX 的状态特性使得 APISIX 是云原生分布式网关;而 Kong 的配置中心选型会有可能出现单点故障的问题,需要额外的基础设施保障做高可用。</li><li><strong>社区活跃度方面</strong>, APISIX 和 Kong 都是属于比较高热度的,每天的平均 commit 量是在 20 次左右。</li><li><strong>扩展性方面</strong>, APISIX 支持的语言非常多,并且还支持 WebAssembly,Kong 只支持使用 Lua 语言进行插件的编写。</li><li><strong>热更新方面</strong>,这是团队重点关注的:APISIX 支持热更新,并且实现了毫秒级别的热更新响应;而 Kong 不支持热更新。</li><li><strong>可观测性方面</strong>,选型的时候 Kong 还未能提供 SkyWalking 的支持。</li></ul><p>基于以上关注点和对比点,最终景顺长城技术团队选择了 APISIX 作为业务系统的 API 网关。</p><h2>基于 APISIX 的解决方案</h2><p><img src="/img/remote/1460000043623885" alt="系统架构图" title="系统架构图"></p><p>如图所示,景顺长城的系统架构改革重点是将网关集群都转换为 APISIX。由于 APISIX 部署在 Kubernetes 集群上,因此可以通过 YAML 以声明式配置的方式统一管理 API,并通过 APISIX Ingress Controller 自动监听 Kubernetes 资源变更事件,实现 APISIX 配置的实时同步,包括 AR(ApisixRoute)、SSL等。</p><p>路由同步时序图:</p><p><img src="/img/remote/1460000043623886" alt="路由同步图" title="路由同步图"></p><p>下面详细介绍景顺长城团队在业务侧比较关心的 4 个场景,包括智能路由、安全认证、可观测性和流量控制。</p><h3>场景一:智能路由</h3><p>主要体现在 APISIX 支持的 7 层和 4 层负载均衡。7 层负载均衡上的方案如下:</p><p><img src="/img/remote/1460000043623887" alt="7 层负载均衡" title="7 层负载均衡"></p><p>然后是生产环境上的路由配置情况:</p><p><img src="/img/remote/1460000043623888" alt="路由配置" title="路由配置"></p><p>如图所示,通过 APISIX Ingress Controller 同步的路由信息,都是带上了特定的标签的,就是为了防止误删除操作。</p><p>经过测试环境验证,即使删除了数据,Kubernetes 也能够快速自动同步到 APISIX 上,因此解决了数据丢失的问题。</p><p>此外,路由更新在 APISIX 上面也达到毫秒级别响应,配置同步延迟几乎无感,体验非常好。</p><p>景顺长城技术团队使用 Consul 作为服务发现的中间件,在调研过程中发现 APISIX 2.15 版本虽然支持了 Consul 的服务发现插件,但仅作为配置中心的方式支持,而在服务发现部分的正常模式上尚未支持。随后,景顺长城技术团队基于该场景开发了支持 Consul 的服务发现插件,并已将其贡献给社区。</p><p>这里补充介绍一下 Consul 插件的内部细节,先看下图:</p><p><img src="/img/remote/1460000043623889" alt="Consul 插件工作原理" title="Consul 插件工作原理"></p><p>从上往下看,Consul 插件是通过 HTTP 方式去获取不同 Consul 集群的 Service 信息的,这里会获取服务信息会做校验,就是 <code>last consul index</code>(X-Consul-Index),这个 index 字段是在 header 里面的,只有在服务信息变化的时候才会返回结果,所以这也很好地保证了 APISIX 和 Consul 之间的请求数量。</p><p>接下来,在 APISIX 中会有多个 worker 同步更新 all_services 表中的信息,该表包含了各个服务名称对应的 IP 和端口号。这些 worker 通过事件机制进行通信,并提供了 debugging API 以方便用户获取当前的服务信息。下面是流程的详细介绍:</p><p>Consul 提供了 Catalog 的 <a href="https://link.segmentfault.com/?enc=O383KZKUirXqid5FUQPz9g%3D%3D.Kzwhu0K9p3vaKKNlDcYghOgiF9BHT0%2BUauI12wCR3L9cshxqnIObvPzprDJJjrdfblZafoNv1Yu%2FMQ07uk762w%3D%3D" rel="nofollow">API</a> 方式获取 services 信息,其中这里可以分成两步:</p><p><strong>获取所有服务名称</strong></p><ul><li>获取所有服务信息中的 API:<a href="https://link.segmentfault.com/?enc=1mbfa52xm3xgJ%2BDtxO19RA%3D%3D.pGaLjJXiHa1Xgp%2BDdI%2F4Vg1jmnDHG9WvcmuzzIshM4%2BLOUNm6WV9s1xrD%2BUEHpuAzNsL0g990LAXRvelLM2MGhHaBYUKshAtueCjNm93GyI%3D" rel="nofollow">List Services</a>,该 API 是支持 <a href="https://link.segmentfault.com/?enc=GmmVMJx9f4d16%2Bgq6glPmg%3D%3D.vs8MpJOyMTXlE1%2BcsyxkKFTxSgyW%2Fa8%2F8lZJ96LkrcFOjllbQIEIlTL51waZmEKYGi5D%2F%2Ftdb9jEZKMvxdNjw4v1viY30bY%2BcaueGIzV2pM%3D" rel="nofollow">Blocking Queries</a>,就是根据请求头的 <code>X-Consul-Index</code> 字段值不同去拉取服务列表,只有服务列表出现变化的时候才会改变 <code>X-Consul-Index</code> 字段的值;</li></ul><p><strong>获取每个服务节点信息</strong></p><ul><li>根据第一步获取的服务列表进行遍历,把每个服务的 node 信息获取并记录到 table 中,对应 Consul API:<a href="https://link.segmentfault.com/?enc=2YFv7pjmaNKqCmcvTRKACg%3D%3D.w9jcSDkAMeBvAAvOfaojyk3RT%2BOpscQOo0ymb3ZLeXm19HFoH04TNp9x6L5BBJZtIjS4W73hJFx7w07riq1okgA2p1awau9T5hO928NkuTg%3D" rel="nofollow">List Nodes for Service</a>,同时通过事件的方式实时更新,下面是简单的流程图:</li></ul><p><img src="/img/remote/1460000043623890" alt="Consul API 工作流" title="Consul API 工作流"></p><p>这就是整个 Consul 服务发现插件的内部实践细节。</p><h3>场景二:安全认证</h3><p>在安全验证方面,我们使用了统一认证和 HTTPS 重定向插件。在引入统一网关之前,每个业务都需要单独开发 Auth 相关内容,以保护其数据接口的安全性。</p><p><img src="/img/remote/1460000043623891" alt="安全认证前后对比" title="安全认证前后对比"></p><p>在业务侧,每个业务系统开发相同的鉴权功能,一方面是开发成本相对比较高,另一方面各自业务系统开发出来的功能质量不一,重复造轮子。所以景顺长城技术团队计划把统一认证功能放在网关上,同时解决前面提到的问题,极大提高了业务系统的研发效率,让开发者更专注业务开发。</p><p><img src="/img/remote/1460000043623892" alt="APISIX 动态管理 SSL" title="APISIX 动态管理 SSL"></p><p>上图是 APISIX 动态管理 SSL 证书详情,景顺长城技术团队通过 Kubernetes 的 cert-manager 统一进行证书的颁发和管理。在统一网关之前,服务的 HTTPS 证书都是在 HA 端进行解析,现在统一放在 APISIX 中,并且 APISIX 支持动态加载 SSL 证书。</p><h3>场景三:可观测性</h3><p>原业务系统在可观测性方面支持相对较弱,因此团队希望能够快速接入 APISIX 提供的日志监控、Tracing 等多个插件。此外,团队还在调研并使用 Apache SkyWalking 进行链路追踪、Prometheus 进行监控、ELK 进行日志收集。对于网关层,只需进行简单的配置即可直接使用这些工具。</p><p><img src="/img/remote/1460000043623893" alt="APISIX 可观测性" title="APISIX 可观测性"></p><p>上图是景顺长城技术团队在生产上接入 SkyWalking 后的服务拓扑图,该图清晰地展示了服务之间的调用关系,包括经过网关的流量方向和成功率等关键信息。通过该拓扑图,团队可以快速定位链路错误点的位置。</p><h3>场景四:流量控制</h3><p>在流量控制的场景下,有基于灰度和基于权重这两种策略。</p><ul><li>基于灰度策略的场景中,网关需要通过 HTTP 请求调用下游某个接口以获取特定的数据,并根据返回结果进行判断,以确定是否需要将请求发送到灰度环境。</li><li>基于权重策略的场景,虚拟机和容器的服务并行对外提供服务,例如将 90% 的流量命中虚拟机服务,10% 的流量命中云服务,以验证云服务的稳定性。目前,景顺长城的生产环境已经配置了基于权重的灰度发布。</li></ul><h2>收益与展望</h2><p>接入 APISIX 后,景顺长城的业务团队获得了以下收益:</p><ol><li>统一了 API 网关框架,降低了业务系统开发的运维成本,目前生产环境已经接入了大约 10% 的业务;</li><li>在 DevOps 和流水线的结合方面,极大地提高了路由发布和服务发布的稳定性;</li><li>APISIX 的热更新能力大大降低了服务重启的压力。</li></ol><p>最后,对于 APISIX 的未来高效稳定运行,景顺长城技术团队提出了以下三点期望:</p><ol><li>监控告警方面,希望 APISIX 能够不断完善路由可用性的高级配置,例如邮件告警、微信指标告警等。</li><li>APISIX 和 etcd 的稳定连接方面,目前 APISIX 和 etcd 通过 HTTP 进行通信,但在 etcd v3 版本之后,API 协议已经切换到了 gRPC 协议,因此希望 APISIX 和 etcd 通过 gRPC 进行通信,以提高效率和稳定性,并避免通过 gRPC Gateway 进行转换。</li><li>链路追踪方面,希望通过 APISIX 网关接入实现服务的全链路追踪。</li></ol><p>以上是景顺长城技术团队对于 APISIX 未来的期望,感谢阅读。</p><h2>关于 API7.ai 与 APISIX</h2><p>API7.ai(<a href="https://link.segmentfault.com/?enc=H03p88%2FE6icMDIThFDq%2F%2FQ%3D%3D.0A4zmuWPLOLIZyvUGy5tvPO5DaLpHJ6xAOrou4GJw84%3D" rel="nofollow">支流科技</a> )是一家提供 API 处理和分析的开源基础软件公司,于 2019 年开源了新一代云原生 API 网关 -- APISIX 并捐赠给 Apache 软件基金会。此后,API7.ai 一直积极投入支持 Apache APISIX 的开发、维护和社区运营。与千万贡献者、使用者、支持者一起做出世界级的开源项目,是 API7.ai 努力的目标。</p>
WebAssembly 助力云原生:APISIX 如何借助 Wasm 插件实现扩展功能?
https://segmentfault.com/a/1190000043623799
2023-04-04T14:49:12+08:00
2023-04-04T14:49:12+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<p>本文将介绍 Wasm,以及 Apache APISIX 如何实现 Wasm 功能。</p><blockquote>作者<a href="https://link.segmentfault.com/?enc=p9FtNClaCkC8rM1eEzkAtQ%3D%3D.X2bJNPIaLWnkifA5%2FqdE5zcDU8xltZ%2F5qNBt56BNaLg%3D" rel="nofollow">朱欣欣</a>,API7.ai 技术工程师</blockquote><p><a href="https://link.segmentfault.com/?enc=QC9RbajmcBT%2BJi1P7cGbVA%3D%3D.a%2FRWJXh%2FkPMsypNGJxqR48AlQQZfZ7Z007chnT9Nlj7KAcsjo7I%2F3mhz2o2jX%2BAbdNVICiBQc2f3aEJIFyPG%2BjQsODfYOLq9XXbybXJ7nrIcwH64YS1gHQG5ov2yuHuM" rel="nofollow">原文链接</a></p><h2>什么是 Wasm</h2><p><a href="https://link.segmentfault.com/?enc=Qh3OK%2BVrtBxfJCHMge3fog%3D%3D.YzxvpoR24s6A5zENJQmC%2F%2BLyTBuWhNVkHc%2BpGeNlZQk%3D" rel="nofollow">Wasm</a> 是 WebAssembly 的缩写。WebAssembly/Wasm 是一个基于堆栈的虚拟机设计的指令格式。<br>在 Wasm 未出现之前,浏览器中只能支持运行 Javascript 语言。当 Wasm 出现之后,使得高级语言例如 C/C++/Golang 能够在浏览器中运行。当前,主流的浏览器包括 Chrome、Firefox、Safari 等浏览器都已完成对 Wasm 的支持。并且得益于 WASI 项目的推进,服务端也已经能够支持运行 Wasm 指令。<br>如今在网关侧,Apache APISIX 也已完成对 Wasm 的支持,开发者可以通过高级语言 C/C++/Go/Rust 并按照 <a href="https://link.segmentfault.com/?enc=a8VKGspbzaewhe02YtZg8w%3D%3D.U%2FcoL6IPAPiduKXN3M1aKxM3wNnzdgDSwKB77vqKvBzUbEeOF%2B7t9a8fVdeQ06wj" rel="nofollow">proxy-wasm</a> 规范来完成 Wasm 插件的开发。</p><p><img src="/img/remote/1460000043623801" alt="wasm" title="wasm"></p><h2>为什么 APISIX 要支持 Wasm 插件</h2><p>相比较原生的 Lua 插件,Wasm 插件存在如下优势:</p><ul><li>可扩展性:APISIX 通过支持 Wasm,我们可以结合 proxy-wasm 提供的 SDK,使用 C++/Golang/Rust 等语言进行插件开发。由于高级语言往往拥有更加丰富的生态,所以我们可以依托于这些生态来实现支持更多功能丰富的插件。</li><li>安全性:由于 APISIX 和 Wasm 之前的调用依托于 proxy-wasm 提供的 ABI(二进制应用接口),这部分的访问调用更为安全。Wasm 插件只允许对请求进行特定的修改。另外,由于 Wasm 插件运行在特定的 VM 中,所以即使插件运行出现崩溃也不会影响 APISIX 主进程的运行。</li></ul><h2>APISIX 如何支持 WASM</h2><p>了解完 Wasm,现在我们将从自顶向下的角度来看 APISIX 是如何支持 Wasm 插件功能的。</p><p><img src="/img/remote/1460000043623802" alt="apisix-wasm" title="apisix-wasm"></p><h3>APISIX Wasm 插件</h3><p>在 APISIX 中,我们可以使用高级语言 C/C++/Go/Rust 来按照 proxy-wasm 规范以及对应的 SDK 来编写插件。</p><blockquote>proxy-wasm 是 Envoy 推出的在 L4/L7 代理之间的 ABI 的规范与标准。<br>在该规范中定义了包含内存管理、四层代理、七层代理扩展等 ABI。<br>例如在七层代理中,proxy-wasm 规范定义了proxy_on_http_request_headers,proxy_on_http_request_body,proxy_on_http_request_trailers,proxy_on_http_response_headers 等 ABI,使得模块能够在各个阶段对请求内容进行获取与修改。</blockquote><p>例如,我们使用 Golang 结合 <a href="https://link.segmentfault.com/?enc=oesQCYwJLOlojyKi90%2FMbw%3D%3D.Q0aMIYGAZnJ4vUZnEKIy4BnrCUXPSImSz1tFYvgKfHcfRrPOO2JEmdjnO8ZRYGtxJtRwlRpYLARtRAxpg9T%2Bxg%3D%3D" rel="nofollow">proxy-wasm-go-sdk</a>, 编写如下插件:</p><blockquote>proxy-wasm-go-sdk 正是上述 proxy-wasm 规范的 SDK,它帮助开发者更好的使用 Golang 编写 proxy-wasm 插件。<br>不过需要注意的是,由于原生 Golang 在支持 WASI 时存在一些问题,因此该 SDK 基于 TinyGo 实现,更多内容可以<a href="https://link.segmentfault.com/?enc=wZaQ6RkQbpauqRUfz0qGtw%3D%3D.X7Aa%2BzulJiUIpxRK2Sq0Oq5WqU%2BUAexjhmUG3mNzvWeLfuscZaj3N2t%2FhepOVLDCukBbkr5z2P4%2F1D5l8X%2BmjJTLeMHQEbrEefKPIivqf2RmyXPOoOxvt9K3hOuzAX7LOX8suq8nt%2F7EKhDrOFTmtA%3D%3D" rel="nofollow">点击</a>进行查看。</blockquote><p>该插件的主要功能用于将 HTTP 修改请求的响应状态码与响应体,引用自 APISIX <a href="https://link.segmentfault.com/?enc=lqfDmzD7NUBYa97k1zbAQQ%3D%3D.vW8egUc53FThUhQduCblxFOW%2Bd5eKZA%2FF3a8%2BopAJvKRN711bffzEnmmhYtBrYBkMBgXHO4cgdlgcbUyLw8eC4lczcqZsKuGIKVRXUXFvJs%3D" rel="nofollow">链接</a>,</p><pre><code>...
func (ctx *pluginContext) OnPluginStart(pluginConfigurationSize int) types.OnPluginStartStatus {
data, err := proxywasm.GetPluginConfiguration()
if err != nil {
proxywasm.LogErrorf("error reading plugin configuration: %v", err)
return types.OnPluginStartStatusFailed
}
var p fastjson.Parser
v, err := p.ParseBytes(data)
if err != nil {
proxywasm.LogErrorf("error decoding plugin configuration: %v", err)
return types.OnPluginStartStatusFailed
}
ctx.Body = v.GetStringBytes("body")
ctx.HttpStatus = uint32(v.GetUint("http_status"))
if v.Exists("percentage") {
ctx.Percentage = v.GetInt("percentage")
} else {
ctx.Percentage = 100
}
// schema check
if ctx.HttpStatus < 200 {
proxywasm.LogError("bad http_status")
return types.OnPluginStartStatusFailed
}
if ctx.Percentage < 0 || ctx.Percentage > 100 {
proxywasm.LogError("bad percentage")
return types.OnPluginStartStatusFailed
}
return types.OnPluginStartStatusOK
}
func (ctx *httpLifecycle) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action {
plugin := ctx.parent
if !sampleHit(plugin.Percentage) {
return types.ActionContinue
}
err := proxywasm.SendHttpResponse(plugin.HttpStatus, nil, plugin.Body, -1)
if err != nil {
proxywasm.LogErrorf("failed to send local response: %v", err)
return types.ActionContinue
}
return types.ActionPause
}
...</code></pre><p>之后,我们通过 tiny-go 将上述的 Golang 代码编译生成 <code>.wasm</code> 文件</p><pre><code>tinygo build -o wasm_fault_injection.go.wasm -scheduler=none -target=wasi ./main.go</code></pre><p>完成编译之后,我们得到了 <code>fault_injection.go.wasm</code> 文件</p><blockquote>如果对 wasm 文件内容感兴趣的话,我们可以使用 <a href="https://link.segmentfault.com/?enc=XGcIoY%2BXh8W9vMA2TmVwIw%3D%3D.TOfVO0nRszemz%2F3skHvunqguCWx3pfQMam7fG7bK0PQA%2B%2Fk0a35nHgjhQ8ctwQdB" rel="nofollow">wasm-tool</a> 工具来查看该 wasm 文件的具体内容。<br><code>wasm-tools dump hello.go.wasm</code></blockquote><p>将 <code>wasm_fault_injection.go.wasm</code> 配置到 APISIX 到 <code>config.yaml</code>,并将该插件命名为 wasm_fault_injection。</p><pre><code>apisix:
...
wasm:
plugins:
- name: wasm_fault_injection
priority: 7997
file: wasm_fault_injection.go.wasm</code></pre><p>之后,我们启动 APISIX ,并创建一条路由引用该 Wasm 插件:</p><pre><code>curl http://127.0.0.1:9180/apisix/admin/routes/1 \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '{
"uri":"/*",
"upstream":{
"type":"roundrobin",
"timeout":{
"connect":1,
"read":1,
"send":1
},
"nodes":{
"httpbin.org:80":1
}
},
"plugins":{
"wasm_fault_injection":{
"conf":"{\"http_status\":200, \"body\":\"Hello WebAssembly!\n\"}"
}
},
"name":"wasm_fault_injection"
}'
</code></pre><p>进行访问测试,发现响应体已被修改为 "Hello WebAssembly",由此 Wasm 插件已经生效。</p><pre><code>curl 127.0.0.1:9080/get -v
* Trying 127.0.0.1:9080...
* Connected to 127.0.0.1 (127.0.0.1) port 9080 (#0)
> GET /get HTTP/1.1
> Host: 127.0.0.1:9080
> User-Agent: curl/7.81.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Thu, 09 Feb 2023 07:46:50 GMT
< Content-Type: text/plain; charset=utf-8
< Transfer-Encoding: chunked
< Connection: keep-alive
< Server: APISIX/3.1.0
<
Hello WebAssembly!</code></pre><h3>Wasm-nginx-module</h3><p>了解完 Apache APISIX 如何使用 Wasm 插件,现在我们更进一步来了解 "为什么我们能在 Wasm 插件中获取到请求的内容并修改请求?" 。<br>由于 APISIX 选用 Openresty 作为底层框架,因此 Wasm 插件中想要能够获取到请求内容和修改请求内容,就需要和 openResty 或者 NGINX 提供的 API 进行交互。<code>wasm-nginx-module</code> 正是提供了这部分能力。</p><blockquote>wasm-nginx-module 是由 <a href="https://link.segmentfault.com/?enc=65bAbLLa87RUbkqEpGekWg%3D%3D.fAzoWjZECIpWc4iuu%2FAPxYbtqaXb5hU4Y46oqPXVy68%3D" rel="nofollow">API7</a> 研发的支持 Wasm 的 NGINX 模块。<br>该模块尝试在 NGINX 的基础上实现 proxy-wasm-abi,并且向上封装了 Lua API,使得我们能够在 Lua 层面完成 proxy-wasm-abi 的调用。<br>更多内容可参考 <a href="https://link.segmentfault.com/?enc=YaJtG5%2FoTC6wjsQ%2FVbMyyg%3D%3D.sIb5eI%2F2AoRRgXUb%2Bpvk1cUI70%2F5ZkAYJ6RkCEGAWNVHzZfNzn0ZqHpdkg7H5v83" rel="nofollow">wasm-nginx-module</a>。</blockquote><p>例如,当我们的 APISIX 运行到 "access" 阶段时,会调用 <code>wasm-nginx-module</code> 中提供的 Lua 方法<br>on_http_request_headers。</p><pre><code>-- apisix/wasm.lua
...
local ok, err = wasm.on_http_request_headers(plugin_ctx)
if not ok then
core.log.error(name, ": failed to run wasm plugin: ", err)
return 503
end
end
...
</code></pre><p>之后在该方法中,将调用 <code>wasm-nginx-module</code> 中 <code>ngx_http_wasm_on_http</code> 方法,</p><pre><code>ngx_int_t
ngx_http_wasm_on_http(ngx_http_wasm_plugin_ctx_t *hwp_ctx, ngx_http_request_t *r,
ngx_http_wasm_phase_t type, const u_char *body, size_t size,
int end_of_body)
{
...
ctx = ngx_http_wasm_get_module_ctx(r);
if (type == HTTP_REQUEST_HEADERS) {
cb_name = &proxy_on_request_headers;
} else if (type == HTTP_REQUEST_BODY) {
cb_name = &proxy_on_request_body;
} else if (type == HTTP_RESPONSE_HEADERS) {
cb_name = &proxy_on_response_headers;
} else {
cb_name = &proxy_on_response_body;
}
if (type == HTTP_REQUEST_HEADERS || type == HTTP_RESPONSE_HEADERS) {
if (hwp_ctx->hw_plugin->abi_version == PROXY_WASM_ABI_VER_010) {
rc = ngx_wasm_vm->call(hwp_ctx->hw_plugin->plugin,
cb_name,
true, NGX_WASM_PARAM_I32_I32, http_ctx->id, 0);
} else {
rc = ngx_wasm_vm->call(hwp_ctx->hw_plugin->plugin,
cb_name,
true, NGX_WASM_PARAM_I32_I32_I32, http_ctx->id,
0, 1);
}
} else {
rc = ngx_wasm_vm->call(hwp_ctx->hw_plugin->plugin,
cb_name,
true, NGX_WASM_PARAM_I32_I32_I32, http_ctx->id,
size, end_of_body);
}
...
}</code></pre><p>在 <code>wasm-nginx-module</code> 中,我们将根据不同的阶段,设置 <code>cb_name</code>,例如:<code>HTTP_REQUEST_HEADERS</code> 对应 <code>proxy_on_request_headers</code>,之后将在 <code>ngx_wasm_vm->call</code> 中调用 vm 中的方法也就是我们在上文中提到的 wasm 插件 <code>OnHttpRequestHeaders</code> 的方法。</p><p>至此,整个 APISIX 调用 wasm 插件,运行 Golang 的调用链便梳理完成,调用链如下:</p><p><img src="/img/remote/1460000043623803" alt="wasm-call" title="wasm-call"></p><h3>Wasm VM</h3><p>Wasm VM 用于真正执行 Wasm 代码的虚拟机,在 <code>wasm-nginx-module</code> 中实现了对两种虚拟机 "wasmtime" 和 "wasmedge" 两种虚拟机,在 APISIX 中默认选择使用 "wasmtime" 作为 Wasm 代码的运行虚拟机。</p><blockquote>Wasmtime<br><a href="https://link.segmentfault.com/?enc=duGZrfBjyr%2FQFL1h7rN0zw%3D%3D.fD2EjrwWXeHNaCXka9g%2BSWrVmch%2BDLsMRFf7akZ9EQxVZz47k431vQ5CAc%2F%2BNeC%2F" rel="nofollow">Wasmtime</a> 是由 bytecodealliance 开源的 WebAssembly 和 WASI 的小型高效运行时。它能够在 Web 外部运行 WebAssembly 代码,即可以用作命令行使用,也可以作为 WebAssembly 运行引擎嵌入到其他程序作为库使用。<br>Wasmedge<br><a href="https://link.segmentfault.com/?enc=ss26jSH9EmhGNnDZna%2FQJA%3D%3D.zbAq75qnricL3DiisuRrRKGgodR%2FHkd4oGThvUkYcBU%3D" rel="nofollow">Wasmedge</a> 是为边缘计算优化的轻量级、高性能、可扩展的 WebAssembly (Wasm) 虚拟机,可用于云原生、边缘和去中心化的应用。</blockquote><p>在 Wasm vm 中首先通过 <code>load</code> 方法将 <code>.wasm</code> 文件加载到内存,之后我们便可以通过 VM 的 call 方法来调用这些方法。VM 底层依托于 WASI 的接口实现,使得 Wasm 代码不仅能够运行在浏览器端,同时也支持能够在服务端进行。</p><h2>总结</h2><p>通过本文我们了解到 Wasm 是什么以及 APISIX 如何支持 Wasm 插件。APISIX 通过支持 Wasm 插件,不但可以扩充对多语言的支持,例如通过 C++, Rust, Golang, AssemblyScript 等进行插件开发,而且由于 WebAssembly 正在从浏览器走向云原生拥有了更加丰富的生态与使用场景,因此 APISIX 也可以借助 Wasm 完成在 API 网关侧更多的扩展功能,解决更多使用场景。</p><h2>关于 API7.ai 与 APISIX</h2><p>API7.ai(<a href="https://link.segmentfault.com/?enc=d13NVroCKVRvPxMlAnCkaA%3D%3D.RCBBNKWpL%2B%2BPS9XqbxttM6sucBcTZ%2FbNRMOdVrYtxmk%3D" rel="nofollow">支流科技</a> )是一家提供 API 处理和分析的开源基础软件公司,于 2019 年开源了新一代云原生 API 网关 -- APISIX 并捐赠给 Apache 软件基金会。此后,API7.ai 一直积极投入支持 Apache APISIX 的开发、维护和社区运营。与千万贡献者、使用者、支持者一起做出世界级的开源项目,是 API7.ai 努力的目标。</p>
什么是 LuaJIT?为什么 Apache APISIX 选择了 LuaJIT?
https://segmentfault.com/a/1190000043574784
2023-03-23T11:59:11+08:00
2023-03-23T11:59:11+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<p>本文介绍了 LuaJIT 的高灵活性和高性能,以及 APISIX 作为云原生 API 网关选择 LuaJIT 的原因。</p><blockquote>作者<a href="https://link.segmentfault.com/?enc=sYM26Tnqgj46yGktRu5t2Q%3D%3D.n053tpxH2QQ7Vi9AvGVRKLptU0HX3H6r4Sx%2Bj2ago0A%3D" rel="nofollow">杨陶</a>,API7.ai 技术工程师。</blockquote><p><a href="https://link.segmentfault.com/?enc=5lHoAZlRyceiV21po3TgQw%3D%3D.hNp5JtQKtWexKo4R5RwwCEvUYRjHAgz2ZewuY8ebQnqSLQf6fZW27mt8nTW5xGhyMpH2LcEB9dVGJb9cQRZR%2FYco4UoGkXAsGT%2Bo8IQtFyE%3D" rel="nofollow">原文链接</a></p><h2>什么是 LuaJIT</h2><h3>定义</h3><p>简单地说,LuaJIT 是 Lua 这种编程语言的实时编译(JIT,Just-In-Time Compilation)器的实现。<br>对于不太了解 LuaJIT 的读者,我们可以将 LuaJIT 拆成 Lua 和 JIT 两个部分来理解。</p><h4>Lua</h4><p>Lua 是一种优雅、易于学习的编程语言,具有自动内存管理、完整的词法作用域、闭包、迭代器、协程、正确的尾部调用以及使用关联数组进行非常实用的数据处理。本文不会涉及 Lua 的语法,有关内容欢迎阅读 <a href="https://link.segmentfault.com/?enc=e6Rv7PoaGrJk7DM%2FveyE6Q%3D%3D.3XKX8kKPd%2BcR62V0W7vcQCIakAqymDnpauhFsjdJl4oahmyFLRqYRvG74yLCUMY5eNszkKl9E5Edk2VmuNn6Q979jSnVOlZmNof6hnWPcZg%3D" rel="nofollow">Getting Started With Lua</a>。</p><p>Lua 的设计目标是能与 C 或其它常用的编程语言相互集成,这样就可以利用其它语言已经做好的方面;而它提供的特性又恰好是 C 这类语言不太擅长的,比如相对于硬件层的高层抽象,动态的结构,简易的测试等等。其袖珍的语言内核和只依赖于 ANSI C 标准的特点,使之在各平台上的可移植性变得非常高。因此 Lua 不仅是一个可以作为独立程序运行的脚本语言,也是一个可以嵌入其它应用的嵌入式语言。</p><p><img src="/img/remote/1460000043574786" alt="Apache APISIX 就是一个在底层同时使用 Lua 和 C 的极佳例子。" title="Apache APISIX 就是一个在底层同时使用 Lua 和 C 的极佳例子。"></p><p>但此时的 Lua 还有传统脚本语言常见的两个问题:效率低和代码暴露。而 LuaJIT 引入的 JIT 技术能够有效地解决了这两个问题。</p><h4>JIT</h4><p>JIT(Just-In-Time Compilation),实时编译,是动态编译的一种形式。在计算机科学中,动态编译并不是唯一的编译形式,比如现今仍然流行的 C 语言使用的就是另一种形式:静态编译。</p><p>需要指出的是,我们也常常将 C 语言的这种与动态编译相反的编译方式称为提前编译(AOT,Ahead-of-Time Compilation),但二者并不是完全对等的。AOT 仅是描述在执行程序前,将某种“高级”语言编译为某种“低级”语言的行为。其编译的目标语言并不一定特定于程序宿主机上的机器码,而是任意定义的。比如将 Java 编译为 C,或者将 JavaScript 编译为 V8 等等这些行为也会被视为 AOT。由于所有静态编译在技术上都是提前执行的,所以在这种特定的上下文中使用时,我们可以将 AOT 视为与 JIT 相反的静态编译。</p><p>抛开这些冗杂的名词,想到静态编译的产物,你可能会发现,Lua 语言面临的问题也可以通过静态编译来解决。但事实上,这就丢失了 Lua 作为脚本语言的优势:热更新的灵活性和良好的平台兼容性。所以目前除了有特殊需求的脚本语言外,大部分脚本语言都在使用 JIT 尝试提高语言性能,比如 Chromium 平台上使用 V8 的 JavaScript 和使用 YJIT 的 Ruby。</p><p>JIT 尝试将 Lua 的动态解释和 C 的静态编译两者的优缺点相结合,在脚本语言的执行期间,通过不断地分析正在执行的代码片段,编译或重新编译这段代码,以得到执行效率的提升。此时,JIT 假设的目标是,由此得到的性能提升能够高于编译或重新编译这段代码的开销。理论上说,由于能够进行动态地重新编译,JIT 在此过程中,可以针对正在运行程序的特定平台架构进行优化、加速,在某些情况下,能产生比静态编译更快的执行速度。</p><p>JIT 分为传统的 Method JIT 和 LuaJIT 正在使用的 Trace JIT 两种。Method JIT 是将每一个方法(Method)翻译为机器码;而如下图所示,更先进的Trace JIT 假定 “对只执行一两次的代码,解释执行比 JIT 编译执行要快”,以此为依据对传统 JIT 进行优化,具体表现为将频繁执行的代码片段(即热路径上的代码)认定为需要跟踪的代码,将这部分代码编译成机器码执行。</p><p><img src="/img/remote/1460000043574787" alt="LuaJIT 的原理" title="LuaJIT 的原理"></p><h4>LuaJIT</h4><p>而 LuaJIT(2.x 版本)在 Trace JIT 的基础上,集成了使用汇编编写的高速解释器和基于 <a href="https://link.segmentfault.com/?enc=Su8ZaxYv5asDKWfhPpPj1g%3D%3D.KpcUhpGzoxsF75cfnG8ZtAlRkgSqy73SceusERDpWmsTLTMl5VfClRKbNESwU%2Fmh3m8uI8lsG7ddzWEx2S91tg%3D%3D" rel="nofollow">SSA</a> 并进行优化的代码生成器后端,大幅提高了 JIT 的表现,最终使得 LuaJIT 成为最快的动态语言实现之一。</p><p>除此之外,相对于原生 Lua 中为了与 C 交互而需要编写 Lua 与 C 的繁复绑定,LuaJIT 还实现了 FFI(外部函数接口,Foreign Function Interface)。该技术允许了我们在不清楚参数个数和类型的情况下,从 Lua 代码中直接调用外部的 C 函数和使用 C 的数据结构。由此功能,我们也可以直接使用 FFI 实现所需的数据结构,而非 Lua 原生的 Table 类型,进一步在性能敏感的场景下,提升程序运行的速度。有关使用 FFI 提高性能的技巧并非本文讨论的范畴,更深入的内容可以参阅 <a href="https://link.segmentfault.com/?enc=dOZGy11HT2v1okKFzVLgYg%3D%3D.1rJ1aSDMQWWghE3ZhdGFky4X9He670%2Bs8zvF8uFy2oodQ%2FfXyfvuFQmtN1D98VqBo0xK%2FsCt0thcHs9fAajLEURhEYpNVF9Xz3N9aSiqG0Q%3D" rel="nofollow">Why Does lua-resty-core Perform Better?</a>。</p><p>总而言之,LuaJIT 在 Lua 语法的基础上,实现了迄今为止脚本语言中最快的 Trace JIT 之一,并提供了 FFI 等功能,解决了 Lua 效率低和代码暴露的问题,让 Lua 真正成为了高灵活性、高性能和超低内存占用的脚本语言和嵌入式语言。</p><h3>与其它语言、WASM 的对比</h3><p>相对于 Lua 和 LuaJIT,我们可能对其它的一些语言更加熟悉,比如 JavaScript (Node.js),Python,Golang,Java 等。对比这些大众化的语言,我们可以看到更多 LuaJIT 的特性和优势,下面简单罗列一些 Lua/LuaJIT 与这些语言的对比:</p><ul><li>Lua 的语法设计是针对非软件工程师所设计的。所以像 R 语言一样,Lua 也拥有数组下标从 1 开始等适合普通人的设计。</li><li>Lua 非常适合作为嵌入式语言。Lua 本身拥有一个轻量的 VM,而 LuaJIT 在添加各种功能和优化后,也仍然很轻量。所以相对 Node.js 和 Python 之类庞大的运行环境,LuaJIT 直接集成到 C 编写的程序中后也不会增大太多体积。因此,实际上 Lua 是所有嵌入式语言中使用量比较大且主流的选择。</li><li>Lua 也很适合做“胶水”语言。类似 JavaScript(Node.js) 和 Python,Lua 也能很好地连接不同的库和代码。但稍有不同的是,Lua 与底层生态的耦合性更高,所以在不同的领域中,Lua 的生态可能并不通用。</li></ul><p>WASM(Web Assembly)是一种新兴的跨平台技术。这种起初设计为补充而非取代 JavaScript 的技术,因为能够将其它的语言编译成 WASM 字节码,同时还能作为安全沙箱运行代码,使得越来越多的程序也在考虑使用 WASM 作为嵌入或者胶水的平台。即便如此,Lua/LuaJIT 在对比新兴的 WASM 时,也仍然有不少优势:</p><ul><li>WASM 的性能是受限的,无法达到汇编的水准。普遍场景下的性能,WASM 肯定好过 Lua,但与 LuaJIT 有所差距。</li><li>WASM 与宿主程序的数据传递效率比较低。而 LuaJIT 可以通过 FFI 进行高效率的数据传递。</li></ul><h2>为什么 Apache APISIX 选择 LuaJIT</h2><p>尽管上文描述了 LuaJIT 自身的诸多优势,但对于大部分开发者而言,Lua 不是一门大众的语言,LuaJIT 更不是一个大众的选择。那为什么 Apache 基金会所属的云原生 API 网关 Apache APISIX 还是选择了 LuaJIT 呢?</p><p>作为云原生的 API 网关,Apache APISIX 兼具动态、实时、高性能等特点,提供了负载均衡、动态上游、灰度发布(金丝雀发布)、服务熔断、身份认证、可观测性等丰富的流量管理功能。我们可以使用 Apache APISIX 来处理传统的南北向流量,也可以处理服务间的东西向流量,还可以用作 k8s 的 Ingress Controller。</p><p>而这一切都建立在 Apache APISIX 所选择的 NGINX 和 LuaJIT 技术栈之上。</p><h3>LuaJIT 与 NGINX 结合带来的优势</h3><p>NGINX 是一个知名的高性能 HTTP 、TCP/UDP 代理和反向代理的 Web 服务器。</p><p>但在使用中,我们会发现很恼人的是,每次修改 NGINX 的配置文件后,都需要使用 <code>nginx -s reload</code> 重新加载 NGINX 配置。</p><p>不仅如此,频繁地使用该命令重新加载配置可能会造成连接的不稳定,增加业务丢失的可能性;而在某些情况下,NGINX 重载配置的机制也可能会造成旧进程的回收时间过长,影响正常的业务。对于该问题的深入讨论,可以阅读 <a href="https://link.segmentfault.com/?enc=mGyES99b54MRFWbmj94WzA%3D%3D.pL7zGxCMjokIJKtJkDnziPAcWcRQqxZQKzWtWwM23Ttv%2FW57LoB55gy0F1Eb1a9GcrgCVJqwZnkSFRQ9HF%2BPAtGu59mjuys5MJ0MRwpVozSWp5jaLemgkmwhvYZYv95E" rel="nofollow">为什么 NGINX 的 reload 不是热加载?</a>,这里不进行深入展开。</p><p>Apache APISIX 诞生的目的之一就是解决 NGINX 的动态配置问题,LuaJIT 的高灵活性、高性能和超低内存占用带来了这种可能性。<br>以最具普遍性的路由为例,Apache APISIX 通过在 NGINX 配置文件中只配置单个 location 作为主入口,而后续的路由分发则由 APISIX 的路由分发模块完成,以此实现了路由的动态配置。</p><p>为了实现足够高的性能,Apache APISIX 使用 C 编写了基于前缀树的匹配路由算法,并在此基础上使用 LuaJIT 提供的 FFI 编写了适用于 Lua 的接口。而 Lua 的灵活性,也使得 Apache APISIX 的路由分发模块,可以轻易地支持通过特定的表达式等方法,对同一前缀的下级路由进行匹配。最终在替代 NGINX 原生路由分发功能的前提下,实现了兼具高性能、高灵活性的动态配置功能。有关这部分功能的详细实现,可以查看 <a href="https://link.segmentfault.com/?enc=Ai1PbvigI1eaWSvD9fjIRw%3D%3D.ahpxfeIyc3yPp7QqILy4QJN9eCkQBNq5XI3Fvj52FEVcpXOUl4iejlt2Uf%2F0r3tg" rel="nofollow">lua-resty-radixtree</a> 和 <a href="https://link.segmentfault.com/?enc=DgkkO%2BSGgyKe5VOPG5wVoQ%3D%3D.OJ7WwSHZdOYGxLJ4cjfaJ8LLKIy%2B5kUg9%2FdsFxNc2QOWIw13TE1EPdy2qqe3%2FP5Ua2dXtZRS7al49oHVCnonCIwFJ62%2Fg%2Fd8uSKEYNLlIuw%3D" rel="nofollow">route.lua</a>。</p><p>另外,不只是路由,从负载均衡、健康检查,到上游节点配置、服务端证书,以及扩展 APISIX 能力的插件本身,都能在 APISIX 不重启的情况下重新加载。</p><p>同时,除了在使用 LuaJIT 进行插件等功能的开发,Apache APISIX 还支持了 Java、Go、Node、Python 以及 WASM 等多种方式开发插件,也让 Apache APISIX 的二次开发门槛大大降低,使 Apache APISIX 获得了丰富的插件生态和活跃的开源社区。</p><p><img src="/img/remote/1460000043574788" alt="Apache APISIX 的插件原理和生态" title="Apache APISIX 的插件原理和生态"></p><h2>总结</h2><p>LuaJIT 是 Lua 的实时编译器实现。</p><p>Apache APISIX 作为一个动态、实时、高性能的开源 API 网关,基于 NGINX 与 LuaJIT 带来的高性能、高灵活等特性,提供了负载均衡、动态上游、灰度发布、服务熔断、身份认证、可观测性等丰富的流量管理功能。</p><p>目前 Apache APISIX 已经来到了全新的 3.x 版本,并带来了更多的开源项目集成、云供应商集成,原生的 gRPC 支持,更多的插件开发方式选择,以及服务网格支持等功能。欢迎加入 Apache APISIX 社区,了解更多 LuaJIT 在云原生 API 网关中的应用。</p><h2>关于 API7.ai 与 APISIX</h2><p>API7.ai(<a href="https://link.segmentfault.com/?enc=zG0%2B0fK9VQx2OH%2F3LC6DhA%3D%3D.9T6DGofxygWHxnY85msV9%2F7r05JNBEDrumv5Sw41Myo%3D" rel="nofollow">支流科技</a> )是一家提供 API 处理和分析的开源基础软件公司,于 2019 年开源了新一代云原生 API 网关 -- APISIX 并捐赠给 Apache 软件基金会。此后,API7.ai 一直积极投入支持 Apache APISIX 的开发、维护和社区运营。与千万贡献者、使用者、支持者一起做出世界级的开源项目,是 API7.ai 努力的目标。</p>
聚焦人机交互智能应用领域,APISIX 在希沃网关的应用与实践
https://segmentfault.com/a/1190000043566227
2023-03-21T16:26:17+08:00
2023-03-21T16:26:17+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<blockquote>分享嘉宾简海青,视源股份运维负责人。</blockquote><p><a href="https://link.segmentfault.com/?enc=0zcqdI3kX3euwJG57KIdGw%3D%3D.dymrt1HMkFdmhNLaLoLIhaVDFiTABz7MPwk7Oh9OKrP3qvJFyh3ClWDzgeHA%2FKS7DelNKe3fV3vF7a9oYRjCGA%3D%3D" rel="nofollow">原文链接</a></p><p>视源股份(CVTE)自成立以来,依托在音视频技术、人机交互、应用开发、系统集成等电子产品领域的软硬件技术积累,建立了教育数字化工具及服务提供商希沃(seewo)、智慧协同平台 MAXHUB 等多个业内知名品牌。其中希沃从 2012 年到 2021 年连续 10 年蝉联中国交互智能平板行业市占率桂冠,已成为名副其实的行业标杆企业。</p><p><strong>随着技术的飞速发展,在人际交互智能领域,业务需求也对架构迭代有了更高的要求。为了应对日趋成熟及快速增长的业务现状,希沃又是如何在网关层面进行跟进的呢?</strong></p><h2>网关往期迭代与痛点</h2><p>希沃网关的发展经历了四个版本的迭代。2013 年公司开始尝试互联网业务,那时候采用了 OpenResty + NGINX 静态配置的方式搭建了最初的网关,开发人员通过 SCP(Secure Copy)进行发布。与此同时一个比较严重的问题就是,每次上线发布都需要运维人员的协助才能保证平滑上线。</p><p>随着业务的发展和人员的扩充,2016 年我们开发了第二代发布系统和相关迭代网关。这次是基于 OpenResty 集成了 upsync 模块,同时配合 Consul 来进行服务发现。第二代的系统解决了上一代开发人员无法独立发布上线的问题,但仍需要运维协助才能进行扩容。</p><p>之后公司业务开始了迅猛发展,开始对网关以及产品的弹性扩缩能力有了更高的要求。2018 年我们基于 K8s 开发了第三代系统。考虑到仍有部分应用遗留在数组机上,所以整个网关架构是在 K8s 上使用 Ingress NGINX 来当作第二层的网关,第一层网关仍是 OpenResty 配合的双层网关架构。这种情况下虽然解决了前代发布扩容等自助问题,但又引入了新的麻烦。</p><p>业务的快速扩充致使对于整体稳定性的要求越来越高。采用这种双层网关架构后,一层 NGINX reload 和二层网关的路由变更,都会造成长连接断开。这对于一些长连接使用场景会影响较大,比如软件需要获取老师的授课状态时连接突然断开,状态获取中断从而影响授课。</p><p><img src="/img/bVc34Ff" alt="" title=""></p><p>本身双层架构就会带来成本层面的一些增加。同时,从上图的网关流量拓扑图可以看到,除上述遗留问题外也还存在一些架构上的痛点:</p><ul><li>在双层网关架构下,不管是在第一层网关添加域名、修改配置或者添加一些特殊规则等,都需要 reload NGINX。</li><li>同时从整体架构来看,组件的配合对于流量控制层面来说比较差。尤其是目前我们的业务用户体量已达到千万级别,一旦客户端出现不可规避的异常,就有可能出现侵蚀服务端的情况,这种时候如果在网关层面没有一定的流量控制能力,对于后端来说将会造成非常严重的雪崩。<br>因此,基于上述迭代遗留问题和架构痛点,在 2022 年我们引入了 APISIX 来解决上述问题。同时借助 APISIX,也加强了在网关层面对于流量的控制能力。</li></ul><p>但是在迁移 APISIX 的过程中,也会存在一些已知挑战。比如:</p><ul><li>⼀层 NGINX 域名多,定制化规则复杂。目前我们的业务中有 700+ 域名,同时还存在非常多的定制化配置,比如重定向、黑白名单等,这些都需要适配 APISIX 的插件。</li><li>由于历史遗留问题,⼀层 NGINX 和二层 Ingress 网关还是⼀对多的关系,对于后续的流量切换是不是会很复杂,这也是一个待解决问题。</li><li><p>内部存在的双层 DNS 架构。目前 DNS 解析主要用于处理公网和服务器内部的解析,所以对于后续的方案我们更希望是一个能方便回滚同时可以优化内网调用性能的。</p><h2>迁移 APISIX 后架构调整</h2></li></ul><p>面对上述已知的挑战,在迁移过程中主要进行了以下三个角度的操作。由于整个迁移过程没有涉及到研发内容,所以全部都是由运维人员实施的。</p><p>在迁移过程中,首先要做的就是 APISIX 路由的生成。基于原本的架构,我们去掉了一层特殊功能,比如 rewrite、set-header 等。弱化一层网关的转发,把所有功能都集中在二层的 Ingress 上,然后基于 Ingress 去生成 APISIX 的路由。同时在 NGINX 配置的基础上适配 APISIX 的插件。</p><p>路由生成后,就需要去校验整个转发过程是否正确。我们基于 goreplay 的录制回放来验证路由转发的正确性,同时通过自动化脚本来验证插件功能是否正常。在功能校验通过的情况下,我们会继续验证 APISIX 在性能层面是否满足内部需求。因此,在性能压测过程中我们自研了 elastic-apm 插件,同时针对部分影响 QPS 的插件进行了性能优化。</p><p>处理完功能跟性能相关的问题后,最大的挑战就是流量切换了,因为流量切换将直接关乎生产质量。</p><p><img src="/img/bVc34Fg" alt="" title=""></p><p>虽然前边我们已经使用了 goreplay 进行流量录制回放,但我们仍然需要一个比较可靠的回滚方案。假设流量切换造成了异常,比如转发异常或者是 APISIX 出现崩溃时,能够进行快速回滚操作。基于此,我们首先切换了公网流量,因为公网是通过 WAF 回源到 SLB 来进行流量切换的,这时如果我们切换到 APISIX,就可以很方便地去修改回源地址来将整个流量进行回滚,如上图标注「切换」字样所示。这个异常情况下的流量切换过程基本是在秒级别,可能 10 秒内就把所有流量都切回来了。</p><p>完成了公网流量切换的情况下,顺利运行了几天,我们就通过 APISIX 将内网流量也进行了变更,然后整个生产上的切换就全部完成了。但是这个上线过程中,其实我们还是遇到了一些问题的。</p><h2>迁移过程中的问题与解决方案</h2><h3>Prometheus 插件转发延迟</h3><p>这个是在我们内网测试环境中发现的一个问题。由于我们的内网是 all-in-one 的测试环境,所有部门都使用同一个 APISIX 的入口,所以路由规则非常多,达到 4000+。这样就会导致每次拉取 Prometheus 插件时, metrics ⼤小达到 80M+,造成单个 worker 进程跑满,从而造成 worker 的转发延迟。</p><p><img src="/img/bVc34Fh" alt="" title=""></p><p>这个问题是目前 APISIX 开源版本存在的一个现象,主要是因为业务流量和内部 API 流量(比如Prometheus metrics 和 Admin API)都共用 worker 造成的。我们在之前是针对 Prometheus 插件进行了修改,其中延迟相关的 metrics 占用了 90%以上(如上图所示),所以我们将这部分采集去掉了。去掉这部分后,业务层面还是满足了我们的监控使用需求,并未造成影响。</p><p>不过最近我们针对这个问题又有了新的方案,目前还处于 demo 阶段。这套新方案是对 NGINX 源码进行修改,通过多启动⼀个或多个 worker 进程(isolation process) 来专⻔监听特定端口的请求(比如 Prometheus、Admin API、Control API 等),不监听处理正常业务端口请求。其它的 worker 进程则取消监听上述端口,只处理正常业务端口请求,来确保 APISIX 内部请求和正常业务请求间不会互相影响。</p><p><img src="/img/bVc34Fj" alt="" title=""></p><h3>默认路由匹配异常</h3><p><img src="/img/bVc34Fl" alt="" title=""></p><p>在上线 APISIX 后,我们发现域名并没有走精确匹配模式,而是采用了通配符匹配,这跟 NGINX 的域名最长匹配是不一致的。为此,我们通过更换路由策略,将 URL 方式改成了 host+URL 的方式,解决了该问题。</p><p>但关于 APISIX 基于 URL 路由策略作为默认路由的问题,大家可以在自己的生产环境中进行压测后再决定是否保留。</p><p>假如你的生产场景中属于 URL 特别多、域名特别少的类型,那 APISIX 这种默认路由策略是完全 OK 的。但在我们的业务场景下,并没有这么多 URL,所以采用 host+URL 的方式是更满足我们的性能需求。</p><h3>默认自动绑核问题</h3><p>在云原生的背景下,大部分用户都会选择将 APISIX 部署在容器中使用。但 APISIX 在默认配置下会进行自动绑核,这样就会导致在容器化场景下,可能只会用到前几个核心,造成前几个核心跑满而后几个核心仍处于空闲的状态。虽然 CPU 使用率不高,但是会使 APISIX 转发出现延迟。</p><p><img src="/img/bVc34Fm" alt="" title=""></p><p>不过 APISIX 社区最近已经开始调整这个配置,将 <code>worker_cpu_affinity</code> 配置的默认值从 <code>true</code> 改为了 <code>false</code>。因此这个问题目前在 APISIX 版本中已经解决了。</p><h3>版本升级兼容问题</h3><p>在上线 APISIX 的过程中,我们还发现在较老的系统或 OpenSSL 库中,它的 ssl_ciphers 和服务端默认值无交集,从而造成 SSL 握手失败。</p><p>针对这个问题,我们建议大家在上线 APISIX 之前,先通过一些 SSL 工具先去探测一下当前旧网关与 APISIX 网关的 SSL 握手交集是否正确或满足使用场景,然后再进行规模化的迁移调整。</p><p>除此之外,在 APISIX 发布 2.15 LTS 版本后,我们就在内网进行了升级,但是升级后就发现了一些路由匹配相关的问题。</p><p>因为从旧版本升级到新版本时,存在一些兼容性问题,导致 redirect 插件参数 <code>http_to_https</code> 为<code>true</code> 时,参数 <code>http_to_https</code> 和 <code>append_query_string</code> 校验失败,进而路由加载失败,导致路由丢失。这种情况下就会在路由匹配时出现 404 或者转发到其他上游的情况。</p><p>目前这个问题已经在 APISIX 的 master 分支中解决了,但是并没有针对 2.15 版本进行单独解决,所以大家在使用该版本时也需要留意这部分问题。</p><h2>应用 APISIX 的收益及展望</h2><p>虽然前边提到了一些我们在上线 APISIX 过程中遇到的问题,但是在应用 APISIX 之后,给公司业务层面还是带来了很多价值的。比如:</p><ul><li><strong>运维效率提升。</strong> 使用 APISIX 后,再也不存在 reload 相关的烦恼,可以做到随时更新路由和证书等,给开发人员带来了操作上的便利。</li><li><strong>流量控制能力提升。</strong> 使用 APISIX 后,我们在熔断和限流方面都得到了提升,从而加强了在流量管控层面的能力,进一步稳固了整个业务核心流程。</li><li><strong>自研插件,增强网关能力。</strong> 得益于 APISIX 的强拓展性和自身插件性能的优异,我们也会更主动地去开发一些插件。比如我们在 APISIX 上集成了统一鉴权的能力,新业务无需单独对接鉴权系统,加快了产品迭代流程。</li></ul><p><img src="/img/bVc34Fn" alt="" title=""></p><ul><li><strong>去掉了冗余的一层 NGINX,实现降本增效。</strong> 之前的双层网关架构中,一层的 NGINX 对于开发人员并不透明。现在将双层网关合并为一层后,开发人员可以很清晰地看到架构中一些关于路由或插件等配置,对于排查问题来说更加方便快捷。<br>在后续使用 APISIX 的规划中,我们还是会继续在网关层面进行增强。比如开发针对内部业务的自研插件,提供多租户的能力,或者是将 API 管理的功能带到网关层面等等。</li></ul><p>当然在这个过程中,我们也在积极回馈社区。目前已在社区中贡献了 8 个 PR,帮忙完善和修复了一些生态插件相关的问题。比如完善 batch_request 支持自定义 uri、为 hmac-auth 插件提供请求 body 校验等功能。</p><p>希望在后续的实践过程中,我们可以更全面地使用和发挥 APISIX 的特性,更加积极地探索 APISIX 的使用场景。期待未来有更多的共建功能上线。</p><h2>关于 API7.ai 与 APISIX</h2><p>API7.ai(<a href="https://link.segmentfault.com/?enc=LOHtxfUdJeKzNgdm4gVgnQ%3D%3D.ug9m%2FcBCgWAjDUpLPtD4BcI0W1ucacA1yV2gdgS2PZc%3D" rel="nofollow">支流科技</a>)是一家提供 API 处理和分析的开源基础软件公司,于 2019 年开源了新一代云原生 API 网关 -- APISIX 并捐赠给 Apache 软件基金会。此后,API7.ai 一直积极投入支持 Apache APISIX 的开发、维护和社区运营。与千万贡献者、使用者、支持者一起做出世界级的开源项目,是 API7.ai 努力的目标。</p>
备战一年半,我们让最火的开源网关上了云
https://segmentfault.com/a/1190000043566211
2023-03-21T16:24:44+08:00
2023-03-21T16:24:44+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<p>这是最好的时代,我们满怀信心施展才华;这也是最坏的时代,我们遇到了前所未有的竞争。工程师们从不畏惧困难,因为热爱能化解一切困难。本文源于对张超(API7 Cloud 团队负责人,Apache APISIX PMC member)的采访,这是一个关于 API7 Cloud 诞生的故事,路转峰回,寻寻觅觅。一年半后,我们舒颜感叹:莫愁千里路,自有到来风!</p><h2>一款优秀的产品只需要一个契机</h2><p>云原生时代风云变幻,开源产品层出不穷。</p><p>2019 年 APISIX 在温铭和院生的代码下诞生,6 月当时仍处于 Demo 阶段的 APISIX 便在 GitHub 上开源。一经开源,APISIX 的独特性让它迅速席卷开源领域。2019 年 8 月,APISIX 成功进入了 Apache 孵化器,次年 7 月 APISIX 顺利毕业,并成为了 Apache 软件基金会的顶级开源项目。</p><p>Apache APISIX 的诞生打响了 API7.ai 商业化版图的“第一枪”。API7.ai 基于对市场发展的理解,定位 SaaS 未来能成为发展的方向,能够真正给公司带来增长,开始投入商业化。<strong>我们 API7 Cloud 团队的负责人张超表示,“我们也非常希望能够基于 Apache APISIX 这样一个优秀的 API 网关去做一款 SaaS 产品”。</strong></p><p>在发现这个机会之后,我们很快进入了调研阶段。2021 年 6-7 月,API7 Cloud 完成了最初的提议,下半年定了一个 MVP(最简化可实行产品),随即工程师们乐此不疲地投入开发。一方面,我们逐步调整产品的稳定性和可用性;另一方面,不断地往 MVP 里加一些新功能。后来又找到了一些试用客户,和客户一起使用并打磨产品。</p><p>API7 Cloud 的出现为云原生时代多云与混合云场景下管理 API 提供了最佳实践,这么一款优秀的产品背后,是一群热血的年轻人,撑起了 SaaS 产品新理念的一片天。</p><h2>一群热血且以用户为中心的工程师</h2><p>正如团队负责人的飞书签名所言:“I wanna create a great SaaS product”,简短的一句话背后是无数个勤勤恳恳加班加点的日夜。</p><p>这一路上,我们同甘也共苦,累并快乐着!</p><p><strong>我们 API7 Cloud 工程师团队几乎都是 Apache Software Foundation 顶级开源项目的 committer 和 PMC member</strong>,因而都对 APISIX 有很深入的了解,尤其是理解了技术最底层核心的部分。我们的成员同时都是<strong>开源爱好者</strong>,曾在快手、阿里巴巴、有赞等国内知名企业担任过重要的角色。对开源的热情把我们汇聚在 API7.ai,而对产品和用户的热忱是我们源源不断的动力。</p><p>在产品投入市场初期,我们团队发现了一个比较严重的设计问题,导致在加入一些和 APISIX 相关的新功能时,发现在技术上改动的工作量、难度以及兼容性上都遇到了很大的挑战。当时用户已经在使用,为了不影响用户的正常使用,我们引入了一个新的方案:<strong>在非工作日的晚上执行发布,错开用户使用的时间。该改进持续了两个月左右,其中涉及 3-4 次发布。</strong></p><p><strong>我们注重用户体验。</strong>平时在用户群里,API7 Cloud 工程师们积极响应,跟进用户的问题。但凡遇到安全问题,便会立即投入人力收紧问题,遇到非安全的问题也会控制在两周内收紧,不过 API7 Cloud 目前为止还未出现安全性的问题。每周工程师们会和客户沟通使用的体验,以不断改进。另外,我们团队成员<strong>还是写文章的好手</strong>,他们自发组织撰写介绍 API7 Cloud 亮点的文章,以向更多用户提供专业的服务。</p><h2>不平坦,才更有成就感</h2><p>在项目中,我们也遇到过很多难点。</p><p>我们团队认为:“<strong>产品如何定价</strong>是一个难点,对此我们也一直在探索,探索对产品的理解和市场的理解,同时结合用户的反馈,为未来制定合理的定价策略,综合多维度去考虑。”</p><p>要想打开海外市场,还需要满足数据主权的管理要求。例如,欧盟制定了 GDPR(General Data Protection Regulation),即《通用数据保护条例》,在欧美市场进行贸易却不满足该规定将面临巨额的赔偿。在数据主权上,我们做了很多的努力。所幸的是,<strong>API7 Cloud 本身在数据主权上有一定的积累,客户也用 APISIX 解决过类似的问题</strong>。因此,API7 基于用户的需要,制定了一套满足需要的方案,逐渐立足于国际市场。</p><p><strong>此外,我们重视产品在用户数据隐私性方面的保护</strong>。2022 年下半年我们投入了很多时间在保障用户数据安全方面,目前已正式通过 SOC 2 Type 1 审计。该审计报告能有效说明<strong> API7 Cloud 在安全性、可用性和保密性上的能力</strong>,其中包括对核心系统、账号管理、变更审批等等方面的管理,例如:备份、容量规划、权限回收。</p><p>API7 Cloud 团队的工程师们在整个过程中投入了很多精力,有效地推动了制度落地,可谓是能文能武!</p><h2>多云与混合云场景下的 API 管理利器</h2><p>在最初的产品定位中,我们考虑到<strong>多云和混合云的场景下统一管理 API 的使用</strong>将会是一个巨大的挑战,因而 API7 Cloud 的定位就是帮助企业解决多云和混合云场景下的问题。它的目标用户是那些业务上了云,且需要一款 API 管理工具的用户;或者不仅仅是上了云,而且是使用了多云或者混合云的用户。</p><p><strong>API7 Cloud 基于 Apache APISIX,围绕着 Apache APISIX 所提供的功能进行了产品化,旨在让用户更简单、更放心地配置和使用 APISIX</strong>。市面上有很多类似的产品,例如:Kong Konnect、Tyk Cloud、Mulesoft Anypoint Platform 和 Amazon API Gateway。但 API7 Cloud 集成了 APISIX 的优势,这些是 API7 Cloud 独有的,例如:</p><ul><li><strong>基金会品牌</strong>:无品牌纠纷,实力过硬</li><li><strong>高性能</strong>:APISIX 的 QPS 能达到 23,000,平均延迟仅 0.6 毫秒</li><li><strong>社区活跃</strong>:APISIX 社区响应快,迭代更新速度快</li><li><strong>生态强大</strong>:支持近 100 个插件,生态包容</li></ul><p>API7 Cloud 以 Apache APISIX 为基础,又对它进行了更企业化的定制。</p><ul><li><strong>支持开源 APISIX 的所有插件功能</strong></li><li><strong>强化了开源 APISIX 的动态能力</strong>,比如支持了动态的服务发现功能</li><li><strong>开放 API 且提供 SDK,允许自动化的 API 管理</strong>,允许通过程序集成 API7 Cloud 实现自动化</li></ul><p>在 2023 年即将支持 APISIX Gateway API 规范,未来允许用户在开源 APISIX、企业版以及 API7 Cloud 之间相互切换。由此一来,为用户节约数据迁移的成本。</p><p><img src="/img/remote/1460000043566213" alt="API7 Cloud 架构" title="API7 Cloud 架构"></p><p>在技术设计层面,API7 Cloud 特色鲜明,提供了诸多功能,例如:</p><ul><li><strong>API 管理</strong>:提供 API 管理、SSL 证书管理、灰度发布、精细化路由等多种功能,并能通过导入 OpenAPI 文档创建 API</li><li><strong>可观测性</strong>:API7 Cloud 聚合延迟、QPS、HTTP 状态码分布、HTTP 请求成功率等状态信息,通过 Cloud 产品界面可视化,让客户可以及时发现系统中的潜在问题</li><li><strong>无供应商锁定</strong>:客户的服务可部署在任何云环境或本地。此外,API7 Cloud 与 Apache APISIX 百分百兼容,开源用户也可以零成本迁移项目到云端,无需担心供应商锁定</li><li><strong>全方位的安全防护和隐私合规</strong>:所有的通信和数据传输都是通过 mTLS 协议进行加密。存储在 API7 Cloud 上的数据,也有身份认证、鉴权和审计功能的保护,符合 GDPR 的合规要求</li></ul><p>以及即将在 2023 年实现的支持<strong>多集群管理</strong>以及<strong>数据主权</strong>的功能。</p><p><strong>API7 Cloud 另一个优势在于它使用简单易上手</strong>。如“UML之父”Grady Booch 所说:“好的软件之所以好,是因为它化繁为简。”用户在使用 API7 Cloud时,在注册登录后按照指引仅需在三步内完成网关实例的搭建,即可进行 API 管理,包括灰度发布、添加限流策略、添加认证等。</p><h2>前路漫漫亦灿灿</h2><p>API7.ai 是一家致力于支持 API 管理和分析的开源软件基础设施公司,它为微服务和实时流量处理提供广泛的产品,例如 API 网关、Kubernetes Ingress Controller 和服务网格。API7 Cloud 是 API7.ai 推出的中心化 API 管理平台,它在多云和混合云的场景下统一管理 API 中发挥了独特的作用。<br>未来我们会在 <strong>API 资产管理、API 数据主权、API 分析</strong>上发力。希望帮用户更好地感知他们 API 的使用以及感知用户的客户是如何使用他们的 API 的,让他们能有一个非常直观的感受。同时基于 API 分析以及 API 的一些能力,希望能给用户做一些<strong> API 上的预测</strong>,从而提前帮助用户做<strong>容量规划</strong>和<strong>风险预警</strong>。</p><p>请期待我们 API7 Cloud 为用户带来更多新的体验,我们也期待更多用户能切身体会到 API7 Cloud 的实力!</p><p><a href="https://link.segmentfault.com/?enc=XrAjF0PMsJ0bOxHTHbNqSg%3D%3D.6uZ2huAHAi4MK8XH8CUvTZ2ugAj%2FFRQui%2Fm%2F7Cjs13NPdRZ6jUp70%2BMztQHgjSYdRHM7%2BSN9fxMsy7QfbOCCWbmKruvAKka1GeDZEJE9WD9kzigifRGplN2J%2FdVHvpx0" rel="nofollow">即刻试用</a></p><h2>关于 API7.ai 与 APISIX</h2><p>API7.ai(<a href="https://link.segmentfault.com/?enc=igOCrUKy2Z0p%2BKyeknqSPA%3D%3D.Y7sdiszQQvJ%2BCKDJnxvdc6snH%2FR9yv316Ah%2F8SDCvlE%3D" rel="nofollow">支流科技</a>)是一家提供 API 处理和分析的开源基础软件公司,于 2019 年开源了新一代云原生 API 网关 -- APISIX 并捐赠给 Apache 软件基金会。此后,API7.ai 一直积极投入支持 Apache APISIX 的开发、维护和社区运营。与千万贡献者、使用者、支持者一起做出世界级的开源项目,是 API7.ai 努力的目标。</p>
Apache APISIX 3.2.0 LTS 正式发布
https://segmentfault.com/a/1190000043551270
2023-03-17T12:12:45+08:00
2023-03-17T12:12:45+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<p>APISIX 3.2.0 是 3.0 大版本以来的第一个 LTS 版本。此次发版,是 3.x 时代更替 2.x 时代的一大里程碑。从此之后,新的一系列 patch 版本将会在 3.2 的基础上发布。本次发布一如往常一样带来了许多新的插件和特性,为 APISIX 的使用者带来不一样的新玩法。</p><p><a href="https://link.segmentfault.com/?enc=SIumX7lFBGklEp7zcDSydg%3D%3D.waQziQ319X8keBtRJkB06mcqS%2Fe%2BuUCeGHp6Z1OJg9YgtO%2BUAxDhshlj8ycFljFxtrwfcJ6VAU0QDziWfZhIxvd%2BmAjY5YUujYRbtx2Qajg%3D" rel="nofollow">原文链接</a></p><h2>新特性:四层上的服务发现</h2><p>只有少数网关支持服务发现,APISIX 就是其中之一。在 3.2.0 版本中,APISIX 把原来七层上的服务发现的功能也做到了四层上。这样一来,将 APISIX 作为 TCP/UDP 代理时也能享受到服务发现带来的便利性。和在七层上的服务发现一样,要想用上服务发现,我们需要先在 <code>config.yaml</code> 中配置服务发现服务器的地址:</p><pre><code class="yaml">discovery:
nacos:
host:
- "http://192.168.33.1:8848"</code></pre><p>然后在具体的 upstream 上配置 <code>discovery_type</code> 和 <code>service_name</code>:</p><pre><code class="shell">$ curl http://127.0.0.1:9180/apisix/admin/stream_routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d '
{
"remote_addr": "127.0.0.1",
"upstream": {
"scheme": "tcp",
"discovery_type": "nacos",
"service_name": "APISIX-NACOS",
"type": "roundrobin"
}
}'</code></pre><p>这样访问 stream_routes 时,上游的节点会从 Nacos 的 APISIX-NACOS 服务中获取。</p><h2>新插件:RESTful 请求转 GraphQL</h2><p>在 3.2 版本中,APISIX 新增了一个能将 RESTful 请求转成 GraphQL 的插件。假如你有这样的 GraphQL 查询语句:</p><pre><code>query($name: String!, $githubAccount: String!) {
persons(filter: { name: $name, githubAccount: $githubAccount }) {
id
name
blog
githubAccount
talks {
id
title
}
}
}</code></pre><p>其中 <code>$name</code> 和 <code>$githubAccount</code> 是两个 GraphQL 变量。</p><p>我们可以用如下的配置来暴露出同样的 RESTful 接口:</p><pre><code class="shell">curl --location --request PUT 'http://localhost:9180/apisix/admin/routes/1' \
--header 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' \
--header 'Content-Type: application/json' \
--data-raw '{
"uri": "/graphql",
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:8080": 1
}
},
"plugins": {
"degraphql": {
"query": "query($name: String!, $githubAccount: String!) {\n persons(filter: { name: $name, githubAccount: $githubAccount }) {\n id\n name\n blog\n githubAccount\n talks {\n id\n title\n }\n }\n}",
"variables": [
"name",
"githubAccount"
]
}
}
}'</code></pre><p>这里 <code>query</code> 是我们要用到的查询语句,<code>variables</code> 是事先声明的变量列表。</p><p>接下来就能像 RESTful 接口一样访问它:</p><pre><code class="shell">curl --location --request POST 'http://localhost:9080/graphql' \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "Niek",
"githubAccount": "npalm"
}'</code></pre><p>结果跟直接用对应的 GraphQL 语句访问上游是一样的:</p><pre><code class="json">{
"data": {
"persons": [
{
"id": "7",
"name": "Niek",
"blog": "https://040code.github.io",
"githubAccount": "npalm",
"talks": [
{
"id": "19",
"title": "GraphQL - The Next API Language"
},
{
"id": "20",
"title": "Immutable Infrastructure"
}
]
}
]
}
}</code></pre><p>你同样能用 GET 请求来访问同样的接口,这时候参数就需要通过 query string 来传递:</p><pre><code>curl 'http://localhost:9080/graphql?name=Niek&githubAccount=npalm'</code></pre><h2>新特性:支持在每个日志插件上设置日志格式</h2><p>在 3.2 版本,我们整理了 APISIX 现有的十多个 access 日志插件。现在每个插件都支持配置自定义日志格式:</p><ol><li>在该插件的 plugin metadata 中定义全局的日志格式</li><li>在具体的路由规则上的该插件的配置中定义当前路由的日志格式</li></ol><p>以 <code>clickhouse-logger</code> 为例,下面是定义全局日志格式的做法:</p><pre><code class="shell">curl http://127.0.0.1:9180/apisix/admin/plugin_metadata/clickhouse-logger \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"log_format": {
"host": "$host",
"@timestamp": "$time_iso8601",
"client_ip": "$remote_addr"
}
}'</code></pre><p>下面则是当前路由的日志格式:</p><pre><code class="shell">curl http://127.0.0.1:9180/apisix/admin/routes/1 \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"plugins": {
"clickhouse-logger": {
"log_format": {
"host": "$host",
"@timestamp": "$time_iso8601",
"client_ip": "$remote_addr"
},
"user": "default",
"password": "a",
"database": "default",
"logtable": "test",
"endpoint_addrs": ["http://127.0.0.1:8123"]
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980": 1
}
},
"uri": "/hello"
}'
</code></pre><h2>新插件:请求体/响应体转换</h2><p>你是否在为如何把返回 XML 的古早上游服务介绍给只接受 JSON 的现代客户端而苦恼?3.2.0 版本新增的 <code>body-transformer</code> 插件开源解决这个问题。</p><p><code>body-transformer</code> 插件支持 JSON 和 XML 之间的互相转换。不过这并非是它唯一能做的事。它还支持通过模板配置输入输出的内容的具体格式。举个例子,</p><p>假设你有下面的 JSON 模板:<code>{"foo":"{{name .. " world"}}","bar":{{age+10}}}</code>,并把它配置到 <code>body-transformer</code> 插件的 <code>request.template</code> 字段中:</p><pre><code> ...
"body-transformer": {
"request": {
"template": "..."
}
}
...</code></pre><p>那么当请求内容为 <code>{"name":"hello","age":20}</code>,发送给上游的是改写之后的 <code>{"foo":"hello world","bar":30}</code>。我们采用了 <code>lua-resty-template</code> 来渲染模板,所以你可以在模板中嵌入 Lua 表达式来实现改写逻辑。</p><p>对上游输出的改写也是类似的,只是需要配置的是插件的 <code>response.template</code> 字段。</p><h2>新功能:优化和更多的小功能</h2><p>除了上面提到的几个大的功能外,此次发布也包含许多值得述说的改动:</p><ul><li><code>error-log-logger</code> 插件支持发送错误日志到 Kafka</li><li><code>limit-count</code> 插件支持返回 <code>X-RateLimit-Reset</code> 响应头</li></ul><p>等等。</p><p>如果你对完整的内容感兴趣,请参考 3.2 发布的 CHANGELOG:<a href="https://link.segmentfault.com/?enc=225%2BxjQlyHlGLAhpY%2FTIOQ%3D%3D.hUo2PuOZBxyYm4sN8fZ1U3B4S9AAMtNMHZl8DrBtSUGmGiEwbwSnuLk6YGWGiczzevn68ip36F89GLN8Halg8tblbXJWUYfvNl%2B6dv3ElWUeqMz9chTywGPpjGQss4Zc" rel="nofollow">https://github.com/apache/apisix/blob/release/3.2/docs/zh/latest/CHANGELOG.md#320</a></p><h2>关于 API7.ai 与 APISIX</h2><p>API7.ai(<a href="https://link.segmentfault.com/?enc=5e35eDsbiwIQH2amCfzEvQ%3D%3D.jktlISMZfFPtyCL8mVwwveUxLZw%2BXMpSO01e3aEPFFM%3D" rel="nofollow">支流科技</a>)是一家提供 API 处理和分析的开源基础软件公司,于 2019 年开源了新一代云原生 API 网关 -- APISIX 并捐赠给 Apache 软件基金会。此后,API7.ai 一直积极投入支持 Apache APISIX 的开发、维护和社区运营。与千万贡献者、使用者、支持者一起做出世界级的开源项目,是 API7.ai 努力的目标。</p>
无需二次开发,SOAP-to-REST 简化企业用户的业务迁移和整合
https://segmentfault.com/a/1190000043548116
2023-03-16T16:42:16+08:00
2023-03-16T16:42:16+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<p>本篇文章分析了 SOAP-to-REST 的多种实现方式,并介绍如何使用 APISIX 做零代码代理。</p><blockquote>作者罗锦华,API7.ai 技术专家/技术工程师,开源项目 pgcat,lua-resty-ffi,lua-resty-inspect 的作者。</blockquote><p><a href="https://link.segmentfault.com/?enc=aPng1dXwhQgB6Rdn73A0Bw%3D%3D.jjPHC6eW17M3c5HDrgZf8w7pNdaITxBBegj6jgvflG8oHK1IJ0Jh4uJwTERVHvo%2FhTbn%2FcSN%2BJYhb9G%2FHU2FTQ%3D%3D" rel="nofollow">原文链接</a></p><h2>1. 什么是 Web Service</h2><p>Web Service 由万维网联盟 (W3C) 定义为一种软件系统,旨在支持通过网络进行可互操作的计算机间交互。</p><p>Web Service 完成特定任务或任务集,并且由名称为 Web Service 描述语言 (WSDL) 的标准 XML 表示法中的服务描述进行描述。服务描述提供了与服务交互必需的所有详细信息,包括消息格式(用于详细说明操作)、传输协议和位置。</p><p>其他系统使用 SOAP 消息与 Web Service 进行交互,通常是通过将 HTTP 与 XML 序列化和其他 Web 相关标准一起使用。</p><p>Web Service 的架构图(注意现实中 Service broker 是可选的):</p><p><img src="/img/remote/1460000043548118" alt="Web Services architecture" title="Web Services architecture"></p><p><em>图片来源(遵循 CC 3.0 BY-SA 版权协议): <a href="https://link.segmentfault.com/?enc=Yg1wi7EuZ9mXLc1VqcLN7Q%3D%3D.ztzRpruBLx9UZyPTCYqXg8j76ulKwut001K05To7fS09rXfgJUePrtuIYs%2F8%2B019" rel="nofollow">https://en.wikipedia.org/wiki/Web_service</a></em></p><p>WSDL 接口隐藏服务实现方式的详细信息,这样服务的使用便独立于实现服务的硬件或软件平台,以及编写服务所使用的编程语言。</p><p>基于 Web Service 的应用程序是松耦合、面向组件和跨技术的实现。 Web Service 可以单独使用,也可以与其他 Web Service 一起用于执行复杂的聚集或业务事务。</p><p>Web Service 是 <a href="https://link.segmentfault.com/?enc=uYBPgFSqHi1Oq5gD6lwFTw%3D%3D.Rjw9DnlzhtoApzeFPzWN66lL3anmRuTtyXzwmyspHth0IisbWPIKx866iApDSba4rOlmGAGONocI%2Bbq11x96ww%3D%3D" rel="nofollow">Service-oriented architecture</a> (SOA) 的实现单元,SOA 是用来替换单体系统的一种设计方法,也就是说,一个庞大的系统可以拆分为多个 Web Service,然后组合起来对外作为一个大的黑盒提供业务逻辑。流行的基于容器的微服务就是 Web Service 最新替代品,但是很多旧系统都已经基于 Web Service 来实现和运作,所以虽然技术日新月异,兼容这些系统也是一个刚性需求。</p><h3>WSDL (Web Services Description Language)</h3><p>WSDL 是用于描述 Web Service 的一种 XML 表示法。 WSDL 定义告诉客户如何编写 Web Service 请求,并且描述了由 Web Service 提供程序提供的接口。</p><p>WSDL 定义划分为多个单独部分,分别指定 Web Service 的逻辑接口和物理详细信息。物理详细信息既包括诸如 HTTP 端口号等端点信息,还包括指定如何表示 SOAP 有效内容和使用哪种传输方法的绑定信息。</p><p><img src="/img/remote/1460000043548119" alt="Representation of concepts defined by WSDL 1.1 and WSDL 2.0 documents." title="Representation of concepts defined by WSDL 1.1 and WSDL 2.0 documents."></p><p><em>图片来源(遵循 CC 3.0 BY-SA 版权协议): <a href="https://link.segmentfault.com/?enc=5gcnZHWbYJISOjS%2FZ1PvpQ%3D%3D.rfBfVP0KknB2epF%2F3OUFbKKUzb%2FF6Z6Rd9P5JfOVIBezFxhSpv1dAjNqq6SsHSBT2J65DAwEaHLFnZBKTK9%2B1A%3D%3D" rel="nofollow">https://en.wikipedia.org/wiki/Web_Services_Description_Language</a></em></p><ul><li>一个 WSDL 文件可以包含多个 service</li><li>一个 service 可以包含多个 port</li><li>一个 port 定义了 URL 地址(每个 port 都可能不同),可以包含多个 operation</li><li>每个 operation 包含 input type 和 output type</li><li>type 定义了消息结构:消息由哪些字段组成,每个字段的类型(可嵌套),以及字段个数约束</li></ul><h3>1.1 什么是 SOAP</h3><p>SOAP 是在 Web Service 交互中使用的 XML 消息格式。 SOAP 消息通常通过 HTTP 或 JMS 发送,但也可以使用其他传输协议。 WSDL 定义描述了特定 Web Service 中的 SOAP 使用。</p><p>常用的 SOAP 有两个版本:SOAP 1.1 和 SOAP 1.2。</p><p><img src="/img/remote/1460000043548120" alt="SOAP structure" title="SOAP structure"></p><p><em>图片来源(遵循 CC 3.0 BY-SA 版权协议): <a href="https://link.segmentfault.com/?enc=nX57qKcWzjOYangLqvwTcQ%3D%3D.Aya5WR1ypyTVqhrfEEiPTN01Y7jogr64M9oEuUSKtKIYH%2Fur5Y1y5%2Fy8xGxMoTyj" rel="nofollow">https://en.wikipedia.org/wiki/SOAP</a></em></p><p>SOAP 消息包含以下部分:</p><ul><li>Header 元信息,一般为空</li><li><p>Body</p><ul><li>WSDL 里面定义的消息类型</li><li>对于响应类型,除了成功响应,还有错误消息,它也是结构化的</li></ul></li></ul><p>例子:</p><pre><code class="xml"><SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header></SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns2:getCountryResponse xmlns:ns2="http://spring.io/guides/gs-producing-web-service">
<ns2:country>
<ns2:name>Spain</ns2:name>
<ns2:population>46704314</ns2:population>
<ns2:capital>Madrid</ns2:capital>
<ns2:currency>EUR</ns2:currency>
</ns2:country>
</ns2:getCountryResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope></code></pre><h3>1.2 什么是 REST</h3><p>Web Service 其实是一种抽象概念,本身可以有任何实现,例如 REST 就是一种流行实现方式。</p><p>REST,即 Representational State Transfer 的缩写,直译就是表现层状态转化。<br>REST 这个词,是 Roy Thomas Fielding 在他 2000 年的博士论文中提出的。当时候正是互联网蓬勃发展的时期,软件开发和网络之间的交互需要一个实用的定义。</p><blockquote>长期以来,软件研究主要关注软件设计的分类、设计方法的演化,很少客观地评估不同的设计选择对系统行为的影响。而相反地,网络研究主要关注系统之间通信行为的细节、如何改进特定通信机制的表现,常常忽视了一个事实,那就是改变应用程序的互动风格比改变互动协议,对整体表现有更大的影响。我这篇文章的写作目的,就是想在符合架构原理的前提下,理解和评估以网络为基础的应用软件的架构设计,得到一个功能强、性能好、适宜通信的架构。</blockquote><p>访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,势必涉及到数据和状态的变化。HTTP 协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生“状态转化”。而这种转化是建立在表现层之上的,所以就是“表现层状态转化”。</p><p>REST 四个基本原则:</p><ol><li>使用 HTTP 动词:GET POST PUT DELETE;</li><li>无状态连接,服务器端不应保存过多上下文状态,即每个请求都是独立的;</li><li>为每个资源设置 URI;</li><li>通过 <code>x-www-form-urlencoded</code> 或者 JSON 作为数据格式;</li></ol><p>将 SOAP 转换为 REST,可以方便用户用 RESTFul 的方式访问传统的 Web Service,降低 SOAP client 的开发成本,如果能动态适配任何 Web Service,零代码开发,那就更完美了。</p><p>REST 最大的好处是没有 schema,开发方便,而且 JSON 的可读性更高,冗余度更低。</p><h2>2. SOAP-to-REST 代理的传统实现</h2><h3>2.1 手工模板转换</h3><p>这种方式需要为 Web Service 的每个 operation 提供 request 和 response 的转换模板,这也是很多网关产品使用的方式。</p><p>我们可以使用 APISIX 的 body transformer plugin 来做简单的 SOAP-to-REST 代理,实践一下这种方式。</p><p>作为例子,我们对上述 WSDL 文件里面的 <code>CountriesPortService</code> 的 <code>getCountry</code> 操作,根据类型定义构造 XML 格式的请求模板。</p><p>这里我们将 JSON 里面的 name 字段填写到 getCountryRequest 里面的 name 字段。</p><pre><code class="bash">req_template=$(cat <<EOF | awk '{gsub(/"/,"\\\"");};1' | awk '{$1=$1};1' | tr -d '\r\n'
<?xml version="1.0"?>
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
<soap-env:Body>
<ns0:getCountryRequest xmlns:ns0="http://spring.io/guides/gs-producing-web-service">
<ns0:name>{{_escape_xml(name)}}</ns0:name>
</ns0:getCountryRequest>
</soap-env:Body>
</soap-env:Envelope>
EOF
)</code></pre><p>对于响应,就要提供 XML-to-JSON 模板,稍微复杂(如果要考虑 SOAP 版本间 fault 的差异,那就更复杂了),因为需要判断是否成功响应:</p><ul><li>成功响应,直接将字段一一对应填入 JSON</li><li>失败响应,也就是 fault,我们需要另外的 JSON 结构,并且判断一些可选字段是否存在</li></ul><pre><code class="bash">rsp_template=$(cat <<EOF | awk '{gsub(/"/,"\\\"");};1' | awk '{$1=$1};1' | tr -d '\r\n'
{% if Envelope.Body.Fault == nil then %}
{
"currency":"{{Envelope.Body.getCountryResponse.country.currency}}",
"population":{{Envelope.Body.getCountryResponse.country.population}},
"capital":"{{Envelope.Body.getCountryResponse.country.capital}}",
"name":"{{Envelope.Body.getCountryResponse.country.name}}"
}
{% else %}
{
"message":{*_escape_json(Envelope.Body.Fault.faultstring[1])*},
"code":"{{Envelope.Body.Fault.faultcode}}"
{% if Envelope.Body.Fault.faultactor ~= nil then %}
, "actor":"{{Envelope.Body.Fault.faultactor}}"
{% end %}
}
{% end %}
EOF
)</code></pre><p>配置 APISIX 路由并且做测试:</p><pre><code class="bash">curl http://127.0.0.1:9180/apisix/admin/routes/1 \
-H 'X-API-KEY: xxx' -X PUT -d '
{
"methods": ["POST"],
"uri": "/ws/getCountry",
"plugins": {
"body-transformer": {
"request": {
"template": "'"$req_template"'"
},
"response": {
"template": "'"$rsp_template"'"
}
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"localhost:8080": 1
}
}
}'
curl -s http://127.0.0.1:9080/ws/getCountry \
-H 'content-type: application/json' \
-X POST -d '{"name": "Spain"}' | jq
{
"currency": "EUR",
"population": 46704314,
"capital": "Madrid",
"name": "Spain"
}
# Fault response
{
"message": "Your name is required.",
"code": "SOAP-ENV:Server"
}</code></pre><p>可见,这种方式需要人工去读懂 WSDL 文件里面每一个操作的定义,并且也要搞清楚每个操作对应的 web service 地址。如果 WSDL 文件庞大,包含大量操作和复杂的嵌套类型定义,那么这种做法是很麻烦的,调试困难,容易出错。</p><h3>2.2 Apache Camel</h3><p><a href="https://link.segmentfault.com/?enc=NvS03UWZDZqBI5ugULhDig%3D%3D.8VirwM5pH%2FYIOx%2BTotNX3zDBKU09Wf%2B2xOypA5wkDH8%3D" rel="nofollow">https://camel.apache.org/</a></p><p>Camel 是一个著名的 Java 整合框架,用于实现对不同协议和业务逻辑相互转换的路由管道,SOAP-to-REST 只是它的其中一个用途。</p><p>使用 Camel 需要下载并导入 WSDL 文件,生成 SOAP client 的 stub 代码,使用 Java 编写代码:</p><ul><li>定义 REST endpoint</li><li>定义协议转换路由,例如 JSON 字段如何映射到 SOAP 字段</li></ul><p>我们以温度单位转换的 Web Service 为例:</p><p><a href="https://link.segmentfault.com/?enc=9jFieDQN97mAD9U4kUIung%3D%3D.x%2BPCnI8B2b4TkPlj417OVIrzQqSNaaOvraK42Ws4lWfthV4ssQu%2BqONaAx%2BXAA0atOw%2BGxMLn4ylcrIr1KCqqg%3D%3D" rel="nofollow">https://apps.learnwebservices.com/services/tempconverter?wsdl</a></p><ol><li>通过 maven 根据 WSDL 文件生成 SOAP client 的代码</li></ol><p><code>cxf-codegen-plugin</code> 会为我们生成 SOAP client endpoint,用于访问 Web Service。</p><pre><code class="xml"><build>
<plugins>
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<configuration>
<wsdlOptions>
<wsdlOption>
<wsdl>src/main/resources/TempConverter.wsdl</wsdl>
</wsdlOption>
</wsdlOptions>
</configuration>
<goals>
<goal>wsdl2java</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build></code></pre><ol start="2"><li>编写 SOAP client bean</li></ol><p>注意这里我们记住 bean 的名字是 <code>cxfConvertTemp</code>,后面定义 Camel 路由用到。</p><pre><code class="java">import com.learnwebservices.services.tempconverter.TempConverterEndpoint;
@Configuration
public class CxfBeans {
@Value("${endpoint.wsdl}")
private String SOAP_URL;
@Bean(name = "cxfConvertTemp")
public CxfEndpoint buildCxfEndpoint() {
CxfEndpoint cxf = new CxfEndpoint();
cxf.setAddress(SOAP_URL);
cxf.setServiceClass(TempConverterEndpoint.class);
return cxf;
}
}</code></pre><ol start="3"><li>先编写下游 REST 的路由</li></ol><p>从这个路由我们可以看到它定义了 RESTFul 风格的 URL 及其参数定义,并且定义了每个 URL 的下一跳路由。例如<code>/convert/celsius/to/fahrenheit/{num}</code>,将 URL 里面最后一个部分作为参数(double 类型)提供给下一跳路由<code>direct:celsius-to-fahrenheit</code>。</p><pre><code class="java">rest("/convert")
.get("/celsius/to/fahrenheit/{num}")
.consumes("text/plain").produces("text/plain")
.description("Convert a temperature in Celsius to Fahrenheit")
.param().name("num").type(RestParamType.path).description("Temperature in Celsius").dataType("int").endParam()
.to("direct:celsius-to-fahrenheit");</code></pre><ol start="4"><li>最后编写上游 SOAP 路由及上下游的转换</li></ol><pre><code class="java">from("direct:celsius-to-fahrenheit")
.removeHeaders("CamelHttp*")
.process(new Processor() {
@Override
public void process(Exchange exchange) throws Exception {
// 初始化 SOAP 请求
// 将下游参数 num 填写到 body,body 就是一个简单的 double 类型
CelsiusToFahrenheitRequest c = new CelsiusToFahrenheitRequest();
c.setTemperatureInCelsius(Double.valueOf(exchange.getIn().getHeader("num").toString()));
exchange.getIn().setBody(c);
}
})
// 指定 SOAP operation 和 namespace
// 在 application.properties 文件定义
.setHeader(CxfConstants.OPERATION_NAME, constant("{{endpoint.operation.celsius.to.fahrenheit}}"))
.setHeader(CxfConstants.OPERATION_NAMESPACE, constant("{{endpoint.namespace}}"))
// 交给 WSDL 生成的 SOAP client bean 去发包
.to("cxf:bean:cxfConvertTemp")
.process(new Processor() {
@Override
public void process(Exchange exchange) throws Exception {
// 处理 SOAP 响应
// 将 body,也就是 double 类型的值填充到字符串里面去
// 将字符串返回给下游
MessageContentsList response = (MessageContentsList) exchange.getIn().getBody();
CelsiusToFahrenheitResponse r = (CelsiusToFahrenheitResponse) response.get(0);
exchange.getIn().setBody("Temp in Farenheit: " + r.getTemperatureInFahrenheit());
}
})
.to("mock:output");</code></pre><ol start="5"><li>测试</li></ol><pre><code class="bash">curl localhost:9090/convert/celsius/to/fahrenheit/50
Temp in Farenheit: 122.0</code></pre><p>可见,通过 Camel 做 SOAP-to-REST,就要针对所有 operation 用 Java 代码定义路由和转换逻辑,需要开发成本。</p><p>同理,如果 WSDL 包含很多 service 和 operation,那么走 Camel 这种方式来做代理,也是比较痛苦的。</p><h3>2.3 结论</h3><p>我们总结一下传统方式的弊端。</p><table><thead><tr><th> </th><th>模板</th><th>Camel</th></tr></thead><tbody><tr><td>WSDL</td><td>人工解析</td><td>通过 maven 去生成代码</td></tr><tr><td>上游</td><td>人工解析</td><td>自动转换</td></tr><tr><td>定义 body</td><td>提供模板做判断和转换</td><td>编写转换代码</td></tr><tr><td>获取参数</td><td>nginx 变量</td><td>在代码里面自定义或者调用 SOAP client 接口获取</td></tr></tbody></table><p>这两种方式都有开发成本,并且对每一个新的 Web Service,都需要重复这个开发成本。</p><p>开发成本与 Web Service 的复杂度成正比。</p><h2>3. APISIX 的 SOAP-to-REST 代理</h2><p>传统的代理方式,要不提供转换模板,要不编写转换代码,都需要用户深度分析 WSDL 文件,有不可忽视的开发成本。</p><p>APISIX 提供了一种自动化的方式,自动分析 WSDL 文件,自动为每个操作提供转换逻辑,为用户消除开发成本。</p><p><img src="/img/remote/1460000043548121" alt="APISIX SOAP-to-REST proxy" title="APISIX SOAP-to-REST proxy"></p><h3>3.1 无代码自动转换</h3><p>使用 APISIX SOAP 代理:</p><ul><li>无需手工解析或导入 WSDL 文件</li><li>无需定义转换模板</li><li>无需编写任何转换或耦合代码。</li></ul><p>用户只需要配置 WSDL 的 URL,APISIX 会自动做转换,它适用于任何 Web Service,是通用程序,无需再针对特定需求做二次开发。</p><h3>3.2 动态配置</h3><ul><li>WSDL URL 可绑定在任何路由,和其他 APISIX 资源对象一样,可在运行时更新配置,配置更改是动态生效的,无需重启 APISIX。</li><li>WSDL 文件里面包含的 service URL(可能有多个 URL),也就是上游地址,会被自动识别并且用作 SOAP 上游,无需用户去解析并配置。</li></ul><h3>3.3 实现机制</h3><ul><li>从 WSDL URL 获取 WSDL 文件内容,分析后自动生成 proxy 对象</li><li><p>proxy 对象负责协议转换</p><ul><li>根据 JSON 输入生成合规的 SOAP XML 请求</li><li>将 SOAP XML 响应转换为 JSON 响应</li><li>访问 Web Service,自动处理 SOAP 协议细节,例如 Fault 类型的响应</li><li>支持 SOAP1.1和 SOAP1.2,以及若干扩展特性,例如 WS-Addressing</li></ul></li></ul><h3>3.4 配置示例</h3><p>SOAP 插件的配置参数说明:</p><table><thead><tr><th>参数</th><th>必选?</th><th>说明</th></tr></thead><tbody><tr><td><code>wsdl_url</code></td><td>是</td><td>WSDL URL,例如 <code>https://apps.learnwebservices.com/services/tempconverter?wsdl</code></td></tr><tr><td><code>operation</code></td><td>否</td><td>操作名,可来自任何 nginx 变量,例如<code>$arg_operation</code>或者<code>$http_soap_operation</code></td></tr><tr><td><code>service</code></td><td>否</td><td>服务名,如果一个 WSDL 文件包含多个服务,可通过这个参数来指定访问哪个服务</td></tr><tr><td><code>ca_cert</code></td><td>否</td><td>校验服务端证书的 CA 证书内容</td></tr><tr><td><code>client_cert</code></td><td>否</td><td>用于 MTLS 的 client 证书内容</td></tr><tr><td><code>client_key</code></td><td>否</td><td>用于 MTLS 的 client 私钥内容</td></tr></tbody></table><p>测试:</p><pre><code class="bash"># 配置 APISIX 路由,使用 SOAP 插件
# 注意这里一条路由能执行所有操作,用 URL 参数来指定操作名
# 这也体现了动态代理的好处,不需要再手工去分析 WSDL 里面每一个操作
curl http://127.0.0.1:9180/apisix/admin/routes/1 \
-H 'X-API-KEY: xxx' -X PUT -d '
{
"methods": ["POST"],
"uri": "/ws",
"plugins": {
"soap": {
"wsdl_url": "http://localhost:8080/ws/countries.wsdl",
"operation": "$arg_operation",
"service": "<use alternative service defined in wsdl if exist>",
"ca_cert": "<ca cert file content>",
"client_cert":"<client cert file content>",
"client_key":"<client key file content>"
}
}
}'
curl 'http://127.0.0.1:9080/ws?operation=getCountry' \
-X POST -d '{"name": "Spain"}'
# 成功响应
HTTP/1.1 200 OK
Date: Tue, 06 Dec 2022 08:07:48 GMT
Content-Type: text/plain; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Server: APISIX/2.99.0
{"currency":"EUR","population":46704314,"capital":"Madrid","name":"Spain"}
# 失败响应
HTTP/1.1 502 Bad Gateway
Date: Tue, 03 Jan 2023 13:43:33 GMT
Content-Type: text/plain; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Server: APISIX/2.99.0
{"message":"Your name is required.","actor":null,"code":"SOAP-ENV:Server","subcodes":null,"detail":null}</code></pre><h2>4. 结论</h2><p>Web Service 发展至今,有大量企业用户使用传统的 SOAP based Web Service 提供服务,这些服务由于历史原因和成本考虑,不适合做 RESTFul 的完全重构,所以 SOAP-to-REST 对不少企业用户有刚性需求。</p><p>APISIX 提供的 SOAP-to-REST 插件,能实现零代码的代理功能,可动态配置,无需二次开发,有利于企业用户的零成本业务迁移和整合。</p><h2>关于 API7.ai 与 APISIX</h2><p><a href="https://link.segmentfault.com/?enc=n6E67IxNg6xXIcLRSeV10A%3D%3D.xLShSe7s34qQFIWLdfPtFFqqNL%2FGWXadYUH56txAVaM%3D" rel="nofollow">API7.ai</a> 是一家提供 API 处理和分析的开源基础软件公司,于 2019 年开源了新一代云原生 API 网关 -- APISIX 并捐赠给 Apache 软件基金会。此后,API7.ai 一直积极投入支持 Apache APISIX 的开发、维护和社区运营。与千万贡献者、使用者、支持者一起做出世界级的开源项目,是 API7.ai 努力的目标。</p>
关于 OAuth 你又了解哪些?
https://segmentfault.com/a/1190000043410832
2023-02-10T16:54:58+08:00
2023-02-10T16:54:58+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<blockquote>作者<a href="https://link.segmentfault.com/?enc=15%2FLI4T2HjFOGFLNdw0kZA%3D%3D.CHQZPLP6CQoChdpS6tmAdb6SRM4DdqNs8F09GOK9JhQ%3D" rel="nofollow">罗锦华</a>,API7.ai 技术专家/技术工程师,开源项目 pgcat,lua-resty-ffi,lua-resty-inspect 的作者。</blockquote><h2>OAuth 的背景</h2><p>OAuth,O 是 Open,Auth 是授权,也就是开放授权的意思。OAuth 始于 2006 年,其设计初衷正是委托授权,就是让最终用户也就是资源拥有者,将他们在受保护资源服务器上的部分权限(例如查询当天订单)委托给第三方应用,使得第三方应用能够代表最终用户执行操作(查询当天订单)。</p><p>OAuth 1.0 协议于 2010 年 4 月作为 RFC 5849 发布,这是一份信息性的评论请求。OAuth 2.0 框架的发布考虑了从更广泛的 IETF 社区收集的其他用例和可扩展性要求。尽管基于 OAuth 1.0 部署体验构建,OAuth 2.0 并不向后兼容 OAuth 1.0。OAuth 2.0 于 2012 年 10 月作为 RFC 6749 发布,承载令牌使用作为 RFC 6750 发布。</p><p>在 OAuth 协议中,通过为每个第三方软件和每个用户的组合分别生成对受保护资源具有受限的访问权限的凭据,也就是访问令牌,来代替之前的用户名和密码。而生成访问令牌之前的登录操作,又是在用户跟平台之间进行的,第三方软件根本无从得知用户的任何信息。</p><p>这样第三方软件的逻辑处理就大大简化了,它今后的动作就变成了请求访问令牌、使用访问令牌、访问受保护资源,同时在第三方软件调用大量 API 的时候,不再传输用户名和密码,从而减少了网络安全的攻击面。</p><p>说白了就是集中授权。</p><p>值得注意的是,OAuth 并非身份验证,这里的 Auth 是 Authorization,OAuth 是发生在用户做了身份验证后的事情,系统授权用户能做什么操作。互联网中所有的受保护资源,几乎都是以 Web API 的形式来提供访问的。不同的用户能做的事情不同,例如一个 GitHub 项目,有些用户只有读取和提交 PR(pull request)的权限,而管理员用户则能合并 PR。将用户权限在 API 层面细分,是 OAuth 要做的事情。</p><h2>OAuth的授权流程</h2><h3>角色</h3><p>在 OAuth 2.0 的体系里面有四种角色:</p><ul><li>第三方应用:一般分为前端浏览器、APP 和后端应用服务器。</li><li>资源拥有者:使用第三方应用的用户,并在授权服务器上有账号。</li><li>授权服务:提供授权的开发平台,例如微博、GitHub、微信。</li><li>受保护资源:用户的各类信息,例如用户名、头像、昵称、邮箱等信息。</li></ul><h3>流程</h3><p><img src="/img/remote/1460000043410834" alt="图片" title="图片"></p><p>步骤A:第三方应用向用户(其实是通过授权服务器)申请授权码</p><p>步骤B:授权服务器返回授权码给第三方应用</p><p>步骤C:第三方应用将授权码发给资源服务器,申请访问口令</p><p>步骤D:授权服务器返回访问口令给第三方应用</p><p>步骤E:第三方应用使用访问口令向资源服务器请求用户信息</p><p>步骤F:资源服务器返回用户信息,第三方应用提供业务逻辑给用户</p><h4>授权码和访问口令</h4><p>获取访问口令的方式在标准里有四种,这里只谈论授权码方式,这也是最常见最安全的方式:</p><p><img src="/img/remote/1460000043410835" alt="图片" title="图片"></p><p>步骤 A:第三方应用让用户选择授权方式,例如 GitHub,然后携带<code>client_id</code>和<code>redirect_uri</code>等参数将用户重定向到授权服务器</p><p>请求示例:</p><pre><code class="plain"> GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.13 Host: server.example.com</code></pre><p>步骤 B:用户登录和授权<br>步骤 C:授权服务器根据<code>redirect_uri</code>将用户重定向回到第三方应用的后端,提供授权码</p><p>响应示例:</p><pre><code class="plain">1 HTTP/1.1 302 Found
2 Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA
3 &state=xyz</code></pre><p>步骤 D:第三方应用的后端访问授权服务器,用授权码去换访问口令<br>请求示例:</p><pre><code class="plain">1 POST /token HTTP/1.1
2 Host: server.example.com
3 Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
4 Content-Type: application/x-www-form-urlencoded
5
6 grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
7 &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb</code></pre><p>步骤 E:授权服务器返回访问口令,第三方应用的后端渲染功能页面(对应步骤C)给浏览器,为用户提供功能<br>授权服务器的响应示例:</p><pre><code class="plain"> HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
"example_parameter":"example_value"
}</code></pre><p><strong>实际场景示例</strong></p><p>小明想通过小兔软件打印他在京东上的订单。</p><p>资源拥有者 -> 小明</p><p>第三方软件 -> 小兔软件</p><p>授权服务 -> 京东商家开放平台的授权服务</p><p>受保护资源 -> 小明店铺在京东上面的订单</p><p><img src="/img/remote/1460000043410836" alt="图片" title="图片"></p><p><strong>为什么授权码和访问口令要分开获取呢?</strong></p><p>OAuth2 协议中,用户登录成功后,OAuth2 认证服务器会将用户的浏览器回调到一个回调地址,并携带一个授权码 <code>code</code>。这个授权码 <code>code</code> 一般有效期十分钟且一次有效,用后作废。这避免了在前端暴露 <code>access_token</code> 或者用户信息的风险,<code>access_token</code> 的有效期都比较长,一般为 1~2 个小时。如果泄露会对用户造成一定影响。后端收到这个 <code>code</code> 之后,需要使用 <code>Client Id + Client Secret + Code</code> 去授权服务器换取用户的 <code>access_token</code>。</p><p>在这一步,实际上授权服务器对第三方应用进行了认证,能够确保来授权服务器获取 <code>access_token</code> 的机器是可信任的,而不是任何一个人拿到 <code>code</code> 之后都能来授权服务器进行 <code>code</code> 换 <code>token</code>。如果 <code>code</code> 被黑客获取到,如果他没有 <code>Client Id + Client Secret</code> 也无法使用,就算有,也要和真正的应用服务器竞争,因为 <code>code</code> 一次有效,用后作废,加大了攻击难度。相反,如果不经过 <code>code</code> 直接返回 <code>access_token</code> 或用户信息,那么一旦泄露就会对用户造成影响。</p><p>简单说就是,client secret 不能暴露给前端(验证 client),用户授权(获取 code)又只能前端做,因此需要分两步。</p><h2>OIDC(OpenID Connect)</h2><p>既然 OAuth 本身就隐含了身份验证,那么为什么不以标准化的形式将身份验证的结果导出,使得第三方应用可以使用呢?这就是 OIDC 要做的事情了。那身份验证的结果是什么?很简单,它就是用户的各种信息。</p><p>OIDC 怎么做?简单来说,就是在 OAuth 返回 <code>access token</code> 的时候顺带返回 <code>id token</code>,<code>id token</code> 的格式是 <code>JWT</code>,第三方应用可使用非对称公钥或者对称密码验证 <code>id token</code> 的合法性和有效性,而 <code>id token</code> 本身也包含了基本用户信息。另外,OIDC 提供了 UserInfo endpoint,第三方应用可携带 access token 访问该 endpoint 以获取额外的用户信息。</p><p>OIDC 还有一个好处,就是单点登录(SSO,Single Sign On)和单点注销(SLO,Single LogOut)。跟 OAuth 类似,OIDC 提供的集中化身份验证,它可以对应多个应用。只要用户成功登录了一个应用,那么当他登录其他应用的时候,就无需再进行一次身份验证了(例如输入用户名密码),那是因为授权服务器在用户的浏览器里面存下了 cookie。而单点注销则是用户注销了一个应用,其他应用也顺便注销了,注销既可以借由浏览器来做,也可以由第三方应用的后端与授权服务器之间来做。注销的时候指定的参数就是 <code>id token</code> 里面的 session 字段。</p><blockquote>注意:OIDC 并没有指定身份验证的具体方式,例如传统的密码或者刷脸,而是指定了如何将身份验证委托给一个集中化的身份验证提供者,在身份验证通过后得到什么凭证(<code>id token</code>),这个凭证如何被校验(JWT 格式),这个凭证包含了哪些用户信息。这样第三方应用就无需重造轮子了。<strong>OAuth 提供了集中化的授权,而 OIDC 则是在此基础上进一步提供了集中化的身份验证。</strong></blockquote><h2>APISIX 对 OAuth/OIDC 的支持</h2><p>Apache APISIX 是一个开源的云原生 API 网关,作为 API 网关,它兼具动态、实时、高性能等特点,提供了负载均衡、动态上游、灰度发布、服务熔断、身份认证、可观测性等丰富的流量管理功能。你可以使用 APISIX 来处理传统的南北向流量,以及服务间的东西向流量,也可以当做 K8s Ingress controller 来使用。</p><p>APISIX 既然是 API 网关,为多个上游应用服务器做代理,那么集中授权、集中身份认证,放在 API 网关是最自然不过的事情了。</p><p>APISIX 的 <code>openid-connect</code> 插件支持 OpenID Connect 协议,用户可以使用该插件让 APISIX 对接众多认证鉴权软件,如 Okta、Keycloak、Ory Hydra、Authing 等,作为集中式认证网关部署于企业中。OIDC是OAuth的超集,所以这个插件也隐含了对OAuth的支持。</p><p>部署图如下所示:</p><p><img src="/img/remote/1460000043410837" alt="图片" title="图片"></p><p>配置实例:使用 Keycloak 与 API 网关保护你的 API配置 Keycloak</p><table><thead><tr><th align="left">信息</th><th align="left">取值</th></tr></thead><tbody><tr><td align="left">keycloak地址</td><td align="left"><a href="https://link.segmentfault.com/?enc=O5AmTk1cLsb%2BLa%2BnbVsqsQ%3D%3D.WQnrcQrgGnYB9NYRUlUXxgKyBf4LX2MSL4cBlz351go%3D" rel="nofollow">http://127.0.0.1:8080/</a></td></tr><tr><td align="left">Realm</td><td align="left">myrealm</td></tr><tr><td align="left">Client Type</td><td align="left">OpenID Connect</td></tr><tr><td align="left">Client ID</td><td align="left">myclient</td></tr><tr><td align="left">Client Secret</td><td align="left">e91CKZQwhxyDqpkP0YFUJBxiXJ0ikJhq</td></tr><tr><td align="left">Redirect URI</td><td align="left"><a href="https://link.segmentfault.com/?enc=e1vnKoDC3XOPJ66E6lQEgA%3D%3D.QkfeLLI8iV2jN4puYtq4baImTJuNczU6hUchpdeIgWkonkfRl2vAOyvwBju%2FYAvr" rel="nofollow">http://127.0.0.1:9080/anything/callback</a></td></tr><tr><td align="left">Discovery</td><td align="left"><a href="https://link.segmentfault.com/?enc=paQlIj3X3G8Dl6jsW%2FpQlg%3D%3D.sb9zqk7Uq95aVTeM7Pn06CQbbcib06%2BXntnueT24e6TQhFCRq9MxlTwPJxltG1i1%2FIMpJLketZ6trtUj9JqnNB2ozVwtblOc%2BiP%2FKTQf54Q%3D" rel="nofollow">http://127.0.0.1:8080/realms/myrealm/.well-known/openid-configuration</a></td></tr><tr><td align="left">Logout URI</td><td align="left">/anything/logout</td></tr><tr><td align="left">Username</td><td align="left">myuser</td></tr><tr><td align="left">Password</td><td align="left">myrealm</td></tr><tr><td align="left">Realm</td><td align="left">mypassword</td></tr></tbody></table><p><strong>场景示例</strong></p><pre><code class="shell">curl -XPUT 127.0.0.1:9080/apisix/admin/routes/1 -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" -d '{
"uri":"/anything/*",
"plugins": {
"openid-connect": {
"client_id": "myclient",
"client_secret": "e91CKZQwhxyDqpkP0YFUJBxiXJ0ikJhq",
"discovery": "http://127.0.0.1:8080/realms/myrealm/.well-known/openid-configuration",
"scope": "openid profile",
"bearer_only": false,
"realm": "myrealm",
"redirect_uri": "http://127.0.0.1:9080/anything/callback",
"logout_path": "/anything/logout"
}
},
"upstream":{
"type":"roundrobin",
"nodes":{
"httpbin.org:80":1
}
}
}'</code></pre><p>创建 API 成功后访问 <a href="https://link.segmentfault.com/?enc=QeHaR4xBIS0tIMVUt6P3mw%3D%3D.ztUzj%2Bh40mxlX1526C5tbq6OPZLtaNAYCYt33pZf22EZXi%2FYpk%2B723W4t%2B0C3pBQ" rel="nofollow">http://127.0.0.1:9080/anything/test</a> 时,由于未进行登录,因此将被引导到 Keycloak 的登录页面:</p><p><img src="/img/remote/1460000043410838" alt="图片" title="图片"></p><p>输入账号(myuser)、密码(mypassword)完成登录后,成功跳转到 <a href="https://link.segmentfault.com/?enc=b6QxIQDC%2BWXQ2yKVZHCxbA%3D%3D.bM0v52XaBZxeknoqZLmCNf%2FEoEJREE9tZ70maMLI7FaBm7S9R%2BndK9vaTYsJzuh1" rel="nofollow">http://127.0.0.1:9080/anything/test</a> 页面:</p><p><img src="/img/remote/1460000043410839" alt="图片" title="图片"></p><p>访问 <a href="https://link.segmentfault.com/?enc=YK%2Bs90d45s8vv8leMDYj8Q%3D%3D.p8YIlI%2FLKsIgwFuEo5U6JGpl1TIIKWVexDfOdCXio5yiuVmTwFcDgzDH%2FIBN8hxR" rel="nofollow">http://127.0.0.1:9080/anything/logout</a> 退出登录:</p><p><img src="/img/remote/1460000043410840" alt="图片" title="图片"></p><h2>总结</h2><p>本文介绍了 OAuth 协议由来和授权流程,引入更上一层的身份层协议 OIDC 并提供了详细的配置示例。<br>作为最流行的鉴权方式,OAuth/OIDC 通过 APISIX 的鉴权插件在 API 网关层面进行集中化鉴权管理,使得客户端和上游服务器之间免去重复繁琐的鉴权部署和维护。</p>
RESTful API 为何成为顶流 API 架构风格?
https://segmentfault.com/a/1190000043410822
2023-02-10T16:53:57+08:00
2023-02-10T16:53:57+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<blockquote>作者孙毅,API7.ai 技术工程师,Apache APISIX Committer</blockquote><p>万物互联的世界充满着各式各样的 API ,如何统筹规范 API 至关重要。<a href="https://link.segmentfault.com/?enc=ziW9z4JTzWvMx4Z8I9Cgow%3D%3D.GvzE22CGv1%2FOcA%2FjYS8S%2BIDtAeR%2FdtWBRLCEUz%2FiGv4uyfH4GPUDx6C4dW3%2FPXGZDk9EbUmnForwrVVq8T0ZzQ%3D%3D" rel="nofollow">RESTful API</a> 是目前世界上最流行的 API 架构风格之一,它可以帮助你实现客户端与服务端关注点分离,让前后端各自迭代,提升管理效率;其无状态的特性可以让应用更容易扩展,更容易的实现缓存策略从而提升系统性能和用户体验。本文将介绍什么是 RESTful API 以及我们如何使用它。</p><p>首先,抛开 API 这个概念,我们来聊聊在生活中如何传递信息。</p><p>当你在商店将钱拿给老板告诉他你需要买电池,老板在收到钱后在货架上找到电池并递给你。一个买电池的交易顺利完成。</p><p>计算机软件则通过 API 来完成信息的传递,先来看维基百科定义:</p><blockquote>An application programming interface (API) is a way for two or more computer programs to communicate with each other. It is a type of software interface, offering a service to other pieces of software.</blockquote><p>软件 A 通过 API 向软件 B 发起请求,软件 B 在查询自己资源后将请求的响应内容通过 API 返回 A。</p><p>软件 A 通过 API 向软件 B 发起请求就好比你跟老板说你需要电池,软件 B 在将数据返回给软件 A 就好比老板找到电池后将电池给你。</p><p>这一过程软件 B 不需要知道软件 A 为什么要数据,就好比商店老板不会问你买电池的目的。软件 A 也不需要知道 B 软件的数据是怎么找到的,就好比你买电池的时候你也不会问老板电池从哪里进货一样。每个软件之间通过 API 传递信息,各司其事,使得程序世界变得有序可靠。</p><h2>什么是 RESTful API</h2><p>Roy Fielding 在他 2000 年的博士论文《建筑风格和基于网络的软件架构设计》中定义了REST(Representational state transfer),REST 架构风格定义了六个指导性约束。一个符合<strong>部分</strong>或<strong>全部</strong>约束的系统被松散地称为 RESTful。</p><p><img src="/img/remote/1460000043410824" alt="RESTful API" title="RESTful API"><br> (图片来源:Seobility)</p><h3>REST 的约束条件(长龙在排版时可以考虑一键截图 or 分点>横向叙述)</h3><table><thead><tr><th align="left">约束条件</th><th align="left">详述</th><th align="left">优势</th></tr></thead><tbody><tr><td align="left">Client–server architecture</td><td align="left">通过将用户界面问题与数据存储问题分开,提高了跨多个平台的用户界面的可移植性,并通过简化服务器组件提高了可扩展性。</td><td align="left">1. 客户端、服务端解耦。<br>2. 提升用户平台跨平台的可移植性。<br>3. 提升服务器端的可拓展性。</td></tr><tr><td align="left">Statelessness</td><td align="left">客户端的每个请求到服务器必须包含请求所需的所有信息,并且不能利用服务器上存储的任何上下文,会话状态完全保存在客户端。</td><td align="left">1. 易扩展,无会话依赖,任何服务器可以处理任何请求。<br>2. 易缓存,提升程序性能。</td></tr><tr><td align="left">Cacheability</td><td align="left">要求请求响应中的数据被隐式或显式标记为可缓存或不可缓存。如果一个响应是可缓存的,那么客户端缓存就被授予为以后的等效请求重用该响应数据的权利。</td><td align="left">1. 减少带宽。<br>2. 减少延迟。<br>3. 减少服务器负载。<br>4. 隐藏网络状态。</td></tr><tr><td align="left">Layered system</td><td align="left">通过约束组件行为允许架构由分层层组成,这样每个组件都不能“看到”超出它们与之交互的直接层。通过将系统知识限制在单个层,降低了整个系统的复杂性并促进了底层独立性。</td><td align="left">1. 降低整个系统的复杂性。<br>2. 促进底层的独立性。<br>3. 可方便的实施负载均衡。<br>4. 可将业务逻辑和安全策略解耦。</td></tr><tr><td align="left">Code on demand (optional)</td><td align="left">允许通过下载和执行小程序或脚本形式的代码来扩展客户端功能。</td><td align="left">1. 提高系统的可扩展性。</td></tr><tr><td align="left">Uniform interface</td><td align="left">主要包含四点:<br>1. Resource identification in requests.<br>客户能够通过请求中包含的 URI 来识别一个资源,将服务端资源和客户端请求资源解耦。<br>2. Resource manipulation through representations.<br>当客户端拥有一个资源的表示,如 URI,那么就有足够的信息来修改或者删除资源。<br>3. Self-descriptive messages.<br>每个消息都包括足够的信息来告知客户客户端该如何处理该信息。<br>4. Hypermedia as the engine of application state (<a href="https://link.segmentfault.com/?enc=IbKERuDQPA69NQoRYTXw%2Bw%3D%3D.ZajFxtwvmRAAR0HHYSa3T7emifHP0dMiZBXsaj6UoafVJMZ5FcUte60W1OyED1mj1eLqObtENPdh9ErV6CKC%2F8%2FVnti82QwnkexdURHGsK2pFIgxz5EFeTnG03pTH1g%2FoFWcOGf6iIh6omIyG1r%2Bca2mHuL7wjxrGz3B0Ko9Gic3akfzITQ9R6VjY3XX1Mpb" rel="nofollow">HATEOAS</a>).<br>客户端不需要任何额外的编码通过服务端返回的资源链接,就可以使得用户获取所有的资源。</td><td align="left">1. 简化了整体系统架构。<br>2. 提高了交互的可见性。</td></tr></tbody></table><h2>RESTful API 最佳实践</h2><p>强调组件间的 <strong>统一接口</strong> 是 REST 架构风格与其他基于网络的风格区分开来的核心特征,基于此特征,本文梳理了RSETful 最佳实践,以帮助你更好的设计 API。</p><h3>路径名称避免动词</h3><p>使用 HTTP 方法来表达资源操作行为,而不是将行为动词定义到路径中。</p><pre><code class="shell">// Good
curl -X GET http://httpbin.org/orders
// Bad
curl -X GET "http://httpbin.org/getOrders"</code></pre><ul><li>GET 获取指定 URI 的资源信息</li></ul><pre><code class="shell">// 代表获取当前系统的所有订单信息
curl -X GET http://httpbin.org/orders
// 代表获取订单编号为 1 的订单详情信息
curl -X GET http://httpbin.org/orders/1</code></pre><ul><li>POST 通过指定的 URI 创建资源</li></ul><pre><code class="plain"> // 代表创建一个名称为 order 的资源
curl -X POST http://httpbin.org/orders \
-d '{"name": "awesome", region: "A"}' \</code></pre><ul><li>PUT 创建或全量替换指定 URI 上的资源</li></ul><pre><code class="shell"> // 代表将 id 为 1 的 order 进行数据替换
curl -X PUT http://httpbin.org/orders/1 \
-d '{"name": "new awesome", region: "B"}' \</code></pre><ul><li>PATCH 执行一个资源的部分更新</li></ul><pre><code class="shell"> // 代表将 id 为 1 的 order 中的 region 字段进行更改,其他数据保持不变
curl -X PATCH http://httpbin.org/orders/1 \
-d '{region: "B"}' \</code></pre><ul><li>DELETE 通过指定的 URI 移除资源</li></ul><pre><code class="shell"> // 代表将 id 为 1 的 order 删除
curl -X DELETE http://httpbin.org/orders/1</code></pre><h3>URI 使用复数形式</h3><p>若使用单数的形式来表示获取某一类资源,例如:</p><pre><code class="shell">curl -X GET "http://httpbin.org/order"</code></pre><p>使用单数形式,会使用户产生该系统中只有一个 order 的困惑,用复数形式在逻辑上则顺畅很多。</p><pre><code class="shell">curl -X GET "http://httpbin.org/orders"</code></pre><h3>善用 HTTP 状态码</h3><p>HTTP 标准定义个状态码,大致可以分为以下几类:</p><table><thead><tr><th align="left">状态码</th><th align="left">含义</th></tr></thead><tbody><tr><td align="left">2xx</td><td align="left">成功,操作被成功接收并处理</td></tr><tr><td align="left">3xx</td><td align="left">重定向,需要进一步的操作以完成请求</td></tr><tr><td align="left">4xx</td><td align="left">客户端错误,请求包含语法错误或无法完成请求</td></tr><tr><td align="left">5xx</td><td align="left">服务器错误,服务器在处理请求的过程中发生了错误</td></tr></tbody></table><p>使用标准状态码,开发人员可以立即识别问题,可以减少发现不同类型错误的时间。</p><h3>版本管理</h3><p>随着业务需求的变更,已经上线的 API 大概率是要对应调整的。如果我们的 API 有第三方在使用,让每一个客户端根据我们 API 的变更而变更显然是不可能的,这个时候就要引入 API 版本管理概念了,既可以保证历史 API 正常使用,又可以迭代新的 API 以满足新的业务需求。</p><p>常见的版本控制手段有:</p><ul><li>通过请求中路径来做版本控制</li></ul><pre><code class="shell">// 请求 v1 版本的API
curl http://httpbin.org/v1/orders
// 请求 v2 版本的API
curl http://httpbin.org/v2/orders</code></pre><ul><li>通过 Query 参数来做版本控制</li></ul><pre><code class="shell">// 请求 v1 版本的API
curl http://httpbin.org/orders?version=v1
// 请求 v2 版本的API
curl http://httpbin.org/v2/orders?version=v2</code></pre><ul><li>通过 Header 来做版本控制</li></ul><pre><code class="shell">// 请求 v1 版本的API
curl http://httpbin.org/orders -H "custom-version: v1"
// 请求 v2 版本的API
curl http://httpbin.org/orders -H "custom-version: v2"</code></pre><h2>APISIX 如何助力 RESTful API</h2><p>作为一个动态、实时、高性能的 API 网关,Apache APISIX 可以在任何 RESTful API 服务上运行,并使用插件来添加新的服务和扩展其功能,这符合 RESTful 定义中的 <strong>Layered system</strong>。此外,对于一些没有遵循 RESTful API 定义的历史服务, APISIX 也可以帮你在<strong>不改动原有业务代码</strong>的情况下完成接口的转换,使你的接口完成 <strong>Uniform interface</strong> 这一 REST 限制条件,使你的 API 可以更好的遵守 RESTful API 规范。</p><h3>分层系统:支持业务逻辑和安全逻辑的分割</h3><p>你可以只用关注业务逻辑的实现,接口的安全逻辑可以交给 APISIX Authentication 类插件处理,例如 <a href="https://link.segmentfault.com/?enc=rPTBvTp9A7Pw6HC16Da%2F1g%3D%3D.dasO0E2eVCp4jDmLqwmMpBRO7Vj8Vx7WfZ2MLjlGgm7%2Fsnnf%2FCaY08uG5qVIgQSJGbuimmqSeAHFwkwWcTvbEQ%3D%3D" rel="nofollow">key-auth</a>。APISIX 支持大量的 Authentication 插件,我们以 <a href="https://link.segmentfault.com/?enc=BpRdPcd09xSSoaYpcWkoRw%3D%3D.wBpN55RYArUMoyAdLupokX2z%2FXWjs64YwdQtgt9SK9HTfTyhXSgzcT903n9oEVJqarjSirOQqIzkXabOcfUh%2BQ%3D%3D" rel="nofollow">openid-connet</a>为例如下图所示:</p><p><img src="/img/remote/1460000043410825" alt="APISIX的作用" title="APISIX的作用"></p><p>我们可以看到,使用 APISIX(API Gateway)在业务服务器前面加一层认证逻辑,就可以起到保护上游服务的作用,让你的业务逻辑和安全逻辑高效解耦。</p><h3>Layered system:多负载均衡协议支持</h3><p>APISIX 作为 API 网关,可以设立在客户端和服务端之间,完成不同的负载需求。你甚至可以自定义负载均衡的逻辑。</p><p>支持的负载均衡算法有:</p><ul><li><code>roundrobin</code>: Round robin balancing with weights.</li><li><code>chash</code>: Consistent hash.</li><li><code>ewma</code>: Pick the node with minimum latency. See <a href="https://link.segmentfault.com/?enc=k2a%2BECFsEF0RHzEasSAlcQ%3D%3D.RAYWL48FdCNmTnU3fsALyzSEtxavFEpsDyAgwVguO5azp1r3I0bgJPnmedfk6dhKjMZDVCrpsXMo03bDObTzkP95G7mVufjahFS2Mn9yBJT6TWmuZxZnzi2AxrwAKlUGLqfp7ftx4NbM3cejIKIgFltHZkQX80pYB0GDeXI1tAFnpNZsDfkabN6Gdq3ggHPM" rel="nofollow">EWMA Chart</a> for more details.</li><li><code>least_conn</code>: Picks the node with the lowest value of <code>(active_conn + 1) / weight</code>. Here, an active connection is a connection being used by the request and is similar to the concept in Nginx.</li><li>user-defined load balancer loaded via <code>require("apisix.balancer.your_balancer")</code></li></ul><h3>统一接口:使历史 API 更加 RESTful</h3><p>对于已经存在很久的历史 API,如果没有很好的遵循 RESTful API 准则。你可以在不改造原有 API 逻辑的情况下重新通过 APISIX 来封装新的 API 以满足不同的业务场景。</p><h4>使用 <a href="https://link.segmentfault.com/?enc=6gOLMkrUrAi3JqIXfb20Fg%3D%3D.e7mRc0AbNOo%2BDRXPmSEyyLWnJn9zIafe5onyCKgtu8oIx0P1t0zgibnkeYQ2okmpyP%2BAzfsvi3bGQh%2B9iWquwA%3D%3D" rel="nofollow">proxy-rewrite</a> 改写客户端请求</h4><p>在本文中上方提过我们的路径中不要有动词。</p><p>例如:历史版本 API 有 <code>/getOrder</code> 接口,我们可以通过 <code>proxy-rewrite</code> 插件来将 API 请求代理到历史 API 上:</p><pre><code class="shell">curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"methods": ["GET"],
"uri": "/orders",
"plugins": {
"proxy-rewrite": {
"uri": "/getOrder",
"scheme": "http",
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:80": 1
}
}
}'</code></pre><p>你也可以同样使用该插件进行 API 版本管理上的操作。</p><h4>使用 <a href="https://link.segmentfault.com/?enc=u1b2tsMY9fgQim%2BUhRwDQg%3D%3D.GlvO3D0Mycbh%2FnTfggKw0JwxxhtQneVuKm36MQfC0%2BrpdKcsrROtlcfQ6gncqizXEMZltGIpCG%2BkD9eY6Hpp8w%3D%3D" rel="nofollow">response-rewrite</a> 插件改写服务端响应</h4><p>当我们的历史 API 存在响应状态码不规范时,我们可以通过 response-rewrite 代理 response 响应从而达到修改响应状态码的目的。</p><pre><code class="shell">curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"methods": ["GET"],
"uri": "/orders",
"plugins": {
"response-rewrite": {
"status_code": 201,
"body": "{\"code\":\"ok\",\"message\":\"new json body\"}",
"vars":[
[ "status","==",200 ]
]
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:80": 1
}
}
}'</code></pre><p>例如,这个例子表示将请求 /orders 路径的 API 中响应为 200 的状态的请求修改为 201。<br>APISIX 支持非常丰富的插件,期待你去挖掘更多的玩法。</p><h2>总结</h2><p>本文详细说明了什么是 API,什么是 RESTful API 以及其最佳实践。另外还介绍了如何通过 APISIX 来实现业务逻辑和安全逻辑分离,如何使用 APISIX 在不改动原有业务代码的情况下将历史 API 服务更加 RESTful。希望本文对你了解 RESTful API 有所帮助,也欢迎你来 GitHub 一起玩耍。</p>
服务网格领域的百花齐放,是否存在一个更优解?
https://segmentfault.com/a/1190000043345634
2023-01-18T11:06:29+08:00
2023-01-18T11:06:29+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<blockquote><p>作者@lingsamuel,API7.ai 云原生技术专家,Apache APISIX Committer。</p><p>作者@林志煌,API7.ai 技术工程师,Apache APISIX contributor。</p></blockquote><p><strong>服务网格是一种技术架构,它用于管理微服务系统中各个服务之间的通信,旨在处理微服务间的流量(也称为东西向流量)。</strong></p><p><img src="/img/remote/1460000043345636" alt="" title=""></p><p>在云原生应用中,一个应用的背后可能存在着成百上千个服务,各个服务可能又有着若干个实例,各个实例的状态也一直在变化。在如此复杂的服务运行环境中,如何保障用户的可靠访问以及维持业务的平稳运行成为一个很大的挑战,服务网格的治理方案便应运而生。</p><p>服务网格就像是微服务间的 TCP/IP,负责服务间的网络调用、限流限速、监控等。服务网格目前主要应用在 Kubernetes 平台上,其最经典的一种实现模式是 Sidecar 模式:将一些通用功能抽象到 Sidecar 容器中,并将 Sidecar 容器与服务容器挂载在同一个 Pod 里。由于 Sidecar 容器与服务容器并行,且各个 Sidecar 间相互通讯,共同构成了网格形式的网络,因此称之为服务网格。当然,Sidecar 并非唯一的一种服务网格实现模式,除此之外还有 <a href="https://link.segmentfault.com/?enc=3%2BxF40cWtn5l8GsuGf5LSQ%3D%3D.mUQzAozxLFD9%2F8g01h6S%2B%2F7oicJ6e7OCa96FjBihPRlKqVgN1wEKJ5f%2BydVACcrj1hvA66iVXMSQNfUUxYfyUg%3D%3D" rel="nofollow">DaemonSet 模式</a>及 <a href="https://link.segmentfault.com/?enc=BIxA7oTWfiRgpQn84LpSpQ%3D%3D.j1HbVUDTsLOizQ%2BeStre1TEYmlfo8r%2Bh6KXhefhMEVDDvtTpAFgIS%2FWcVFa8sLdFe5lDWxboDbiGtrgDLX2X7A%3D%3D" rel="nofollow">Ambient mesh 模式</a>。</p><h2>为什么需要服务网格?</h2><p>在服务网格流行之前,很多微服务架构的服务治理都是通过微服务框架配合控制平台实现的,这种方式存在以下几个问题:</p><ol><li>框架与业务耦合,整体复杂度与运维难度很高,且开发者需要对公共库有一定的了解,没法只专注于业务实现。</li><li>需要维护多种语言版本的框架,增加了维护的成本。</li><li>微服务框架本身的升级成本比较高,在升级时往往需要进行业务重启等操作。</li><li>线上存在很多版本的框架,会导致复杂的兼容性考虑。<br>面对以上这些问题,原 Twitter 工程师 Willian Morgan 提出了 Service Mesh 的概念,即服务网格。服务网格通过 Sidecar 模式实现在不侵入业务服务的情况下将基础设施与业务逻辑解耦,实现跨语言统一更新发布及运维。</li></ol><p><img src="/img/remote/1460000043345637" alt="" title=""></p><p>服务网格将流量管理、可观测性和安全通讯等功能下沉到基础组件,因此开发者无需关心通信层和服务治理的具体实现,与通信相关的一切工作直接交给服务网格,让开发者能够更关注于业务的开发。基于服务网格的这些特点,前面提到的几个问题都能够得到有效解决。</p><h2>服务网格是如何工作的?</h2><p>服务网格不会为应用的运行时环境加入新功能,任何架构中的应用还是需要相应的规则来指定请求如何从 A 点到达 B 点。但服务网格的不同之处在于,它从各个服务中提取逻辑管理的服务间通信,并将其抽象为一个基础架构层。</p><p>目前服务网格大多数采用是<strong>数据面</strong> + <strong>控制面</strong>的架构模式,如下图所示:</p><p><img src="/img/remote/1460000043345638" alt="" title=""></p><p>其中控制面用于管理和配置数据面以及在运行时执行策略。单个网格中控制平面的所有实例共享相同的配置资源。主要聚焦于安全、可观测性、流量管控等策略的处理和下发,同时还能够收集和集中数据平面的遥测数据,供运维人员使用。</p><p>而数据面通常以代理的形式实现,是由一个个的网络代理 Sidecar 组成,Sidecar 与业务应用实例并行,通过拦截业务数据流以管控业务应用的流量。</p><p>在前面的介绍中有提到服务网格是将 Sidecar 设计模式在 Kubernetes 进行实现,通过容器化的方式实现了封装,Sidecar 主张以额外的容器来扩展或增强主容器,这个额外的容器被称为 Sidecar 容器,它与业务容器在同一个 Pod 中。而服务网格即是一个个 Sidecar 代理所构成的网格式网络。</p><h2>服务网格的实际应用</h2><p>在微服务架构中,工程师往往会为对外暴露的服务采取加密或访问限制的措施以保障服务的安全,但却忽视了集群内部的流量通信安全,所以至今仍有很多微服务应用没有采取服务间通信的加密措施,集群内部的流量以明文的形式进行传输,非常容易导致内部流量遭到数据窃听或是中间人攻击。</p><p>而为了防止集群内部流量遭到攻击,通常会使用 mTLS 将通讯数据进行加密。mTLS 可以用于确保服务网格中微服务之间的通信安全。它使用加密安全技术相互认证各个微服务并加密它们之间的流量。</p><p><img src="/img/remote/1460000043345639" alt="" title=""></p><p>虽然可以直接在微服务中定义通信安全策略并执行身份验证和加密,但在每一个微服务中去单独实现相同的功能效率是很低的。而且增加功能还需要改动业务代码,侵入业务逻辑。且即便完成了功能,后期的升级迭代与测试都需要开发者投入额外精力去维护,无法专注于业务功能的开发。</p><p>而使用服务网格,我们就可以在不影响原本业务的情况下零感知的为服务提供 mTLS 通信。因为在服务网格中,服务通信相关的功能都被转移至 Sidecar 代理中实现。<strong>在整个实现过程中,业务应用都不会受到影响,降低开发者心智负担。</strong></p><p>当然,服务网格除了可以实现类似 mTLS 这类的内部流量安全配置功能,通过调整控制面的配置还能快速的拓展包括流量管控,可观测性,协议编解码等更多功能。</p><h2>更优的服务网格方案?</h2><p>虽然服务网格解决了很多微服务架构中的痛点,但它也同时有自己的局限性,在软件开发中复杂度是不灭的,只是在不同的部分之间做转移。我们将服务治理抽离为单独的一层就要面对流量链路的增长以及运维难度的提升,且服务网格需要在云原生的环境中使用,这对于运维的专业能力及工程实践经验有了更高的要求。所以说技术只是用于解决问题的工具,服务网格能带来的价值还是得从应用的从实际情况出发。</p><p>作为 Apache 软件基金会的顶级项目,Apache APISIX 是一个动态、实时、高性能的云原生 API 网关,提供负载均衡、动态上游、灰度发布、服务熔断、身份认证、可观测性等丰富的流量管理功能。在基于 APISIX 的扩展道路上,除了 APISIX Ingress 在云原生领域被各大厂商开始关注外,基于 APISIX 的服务网格方案也在积极迭代中。</p><h2>基于 APISIX 的服务网格方案——Amesh</h2><p><a href="https://link.segmentfault.com/?enc=3Dbim0QHCXyBp1HGsGkiiQ%3D%3D.HqD0C1eu2VA3yz7q%2FaSplkhN5Hjnt5QRBgbl3x8LRVs%3D" rel="nofollow">Amesh</a> 是 <a href="https://link.segmentfault.com/?enc=3NqSG6gS%2BMTp7D703O6fKw%3D%3D.QOmbCn1d80GIfIZia1rIPWH55aRBBoeu5yF%2FwiW4XclHJobV4mhk0I85r3vuD1kB" rel="nofollow">Apache APISIX</a> 的服务网格库。它适配了 xDS 协议,可以从诸如 Istio 的控制平面中接收数据,并生成 APISIX 所需的数据结构,使得 APISIX 能够在服务网格领域作为数据面发挥作用。</p><p>依靠 Amesh,APISIX 可以工作在服务网格模式下,不使用传统的 etcd 作为数据中心,而是使用 shdict 与 Amesh 库直接进行数据交换,避免了额外的性能损耗,使得大规模部署成为可能。</p><p>通过使用 Amesh,可以在服务网格领域获得 APISIX 具备的高性能、丰富的流量管理功能、易扩展性等多种优势。</p><p>Amesh 通过适配 xDS 协议,可以让 APISIX 替代 Istio 所使用的 Envoy 组件来接管集群流量。在实际使用中,APISIX 将作为 Pod 的 Sidecar 接管网格内的所有流量。目前 Amesh 的架构如下图所示:</p><p><img src="/img/remote/1460000043345640" alt="" title=""></p><p>通过架构图可以看到,通过 xDS 协议,Amesh 可以将 Istio 作为控制面,从 Istio 侧获取配置信息,并将其转义为 APISIX 所需的配置。</p><p>而网格内部的所有流量都将由 APISIX 接管。其中,APISIX 的配置中心被设置为 Amesh,这使得 APISIX 脱离 etcd 的依赖。Amesh 为 APISIX 提供了一种从 xDS 协议中获取配置信息的方式。</p><p>此外,Amesh 在 v0.2 中提供了额外的可选控制面组件:<a href="https://link.segmentfault.com/?enc=9BbeRTq0OMzsgqldh0%2BkDw%3D%3D.kr2il0kR39h%2Bb7BCQwFJVlKfzJg3jyiPeQB1e03VGTAa5hgyu5grHUzUpI%2BZQwhOdELEuPsOpP%2FXNtzo7kaojzHIKUln4MNIKH9J3TuH6b0%3D" rel="nofollow">amesh-controller</a>。Amesh Controller 增加了 Amesh 专用的 CRD,可以为 APISIX 配置一些 Istio 所不支持的额外功能。额外带有 <code>amesh-controller</code> 的架构如下图所示:</p><p><img src="/img/remote/1460000043345641" alt="" title=""></p><p>正如前文所提到的,Amesh Controller 是可选组件。在未安装时,Amesh 也能正常使用 Istio 的原生能力提供服务。在安装了 <code>amesh-controller</code> 后,Amesh 能自动检测到控制面的加入,并动态地从中获取配置,而无需重启。</p><p>Amesh controller 为 Amesh 提供了 Istio 无法提供的 APISIX 特有功能。例如在安装 <code>amesh-controller</code> 后,用户能为服务配置 APISIX 原生具备的海量插件,使 APISIX 众多的插件在服务网格场景下也能开箱即用,而无需用户进行自定义的开发。</p><h2>Amesh 发展状态</h2><p>目前 Amesh 项目正在积极开发中。在最近发布的的 <a href="https://link.segmentfault.com/?enc=DbpMOr1%2FzwQe6aW3jNf72g%3D%3D.sgLKV6d9YbnCDJGbDNR%2BdKwp8Kmkmyjfe4mSY2sDFCHJtVdI0kOX62TfWvLpPV3AIqtPmUqOj1FBmVeVdjqOsw%3D%3D" rel="nofollow">v0.2 版本</a>中,Amesh 新增了可选的控制面 amesh-controller 组件,为 Amesh 提供了 APISIX 所支持的强大的插件系统,大大增强了 Amesh 的可扩展性。</p><h3>扩展能力</h3><p>在使用 Amesh 时,如果是常规的 Istio 部署,用户则可以通过 Lua 或 Wasm 来对 Envoy 进行功能扩展。</p><p>与 Envoy 原生能力相比,APISIX 官方即支持插件扩展能力,维护了 80+ 的插件可供用户使用,许多功能已经原生集成。但由于在 Istio 中,不能对这些插件进行配置,无法直接使用这些插件所提供的能力。</p><p>为此,Amesh v2.0 版本新增了一个控制面组件,即前文提到的 <code>amesh-controller</code>。它为用户提供了配置 APISIX 插件的能力,使 APISIX 众多的插件在服务网格场景下也能开箱即用,而无需用户进行自定义的开发。</p><h3>应用示例</h3><p>在 Amesh v0.2 版本中,可以通过安装 <code>amesh-controller</code> 并使用提供的 <code>AmeshPluginConfig</code> CRD 来进行 APISIX 的插件配置。</p><p>例如,我们可以为请求的响应添加特定的 header,这里可以通过配置 APISIX 的 <code>response-rewrite</code> 插件实现。</p><p>假设我们需要添加的 header 为 <code>X-Header</code>,其值为 <code>AddedHeader</code>,我们可以配置如下的 <code>AmeshPluginConfig</code>,此时请求的响应中就会带上我们所需的 header。</p><pre><code class="yaml">apiVersion: apisix.apache.org/v1alpha1
kind: AmeshPluginConfig
metadata:
name: ampc-sample
spec:
plugins:
- name: response-rewrite
config: '{"headers": {"X-Header":"AddedHeader"}}'</code></pre><h2>总结</h2><p>随着云原生的爆炸式发展及服务网格的不断优化,未来的服务网格可能会完全取代传统微服务架构,成为各个企业微服务及云原生改造的首选架构。虽然现在各种方案都还不太完善,但整体都属于螺旋上升的状态。当然,基于 APISIX 的服务网格也正朝着大家心目中的理想型服务网格解决方案奋进,也欢迎各位对 APISIX 服务网格方案感兴趣的朋友们进行尝鲜。</p>
马蜂窝如何利用 APISIX 网关实现微服务架构升级
https://segmentfault.com/a/1190000043342549
2023-01-17T11:09:57+08:00
2023-01-17T11:09:57+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<blockquote>作者:董红帅,马蜂窝微服务体系建设以及基础服务能力建设专家。</blockquote><p>马蜂窝作为旅行社交平台,是数据驱动的新型旅行电商。基于十余年的内容积累,马蜂窝通过 AI 技术与大数据算法,将个性化旅行信息与来自全球各地的旅游产品供应商实现连接,为用户提供与众不同的旅行体验。</p><p>随着业务的发展,马蜂窝架构也在跟随技术步伐进行更迭,开始基于 Kubernetes 进行更多的延展。在这个技术背景下,需要针对云服务开启新一轮的架构更新,比如:微服务场景建设新的蜂效平台及周边设施来支持迭代和流量泳道的能力,在多 Kubernetes 集群场景引入 Karmada 实现多集群管理,在微服务网关领域将 Istio + Envoy 的架构替换为 Apache APISIX 与 Envoy 共存的微服务网关模式。</p><h2>微服务 1.0 模式现状</h2><p>目前马蜂窝内部的微服务架构经历了两次迭代,本文中将针对原有架构的第一次调整定义为 1.0 版本。在进行微服务 1.0 架构的搭建之前,我们从发布系统能力、Kubernetes 容器、服务发现和微服务网关等角度进行了一些考量与目标对齐。比如 Kubernetes 的广泛应用,需要开始考虑基于容器做多语言支持,在 CI/CD 环节实现全面容器化,并支持多 Kubernetes 集群等。</p><p>在进行第一次迭代之前,内部架构的微服务网关使用的是 NGINX Ingress,但它其实是存在问题的。比如配置变更基于 NGINX reload,会造成业务有损;同时在应用范围内仅支持单 Kubernetes 集群,场景受限;内置资源过于简单,大量匹配规则依赖 Annotations,配置繁杂不友好,尤其是对外部服务发现能力支持很弱。</p><p>因此在进行微服务 1.0 迭代落地的过程中,为了满足一些业务需求,我们进行了如下动作(选取部分操作列举):</p><ul><li>在 Kubernetes 容器内,基于 macvlan 改造容器网络,IDC 机房网络与云厂商网络互通(容器互通的通信基础);借助网关或容器直连实现服务互访,不再使用 Kubernetes Service。</li><li>基于 Kubernetes 容器场景部署;基于 Consul 物理机虚拟机部署。</li><li>增加统一服务发现能力,基于 Kubernetes、Consul 建设统一的发现中心 — Atlas;同时基于 Atlas 扩展微服务网关、Java 生态、监控体系等。</li><li>在微服务网关的选择上,基于 Istio + Envoy 架构进行构建。对 Istio 中 Pilot 进行二次开发,对接 Atlas 发现中心,由于 Atlas 数据来源于多套 Kubernetes 和 Consul,进而将实例发现与 Kubernetes 集群解耦,间接做到网关对接多 Kubernetes 集群的能力,实现整个网关动态感知识别的变化。</li></ul><p><img src="/img/remote/1460000043342551" alt="蜂效 1.0 微服务网关架构" title="蜂效 1.0 微服务网关架构"></p><h3>痛点梳理</h3><p>在微服务 1.0 的这套架构中,实践下来还是存在一些痛点。</p><p>首先是在发布系统能力方面,微服务 1.0 中的发布系统,仅仅是一个发布系统,无法有效融合项目需求的管理(发布也是度量的一环);同时这套发布系统基于 PHP 构建,无法很好地支持自动化滚动部署、多版本滚动部署容量变更等较为复杂的部署场景;当多人对同一个项目开发,或一个需求关联多个项目时,缺少机制让流量在同一个迭代中自动串联(流量泳道)。同时对多 Kubernetes 集群管理并不方便,当 Kubernetes 下线时需要业务侧参与,给业务线带来了时间成本等。</p><p>其次就是微服务网关架构层面。前文也提到了 1.0 架构下网关是基于 Istio+Envoy 对 Pilot 做了二次开发,主要是对接 Atlas 发现中心。随着业务的应用数量越来越多,访问规则也越来越多,这就导致我们线上的网关生效延迟也越来越高。在流量巅峰状态下,大概有 15 秒左右的延迟。这个延迟主要来自于 CRD 资源,几乎全都在相同的 namespace 下,同时上下线时会触发全量更新导致延迟较高。</p><p>而在早期的使用过程中,Envoy 在数据全量推送过程中还会出现链接中断的状况,造成 <code>503</code> 问题的产生。除此之外,Envoy 还存在使用内存持续增长导致 OOM 的情况,当网关出现问题时,对 Envoy 和 Pilot 进行排错的成本较高。当使用一些高阶配置时,需要借助 Envoy Filter 实现,其上手门槛较高,对于想简单实现熔断、限流、Auth 鉴权等功能而言,成本较高。</p><p>除此之外,两个社区(Istio 和 Envoy)的发展速度很快,这也导致我们的架构比较难跟进上游社区的发展。</p><h2>基于 APISIX 的微服务 2.0 模式</h2><h3>新平台与新架构</h3><p>面对 1.0 架构下存在的痛点与问题,内部对于这套微服务架构进行了再次迭代,来到了 2.0 时代。2.0 架构场景下,我们新增了一套发布平台——蜂效平台。</p><p>蜂效平台重点突出了以下能力:</p><ul><li>融合了需求管理,支持了迭代部署能力,使得发布系统可以增益度量。</li><li>将容器部署和机器部署(物理机部署)在产品能力上进行了统一。</li><li>增强了精细化的流量管理能力、回滚能力(回滚策略:批次、实例数、间隔等)</li><li>与 Java 生态融合共建,支持了流量泳道能力,环境流量隔离。</li><li>网关基于 APISIX 进行重构,解决 Envoy OOM 及规则生效延迟较高的同时,通过 APISIX 产品化能力降低了问题排错成本,降低了扩展和配置网关的上手门槛。</li></ul><p>在蜂效平台产品侧中,我们将需求管理与迭代部署关联结合,并且为了支持了多种迭代流水线模式。在流量管理方面,我们借助 Atlas Instance 模型中的 env 属性以及扩展属性,实现了流量泳道能力。基于流量泳道模型,在上层产品侧构建迭代流量环境调用链路隔离。</p><p><img src="/img/remote/1460000043342552" alt="规划蜂效 2.0 " title="规划蜂效 2.0 "></p><p>在周边生态建设方面,Java SDK 侧做到了应用在迭代环境中可以实现隔离的链路调用,网关侧也进行了类似的操作。只是网关侧作为整个流量的入口,我们是通过 Cookie 的规则,也就 Cookie 的方式进行配置的。基线环境的流量只能到达基线环境的版本中,迭代环境的流量就会转发到迭代的版本上去。</p><p><img src="/img/remote/1460000043342553" alt="v1 与 v2 版本的流量分配" title="v1 与 v2 版本的流量分配"></p><p>同时在部署层的 Kubernetes 多集群管理层面,我们则借助 Karmada 实现了一个多 Kubernetes 集群的管理。在整个架构中(如下图所示),底层的能力主要是由 Kubernetes 多集群和流量网关 Envoy 与 APISIX、发现中心 Atlas、日志服务与监控服务等组成。</p><p>而蜂效平台在整个架构中主要是进行配置管理、构建部署、扩缩容和上下线等等,同时包含任务流的模块。最上方则是应用市场的一些能力,比如迭代能力和插件能力等。</p><p><img src="/img/remote/1460000043342554" alt="蜂效平台 2.0 架构图" title="蜂效平台 2.0 架构图"></p><p>整体来说,我们基于应用中心和服务打造出了 2.0 新架构。这套新架构在 Kubernetes 集群发生变更时,可通过 PropagationPolicy、OveridePolicy 等策略,实现 Deployment 等在 Kubernetes 集群级别的资源分发,减少集群变更时业务参与的成本,减轻了业务研发侧的一些压力。</p><h3>网关选型</h3><p>在 2.0 模式的架构中,流量网关我们提到了两个网关产品,即 Envoy 与 APISIX。Envoy 作为之前 1.0 版本的选择,我们并没有完全放弃,在 2.0 中我们也因为一些需求和产品期望,开始考虑新的网关产品进行替代,比如:</p><ul><li>访问规则变化时,网关的生效速度需要控制在毫秒级(生效慢,会导致网关生效速度不一,在使用了 CDN 场景下可能导致业务资源长时间 404)。</li><li>可在现有场景中,完全替换 Istio+Envoy 架构;同时支持 HTTP、gRPC,并兼容现有路由策略。</li><li>需要降低问题的排查成本,最好有产品化支持(如 Dashboard)。</li><li>产品足够稳定,社区活跃,功能强(对限流等场景支持)。</li><li>不需要二次开发即可支持公司现有架构的替换。</li><li>在替换 Istio+Envoy 架构过程中,需要保持双架构可用(Istio、Envoy 与新网关并存),如果新架构有问题可快速回退至原来方案。</li></ul><p>在调研了一些关键网关产品的模型之后,我们最终将方案锁定在了 Apache APISIX。APISIX 的架构也分为控制面和数据面,同时还附带 Dashboard 产品。在功能使用上,APISIX 提供了丰富的插件,比如限流、熔断、日志安全和监控等等。我们可以通过 APISIX 的 Admin API 提供的接口,去完整操作 APISIX 的所有能力,比如 Upstream、Consumer 还有各种插件等。对我们而言,APISIX 还有一个特别有优势的点,就是 APISIX 在升级时,能够做到对低版本的 API 进行统一的兼容。</p><p><img src="/img/remote/1460000043342555" alt="APISIX 架构" title="APISIX 架构"></p><p>除此之外,我们认为 APISIX 还有以下几个优势:</p><ul><li>APISIX 基于 Openresty 的性能很好,相比 Envoy 来说,性能损耗很少。在经过我们测试之后,在 QPS 的表现上,APISIX 损耗 3%,而 Envoy 损耗 16%;在时延表现上,APISIX 平均转发耗时 2ms,而 Envoy 耗时 7ms。数据的体现,已经展示了 APISIX 在性能上的卓越优势。</li><li>APISIX 还附有 Dashboard 支持,对于路由匹配异常等场景可快速判断是否为规则配置错误。</li><li>作为开源产品,APISIX 的社区更为活跃。在产品的功能上,在限流、鉴权、监控等方面相比 Envoy 成本更低,支持性更好。</li><li>APISIX 相比 Envoy 内存占用很低,但在配置的动态变更上相比 Envoy 要弱(Envoy 几乎大部分配置可动态下发),但也足够满足需求。</li></ul><p>因此,在调研与测试之后,我们在微服务 2.0 的架构下增添了 Apache APISIX 作为流量网关加入。由于网关是整个微服务流量的核心,如果从一套旧架构切换到一套新的架构,其实成本是比较高的。所以我们希望微服务的网关规则变化能够对新旧两套网关(Envoy 与 APISIX)同时生效,也就是一套配置可以适用于两种架构,因此我们在 2.0 架构中,针对这些变动做了一些调整。</p><h3>落地方案与实践问题</h3><p>首先考虑到成本问题,对原本的 Istio 架构保持不变,并未进行改造。同时在网关架构中,引入了新开发的关键组件—— <strong>istio-apisix-translator</strong>。</p><p>istio-apisix-translator 主要是去对接 Atlas 发现中心以及 Istio 的 CRD 数据。作为数据同步服务,实时将 VirtualService、DestinationRule 等规则变化转换为 APISIX 路由规则,将 Atlas Instance 数据实时转换为 APISIX Upstream 规则等。简单来说,就是通过这样一个服务组件,实现了对 Atlas 以及 Istio CRD 的数据支持。</p><p>借助这种架构,我们就实现了对两种网关的完整支持,如下图所示。</p><p><img src="/img/remote/1460000043342556" alt="蜂效 2.0 微服务网关架构" title="蜂效 2.0 微服务网关架构"></p><p>网关架构的核心部分则是图中最下方的 APISIX,上层的 istio-apisix-translator 则充当类似 Istio 架构中的 Pilot 角色,将 Instance 与 CR 数据整合后,借由 APISIX Admin API 推送至 APISIX 中,实例则是对接到内部业务的 Atlas 发现中心。因此,无论是访问规则发生变化还是 Atlas 的数据源发生变化,都可以将这份数据变化转换成 APISIX 的数据推到 APISIX 中。目前全链路都是基于 Watch 机制保证数据变化的实时处理,因此在实际应用场景下,基本可以达到数据变化的毫秒级生效。</p><p><strong>当然,在使用 APISIX 的过程中,我们也遇到了几处问题。但均已解决并将结果同步给了社区进行反馈。</strong></p><p>第一个问题就是在 APISIX 使用证书对接 etcd 时,如果 APISIX 节点较多,可能会导致 APISIX Admin API 接口响应非常慢。这个问题的原因是因为目前 etcd 存在一个关于 HTTP/2 的 BUG。如果通过 HTTPS 操作 etcd(HTTP 不受影响),HTTP/2 的连接数上限为 Golang 默认的 250 个。而 APISIX 的控制面和 Istio 架构的控制面有区别,APISIX 节点是直连 etcd,当 APISIX 数据面节点数较多时,一旦所有 APISIX 节点与 etcd 连接数超过这个上限,则 APISIX 的接口响应会非常的慢。</p><p>为了解决这个问题,我们也在 etcd 和 APISIX 的社区均进行了反馈,后续也通过升级版本(etcd 3.4 升级到 3.4.20 及以上,etcd 3.5 升级到 3.5.5 及以上)解决了这个问题。同时我们也已将这个结果同步到了 APISIX 社区官网 Q&A 文档中,方便后续用户遇到同样问题时,可以有所参考。</p><p>第二个问题就是在使用 APISIX 的过程中会遇到性能抖动的问题。</p><p>首先是会出现 <code>499</code> 响应抖动,这个问题主要出现在连续两次以上过快的 post 请求(也不止 post)的场景下。这种情况是 NGINX 认定为不安全的连接,则主动断开了客户端的连接。为了处理这个问题,只需将 <code>proxy_ignore_client_abort</code> 的配置调整为 on 即可。</p><p>除此之外,当 APISIX Admin API 接口请求密集时,也会出现 APISIX 数据面少部分响应超时的状况。这个主要是因为 istio-apisix-translator 在重启时,会将 Atlas、Istio CR 数据聚合,全量同步至 APISIX 中,大量请求引发 APISIX 数据变更,APISIX 数据面密集的同步变更导致小部分响应超时。为此,我们引入协程池和令牌桶限流,减少 APISIX 数据密集变更的场景来应对此问题。</p><h2>总结与发展</h2><p>马蜂窝当前是基于 Kubernetes 容器部署以及基于 Consul 的机器部署场景,自建 Atlas 服务发现中心,同时,在 Java 生态、微服务网关,微服务体系的流量泳道,以及监控体系做对接和适配。</p><p>在微服务网关前期,是基于 Istio 1.5.10 对 Pilot 二次开发,也在网关侧支持非容器部署场景。目前阶段则是保持了 Istio+Envoy 架构与 APISIX 架构同时支持,通过引入外部服务组件,让 APISIX 也复用 Istio CRD 资源。</p><p>从网关发展视角来看,未来我们也会跟随网关的一些趋势。比如现在很多产品都开始支持 Gateway API,像 APISIX Ingress、Traefik、Contour 等;同时网关的动态化配置也是未来非常明显的趋势,对于运维或者基础研发的同学来说,在后续考虑网关架构的选型和迭代时,也可以更多关注网关动态配置的方面。</p>
服务网格|如何使用 Amesh 配置插件
https://segmentfault.com/a/1190000043315153
2023-01-12T14:38:35+08:00
2023-01-12T14:38:35+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<blockquote>作者@lingsamuel,API7.ai 云原生技术专家,Apache APISIX Committer。</blockquote><p>在上一篇 Amesh 产品介绍中,我们有提到在 Amesh v2.0 版本新增了一个可选的控制面组件,即 amesh-controller 及相应的 CRD。Amesh controller 为用户提供了配置 APISIX 插件的能力,使 APISIX 众多的插件在服务网格场景下也能开箱即用,而无需用户进行自定义的开发。</p><p>那么如何使用这些组件,来进行 APISIX 插件能力的使用呢?本文在假设已经成功安装 Amesh 后,如何在 Amesh 中进行部署、配置和更新插件等操作。</p><h2>部署 Amesh Controller 与 CRD</h2><p>使用如下命令部署 Amesh Controller 与 CRD:</p><pre><code class="bash">cd controller
kubectl apply -k ./config/crd/
helm install amesh-controller -n istio-system ./charts/amesh-controller</code></pre><p>默认情况下,Amesh 将会自动连接到位于 <code>istio-system</code> namespace 下的 <code>amesh-controller</code> 服务 <code>15810</code> 端口,而无需重启 Sidecar。</p><p>如需自定义,可以使用 <code>AMESH_GRPC_SOURCE</code> 环境变量进行配置。该变量默认值为 <code>"grpc://amesh-controller.istio-system.svc:15810"</code>,按需指定到对应的 <code>amesh-controller</code> 即可。</p><h2>部署示例应用</h2><p>这里我们以 Istio 提供的 Bookinfo 应用为测试用例。</p><pre><code class="bash"># 在 Istio 目录下执行
kubectl -n test apply -f samples/bookinfo/platform/kube/bookinfo.yaml
kubectl -n test run consumer --image curlimages/curl --image-pull-policy IfNotPresent --command sleep 1d</code></pre><p>测试是否能够正常访问:</p><pre><code class="bash">kubectl -n test exec -it -c istio-proxy consumer -- curl -i -XGET "http://productpage:9080/productpage" | grep -o "<title>.*</title>"</code></pre><p>输出细节类似如下所示:</p><pre><code class="html"><title>Simple Bookstore App</title></code></pre><h2>配置示例插件</h2><p>本文将以 <code>response-rewrite</code> 插件为例进行演示。首先为集群应用下列示例配置:</p><pre><code class="yaml">apiVersion: apisix.apache.org/v1alpha1
kind: AmeshPluginConfig
metadata:
name: ameshpluginconfig-sample
spec:
plugins:
- name: response-rewrite
config: '{"body": "BODY_REWRITE", "headers": {"X-Header":"Rewrite"}}'</code></pre><p>尝试访问测试负载:</p><pre><code class="bash">kubectl -n test exec -it -c istio-proxy consumer -- curl -i -XGET "http://productpage:9080/productpage"</code></pre><p>输出细节应该包含如下内容:</p><pre><code class="yaml">Via: APISIX
Server: APISIX/2.15.0
X-Header: Rewrite
BODY_REWRITE</code></pre><h3>更新插件配置</h3><p>将示例 AmeshPluginConfig 文件修改为如下,移除 Body 配置:</p><pre><code class="yaml">apiVersion: apisix.apache.org/v1alpha1
kind: AmeshPluginConfig
metadata:
name: ameshpluginconfig-sample
spec:
plugins:
- name: response-rewrite
config: '{"headers": {"X-Header":"Rewrite"}}'</code></pre><p>再次请求测试负载:</p><pre><code class="bash">kubectl -n test exec -it -c istio-proxy consumer -- curl -i -XGET "http://productpage:9080/productpage" | grep -o "<title>.*</title>"</code></pre><p>输出应该包含如下内容:</p><pre><code class="html"><title>Simple Bookstore App</title></code></pre><p>这表明响应 Body 没有被重写,符合我们的预期。<br>下面的命令可以验证 Header 是否被正常重写:</p><pre><code class="bash">kubectl -n test exec -it -c istio-proxy consumer -- curl -i -XGET "http://productpage:9080/productpage" | grep "X-Header"</code></pre><p>输出内容如下所示:</p><pre><code class="yaml">X-Header: Rewrite</code></pre><h2>其他场景</h2><p>除了上文演示的 <code>response-rewrite</code> 插件之外,Amesh 还支持配置 APISIX 的所有插件。用户可以根据需要进行配置。</p><p>例如,当需要进行故障注入时,只需要将配置中的插件名改为 <code>fault-injection</code>,插件配置改为 <code>'{"abort": { "http_status": 200, "body": "Fault Injection!" }'</code> 即可实现,具体如下所示:</p><pre><code class="yaml">apiVersion: apisix.apache.org/v1alpha1
kind: AmeshPluginConfig
metadata:
name: faultinjection-sample
spec:
plugins:
- name: fault-injection
config: '{"abort": { "http_status": 200, "body": "Fault Injection!" }'</code></pre><h2>总结</h2><p>本文以 <code>response-rewrite</code> 插件为例,演示了 Amesh v0.2 版本新增的插件配置功能。在实际使用中,用户可以根据需要,为 Amesh 配置适合的 APISIX 中已有的插件。也欢迎各位在实践中进行体验,并反馈更多关于服务网格的想法。</p>
为什么 APISIX Ingress 是比 Ingress NGINX 更好的选择?
https://segmentfault.com/a/1190000043304207
2023-01-11T15:05:19+08:00
2023-01-11T15:05:19+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<blockquote>作者容鑫,API7.ai 云原生技术工程师,Apache APISIX Committer。</blockquote><p>本文将会对比两个比较流行的 Ingress controller 实现,希望能对读者进行 Ingress controller 选型中有所帮助。</p><blockquote><a href="https://link.segmentfault.com/?enc=%2BpSuYqrMJzl09YXC%2Fvgn8g%3D%3D.6ZmN8C0tBYgBi%2FYZkldjVmFEdVNhWj61pvm12l%2FD2jRUv5wrYow81uB0jsEThKoN" rel="nofollow">Ingress NGINX</a> 是 Kubernetes 社区实现的 Ingress controller,在社区中被广泛使用。<a href="https://link.segmentfault.com/?enc=WT%2BA4xEU3Vkqv4k%2F76U6FA%3D%3D.DOt6hps1aoHd2gPS3IJVyP4Dagtw8ps8kYALEQxSVeWfydv0f%2B6nxxlnpnbJhO7DCRbBqJXnc0B783yC2mvB5g%3D%3D" rel="nofollow">Apache APISIX Ingress</a> 则是 Apache 软件基金会下的开源项目,使用 APISIX 作为数据面的 Kubernetes Ingress controller。</blockquote><h2>Ingress NGINX vs APISIX Ingress</h2><h3>功能对比</h3><p>下列表格中,对比了 Ingress NGINX 和 APISIX Ingress 基本功能,包括协议支持、鉴权方式、上游探针/策略、负载均衡策略、Kubenertes 集成等。以下表格数据取自 <a href="https://link.segmentfault.com/?enc=7y4Efs3ZVucoBxrgbnL1XQ%3D%3D.kDmt41%2F%2B%2FLAAwz4eZbUHAJ0lFCAQrvlgNJ03CZcv5QbfW7SfoVb0zGF4WAB2UnHczbgNFqT9vKP9egWcyORfCX%2FboOSfAwJoCTyP44r93GQWyemFKQt7yzzoc2nRQTaN" rel="nofollow">learnk8s.io</a>。</p><table><thead><tr><th align="left">Product/Project</th><th align="left"> </th><th align="left">Ingress NGINX</th><th align="left">Apache APISIX Ingress</th></tr></thead><tbody><tr><td align="left">1. General info</td><td align="left"> </td><td align="left"> </td><td align="left"> </td></tr><tr><td align="left"> </td><td align="left">Based on</td><td align="left">nginx</td><td align="left">nginx</td></tr><tr><td align="left">2. Protocols</td><td align="left"> </td><td align="left"> </td><td align="left"> </td></tr><tr><td align="left"> </td><td align="left">HTTP/HTTPS</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">HTTP2</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">gRPC</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">TCP</td><td align="left">Partial</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">TCP+TLS</td><td align="left">✖︎</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">UDP</td><td align="left">Partial</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">Websockets</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">Proxy Protocol</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">QUIC/HTTP3</td><td align="left">Preview</td><td align="left">Preview</td></tr><tr><td align="left">3. Clients</td><td align="left"> </td><td align="left"> </td><td align="left"> </td></tr><tr><td align="left"> </td><td align="left">Rate limiting (L7)</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">WAF</td><td align="left">✔️</td><td align="left">Partial</td></tr><tr><td align="left"> </td><td align="left">Timeouts</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">Safe-list/Block-list</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">Authentication</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">Authorisation</td><td align="left">✖︎</td><td align="left">✔️</td></tr><tr><td align="left">4. Traffic routing</td><td align="left"> </td><td align="left"> </td><td align="left"> </td></tr><tr><td align="left"> </td><td align="left">Host</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">Path</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">Headers</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">Querystring</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">Method</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">ClientIP</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left">5. Upstream probes/resiliency</td><td align="left"> </td><td align="left"> </td><td align="left"> </td></tr><tr><td align="left"> </td><td align="left">Healthchecks</td><td align="left">✖︎</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">Retries</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">Circuit Breaker</td><td align="left">✖︎</td><td align="left">✔️</td></tr><tr><td align="left">6.Load balancer strategies</td><td align="left"> </td><td align="left"> </td><td align="left"> </td></tr><tr><td align="left"> </td><td align="left">Round robin</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">Sticky sessions</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">Least connections</td><td align="left">✖︎</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">Ring hash</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">Custom load balancing</td><td align="left">✖︎</td><td align="left">✔️</td></tr><tr><td align="left">7. Authentication</td><td align="left"> </td><td align="left"> </td><td align="left"> </td></tr><tr><td align="left"> </td><td align="left">Basic auth</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">External Auth</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">Client certificate - mTLS</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">OAuth</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">OpenID</td><td align="left">✖︎</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">JWT</td><td align="left">✖︎</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">LDAP</td><td align="left">✖︎</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">HMAC</td><td align="left">✖︎</td><td align="left">✔️</td></tr><tr><td align="left">8. Observability</td><td align="left"> </td><td align="left"> </td><td align="left"> </td></tr><tr><td align="left"> </td><td align="left">Logging</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">Metrics</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">Tracing</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left">9. Kubernetes Integration</td><td align="left"> </td><td align="left"> </td><td align="left"> </td></tr><tr><td align="left"> </td><td align="left">State</td><td align="left">Kubernetes</td><td align="left">Kubernetes</td></tr><tr><td align="left"> </td><td align="left">CRD</td><td align="left">✖︎</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">Scope</td><td align="left">Clusterwide<br>namespace</td><td align="left">namespace</td></tr><tr><td align="left"> </td><td align="left">Support for the Gateway API</td><td align="left">✖︎</td><td align="left">Preview</td></tr><tr><td align="left"> </td><td align="left">Integrates with service meshes</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left">10. Traffic shaping</td><td align="left"> </td><td align="left"> </td><td align="left"> </td></tr><tr><td align="left"> </td><td align="left">Canary</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">Session Affinity</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">Traffic Mirroring</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left">11. Other</td><td align="left"> </td><td align="left"> </td><td align="left"> </td></tr><tr><td align="left"> </td><td align="left">Hot reloading</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">LetsEncrypt Integration</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">Wildcard certificate support</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">Configure hot reloading</td><td align="left">Preview</td><td align="left">✔️</td></tr><tr><td align="left"> </td><td align="left">Service Discovery</td><td align="left">✖</td><td align="left">✔️</td></tr></tbody></table><h3>功能差异</h3><p>通过下图,可以粗略看到 APISIX Ingress 内置的功能和特性相比 Ingress NGINX 更加丰富,其中包括服务发现、协议支持、认证鉴权等等。</p><p><img src="/img/remote/1460000043304209" alt="功能差异" title="功能差异"></p><h3>服务发现</h3><p>在微服务架构中,应用被拆分为很多微服务,无论是微服务故障,还是对应用服务进行扩缩容,都需要尽快的通知到调用方,以免调用失败。因此,在微服务架构中,服务注册和发现机制就显得很重要了,通常这会通过注册中心来完成。</p><table><thead><tr><th align="left">Service Discovery</th><th align="left">Ingress NGINX</th><th align="left">Apache APISIX Ingress</th></tr></thead><tbody><tr><td align="left">Kubernetes</td><td align="left">✔️</td><td align="left">✔️</td></tr><tr><td align="left">DNS</td><td align="left">✖</td><td align="left">✔️</td></tr><tr><td align="left">nacos</td><td align="left">✖</td><td align="left">✔️</td></tr><tr><td align="left">exureka</td><td align="left">✖</td><td align="left">✔️</td></tr><tr><td align="left">consul_kv</td><td align="left">✖</td><td align="left">✔️</td></tr></tbody></table><h3>协议支持</h3><p>两者都对 HTTP/HTTPS 协议提供完整支持,APISIX Ingress 在协议支持上更丰富一些,能够的使用 TLS 来加密 TCP 流量,还支持 <a href="https://link.segmentfault.com/?enc=UjAjS9XFNRdmfveb%2BI5yJQ%3D%3D.3lSj1OMmjOGbPPElf1xIiJmg094b1C%2F8U%2FqUWdrV93Cz9%2BSU7QNs3xCoOkZhGnDN6LXsamABVQ6WW80GJ4nfdg%3D%3D" rel="nofollow">MQTT</a>,<a href="https://link.segmentfault.com/?enc=0ZQuyHS%2F37%2BzzyUZjOO0DA%3D%3D.QT213c0kLwAeKefz2xmBaquF8OmfXVkkfeHpF0CNx8rZFaX%2FOue1Bg%2FOwllLGjOUS5jegoa8BtTvEyey7ZP%2Bww%3D%3D" rel="nofollow">Dubbo</a>、<a href="https://link.segmentfault.com/?enc=sen10YHJOUUZ3B%2BMWM%2B35A%3D%3D.isMkCcc0bUaC%2FTwSPFGltXi8ISiL0OuX84cJRyT6udgLYlPyabnau%2BE3aSlLG3tjmOaPrg7wIi1O1INLXpoYog%3D%3D" rel="nofollow">Kafka</a> 等协议进行代理。</p><h3>服务治理能力</h3><h4>健康检查</h4><p>在后端节点故障或者迁移时,不可避免会出现节点不可用的情况。如果大量请求访问到了这些不可用的节点时,将会造成流量损失,导致业务中断。因此,需要对节点进行健康检查,通过探针的形式探测后端节点的可用性,将请求代理到健康的节点,从而减少或避免流量损失。</p><p>健康检查的能力在 Ingress NGINX 中尚未支持,而 APISIX Ingress 提供了该能力,其中包括:</p><ul><li><strong>主动健康检查</strong>:确保后端服务中的 Pod 处于可用的状态。在应用服务进行滚动更新时,会牵扯大量的节点进行更新,不健康的节点将会被负载均衡器忽略,开启健康检查能够有效的挑选出可用的 Pod,避免流量损失。</li><li><strong>被动健康检查</strong>:被动的方式无需发起额外的探针,每个请求就是探针,若一个健康节点连续 N 个请求都被判定为失败(取决于如何配置),则该节点将被标记为不健康。由于无法提前感知节点的状态,可能会有一定量的失败请求,在滚动更新时这种情况会相对常见,可以通过服务降级来避免失败的请求量。</li></ul><p>如下是 APISIX Ingress 为 httpbin 服务配置健康检查示例:</p><pre><code class="yaml">apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
name: httpbin
spec:
healthCheck:
passive:
unhealthy:
httpCodes:
- 500
httpFailures: 3
active:
type: http
httpPath: /healthz
healthy:
successes: 3
interval: 2s
httpCodes:
- 200</code></pre><h4>服务熔断</h4><p>流量高峰时,网关作为流量入口向后端服务发起调用,后端服务有可能会产生调用失败(超时或者异常),失败时不能让请求堆积在网关上,需要快速失败并返回回去,这就需要在网关上进行熔断。</p><p>服务熔断的功能在 Ingress NGINX 中尚未支持。在 APISIX Ingress 中则可以通过 <a href="https://link.segmentfault.com/?enc=miJ72yc5jKduZTY9mY%2BM3g%3D%3D.bTvprNtjRQfB%2FsGvcrgVT8V4et1PLKbUVdiUS9xjtvxGvhjHAy6oH1WZUQvrIFkiSpvIOlecuBrA91t3%2BpYjyQ%3D%3D" rel="nofollow">api-breaker</a> 熔断插件来实现。</p><p>具体使用配置示例如下:</p><pre><code class="yaml">apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
name: httpbin-route
spec:
http:
- name: rule1
match:
hosts:
- httpbin.org
paths:
- /status/*
backends:
- serviceName: httpbin
servicePort: 80
plugins:
- name: api-breaker
enable: true
config:
break_response_code: 502
unhealthy:
http_statuses:
- 505
failures: 2
healthy:
http_statuses:
- 200
successes: 2</code></pre><h3>插件和鉴权方式</h3><p>目前 Ingress NGINX 主要通过 <a href="https://link.segmentfault.com/?enc=yI7zmT3K4pDn9hpipLHEXw%3D%3D.Mo%2BvXtp0HgZ0gBSd%2B0CadKj2OtmdoHl5074Xa%2FvYl%2Ff5VF%2BIozlApfqpmrWhgf8cj4Gjf8Gm9bqq2EfCNjrGPE674qt6X22ixs0dcUJ4JRJUQ%2FSp9%2BlFwnqSeSMDNIkU" rel="nofollow">Annotations</a> 和 <a href="https://link.segmentfault.com/?enc=x7EqZ9oSsvOOAoPU%2FpEotg%3D%3D.m%2FfEMl1LhVzX6n2EqaP3X6C8Phzk%2Bz365mVzyWynPEAsacI%2FiB48268u9Gai46X2U0ZK3meyRjAwf7wcOLGPp3aRJYaT7INwspPCvbaPv1iCyxulfuibu5vCDMHHyUE6" rel="nofollow">ConfigMap</a> 等方式进行配置,支持的插件功能比较有限。如果想要使用 JWT、HAMC 等鉴权方式,只能自行开发。</p><p>而 APISIX Ingress 得益于 APISIX 的丰富功能,原生支持 APISIX 内置的 <a href="https://link.segmentfault.com/?enc=rv0t8D0MV9SDRxfhTJB6%2FQ%3D%3D.Y9Px1FSwtoXf5YuENeW7u3cfRttP4gQcLD0mz5BOLm4zcl%2Bgny6frVKuYTzPutkH4BKmjzEwRDKZhcYSYDtRONgc4ipTWqYK%2BN5COOTro1I%3D" rel="nofollow">80+ 插件</a>,能够覆盖大部分使用场景,还支持 JWT、HMAC、wolf-rbac 等多种鉴权方式。以下仅展示 APISIX Ingress 使用 HMAC 认证并在路由上的应用示例:</p><pre><code class="yaml">apiVersion: apisix.apache.org/v2
kind: ApisixConsumer
metadata:
name: hmac-value
spec:
authParameter:
hmacAuth:
value:
access_key: papa
secret_key: fatpa
algorithm: "hmac-sha256"
clock_skew: 0
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
name: httpbin-route
spec:
http:
- name: rule1
match:
hosts:
- httpbin.org
paths:
- /ip
backends:
- serviceName: httpbin
servicePort: 80
authentication:
enable: true
type: hmacAuth</code></pre><h2>Ingress NGINX 和 APISIX Ingress 的扩展方式</h2><p>除了以上这些细节对比外,两者对于额外功能的扩展也有所不同。当 Ingress controller 的基础功能无法满足企业用户的需求时,只能通过扩展的方式进行定制开发。接下来将具体介绍 Ingress NGINX 和 APISIX Ingress 如何进行功能扩展。</p><h3>Ingress NGINX 如何进行功能扩展</h3><p>Ingress NGINX 在扩展方式上比较单一,只能通过嵌入 Lua 程序的方式来扩展功能。我们以 <a href="https://link.segmentfault.com/?enc=3DWNDRHHWHIlSjYkp1ewqA%3D%3D.%2FYOlAk0dq4dp6Y1UAnr08LDJSP4d68EuBtLSihU80OestG%2F2SbjpNgLJjabwBJuBxyUcQsFtRzgC2j7zysUr6PRl9uteumSgiYiDQjrdlqGGsZeVOyQwL7s7YIYOdAsR" rel="nofollow">Ingress NGINX 插件开发</a>为例,大概需要以下步骤:</p><ol><li>编写 Lua 程序 example-plugin</li><li>将插件安装到 ingress-nginx pod 中的 <code>/etc/nginx/lua/plugins/<your plugin name></code> → <code>/etc/nginx/lua/plugins/example-plugin</code></li><li>在 ConfigMap 中启用 example-plugin 插件,需要在安装 Ingress NGINX 时引用此 ConfigMap 对象</li></ol><pre><code class="yaml">apiVersion: v1
kind: ConfigMap
metadata:
name: ingress-nginx-controller
namespace: ingress-nginx
data:
plugins: "example-plugin"</code></pre><h3>APISIX Ingress 如何进行功能扩展</h3><p>APISIX Ingress 提供了多种扩展方式,企业用户可以根据自身情况自由选择或组合,当前支持如下拓展方式:</p><ul><li>通过 <a href="https://link.segmentfault.com/?enc=91AANGlfFCYbbN6GDuEimA%3D%3D.%2F8aB2qsp2H9g9Cp6OOQkmZtVy67inAwUbxeo5EEtaj%2FQ3%2B89xYBOuLBLZWSU4k0ocAVi0nEWORJTBfuuQfr2pQ%3D%3D" rel="nofollow">Lua 进行插件开发</a>:这种方式相对简单,并且几乎没有性能损耗;</li><li>通过 plugin-runner 开发:这种模式下支持 Java/Python/Go 等语言进行开发,这可以方便用户利用一些现有的业务逻辑,并且无需学习新语言;</li><li>通过 WASM 进行插件插件:这种模式下,可以使用任何支持构建出 WASM 的语言进行插件开发;</li></ul><p>此外还可以通过 Serverless 插件来直接编写 Lua 代码,快速满足业务需求。</p><h2>为什么 APISIX Ingress 选择维护 CRD</h2><p>目前 APISIX Ingress 支持三种声明式配置:Ingress 、CRD 和 Gateway API。这里主要对比 Ingress 和 CRD,Gateway API 将在后续展开。</p><p>Ingress 比较适合从 Ingress NGINX 迁移的企业用户,其转换成本较低。但缺点也较明显,比如语义化能力弱、没有细致规范等,同时也只能通过 Annotations 方式扩展,且 Annotations 无法支撑复杂配置场景。相对的使用 CRD 主要有以下好处:</p><ul><li>更契合数据面的设计语义,更加简单易用;</li><li>一些重要配置能够被复用,而不会存在冗余庞大的单个配置;</li><li>功能性和可扩展能力有了巨大提升;</li><li>数据面 APISIX 有着活跃的社区,更新和发布版本快,CRD 的方式能够轻易支持数据面的更多能力;</li></ul><h2>Ingress NGXIN 的痛点:不支持配置热加载</h2><h3>静态配置带来的问题</h3><p>Ingress NGINX 主要基于 NGINX 配置文件的方式,尽管使用 NGINX + Lua 来实现功能扩展,但没有彻底解决静态配置文件的问题。在路由能力和加载模式上稍显不足,并且存在一些明显劣势。</p><p>比如添加、修改任何新的规则时,需要重新加载 NGINX 配置。随着越来越多的路由规则和证书,在触发变更时,reload 操作将会更耗时,甚至需要几秒到十几秒的时间,对线上流量的影响将会非常大的,会导致流量短暂中断、影响响应延迟、负载均衡质量(每次重新加载 NGINX 都会重置负载均衡状态)等。</p><h3>触发 NGINX 重新加载的情况</h3><p>以下这些情况,涵盖了 Ingress controller 大量的使用场景:</p><ul><li>创建新的 Inresss 资源;</li><li>将TLS 部分添加到现有 Ingress;</li><li>Ingress Annotations 的变化可能影响上游配置(例如 load-balance 注释不需要重新加载);</li><li>在 Ingress 中添加或删除 path;</li><li>Ingress、Service、Secret 资源被删除;</li><li>Secret 发生更新;</li></ul><p>在上述场景下,具有频繁部署应用程序的集群环境中,会不断触发 Ingress、Secret 等资源的操作(创建、更新、删除等),导致 NGINX 重新加载次数剧增,给生产环境带来了极大的影响。</p><h3>小结</h3><p>Ingress NGINX 的架构决定了它必须生成 NGINX 配置然后通过 reload 方式完成配置更新,架构不调整是无法解决这些已知问题。比如路由的实现,APISIX Ingress 则不再依赖 NGINX 配置改为了纯内存结构,通过热更新方式实现动态路由,不再需要重启 NGINX。</p><h2>云原生新一代网关规范 Gateway API</h2><h3>Gateway API 优势</h3><p>Gateway API 相比 Ingress 的功能性更强,旨在通过由许多供应商实现并具有广泛行业支持的富有表现力、可扩展和面向角色的接口来发展 Kubernetes 服务网络。当下 Gateway API 具有如下的优势:</p><ul><li>面向角色:Gateway 是由一组 API 资源组成的。不同的 API 资源代表了使用与配置 Kubernetes 网络资源的不同角色;</li><li>表现力强:Gateway API 的核心功能就包含诸如基于头的匹配、流量加权以及其他在 Ingress 中只能通过各实现者自定义的非标准化 Annotations 等方式实现的功能;</li><li>可扩展:Gateway API 允许不同资源在不同层级一同使用。这使得能够对 API 结构进行更精细化的控制。</li></ul><h3>支持情况</h3><p>Gateway API 作为一种扩展 Kubernetes 服务网络的标准,其 Gateway 资源能够实现作为 Kubernetes API 来管理网关的生命周期,功能十分强大。目前许多 Ingress controller 都在积极支持它,包括 Istio、Kong、Traefik 等。在目前 <a href="https://link.segmentfault.com/?enc=ZHq5L8KvUkWZI%2FoUb5qqRg%3D%3D.I4kms62VAYki59Zycb3Ua0iIpfSXshkCq3Tqt%2FtS0Jvmi3mKHb5jVwL0d1sB8nx973gz6IflvH1FhD0zfdnKpckLTolTZf6MeNNoV8%2FGDD4%3D" rel="nofollow">Gateway API 实现情况</a>中,很遗憾的是,<strong>Ingress NGXIN 尚未计划支持 Gateway API</strong> 。而 APISIX Ingress 已经支持了 Gateway API 的大部分特性:包括 HTTPRoute、TCPRoute、TLSRoute、UDPRoute 等。</p><h2>总结</h2><p>经过 APISIX Ingress 与 Ingress NGINX 的完整对比,我们可以看到两者基础功能差异不大,也都具备扩展能力。但在微服务的架构中,APISIX Ingress 对服务治理和服务发现的支持更具优势。</p><p>总体来看,两款开源软件均非常优秀,Ingress NGINX 主要特点是简单、易接入,但缺点也十分明显;APISIX Ingress 作为后来者解决了 NGINX 不支持热加载的痛点,在扩展能力和功能上相比 Ingress NGINX 也具有很大的优势。从项目发展角度而言,支持 Gateway API 和 CRD 能够扩展和丰富 Ingress controller 基础能力。</p><p>如果读者正在进行 Ingress controller 选型,倾向于功能丰富和更强的扩展能力,推荐使用 APISIX Ingress 。如果只是刚接触 Ingress controller,没有更多的功能需求,Ingress NGINX 也是一个比较好的选择。</p>
基于 APISIX 的服务网格方案 Amesh 积极开发中!
https://segmentfault.com/a/1190000043303960
2023-01-11T14:43:19+08:00
2023-01-11T14:43:19+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<blockquote>作者@lingsamuel,API7.ai 云原生技术专家,Apache APISIX Committer。</blockquote><p>在云原生快速发展的前提下,服务网格领域也开始逐渐火热。目前阶段,大家所熟知的服务网格解决方案很多,每种产品又各有其优势。因此在面对不同的行业或者业务背景时,每个人的选型想法都各不相同。</p><p>Apache APISIX 是一个动态、实时、高性能的云原生 API 网关,提供负载均衡、动态上游、灰度发布、服务熔断、身份认证、可观测性等丰富的流量管理功能。基于 APISIX 的扩展道路上,除了 APISIX Ingress 在云原生领域被各大厂商开始关注外,基于 APISIX 的服务网格方案也在积极迭代中。</p><h2>基于 APISIX 的服务网格方案</h2><p><a href="https://link.segmentfault.com/?enc=UAV8JWGlrOjseDQaAAWyCw%3D%3D.e5MGJWu3AXIt6mdHV3HUG65vVpez2ypHHK0%2BRqsK6cw%3D" rel="nofollow">Amesh</a> 是 <a href="https://link.segmentfault.com/?enc=Uc7%2Bv2jg0%2BQXGVGi8aqCrQ%3D%3D.%2B9TleVn07jkS6kf1wHaXy9t4xKIV6aBIc23Dbi3Ad%2FUa7A7Sn%2BNVWkZ2uzEaeaEc" rel="nofollow">Apache APISIX</a> 的服务网格库。它适配了 xDS 协议,可以从诸如 Istio 的控制平面中接收数据,并生成 APISIX 所需的数据结构,使得 APISIX 能够在服务网格领域作为数据面发挥作用。</p><p>依靠 Amesh,APISIX 可以工作在服务网格模式下,不使用传统的 etcd 作为数据中心,而是使用 shdict 与 Amesh 库直接进行数据交换,避免了额外的性能损耗,使得大规模部署成为可能。</p><p>通过使用 Amesh,可以在服务网格领域获得 APISIX 具备的高性能、丰富的流量管理功能、易扩展性等多种优势。</p><h3>Amesh 的架构</h3><p>Amesh 通过适配 xDS 协议,可以让 APISIX 替代 Istio 所使用的 envoy 组件来接管集群流量。在实际使用中,APISIX 将作为 Pod 的 Sidecar 接管网格内的所有流量。目前 Amesh 的架构如下图所示:</p><p><img src="/img/remote/1460000043303962" alt="Amesh 架构" title="Amesh 架构"></p><p>通过架构图可以看到,通过 xDS 协议,Amesh 可以将 Istio 作为控制面,从 Istio 侧获取配置信息,并将其转义为 APISIX 所需的配置。</p><p>而网格内部的所有流量都将由 APISIX 接管。其中,APISIX 的配置中心被设置为 Amesh,这使得 APISIX 脱离 etcd 的依赖。Amesh 为 APISIX 提供了一种从 xDS 协议中获取配置信息的方式。</p><p>此外,Amesh 在 v0.2 中提供了额外的可选控制面组件:amesh-controller。Amesh controller 增加了 Amesh 专用的 CRD,可以为 APISIX 配置一些 Istio 所不支持的额外功能。额外带有 amesh-controller 的架构如下图所示:</p><p><img src="/img/remote/1460000043303963" alt="amesh-controller 架构" title="amesh-controller 架构"></p><p>正如前文所提到的,Amesh controller 是可选组件。在未安装时,Amesh 也能正常使用 Istio 的原生能力提供服务。在安装了 amesh-controller 后,Amesh 能自动检测到控制面的加入,并动态地从中获取配置,而无需重启。</p><p>Amesh controller 为 Amesh 提供了 Istio 无法提供的 APISIX 特有功能。例如在安装 amesh-controller 后,用户能为服务配置 APISIX 原生具备的海量插件。</p><h2>Amesh 发展状态</h2><p>目前 Amesh 项目正在积极开发中。在最近发布的的 v0.2 版本中,Amesh 新增了可选的控制面 amesh-controller 组件,为 Amesh 提供了 APISIX 所支持的强大的插件系统,大大增强了 Amesh 的可扩展性。</p><h3>扩展能力</h3><p>在使用 Amesh 时,如果是常规的 Istio 部署,用户则可以通过 Lua 或 Wasm 来对 envoy 进行功能扩展。</p><p>与 Envoy 原生能力相比,APISIX 官方即支持插件扩展能力,维护了 80+ 的插件可供用户使用,许多功能已经原生集成。但由于在 Istio 中,不能对这些插件进行配置,无法直接使用这些插件所提供的能力。</p><p>为此,Amesh v2.0 版本新增了一个控制面组件,即前文提到的 amesh-controller。它为用户提供了配置 APISIX 插件的能力,使 APISIX 众多的插件在服务网格场景下也能开箱即用,而无需用户进行自定义的开发。</p><h3>应用示例</h3><p>在 Amesh v0.2 版本中,可以通过安装 amesh-controller 并使用提供的 <code>AmeshPluginConfig</code> CRD 来进行 APISIX 的插件配置。</p><p>例如,我们可以为请求的响应添加特定的 header,这里可以通过配置 APISIX 的 <code>response-rewrite</code> 插件实现。</p><p>假设我们需要添加的 header 为 <code>X-Header</code>,其值为 <code>AddedHeader</code>,我们可以配置如下的 <code>AmeshPluginConfig</code>,此时请求的响应中就会带上我们所需的 header。</p><pre><code class="YAML">apiVersion: apisix.apache.org/v1alpha1
kind: AmeshPluginConfig
metadata:
name: ampc-sample
spec:
plugins:
- name: response-rewrite
config: '{"headers": {"X-Header":"AddedHeader"}}'</code></pre><h2>总结</h2><p>在本文中,我们简单介绍了 Amesh 的架构,以及在 v0.2 版本中提供的 amesh-controller 所带来的架构变更,可以更好地帮助用户理解 Amesh 的工作原理。</p><p>在当下技术发展趋势中,服务网格势必是未来的流行趋势。虽然现在各种方案都还不太完善,但整体都属于螺旋上升的状态。当然,基于 APISIX 的服务网格也正朝着大家心目中的理想型服务网格解决方案奋进,也欢迎各位对 APISIX 服务网格方案感兴趣的朋友们进行尝鲜。</p>
盘点微服务架构下的诸多身份验证方式
https://segmentfault.com/a/1190000043292009
2023-01-10T17:09:28+08:00
2023-01-10T17:09:28+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<blockquote>联合作者:罗泽轩,API7.ai 技术专家、Apache APISIX PMC 成员<br>联合作者:赵士瑞,API7.ai 技术工程师,Apache APISIX Committer</blockquote><p><strong>身份认证是授予用户访问系统并授予使用系统的必要权限的过程。而提供了这一功能的服务,就是身份认证服务。</strong></p><p>在传统的单体软件应用程序中,所有这些都发生在同一个应用程序中。但在微服务架构中,系统由多个服务组成,在这样的架构中,每个微服务都有自己的任务,因此为每个微服务分别实现授权和身份验证过程并不完全符合此原则。</p><p>本文将从传统服务架构和微服务架构下的身份认证方式区别进行讨论,并最终衡量微服务架构中身份认证服务的各种实现方式的优劣。</p><h2>传统服务架构中的身份认证服务</h2><p>在企业开发服务的早期,所有功能都是做到同一个应用程序里面的。我们把这种模式称之为 “单体”,以跟当下更为主流的 “微服务” 架构区分开来。</p><p>单体应用由单个不可分割的单元组成。它通常由各个业务线各自开发,但是部署时放入到同一个环境中。所有这些都紧密集成以在一个单元中提供所有功能。这一单元里拥有所需的所有资源。单体应用的好处在于部署迭代简单,适合业务线较少且比较独立的公司采用。</p><p>随着企业开发出来的业务越来越复杂,我们会发现单体服务已经无法满足现实生活里面快速迭代的需要了。我们需要把这个单体的巨无霸拆分一下,同时保证现有的各个功能间的调用能正常进行。这时候,ESB(企业服务总线)便应运而生了。</p><p>所谓的 “企业服务总线”,就是一根连接各个企业服务的管道。ESB 的存在是为了集成基于不同协议的不同服务,ESB 做了消息的转化、解释以及路由的工作,以此来让不同的服务互联互通。从名称就能知道,它的概念借鉴了计算机组成原理中的通信模型 —— 总线,所有需要和外部系统通信的系统,统统接入 ESB,就可以利用现有的系统构建一个全新的松耦合的异构的分布式系统。</p><p>ESB 做了消息的转换解释与路由等工作,让不同的服务互联互通。传统的 ESB 的服务调用方式是,每一次服务的调用者要向服务提供者进行服务交互请求时都必须通过中心的 ESB 来进行路由。</p><p>接下来将按照这两种情况,分别描述对应的身份认证功能的实现。</p><h3>单体架构</h3><p>单体架构下,用户身份验证和会话管理相对简单。身份认证和授权发生在同一个应用程序中,通常使用基于 session 的认证方案。一旦通过身份验证,就会创建一个会话并将其存储在服务器上,任何需要它的组件都可以访问它并用于通知和授权后续要求。会话 ID 被发送到客户端并用于应用程序的所有后续请求,以将请求与当前会话相关联。</p><h3>ESB 架构</h3><p>在 ESB 架构下,所有用户与服务之间,服务与服务之间全部通过 ESB 总线进行处理。由于 ESB 的架构是从单体拆分下来的,身份认证方式相对于单体架构并没有变化。</p><h2>微服务架构中的身份认证服务</h2><p>从单体架构迁移到微服务架构有很多优势,但微服务架构作为一种分布式架构,会存在更大的攻击面,共享用户上下文更加困难。因此微服务架构下需要有跟传统架构不一样的身份认证服务,才能响应更大的安全性挑战。</p><p>目前,我们可以把微服务架构下的身份认证服务分为以下三类:</p><ol><li>通过每个微服务实现身份认证;</li><li>通过身份认证服务实现身份认证;</li><li>通过网关实现身份认证。<br>当然,每种做法都有自己特定的优缺点。</li></ol><h3>通过每个微服务实现身份认证</h3><p>既然微服务架构是从单体架构拆分出来的,因此比较自然的过渡方式就是由每个微服务自己实现身份认证。</p><p><img src="/img/remote/1460000043292013" alt="每个微服务自己实现身份认证" title="每个微服务自己实现身份认证"></p><p>每个微服务都需要实现自己独立的安全性保障,并在每个入口点上强制执行。此方法使微服务团队能够自主决定如何实现其安全解决方案。但是,这种方法有几个缺点:</p><ul><li>安全逻辑需要在每个微服务中重复实现,这会导致服务之间的代码重复。</li><li>它分散了开发团队的注意力,使其无法专注于其主要服务。</li><li>每个微服务都依赖于它不拥有的用户身份验证数据。</li><li>很难维护和监控。<br>完善这个解决方案的选择之一就是使用一个加载在每个微服务上的共享认证库。这个操作可以防止代码重复,开发团队将只关注他们的业务领域,但仍然存在这种改进无法解决的缺点。</li></ul><p>因为共享的认证库仍然需要有对应的用户身份数据,而且还需要保证各个微服务使用同样版本的认证库。老实说,共享认证库更像是服务拆分不透彻的结果。</p><p>因此这种方式总结来说,优势在于实施速度快,独立性强;而劣势也比较明显,服务之间的代码重复、违反单一职责原则,较难维护。</p><h3>通过身份认证服务实现身份认证</h3><p>既然每个微服务自己实现身份认证难以维护,而使用共享认证库又违背了微服务拆分的本意,那么能不能把共享认证库升级成专门的身份认证服务呢?</p><p><img src="/img/remote/1460000043292014" alt="身份认证服务" title="身份认证服务"></p><p>在这种情况下,所有访问都通过同一服务进行控制,类似于单体应用里面的身份认证功能。每个业务服务都必须在执行操作时,向访问控制模块发送单独的授权请求。</p><p>但是,这种方法在一定程度上减慢了服务的运行速度,并增加了服务之间的互连量。并且各个微服务会依赖这个“单点”的身份认证服务。万一统一的身份认证服务出问题,会造成链式反应,带来二次伤害。</p><p>所以总结来看,这种方式虽然确保了每个微服务职责单一,使得身份认证功能更加集中。但是仍会造成单点依赖,进而增加请求延迟。</p><h3>通过网关实现身份认证</h3><p>迁移到微服务体系结构时,需要回答的问题之一是微服务之间如何通信。前面提到的 ESB 是种方案,但是更常见的选择则是采用 API 网关。</p><p><img src="/img/remote/1460000043292014" alt="网关实现身份认证" title="网关实现身份认证"></p><p>API 网关是所有请求的单个终端节点入口,它通过充当使用这些微服务的中央接口来提供灵活性。某个需要访问其他微服务的微服务(以下称之为“客户端”,以跟被它访问的微服务相区分)无权访问多个服务,而是需要向负责将其路由到上游服务的 API 网关发送请求。</p><p>由于 API 网关位于客户端访问的必经之路上,因此它是强制实施身份验证问题的绝佳选择。使用 API 网关可以减少延迟(调用身份验证服务),并确保身份验证过程在整个应用程序中保持一致。</p><p>举个例子,通过 APISIX 的 <a href="https://link.segmentfault.com/?enc=9kn4T%2FRMqvCnU9FE63BmOw%3D%3D.mZ%2BP38aSWcICPYdVrT2XHlgJ0QsdeMSrC0xbWg6kylS6B%2FFaRx3d6qO5g%2Fw%2FLGPsfOSx44R7gGsnd%2F4IRVyAJw%3D%3D" rel="nofollow">jwt-auth</a> 插件,我们可以在网关上实现身份认证。</p><p>首先,我们需要规划若干个用户身份信息(名称、密钥等等),并将其配置到 APISIX 上。 其次,根据给定的用户密钥,向 APISIX 发起签名请求,得到这个用户的 JWT token。 接着,当客户端需要访问某个上游服务时,带上 JWT token,由 APISIX 作为 API 网关代理该访问。 最后,APISIX 会通过 JWT token,完成身份认证的操作。</p><p>当然,凡事有利就有弊,没有完全无劣势的技术选型。使用网关来完成身份认证,还是带来了少许单点问题。比起在每个微服务内完成身份认证,在网关上解决该问题,安全性相比会降低些。比如 API 网关被攻破之后,就可以访问该网关背后的任何微服务。但是风险是相对的,比起统一的身份认证服务,使用 API 网关的单点问题并没有那么严重。</p><p>因此这种方式操作起来,在优势上较为明显,比如可以有效保护后端微服务,微服务不用处理任何认证逻辑等。但同时还是会存有少许的单点依赖。</p><h2>总结</h2><p>在不同的场景下,我们会需要不同的身份认证方案。在单体应用中,身份认证发生在同一个应用程序中,服务端保存了所有的会话。进入微服务时代,单体应用演变为分布式服务,单体应用中的身份认证方式在微服务中并不适用。在微服务架构中,我们有上述提到的三种身份认证的方式可供选择。每种选择都有属于自己的利弊,需要根据具体的实际情况做具体分析。</p>
Apache APISIX Ingress 1.6 正式发布!
https://segmentfault.com/a/1190000043290917
2023-01-10T14:45:54+08:00
2023-01-10T14:45:54+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<p>距离上一个版本 v1.5 发布,已经过了 3 个月,我们很高兴地宣布 Apache APISIX Ingress v1.6 正式发布!</p><p>在该版本中,共有 <strong>29 位贡献者</strong> 参与代码提交,其中 <strong>17 位是新晋贡献者</strong> ,感谢大家的支持和参与!</p><p>本次发布的 Apache APISIX Ingress v1.6 版本带来了众多新特性,主要集中在对 Gateway API 的支持,同时也在扩展 APISIX Ingress 的使用场景和易用性方面的提升。以下是一些重点特性的介绍。</p><h2>扩展对 Gateway API 的支持</h2><p>Gateway API 是 Kubernetes 中下一代的 Ingress 规范,致力于提供富有表现力,可扩展和面向角色的接口来发展 Kubernetes 的网络,各个 Ingress controller 项目都在积极推进对该规范的支持。Apache APISIX Ingress 项目自 2021 年开始就在积极地紧跟上游社区的发展,并积极推进 Gateway API 在 APISIX Ingress 项目中的实现。</p><p>当前,Apache APISIX Ingress 项目中通过 Gateway API 进行配置的特性尚处于 beta 阶段,欢迎大家在测试环境中积极进行测试,并提供反馈,我们将持续的对此特性进行优化和改进,尽早完成此特性的 GA。</p><p>在 APISIX Ingress v1.6 版本中,我们添加了对 Gateway API 中的 <code>TCPRoute</code> 和 <code>UDPRoute</code> 这两种资源的支持。同时,扩展了对 <code>HTTPRoute</code> 资源中 <code>Filters</code> 的支持,这样用户在使用 <code>HTTPRoute</code> 资源时,就可以在该资源中应用一些重定向、Header 改写等能力了。</p><p>例如可以使用如下配置:</p><pre><code class="YAML">apiVersion: gateway.networking.k8s.io/v1alpha2
kind: HTTPRoute
metadata:
name: http-route
spec:
hostnames: ["httpbin.org"]
rules:
- matches:
- path:
type: PathPrefix
value: /headers
filters:
- type: RequestHeaderModifier
requestHeaderModifier:
add:
- name: X-Api-Version
value: v1
- name: X-api-key
value: api-value
set:
- name: X-Auth
value: filter
remove:
- Remove-header
backendRefs:
- name: httpbin
port: 80</code></pre><p>通过使用此配置,客户端在对 <a href="httpbin.org">httpbin.org</a> 进行请求时,将会添加 <code>"X-Api-Version": "v1"</code> 和 <code>"X-Api-Key": "api-value"</code> 的请求头,并将 <code>"X-Auth"</code> 请求头的值设置为 <code>filter</code> ,同时将移除 <code>"Remove-Header"</code> 这个请求头。</p><h2>支持与服务发现组件的集成</h2><p>Kubernetes 中默认是使用基于 DNS 的服务发现机制,但是应用在迁移和改造的过程中,并非所有的业务都会选择改造成基于 DNS 的这种服务发现机制,仍然有大量微服务架构的应用会继续使用原有的服务注册发现组件,比如 Consul,Nacos,Eureka 等。</p><p>为了将 APISIX Ingress 打造成一款更加好用的 Ingress controller,我们在 v1.6 版本中新增了与服务发现组件集成的能力,用户可以将注册在 Consul/Nacos/Eureka/DNS 中的服务,通过 APISIX Ingress 暴露出来,无论是南北向还是东西向流量的场景均可使用。</p><p>例如通过如下配置,声明要代理的服务是通过 Nacos 注册的名为 <code>httpbin</code> 的服务。</p><pre><code class="YAML">apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
name: httpbin-upstream
spec:
discovery:
type: nacos
serviceName: httpbin</code></pre><p>然后在 ApisixRoute 资源中对其进行引用即可:</p><pre><code class="YAML">apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
name: httpbin-route
spec:
http:
- name: rule1
match:
hosts:
- local.httpbin.org
paths:
- /*
upstreams:
- name: httpbin-upstream</code></pre><p>这样客户端在访问时,就会被 APISIX 代理到 Nacos 中注册的服务了。更多内容可<a href="https://link.segmentfault.com/?enc=ri6el8g3oxOzgJ5Q2Egb3w%3D%3D.SlCYTp8KOaKcBIfdXKAFsS1T2fP5ON0%2BMnJ4AGy7%2BgmO%2BynwMnoOUkhI9trLdz407dN6qU2KKC4sRfjLK4OUND7xKcghJUQI765g%2F8ranNi0XK7omkeNbT3nrq0Drtje" rel="nofollow">参考文档</a>。</p><h2>支持代理外部服务</h2><p>与上述功能类似,Apache APISIX Ingress v1.6 版本中还添加了对外部服务代理的能力。主要是为了便于用户对一些没有部署在 Kubernetes 中的外部服务进行代理。</p><p>最典型的场景比如说消息推送。业务为了保障服务的高可用,通常会选择多家供应商提供服务,但供应商也可能会出现一些异常的情况。这种时候就可以通过这个功能,在多个供应商提供的服务中进行动态调度了。</p><p>比如通过如下配置设置两个供应商的域名作为后端:</p><pre><code class="YAML">apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
name: notify-api
spec:
externalNodes:
- type: Domain
name: foo.com
- type: Domain
name: bar.com
healthCheck:
passive:
unhealthy:
httpCodes:
- 500
- 502
- 503
- 504
httpFailures: 3
timeout: 5s
active:
type: http
httpPath: /healthz
timeout: 5s
healthy:
successes: 3
interval: 2s
httpCodes:
- 200</code></pre><p>然后在 ApisixRoute 资源中进行引用:</p><pre><code class="YAML">apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
name: notify-route
spec:
http:
- name: rule1
match:
hosts:
- local.notify.app
paths:
- /*
upstreams:
- name: notify-api</code></pre><p>这样,如果某个供应商的服务出现异常,则会根据健康检查的规则自动地代理到备用的服务上,从而保障业务的可用性。</p><p>同样的,如果业务中存在需要代理 ExternalName Service 的场景也可以使用这种方式进行代理。更多内容可<a href="https://link.segmentfault.com/?enc=Rl%2F0VV%2BBVmWD6jVpqLlE3g%3D%3D.58orx3yx6I3AJx%2FufWVnPL1ofaswqCvC8QmHgs9hNajxb%2BUg9WhR%2BbfWfL%2FK0hTdMGcyG4RaEaCo1ITVIk3CsbGlcX%2Be0n4MAwzylhcmhb4%3D" rel="nofollow">参考文档</a>。</p><h2>其他</h2><p>除了上述的这些功能外,在此版本中还添加了很多其他功能,包括:</p><ul><li>支持 Ingress 资源中代理不同 namespace 中的后端服务;</li><li>原生的 MQTT 协议的代理支持;</li><li>允许为 4 层代理添加插件支持;</li><li>允许在 ApisixRoute 资源中使用 vars 进行条件匹配;</li><li>日志轮转支持;</li></ul><p>更多详细的变更请查看 Release Note:<a href="https://link.segmentfault.com/?enc=eQN7liH9iCFbGeJWjGs1Hw%3D%3D.UcDJWL0XDPrrYaOCLD6NDxHMzb4KzagJCzk5cPCTCA2i0ghK9cTDAAa0ko%2BPQN1djwERUrgn514egQQnhyoIeSt%2F1LEtR%2BjEeJAneLAlWBUpbkBomrOOcJUV1ff8NLJo" rel="nofollow">https://github.com/apache/apisix-ingress-controller/blob/master/CHANGELOG.md#160</a></p>
2022 Apache APISIX 年度记忆
https://segmentfault.com/a/1190000043250970
2023-01-05T15:10:52+08:00
2023-01-05T15:10:52+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<p>时光已逝,转眼 2022 年已然落下了帷幕。Apache APISIX 社区在众多开源爱好者和开发者的陪伴下,又一起走过了新的四季。</p><p>在过去的一年中,我们的开源项目和社区都取得了许多成就,并得到了来自全球范围的广泛关注和支持。2022 年,APISIX 社区仍然在为全球技术开发者和企业提供优质的产品与支持,并不断改进和升级产品。这一年中,APISIX 新增了许多功能,并迈入了全新的 3.0 时代;改善了用户体验,并得到了许多优秀贡献者的陪伴与合作。</p><p>我们希望通过这一份纸上告白,向大家展示 Apache APISIX 在过去一年中取得的成就,并感谢所有为 APISIX 项目做出贡献的人。</p><h2>迈进 3.0 大版本,新增超多实用性功能</h2><p>进入 2022 年后,APISIX 从 1 月份开始依旧保持着良好的发版速度。<strong>这一年中,APISIX 一共发布了 15 个版本,涉及 6 个版本的更迭,包括旧版本的维护更新以及正常版本的迭代。</strong></p><p><img src="/img/remote/1460000043250972" alt="releases" title="releases"></p><p>同时,在今年 APISIX 也将产品拉进了新的 3.0 时代,目前已针对 3.x 系列发布了 3 个更新版本。在 3.0 的整体规划中,将会陆续带来更多的针对生态和产品体验上的功能提升,使 APISIX 的可用性更上一层楼。<strong>在新的一年中,APISIX 也会为产品带来更多实用性功能,助力企业在数字化转型的过程中,愈发轻松。</strong></p><p><img src="/img/remote/1460000043250973" alt="3.0 roadmap" title="3.0 roadmap"></p><p>从 2019 年在 Apache 软件基金会毕业成为 Apache 社区顶级项目,到现在保持着稳定发版速度和活跃社区氛围的三年时间里,APISIX 项目在 GitHub 上已得到 10K+ star 并拥有 6 亿多下载量,contributor 数量已经超过 500 人。同时每月保持贡献 70+ commit、处理 80+ issue 的响应速度。高活跃度的社区氛围,正是推动产品快速成长的关键因素。</p><p><img src="/img/remote/1460000043250974" alt="github data" title="github data"></p><h2>泵入新鲜血液,收获行业认可</h2><p><strong>产品快速成长的背后,离不开社区中每一位成员的支持。</strong>在过去的一年中,APISIX 的贡献者人数增加了 200 人左右。相比总贡献者人数,这一年的增长相当于总人数的近 40% 比例,可见今年 APISIX 收获了开发者们超高的热情反馈,并将对开源产品的喜爱体现在了社区贡献中来。</p><p>这三年的发展过程中,APISIX 得到了非常多开发者的肯定与认可,他们也在全球 20 多个国家和地区进行 APISIX 项目的宣传。为了感谢各位对 APISIX 社区的喜爱,我们也会定期为参与 APISIX 社区的新晋贡献者们赠送社区周边,今年也是一样。上半年的新晋贡献者我们已派发过相关周边,下半年的新晋贡献者礼物也在紧锣密鼓地制作中。如果你有收到来自 APISIX 社区的相关邮件,记得填写信息哦。当然,你也可以主动在 <a href="https://link.segmentfault.com/?enc=5EQmgbKeKrGiLR8ckY1Uvg%3D%3D.4ZJ6Yo7enuM4ji11CKCdwzQJ12Z6ufCP02%2FCzKWrYGqLehgH8h4FtHB55wkO5qiiwNYXEfPCBAcssLg%2BliHPDw%3D%3D" rel="nofollow">GitHub 页面</a>进行填写,之后就会有精美周边送到你手上。</p><p><img src="/img/remote/1460000043250975" alt="contributor" title="contributor"></p><p>除了国内外开发者的认可外,今年 APISIX 也收获了很多企业级的技术赞誉。比如在互联网保险领域,众安保险在整个 DevOps 的全生命周期中,将 APISIX 作为流量治理的关键环节去处理,实现了更好的多渠道流量隔离;在智能汽车 AI 领域,地平线则选择了 APISIX Ingress 替换 Traefic 作为公司的统一流量网关,丰富了多云环境下的服务调用等功能;在国产手机领军队伍中,vivo 使用 APISIX 替换了之前传统的 NGINX,同时基于 APISIX 还自研了内部业务配置管理系统,在 vivo 业务架构的实践和场景支持上都表现得十分出色。</p><p><img src="/img/remote/1460000043250976" alt="usercase" title="usercase"></p><p>当然,以上也仅仅只选取了部分使用 APISIX 的企业用户,想要了解不同行业对于 APISIX 的实践,可以前往 <a href="https://link.segmentfault.com/?enc=%2BP3FrDIa1vFC5NrIdNYUZA%3D%3D.nhKAtipPAdwqFxKlujrTnhuW6Zn2rEuXot38TDWRNabmdhf%2FkFq7ZOfHdp11FFHLXd9kLfPqE9wS5nB50L1bBw%3D%3D" rel="nofollow">社区官网</a> 查看更多细节。</p><h2>活动形式多样,持续践行 Apache Way</h2><p>在 2022 年中旬,APISIX 社区举办了第一次全球性质的大会—— Apache APISIX Summit Asia 2022。在为期两天的活动中,我们见证了不同行业领域下的 APISIX 实践用例。由来自腾讯云、阿里云、VMvare Tanzu Labs 等顶级科技公司的工程师以及 APISIX PMC 成员们,带来了干货满满又富有借鉴意义的技术展示。</p><p>除此之外,还有许多深入但有趣的圆桌研讨会,众多的开源贡献者和投资者就构建社区优先开源软件的原因和方法分享了他们的想法,帮助组织和企业更好地理解和使用开源 API 管理工具。</p><p><img src="/img/remote/1460000043250977" alt="image (21).png" title="image (21).png"></p><p>今年虽然没有太多机会进行线下活动的开展,但 APISIX 社区仍然在南京、杭州、深圳和多伦多等城市举办了 APISIX 线下 Meetup,与众多社区用户和开源爱好者见面,为技术人搭建线下交流平台。</p><p><img src="/img/remote/1460000043250978" alt="meetup" title="meetup"></p><p>除此之外,APISIX 社区的小伙伴们也活跃在其他活动的现场,他们积极参与 QCon 大会、Open Source Summit、Service Mesh Summit、亚马逊云科技中国峰会、阿里云开发者大会等行业会议,为其他领域也带来了 APISIX 的相关集成分享。</p><p><img src="/img/remote/1460000043250979" alt="topics" title="topics"></p><p>同时在社区合作方面,先后与 Apache Pulsar,Apache RocketMQ、Apache SeaTunnel,Apache EventMesh,KubeSphere 等社区组织了多次联合线上 Meetup,在打造 APISIX 上下游生态的同时,也为不同社区开发者近距离讨论提供了平台。</p><p>除了让更多的开发者加入到线下活动之外,APISIX 社区也一直致力于拉近与学生们的开源之旅。APISIX 社区已连续两年积极推进全球化的开源计划 Google Summer of Code (GSoC) ,让全球各地的学生们参与开源并开始接触 APISIX;在国内,APISIX 项目也已连续两年积极参与到由中科院发起的开源供应链点亮计划中来。并通过这些活动项目的产出,让学生们对开源的兴趣大增,为后续开源年轻势力输送了超多的新鲜血液。</p><h2>全球化开启,让所有人都可以使用上优秀产品</h2><p>在今年,APISIX 也在尝试打开更广泛的全球视角,去接触世界各地有趣的开发者们。在这一年的时间里,APISIX 社区的工程师和布道师们穿梭在 30 多个国家,参与了 80 多场线下技术会议,从中国、美国、加拿大到印度、丹麦、瑞士等,遍布各大洲。你可以在很多国际会议上,看到来自 APISIX 社区可可爱爱的面孔们。</p><p><img src="/img/remote/1460000043250980" alt="Global users" title="Global users"></p><p>除此之外,来自国外布道师和社区贡献者们的技术内容也开始在各个平台(Dev.to、Medium、Dzone、Hackernoon、Reddit、InfoQ 等),收获了令人惊喜的关注度变化,比如 APISIX 的相关内容已在 Hacker News 的前十榜单上出现过数次。而从今年开始,APISIX 社区的一些宣传渠道也开始进行活跃,在 YouTube 频道新增了 20 个全新的教程视频,收获了近 2 万的观看量,Twitter 的关注今年也翻倍提升,从 500 跃升到了 1100 人左右。下图仅选取了部分渠道进行关注者人数变化展示。</p><p><img src="/img/remote/1460000043250981" alt="Global data" title="Global data"></p><p>同时在今年的海外探索中,我们也积极与学生群体进行交流,与伯克利、多伦多大学、滑铁卢大学等在美国、加拿大、印度、非洲的大学和组织共举办了 15 场 workshops,总量覆盖数百名 CS 和热爱开源的学生们。这也让 APISIX 的贡献者群体从公司的核心工程师扩展到了知名高校的大学生们,为后续的开源群体带来了国际化新鲜血液。得益于 workshop 的活跃,APISIX 社区的 Slack 渠道活跃人数也得到了巨大的增长。</p><p>全球化的探索,也让 APISIX 收获了来自国际化企业的认可。来自土耳其最大的电子商务公司 Trendyol,在今年开始在生产环境中更换为 APISIX,期间也积极地在社区中进行相关进度反馈。与此同时,甲骨文 Oracle 在其 Spring Cloud 的 BaaS 产品中也开始使用 APISIX 来部署和保护示例应用程序的 API。</p><p><img src="/img/remote/1460000043250982" alt="Global usercase" title="Global usercase"></p><h2>新的一年,新的展望</h2><p>在过去的时间里,Apache APISIX 先后获得云计算开源产业联盟评选的「尖峰开源项目及开源社区」、CSDN 机构评选的「年度云原生技术产品」等,并被 Stackshare 评为「2021 年全球 Top100 开发者工具」。非常感谢来自全球视角的用户肯定,新的一年,APISIX 社区会更加努力地为全世界用户提供更多的惊喜。</p><p>不管是产品方面的功能迭代与生态集成,还是社区层面的全球化布道,亦或是加强与学生群体的开源技术交流。开源世界内,人依旧是最重要的组成因素。所以我们希望在未来的时间里,APISIX 社区会出现更多种族、更多语言、更多行业的人们来献计献策,一起让 APISIX 变得更加多姿多彩。</p>
聚焦人机交互智能应用领域,APISIX 在希沃网关的应用与实践
https://segmentfault.com/a/1190000043244587
2023-01-04T19:22:27+08:00
2023-01-04T19:22:27+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<blockquote>分享嘉宾简海清,视源股份运维负责人。</blockquote><p>视源股份(CVTE)自成立以来,依托在音视频技术、人机交互、应用开发、系统集成等电子产品领域的软硬件技术积累,建立了教育数字化工具及服务提供商希沃(seewo)、智慧协同平台 MAXHUB 等多个业内知名品牌。其中希沃从 2012 年到 2021 年连续 10 年蝉联中国交互智能平板行业市占率桂冠,已成为名副其实的行业标杆企业。</p><p>为了应对日趋成熟及快速增长的业务现状,希沃又是如何在网关层面进行跟进的呢?</p><p><strong>随着技术的飞速发展,在人际交互智能领域,业务需求也对架构迭代有了更高的要求。为了应对日趋成熟及快速增长的业务现状,希沃又是如何在网关层面进行跟进的呢?</strong></p><h2>网关往期迭代与痛点</h2><p>希沃网关的发展经历了四个版本的迭代。2013 年公司开始尝试互联网业务,那时候采用了 OpenResty + NGINX 静态配置的方式搭建了最初的网关,开发人员通过 SCP(Secure Copy)进行发布。与此同时一个比较严重的问题就是,每次上线发布都需要运维人员的协助才能保证平滑上线。</p><p>随着业务的发展和人员的扩充,2016 年我们开发了第二代发布系统和相关迭代网关。这次是基于 OpenResty 集成了 upsync 模块,同时配合 Consul 来进行服务发现。第二代的系统解决了上一代开发人员无法独立发布上线的问题,但仍需要运维协助才能进行扩容。</p><p>之后公司业务开始了迅猛发展,开始对网关以及产品的弹性扩缩能力有了更高的要求。2018 年我们基于 K8s 开发了第三代系统。考虑到仍有部分应用遗留在数组机上,所以整个网关架构是在 K8s 上使用 Ingress NGINX 来当作第二层的网关,第一层网关仍是 OpenResty 配合的双层网关架构。这种情况下虽然解决了前代发布扩容等自助问题,但又引入了新的麻烦。</p><p>业务的快速扩充致使对于整体稳定性的要求越来越高。采用这种双层网关架构后,一层 NGINX reload 和二层网关的路由变更,都会造成长连接断开。这对于一些长连接使用场景会影响较大,比如软件需要获取老师的授课状态时连接突然断开,状态获取中断从而影响授课。</p><p><img src="/img/remote/1460000043244589" alt="output (4).png" title="output (4).png"></p><p>本身双层架构就会带来成本层面的一些增加。同时,从上图的网关流量拓扑图可以看到,除上述遗留问题外也还存在一些架构上的痛点:</p><ul><li>在双层网关架构下,不管是在第一层网关添加域名、修改配置或者添加一些特殊规则等,都需要 reload NGINX。</li><li>同时从整体架构来看,组件的配合对于流量控制层面来说比较差。尤其是目前我们的业务用户体量已达到千万级别,一旦客户端出现不可规避的异常,就有可能出现侵蚀服务端的情况,这种时候如果在网关层面没有一定的流量控制能力,对于后端来说将会造成非常严重的雪崩。</li></ul><p>因此,基于上述迭代遗留问题和架构痛点,在 2022 年我们引入了 APISIX 来解决上述问题。同时借助 APISIX,也加强了在网关层面对于流量的控制能力。</p><p>但是在迁移 APISIX 的过程中,也会存在一些已知挑战。比如:</p><ul><li>⼀层 NGINX 域名多,定制化规则复杂。目前我们的业务中有 700+ 域名,同时还存在非常多的定制化配置,比如重定向、黑白名单等,这些都需要适配 APISIX 的插件。</li><li>由于历史遗留问题,⼀层 NGINX 和二层 Ingress 网关还是⼀对多的关系,对于后续的流量切换是不是会很复杂,这也是一个待解决问题。</li><li>内部存在的双层 DNS 架构。目前 DNS 解析主要用于处理公网和服务器内部的解析,所以对于后续的方案我们更希望是一个能方便回滚同时可以优化内网调用性能的。</li></ul><h2>迁移 APISIX 后架构调整</h2><p>面对上述已知的挑战,在迁移过程中主要进行了以下三个角度的操作。由于整个迁移过程没有涉及到研发内容,所以全部都是由运维人员实施的。</p><p>在迁移过程中,首先要做的就是 APISIX 路由的生成。基于原本的架构,我们去掉了一层特殊功能,比如 rewrite、set-header 等。弱化一层网关的转发,把所有功能都集中在二层的 Ingress 上,然后基于 Ingress 去生成 APISIX 的路由。同时在 NGINX 配置的基础上适配 APISIX 的插件。</p><p>路由生成后,就需要去校验整个转发过程是否正确。我们基于 goreplay 的录制回放来验证路由转发的正确性,同时通过自动化脚本来验证插件功能是否正常。在功能校验通过的情况下,我们会继续验证 APISIX 在性能层面是否满足内部需求。因此,在性能压测过程中我们自研了 <code>elastic-apm</code> 插件,同时针对部分影响 QPS 的插件进行了性能优化。</p><p>处理完功能跟性能相关的问题后,最大的挑战就是流量切换了,因为流量切换将直接关乎生产质量。</p><p><img src="/img/remote/1460000043244590" alt="output.png" title="output.png"></p><p>虽然前边我们已经使用了 goreplay 进行流量录制回放,但我们仍然需要一个比较可靠的回滚方案。假设流量切换造成了异常,比如转发异常或者是 APISIX 出现崩溃时,能够进行快速回滚操作。基于此,我们首先切换了公网流量,因为公网是通过 WAF 回源到 SLB 来进行流量切换的,这时如果我们切换到 APISIX,就可以很方便地去修改回源地址来将整个流量进行回滚,如上图标注「切换」字样所示。这个异常情况下的流量切换过程基本是在秒级别,可能 10 秒内就把所有流量都切回来了。</p><p>完成了公网流量切换的情况下,顺利运行了几天,我们就通过 APISIX 将内网流量也进行了变更,然后整个生产上的切换就全部完成了。但是这个上线过程中,其实我们还是遇到了一些问题的。</p><h2>迁移过程中的问题与解决方案</h2><h3>Prometheus 插件转发延迟</h3><p>这个是在我们内网测试环境中发现的一个问题。由于我们的内网是 all-in-one 的测试环境,所有部门都使用同一个 APISIX 的入口,所以路由规则非常多,达到 4000+。这样就会导致每次拉取 <code>Prometheus</code> 插件时, metrics ⼤小达到 80M+,造成单个 worker 进程跑满,从而造成 worker 的转发延迟。</p><p><img src="/img/remote/1460000043244591" alt="output (1).png" title="output (1).png"></p><p>这个问题是目前 APISIX 开源版本存在的一个现象,主要是因为业务流量和内部 API 流量(比如Prometheus metrics 和 Admin API)都共用 worker 造成的。我们在之前是针对 Prometheus 插件进行了修改,其中延迟相关的 metrics 占用了 90%以上(如上图所示),所以我们将这部分采集去掉了。去掉这部分后,业务层面还是满足了我们的监控使用需求,并未造成影响。</p><p>不过最近我们针对这个问题又有了新的方案,目前还处于 demo 阶段。这套新方案是对 NGINX 源码进行修改,通过多启动⼀个或多个 worker 进程(isolation process) 来专⻔监听特定端口的请求(比如 Prometheus、Admin API、Control API 等),不监听处理正常业务端口请求。其它的 worker 进程则取消监听上述端口,只处理正常业务端口请求,来确保 APISIX 内部请求和正常业务请求间不会互相影响。</p><p><img src="/img/remote/1460000043244592" alt="output (2).png" title="output (2).png"></p><h3>默认路由匹配异常</h3><p><img src="/img/remote/1460000043244593" alt="output (3).png" title="output (3).png"></p><p>在上线 APISIX 后,我们发现域名并没有走精确匹配模式,而是采用了通配符匹配,这跟 NGINX 的域名最长匹配是不一致的。为此,我们通过更换路由策略,将 URL 方式改成了 host+URL 的方式,解决了该问题。</p><p>但关于 APISIX 基于 URL 路由策略作为默认路由的问题,大家可以在自己的生产环境中进行压测后再决定是否保留。</p><p>假如你的生产场景中属于 URL 特别多、域名特别少的类型,那 APISIX 这种默认路由策略是完全 OK 的。但在我们的业务场景下,并没有这么多 URL,所以采用 host+URL 的方式是更满足我们的性能需求。</p><h3>默认自动绑核问题</h3><p>在云原生的背景下,大部分用户都会选择将 APISIX 部署在容器中使用。但 APISIX 在默认配置下会进行自动绑核,这样就会导致在容器化场景下,可能只会用到前几个核心,造成前几个核心跑满而后几个核心仍处于空闲的状态。虽然 CPU 使用率不高,但是会使 APISIX 转发出现延迟。</p><p><img src="/img/remote/1460000043244594" alt="output (5).png" title="output (5).png"></p><p>不过 APISIX 社区最近已经开始调整这个配置,将 <code>worker_cpu_affinity</code> 配置的默认值从 <code>true</code> 改为了 <code>false</code>。因此这个问题目前在 APISIX 版本中已经解决了。</p><h3>版本升级兼容问题</h3><p>在上线 APISIX 的过程中,我们还发现在较老的系统或 OpenSSL 库中,它的 ssl_ciphers 和服务端默认值无交集,从而造成 SSL 握手失败。</p><p>针对这个问题,我们建议大家在上线 APISIX 之前,先通过一些 SSL 工具先去探测一下当前旧网关与 APISIX 网关的 SSL 握手交集是否正确或满足使用场景,然后再进行规模化的迁移调整。</p><p>除此之外,在 APISIX 发布 2.15 LTS 版本后,我们就在内网进行了升级,但是升级后就发现了一些路由匹配相关的问题。</p><p>因为从旧版本升级到新版本时,存在一些兼容性问题,导致 redirect 插件参数 <code>http_to_https</code> 为<code>true</code> 时,参数 <code>http_to_https</code> 和 <code>append_query_string</code> 校验失败,进而路由加载失败,导致路由丢失。这种情况下就会在路由匹配时出现 404 或者转发到其他上游的情况。</p><p>目前这个问题已经在 APISIX 的 master 分支中解决了,但是并没有针对 2.15 版本进行单独解决,所以大家在使用该版本时也需要留意这部分问题。</p><h2>应用 APISIX 的收益及展望</h2><p>虽然前边提到了一些我们在上线 APISIX 过程中遇到的问题,但是在应用 APISIX 之后,给公司业务层面还是带来了很多价值的。比如:</p><ul><li><strong>运维效率提升。</strong> 使用 APISIX 后,再也不存在 reload 相关的烦恼,可以做到随时更新路由和证书等,给开发人员带来了操作上的便利。</li><li><strong>流量控制能力提升。</strong> 使用 APISIX 后,我们在熔断和限流方面都得到了提升,从而加强了在流量管控层面的能力,进一步稳固了整个业务核心流程。</li><li><p><strong>自研插件,增强网关能力。</strong> 得益于 APISIX 的强拓展性和自身插件性能的优异,我们也会更主动地去开发一些插件。比如我们在 APISIX 上集成了统一鉴权的能力,新业务无需单独对接鉴权系统,加快了产品迭代流程。</p><p><img src="/img/remote/1460000043244595" alt="output (6).png" title="output (6).png"></p></li><li><strong>去掉了冗余的一层 NGINX,实现降本增效。</strong> 之前的双层网关架构中,一层的 NGINX 对于开发人员并不透明。现在将双层网关合并为一层后,开发人员可以很清晰地看到架构中一些关于路由或插件等配置,对于排查问题来说更加方便快捷。<br>在后续使用 APISIX 的规划中,我们还是会继续在网关层面进行增强。比如开发针对内部业务的自研插件,提供多租户的能力,或者是将 API 管理的功能带到网关层面等等。</li></ul><p>当然在这个过程中,我们也在积极回馈社区。目前已在社区中贡献了 8 个 PR,帮忙完善和修复了一些生态插件相关的问题。比如完善 <code>batch_request</code> 支持自定义 uri、为 <code>hmac-auth</code> 插件提供请求 body 校验等功能。</p><p>希望在后续的实践过程中,我们可以更全面地使用和发挥 APISIX 的特性,更加积极地探索 APISIX 的使用场景。期待未来有更多的共建功能上线。</p>
Apache APISIX 3.1.0 版本正式发布
https://segmentfault.com/a/1190000043201980
2022-12-30T10:53:25+08:00
2022-12-30T10:53:25+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<p>时隔一个月,新版本又来了。这次的 APISIX 3.1.0 是 3.0 大版本以来的第一个新版本,在 3.x 的新时代里,我们一如既往地在每个版本中给大家奉上更多的新功能。</p><p>此次发布的 3.1.0 版本,添加了对插件配置的加密存储和存储在外部安全服务的支持,着重于让用户能够更安全、更放心地使用他们的配置。在这之外,我们还引入了许多新的特性,旨在优化对 APISIX 的使用体验。</p><h2>新特性:插件配置的加密存储</h2><p>新版本支持将插件的特定字段加密保存到 etcd 中。</p><p>在之前的版本中,APISIX 提供了一个 <code>key_encrypt_salt</code> 的配置项,支持对 etcd 里面存储的 SSL key 进行加密,避免明文存储私钥数据。毕竟像私钥这样的敏感数据,少一个地方存储明文,就能多一份安心。那么对于其他同样敏感的配置,比如 <code>jwt-auth</code> 插件中的 secret,我们能不能也加密起来,避免在 etcd 里面存储明文呢?</p><p>3.1 版本中就把加密存储的功能拓展到其他字段上。有了这个功能,我们可以在某个特定的插件上指定需要加密的字段,然后在 <code>config.yaml</code> 文件中开启加密,即可避免明文存储。</p><p>举个例子,我们给 <code>jwt-auth</code> 插件新增了如下的标记:</p><pre><code class="lua"> encrypt_fields = {"secret", "private_key"},</code></pre><p>当我们在 <code>config.yaml</code> 里开启了字段的加密功能:</p><pre><code class="yaml">apisix:
data_encryption:
enable: true
keyring:
- edd1c9f0985e76a2</code></pre><p>那么写入到 etcd 的 <code>jwt-auth</code> 插件的配置中的 secret 和 private_key,就会被加密存储。通过 <code>etcdctl get --prefix /</code> 看到的配置,会是诸如 “"secret":"77+NmbYqNfN+oL..."” 这样的数据,而不是原始的配置信息。</p><h2>新特性:将敏感信息存储在外部安全服务</h2><p>除了可以将敏感信息加密存储在 etcd 之外,还可以选择从别的系统中动态获取敏感信息,而不再要求敏感信息必须存储在 APISIX 的配置存储(如 etcd)中。</p><p>在 3.1 版本中,我们上线了名为 APISIX Secret 的功能。APISIX Secret 允许用户在 APISIX 中通过一些密钥管理服务(Vault 等)来存储 secret,在使用的时候根据 key 进行读取,确保 secret 在整个平台中不以明文的形式存在。</p><p>APISIX 目前支持通过以下方式存储 secret:</p><ul><li>环境变量</li><li>HashiCorp Vault</li></ul><h3>相关示例</h3><p>以 <code>key-auth</code> 插件为例,我们来简单示范下如何使用该特性。</p><h4>基于环境变量的敏感信息存储</h4><p>第一步:APISIX 实例启动前创建环境变量</p><pre><code class="shell">export JACK_AUTH_KEY=abc</code></pre><p>第二步:在 <code>key-auth</code> 插件中引用环境变量</p><pre><code class="shell">curl http://127.0.0.1:9180/apisix/admin/consumers \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"username": "jack",
"plugins": {
"key-auth": {
"key": "$ENV://JACK_AUTH_KEY"
}
}
}'</code></pre><p>通过以上步骤,可以将 <code>key-auth</code> 插件中的 key 配置保存在环境变量中,而不是在配置插件时明文显示。</p><h4>基于 Vault 的敏感信息存储</h4><p>第一步:在 Vault 中创建对应的配置,可以使用如下命令:</p><pre><code class="shell">vault kv put apisix/jack auth-key=value</code></pre><p>第二步:通过 Admin API 添加 Secret 资源,配置 Vault 的地址等连接信息:</p><pre><code class="shell">curl http://127.0.0.1:9180/apisix/admin/secrets/vault/1 \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"uri": "https://127.0.0.1:8200",
"prefix": "apisix",
"token": "root"
}'</code></pre><p>第三步:在 <code>key-auth</code> 插件中引用 APISIX Secret 资源,填充配置在 Vault 中的位置:</p><pre><code class="shell">curl http://127.0.0.1:9180/apisix/admin/consumers \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"username": "jack",
"plugins": {
"key-auth": {
"key": "$secret://vault/1/jack/auth-key"
}
}
}'</code></pre><p>通过以上步骤,可以将 <code>key-auth</code> 插件中的 key 配置保存在 Vault 中,而不是在配置插件时明文显示。</p><h2>新特性:实验性基于 gRPC 的 etcd 配置同步</h2><p>在本次新版本中,我们还引入了实验性的基于 gRPC 的 etcd 配置同步。当前 APISIX 同步 etcd 的配置,是基于 HTTP long pulling,这就要求 etcd 开启 gRPC-gateway (所幸的是默认就是开启的)。</p><p>在实践过程中,我们遇到了 etcd 的 HTTP API 出现问题,也许是因为通过 HTTP 同步配置并非 etcd 的主流使用方式,所以会更容易遇到 bug。通过把 etcd 配置同步由 HTTP long pulling 切换到 gRPC 上面来,APISIX 实现了同步方式与主流接轨。</p><p>另外由于 gRPC 本身提供了多路复用的支持,改用 gRPC 同步配置能大幅降低 APISIX 到 etcd 的连接数。当前 APISIX 同步每一类配置都要有独立的 HTTP 连接,切换到 gRPC 后每个进程只有一条用于配置同步的连接(如果开启了 L4 代理,那么是两条)。</p><p>启用实验性的基于 gRPC 的配置同步,需要在配置文件 <code>config.yaml</code> 中设置 <code>use_grpc: true</code>,如下所示:</p><pre><code class="yaml"> etcd:
use_grpc: true
timeout: 3600
host:
- "http://127.0.0.1:2379"
prefix: "/apisix"</code></pre><h2>新特性:基于 Consul 的服务发现</h2><p>在 APISIX 之前的版本里,有热心的贡献者提供了基于 Consul KV 的服务发现实现。不过 Consul KV 跟 Consul 自身的服务发现功能有些不同,Consul 自身的服务发现支持额外的一些功能,比如对注册服务的健康检查,所以在使用上会更为广泛些。本次 3.1 版本中,另一位热心贡献者提供了基于 Consul 的服务发现,填补了这一空缺。</p><p>基于 Consul 的服务发现和之前版本里基于 Consul KV 的服务发现有着相似的配置。首先,需要在 <code>config.yaml</code> 文件中启用该服务发现:</p><pre><code class="yaml">discovery:
consul:
servers:
- "http://127.0.0.1:8500"</code></pre><p>然后在具体的 upstream 中配置对应的 <code>service_name</code> 和 <code>discovery_type</code>:</p><pre><code class="shell">curl http://127.0.0.1:9180/apisix/admin/upstreams/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d '
{
"service_name": "service_a",
"discovery_type": "consul"
}'</code></pre><p>对应的 upstream 在使用过程中,就会根据 Consul 里面配置的值去得到真正的上游节点。</p><h2>新特性:内置的调试插件</h2><p>工欲善其事,必先利其器,调试是程序员日常工作的一部分。作为注重调试体验的网关,APISIX 在 3.1 版本中以插件的形式内置了一个 Lua 调试器插件,支持动态设置断点、添加回调等等。</p><p>默认的配置如下:</p><pre><code class="yaml">plugins:
...
- inspect
...
plugin_attr:
inspect:
delay: 3
hooks_file: "/usr/local/apisix/plugin_inspect_hooks.lua"</code></pre><p>APISIX 在启动后,会定期查看配置的 hooks_file (这里是 "/usr/local/apisix/plugin_inspect_hooks.lua"),如果文件中有内容,就会根据里面的内容设置断点和回调。比如下方内容会给 limit-req.lua 的 88 行上设置一个断点,并在该断点上注册了回调函数 <code>function(info) ... end</code>。</p><pre><code class="Lua">local dbg = require "apisix.inspect.dbg"
dbg.set_hook("limit-req.lua", 88, require("apisix.plugins.limit-req").access, function(info)
ngx.log(ngx.INFO, debug.traceback("foo traceback", 3))
return true
end)</code></pre><h2>新功能:优化以及更多小功能</h2><p>除了上面提到的几个大的功能外,此次发布也包含许多值得述说的改动,比如:</p><ul><li>优化 Prometheus 指标采集的资源占用</li><li>支持在 L4 的代理中,配置域名作为上游<br>如果你对新版本的完整更新细节感兴趣,请参考 3.1.0 发布的 changelog:<a href="https://link.segmentfault.com/?enc=bo8Fcla%2B7k%2FKQb1gUOWzdg%3D%3D.GKk6I6fWDAHEbQ89rUWEjNk8AHdEu129cQ521M0SLiFSxmR53lgdrQhBHqhi5Dh54WwqAsY1uCwRS1wHV6TZuES%2FHNpUhOgkubkoy0RFPMZb4iP11n8HgySnXbo7F%2Bh3" rel="nofollow">https://github.com/apache/apisix/blob/release/3.1/docs/zh/latest/CHANGELOG.md#310</a></li></ul>
APISIX Ingress 对 Gateway API 的支持和应用
https://segmentfault.com/a/1190000043190912
2022-12-29T18:43:08+08:00
2022-12-29T18:43:08+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<p>本文介绍了 Gateway API 这个将服务暴露到集群之外的全新规范,并且介绍了如何在 APISIX Ingress Controller 中使用它。</p><blockquote>作者@lingsamuel,API7.ai 云原生技术专家,Apache APISIX Committer。</blockquote><p>Gateway API 是除原生 Service 与 Ingress 之外,APISIX 社区发起的独立规范,帮助用户将 Kubernetes 中的服务暴露到集群之外,由 <a href="https://link.segmentfault.com/?enc=k74sHROzLK4KOt27TpRcAw%3D%3D.hN0RjFez8cycGxIzbzmMK9%2F8lxANEWwi5wFNn1uI2HUfXEA66Bm0o81ALhnD%2B1E5JPBBjQpAGysPQhuwlYAqjQ%3D%3D" rel="nofollow">sig-network</a> (特别兴趣小组)管理。</p><p>Gateway API 支持大部分常用网络协议(HTTP、TCP、UDP 等)和对 TLS 的支持。此外,Gateway API 中的 Gateway 资源能够通过 Kubernetes API 来管理代理和网关的生命周期。</p><p>随着 Gateway API 被<a href="https://link.segmentfault.com/?enc=%2BLsrmwVEgm5xoKLav%2FDkPw%3D%3D.WZ%2BM6cqfZhq9aacX8zNRelo9MuprC1xiSGMxsmKLGZ6CBsCWvr3uvtvqjJAxexIGpIUUEjoRvfOhcQqle4u31w%3D%3D" rel="nofollow">广泛实现</a>与应用,Gateway API 发布了 v0.5.0 版本。在该版本中,一些核心 API 首次进入 Beta 阶段,包括:GatewayClass、Gateway、HTTPRoute。</p><h3><strong>Gateway API 优势盘点</strong></h3><p>Gateway API 不仅是 Ingress 的功能父集,还具有如下改进:</p><ul><li><strong>面向角色</strong>:Gateway 由一组 API 资源组成,不同的 API 资源代表了使用与配置 Kubernetes 网络资源的不同角色。</li><li><strong>表现力强</strong>:Gateway API 的核心功能包含基于 Header 匹配、流量加权以及以及其他部分由 annotations 实现的非标准化功能。</li><li><strong>可扩展</strong>:Gateway API 允许不同资源在不同层级一同使用。这使得能够对 API 结构进行更精细化的控制。</li></ul><p>此外,Gateway API 还包含了可移植、网关共享与跨命名空间引用等特性。</p><p>如下图所示,面向角色的设计使不同团队之间可以共享集群内部的网络基础设施,和集群管理员设置的策略与约束。由此,基础设施提供方、集群管理员、应用开发者等各种身份的角色可以只专注自己的工作,无需对其他角色负责。</p><p>不同的角色配置不同层级的 Gateway API 资源,这些资源相互配合,共同作用:</p><h2><strong>如何在 APISIX Ingress 中使用 Gateway API</strong></h2><h3><strong>1 安装 Gateway API CRD</strong></h3><p>要使用 Gateway API,需要先安装 Gateway API 的 CRD,有两种安装方式:APISIX Ingress Controller 仓库下的副本或官方仓库 <a href="https://link.segmentfault.com/?enc=O29vNQqQTUPHpqSrkYRPPg%3D%3D.9xKVrf1sdoBbqAOANEH3NzDrUo03ZJFlphDhR9IKcTNKj6%2BoeF%2BSN6mDw9lfWYtYAk6jZvdnBhxsHrB6lAMIs4G%2BdqRvhl%2FTN2CQiwwLGagt%2B7lJvvQP33Ni4f56CmKC" rel="nofollow">kubernetes-sigs/gateway-api</a>。</p><p>此处以 APISIX Ingress Controller 仓库的 CRD 副本为例,安装命令如下:</p><pre><code class="shell">git pull git@github.com:apache/apisix-ingress-controller.git
cd apisix-ingress-controller
kubectl apply -f ./samples/deploy/gateway-api/</code></pre><h3><strong>2 安装 APISIX Ingress Controller</strong></h3><p>在 APISIX Ingress Controller 中,默认不启用 Gateway API 支持,可通过参数 <code>--enable-gateway-api=true</code> 启用。随后在使用 Helm 安装时,可通过配置 values 来启用。</p><p>APISIX 与 APISIX Ingress Controller 安装命令如下:</p><pre><code class="shell">helm repo add apisix https://charts.apiseven.com
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
kubectl create ns apisix-ingress
helm install apisix apisix/apisix --namespace apisix-ingress \
--set gateway.type=NodePort \
--set ingress-controller.enabled=true \
--set ingress-controller.config.apisix.serviceNamespace=apisix-ingress \
--set ingress-controller.config.kubernetes.enableGatewayAPI=true</code></pre><p>参数 <code>--set ingress-controller.config.kubernetes.enableGatewayAPI=true</code> 用于开启 Gateway API 支持。</p><p>这些命令将在 <code>apisix-ingress</code> 命名空间下创建完整的测试环境,包括 APISIX、etcd 与 APISIX Ingress Controller。</p><h3><strong>3 部署测试负载</strong></h3><p>使用 <code>kennethreitz/httpbin</code> 镜像作为测试负载。</p><p>在默认命名空间下部署这些负载,安装命令如下:</p><pre><code class="shell">kubectl run httpbin --image kennethreitz/httpbin --port 80
kubectl expose pod httpbin --port 80</code></pre><h3><strong>4 配置 HTTPRoute</strong></h3><p>目前,APISIX Ingress Controller 支持 <code>v1alpha2</code> 版本的 Gateway API 资源。</p><p>在测试时,使用如下 HTTPRoute 配置,将其保存到 <code>httproute.yaml</code> 文件中:</p><pre><code class="yaml"># httproute.yaml
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: HTTPRoute
metadata:
name: basic-http-route
spec:
hostnames:
- local.httpbin.org
rules:
- backendRefs:
- name: httpbin
port: 80
matches:
- path:
type: PathPrefix
value: /</code></pre><p>部署该 HTTPRoute 配置:</p><pre><code class="shell">kubectl apply -f ./httproute.yaml</code></pre><h3><strong>5 验证</strong></h3><p>在 APISIX 的 Pod 中直接验证,执行以下命令:</p><pre><code class="shell">kubectl -n apisix-ingress exec -it \
$(kubectl -n apisix-ingress get Pods -l "app.kubernetes.io/name=apisix" -o name) -c apisix -- \
curl -H "Host: local.httpbin.org" localhost:9080/ip</code></pre><p>预期输出为:</p><pre><code class="shell">{
"origin": "127.0.0.1"
}</code></pre><p>由此,配置成功生效。</p><h2><strong>APISIX Ingress 对 Gateway API 的支持状态</strong></h2><p>APISIX Ingress Controller 正在对 Gateway API 添加支持,现已处于 Alpha 阶段,支持 HTTPRoute、TCPRoute 等资源。</p><p>目前 Gateway API 已更新至 v0.5.1 版本,但对于 Gateway 与 Gateway Class 的支持还处于开发阶段,因此目前这些资源的配置暂时不会生效。</p><h2><strong>总结</strong></h2><p>在本文中,我们介绍了 Gateway API 这个将服务暴露到集群之外的全新规范,并且介绍了如何在 APISIX Ingress Controller 中使用它。APISIX Ingress Controller 对 Gateway API 的完整支持仍在积极开发中,欢迎感兴趣的朋友参与贡献。</p>
认证鉴权对于 API 网关的重要性
https://segmentfault.com/a/1190000043143527
2022-12-22T18:28:44+08:00
2022-12-22T18:28:44+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<p>认证鉴权作为 API 网关不可或缺的能力,已然成为用户在选型 API 网关时考量的重要因素之一。</p><blockquote>作者钱勇,API7.ai 开发工程师,Apache APISIX Committer</blockquote><p>在当下云原生越发成熟的环境下,API 网关最核心的功能可以概括为:连接 API 消费者和 API 提供者。</p><p>实际场景中,除去少部分允许匿名访问的 API 外,提供者往往都会对消费者有所限制,比如只有符合条件的消费者才可以对 API 进行访问。其次,提供者对于不同的消费者的访问策略可能并不相同,例如 A、B 消费者都可以访问 <code>/send_mail</code> API,但每分钟的调用频次需要区分计算。</p><p>从以上两点可以看出在 API 网关层面鉴别和验证 API 消费者的身份至关重要。本文将介绍云原生开源 API 网关 Apache APISIX 如何实现对于消费者的认证,以及目前被企业广泛采用的认证方式。进一步介绍 APISIX 的用户认证体系是如何与其他安全特性联动使用,从而进一步提升 API 网关的安全防护能力。</p><p><img src="/img/remote/1460000043143529" alt="2020785233.png" title="2020785233.png"></p><h2><strong>Apache APISIX 的认证鉴权</strong></h2><p>Apache APISIX 是一个动态、实时、高性能的 API 网关,提供负载均衡、动态上游、灰度发布、精细化路由、限流限速、服务降级、服务熔断、身份认证、可观测性等数百项功能。在认证鉴权方面,APISIX 也是提供了非常多且方便的途径。</p><p>传统的 HTTP 代理往往只能基于请求域名、客户端 IP 等粗粒度手段对请求方进行识别,这对于一款 API 网关来说是远远不够的,我们需要有更丰富的认证方式来解决越来越复杂的业务需求。而 APISIX 区分于传统代理的一大优势就是灵活的插件扩展能力,这其中就包括一套用于用户认证的插件集合,这些插件根据实现方式的不同可以分为两大类。</p><p>第一种是对接外部认证服务,委托其进行认证。</p><p><img src="/img/remote/1460000043143530" alt="721355468.jpg" title="721355468.jpg"></p><p>第二种则是在网关内部认证,配合 APISIX 设计的 Consumer 对象进行认证。</p><p><img src="/img/remote/1460000043143531" alt="2821843005.jpg" title="2821843005.jpg"></p><p>下面将会依次介绍这两种认证方式。</p><h3><strong>对接外部认证服务</strong></h3><p>在企业采用 API 网关之前,系统中往往已经部署了独立的认证服务,此时我们要如何将 APISIX 与已有的认证服务进行对接呢?APISIX 提供了这样一系列插件,它们的工作原理就是通过对接各种外部的认证服务,委托它们完成认证。</p><p>例如,我们可以使用 <code>openid-connect</code> 插件对接任意支持 OIDC 协议的认证服务,下面是一段对接到 Keycloak 服务的样例配置:</p><pre><code class="bash">curl http://127.0.0.1:9180/apisix/admin/routes -H "X-Api-Key: your-API-key" -XPOST -d '
{
"uri":"/*",
"plugins":{
"openid-connect":{
"client_id":"apisix", // keycloak 创建 client 时生成
"client_secret":"d5c42c50-3e71-4bbe-aa9e-31083ab29da4",
"discovery":"http://keycloak:8080/auth/realms/apisix_test_realm/.well-known/openid-configuration", // keycloak OpenID Endpoint
"scope":"openid profile",
"bearer_only":false,
"realm":"apisix_test_realm",
"introspection_endpoint_auth_method":"client_secret_post",
"redirect_uri":"http://127.0.0.1:9080/"
}
},
"upstream":{
...
}
}'</code></pre><h3><strong>网关内部认证</strong></h3><h4><strong>Consumer</strong></h4><p><img src="/img/remote/1460000043143532" alt="1391469438.jpg" title="1391469438.jpg"></p><p>当来自多渠道的请求到达 API 网关后,网关需要识别出这些调用方。为此,Apache APISIX 提出了 Consumer 概念,用来代表某类服务的调用方。</p><p>Consumer 对象需要配置认证插件进行使用,以最简单的 <code>key-auth</code> 插件为例:</p><pre><code class="bash">curl http://127.0.0.1:9180/apisix/admin/consumers -H 'X-API-KEY: your-API-key' -X PUT -i -d '
{
"username": "jack",
"plugins": {
"key-auth": {
"key": "auth-jack"
}
}'</code></pre><p>以上配置表示当请求中携带指定的 key(auth-jack)时,当前请求将会与 jack 这个消费者进行关联。可以看到,Consumer 上配置的认证插件实际上就是一个指定认证机制下的身份凭证,在 <code>key-auth</code> 插件中,key 即是标识某个消费者的凭证,类似的还有 <code>basic-auth</code> 插件的用户名与密码等等。</p><h4><strong>为路由配置认证插件</strong></h4><p>当我们通过 Consumer 将凭证信息与具体的消费者进行关联后,还需要在相应的路由上开启认证插件:</p><pre><code class="bash">curl http://127.0.0.1:9180/apisix/admin/routes/orders -H 'X-API-KEY: your-API-key' -X PUT -i -d '
{
"uri": "/orders",
"plugins": {
"key-auth": {
"header": "Authorization"
}
}
}'</code></pre><p>以上配置表示在 <code>/orders</code> 这个路由上开启 <code>key-auth</code> 插件,验证效果如下:</p><pre><code class="bash">curl http://127.0.0.1:9080/orders -H 'Authorization: auth-jack' -i
HTTP/1.1 200 OK
...
curl http://127.0.0.1:9080/orders -H 'Authorization: wrong-key' -i
HTTP/1.1 401 Unauthorized
...
{"message":"Invalid API key in request"}</code></pre><p>当来自用户的请求命中这条路由时,APISIX 会尝试通过 <code>Authorization</code> 头部拿到用户提供的 Key。如果无法获取或者获取到的 Key 是不合法的,那么该请求将会被网关直接拒绝,从而保护上游服务。</p><p>可以看到以上两种认证方式中,认证插件都处于整个体系中的核心地位,它的丰富度将直接影响着 API 网关用户对于认证方式的选择空间。</p><h2><strong>APISIX 支持的认证方式</strong></h2><p>认证鉴权作为计算机世界从第一天起就存在的基础机制,经过这么多年的迭代,已经发展成为一个非常多样化的领域。而 APISIX 的插件机制极大地降低了实现各种认证方式的开发成本,以下是部分 APISIX 已经支持的主流认证方式。</p><h3><strong>Key Auth</strong></h3><p>Key Auth 是目前 APISIX 所支持的认证方式中,使用起来最简单的。但 <code>key-auth</code> 插件在实际环境中有着非常丰富的应用场景,例如:收费软件中的 License、开放 API 平台中的用于标识开发者的 token 等等,都可以非常轻松地使用 <code>key-auth</code> 来实现。并且基于 APISIX 全动态的配置下发能力,Key 可以被迅速创建、吊销,而且实时生效。</p><h3><strong>Basic Auth</strong></h3><p>Basic Auth 是基于用户名、密码进行认证的方式,常用于网页登录场景,例如:网站的管理后台需要管理员登录后才可以使用,那么我们可以使用 Basic Auth 方式进行认证。</p><h3><strong>LDAP</strong></h3><p>LDAP(Lightweight Directory Access Protocol)是一种基于X.500 标准的轻量级文件访问协议,通过IP 协议提供访问控制和维护分布式信息的目录信息,借助于 LDAP ,运维人员可以细粒度地控制用户对资源的访问权限。通过 APISIX 的 <code>ldap-auth</code> 插件,可以轻松对接实现了 LDAP 协议的平台,例如微软的 Active Direcory,或者 Linux 平台的 OpenLDAP Server,从而能够精细化地控制 Consumer 对具体路由的访问权限。</p><h3><strong>OIDC</strong></h3><p>OpenID 是一个去中心化的网上身份认证系统。对于支持 OpenID 的网站,用户不需要记住像用户名和密码这样的传统验证标记。取而代之的是,他们只需要预先在一个作为 OpenID 身份提供者(identity provider, IdP)的网站上注册账号,而后就可以用这个账号登录所有对接了该提供者的应用,例如:可以通过知名的 Google 或者 Facebook 服务的账号来认证我们自身系统的用户。</p><p>针对 OIDC,APISIX 提供了 <code>openid-connect</code> 插件,可以用于对接支持了 OIDC 协议的认证服务。</p><h3><strong>Forward Auth</strong></h3><p>当 APISIX 的标准认证插件无法满足你当前需求时,或者当前系统中已经部署了专门的并且是非标准协议的认证服务,此时你可以考虑使用 <code>forward-auth</code> 插件。使用该插件可以将用户的请求通过 HTTP 形式转发至认证服务中,并在认证服务响应非正常状态(错误码非 20x)时,返回自定义报错或者将用户重定向至认证页面。</p><p>借助 <code>forward-auth</code> 插件的能力,可以非常巧妙地将认证与授权逻辑转移到专门的、非标准协议的外部服务中。</p><h2><strong>“认证+任意功能”,APISIX 助力 API 安全</strong></h2><p>用户认证只是 APISIX 保障 API 安全的第一步,将认证能力与其他安全类型插件的有机结合将会进一步放大网关的安全能力。</p><h3><strong>ACL 访问控制</strong></h3><p>在一个复杂的后端系统中,可能会存在部分 API 的安全限制是高于其他 API 的,这种限制不仅需要拦截匿名用户,而且需要对认证用户进行限制,例如:只允许白名单用户访问用户管理 API。</p><p><img src="/img/remote/1460000043143533" alt="3322406862.jpg" title="3322406862.jpg"></p><p>此时我们可以使用 APISIX 提供的 <code>consumer-restriction</code> 插件去实现一个访问控制机制。</p><pre><code class="plain">curl http://127.0.0.1:9180/apisix/admin/routes -H 'X-API-KEY: your-API-key' -X POST -i -d '
{
"uri": "/api/v1/users/admin",
"plugins": {
"key-auth": {},
"consumer-restriction": {
"whitelist": [
"Rose",
"Peter
]
}
},
"upstream": {
...
},
}'</code></pre><p>上述配置中,通过 <code>key-auth</code> 和 <code>consumer-restriction</code> 两个插件限制了:<code>/api/v1/users/admin</code> 路由需要通过 key auth 认证,并且仅 Rose 和 Peter 可以访问。</p><h3><strong>限流限速</strong></h3><p>前面我们介绍了可以通过在 Consumer 中配置认证插件将用户凭证与消费者进行关联,事实 APISIX Consumer 对象不仅仅可以挂载认证类型的插件,而是可以像路由和 Service 一样,挂载任意插件。</p><p>以限流场景举例,在实际应用中,限流策略往往不是一成不变而是"千人千面",不同服务等级的调用方拥有不同的 API 限流策略是非常常见的需求,这样的需求是无法通过在路由上挂载限流插件进行解决的。为此,我们可以在消费者上挂载限流插件,并且为每一个消费者指定不同的限流策略。</p><pre><code class="bash">curl http://127.0.0.1:9180/apisix/admin/consumers -H 'X-API-KEY: your-API-key' -X PUT -i -d '
{
"username": "jack",
"plugins": {
"key-auth": {
"key": "jack"
},
"limit-count": {
"count": 200,
"time_window": 60,
"rejected_code": 503,
"key": "$consumer_name",
}
}'
curl http://127.0.0.1:9180/apisix/admin/consumers -H 'X-API-KEY: your-API-key' -X PUT -i -d '
{
"username": "rose",
"plugins": {
"key-auth": {
"key": "rose"
},
"limit-count": {
"count": 1000,
"time_window": 60,
"rejected_code": 503,
"key": "$consumer_name",
}
}'</code></pre><p>通过上方的配置,我们为 jack 和 rose 分别指定了不同的限流策略。Rose 在 60 秒内拥有更多的请求次数配额 1000,而 Jack 只有 200 配额。</p><h2><strong>总结</strong></h2><p>认证鉴权作为 API 网关不可或缺的能力,已然成为用户在选型 API 网关时考量的重要因素之一。</p><p>本文中介绍的开源网关 Apache APISIX,覆盖了所有主流的认证方式,可以满足企业用户对于认证鉴权的需求。同时 APISIX 还拥有其他以下优势:</p><ul><li>丰富的、开箱即用的认证插件;</li><li>同时支持内置、外置认证方式,用户可以自由选择;</li><li>支持二次开发,方便对接自定义鉴权中心。</li></ul><p>这些优势都可以帮助企业更轻松的落地网关层面的认证鉴权,加强 API 安全。</p>
为什么 APISIX Ingress 是比 Traefik 更好的选择?
https://segmentfault.com/a/1190000043143459
2022-12-22T18:16:57+08:00
2022-12-22T18:16:57+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<p>本文可以为正在选型 Kubernetes Ingress Controller 产品的用户提供一些帮助。</p><blockquote>作者张晋涛,API7.ai 云原生专家,Apache APISIX Committer、Kubernetes Ingress Nginx Reviewer</blockquote><h2>Apache APISIX Ingress</h2><p><a href="https://link.segmentfault.com/?enc=KCCh7ul4JXBbeQTCHdrRag%3D%3D.eRgqTb11htafejH6SFBK%2F8o5CBy2RtkRmOnnsM2WOjvHBY7T89C5dfThc9cMftFnqjpquSrgrbFMDbU0ic7uvQ%3D%3D" rel="nofollow">Apache APISIX Ingress</a> 是一个使用 Apache APISIX 作为数据面的 Kubernetes Ingress controller 实现。</p><p>目前,它支持多种规则的配置方式,包括 Ingress、APISIX Ingress CRD (自定义资源)以及 Gateway API。</p><p>其整体采用数据面与控制面分离的架构,由 Apache APISIX 承载实际的业务流量。因此大大提升了整体的安全性,极大避免了由于数据面被攻击而导致 Kubernetes 集群被攻击的可能。</p><p><img src="/img/remote/1460000043143461" alt="image (4).png" title="image (4).png"></p><h2>Traefik</h2><p>Traefik 是由 Traefik Labs 开源的一款反向代理和负载均衡器。它在 Kubernetes 中支持多种规则的配置方式,包括 Ingress、Traefik IngressRoute(自定义资源)和 Gateway API。</p><p>Traefik 是一个统一的二进制文件,控制面和数据面的代理逻辑均绑定在一起。因此,如果受到攻击或者有远程执行的安全漏洞被利用,极有可能存在 Kubernetes 集群被攻击的情况。</p><p><img src="/img/remote/1460000043143462" alt="image (5).png" title="image (5).png"></p><h2>APISIX Ingress vs Traefik</h2><p>接下来我将从以下几个维度对 Apache APISIX Ingress 和 Traefik 进行一些对比,方便大家在选型时对产品有更多的认知。</p><h3>协议支持</h3><p>作为网关,最为核心的能力便是要能够正确的代理流量。作为 Kubernetes 集群的入口网关,主要处理如下两部分的流量:即 <strong>Client 到网关的流量</strong>和<strong>网关与 Upstream 的流量</strong>。如下所示:</p><pre><code class="bash">Client <----> Ingress <----> Upstream Service</code></pre><p>当前的协议多种多样,以下简单汇总了两个项目对协议的支持,仅供参考。</p><table><thead><tr><th align="center">协议</th><th align="center">APISIX Ingress</th><th align="center">Traefik</th></tr></thead><tbody><tr><td align="center">HTTP/HTTPS</td><td align="center">支持</td><td align="center">支持</td></tr><tr><td align="center">HTTP/2</td><td align="center">支持</td><td align="center">支持</td></tr><tr><td align="center">HTTP/3</td><td align="center">不支持</td><td align="center">支持</td></tr><tr><td align="center">TCP</td><td align="center">支持</td><td align="center">支持</td></tr><tr><td align="center">UDP</td><td align="center">支持</td><td align="center">支持</td></tr><tr><td align="center">WebSocket</td><td align="center">支持</td><td align="center">支持</td></tr><tr><td align="center">Dubbo</td><td align="center">支持</td><td align="center">不支持</td></tr></tbody></table><p>此外,无论是 APISIX Ingress 还是 Traefik,均可通过 HTTP/2 或者 TCP 代理等方式支持 gRPC、MQTT 等协议,故而未在上述表格中列出。</p><p>从协议支持的角度来看,APISIX Ingress 和 Traefik 各有优势。此外,APISIX 对于 HTTP/3 的支持正在规划中,后续也可随时关注社区动态。</p><h3>可扩展性</h3><p>由于业务需求多种多样,所以可扩展性也是进行技术选型的一个主要指标。APISIX Ingress 和 Traefik 均提供了一些扩展方式,我们将分别进行介绍。</p><h4>APISIX Ingress</h4><p>在 APISIX Ingress 中进行功能扩展,主要是通过开发自定义插件来完成。当前,APISIX Ingress 主要支持如下几种插件的开发方式:</p><ul><li>通过 Lua 进行插件的开发:这种方式相对简单,并且几乎没有性能损耗;</li><li>通过 Plugin Runner 开发:这种模式下支持 JAVA/Python/Go 等多种计算语言进行开发,方便用户利用现有的业务逻辑,同时无需学习新语言;</li><li>通过 WASM 进行插件插件:这种模式下,可以使用任何支持构建出 WASM 的语言进行插件开发;</li></ul><p>此外,还可以通过 Serverless 插件来直接编排 Lua 代码,满足业务需求。</p><p>当然,如果你有 Lua 模块的开发经验,也可以直接编写 Lua 模块,然后进行加载即可,只需在配置文件中增加如下内容即可:</p><pre><code class="yaml">apisix:
...
extra_lua_path: "/path/to/example/?.lua"</code></pre><p>具体关于插件的的开发步骤和使用,请参考 <a href="https://link.segmentfault.com/?enc=KgPUugBQSi2z1%2BlgBHYCtA%3D%3D.Ox8ysI1%2FUpKfdLltOXpqpK0W2%2BG%2FRulmD9hQ7sGqqW7x%2BoyThISxmNrxsFhcg29j1RBzG%2F3Z5%2FaXz41V63ItGA%3D%3D" rel="nofollow">Apache APISIX 的插件开发文档</a>。</p><h4>Traefik</h4><p>Traefik 也提供了相关插件机制用于功能扩展。但是 Traefik 是由 Go 进行开发的,因此它的插件也需要用 Go 进行开发。</p><p>在开发完成后,就可以在 Traefik 的配置中添加如下内容进行引用了(需注意,插件的名字需要与包名保持一致)。例如:</p><pre><code class="yaml">experimental:
localPlugins:
example:
moduleName: github.com/traefik/pluginproviderdemo</code></pre><p>总体来看,APISIX Ingress 提供了更多种的扩展方式,可以根据实际情况进行灵活选择。可以根据自己喜欢或擅长的工具即可,更容易实现与现有业务集成。而 Traefik 目前则只支持通过 Go 语言进行开发,选择较少。</p><h3>生态</h3><p>在进行技术选型时候,除了考虑一些性能表现,还需要对产品的整个生态支持进行考察。比如项目所使用的协议、项目归属以及与现有基础设施是否可以整合等等。下方简单整理了几个角度进行呈现(包含了控制面和数据面)。</p><table><thead><tr><th align="center">对比维度</th><th align="center">APISIX Ingress</th><th align="center">Traefik</th></tr></thead><tbody><tr><td align="center">归属</td><td align="center">Apache 软件基金会(ASF)</td><td align="center">Traefik Labs</td></tr><tr><td align="center">协议</td><td align="center">Apache 2.0</td><td align="center">MIT</td></tr><tr><td align="center">诞生时间</td><td align="center">2019 年 6 月</td><td align="center">2015 年 8 月</td></tr><tr><td align="center">consul</td><td align="center">支持</td><td align="center">支持</td></tr><tr><td align="center">nacos</td><td align="center">支持</td><td align="center">不支持</td></tr><tr><td align="center">Eureka</td><td align="center">支持</td><td align="center">不支持</td></tr><tr><td align="center">etcd</td><td align="center">支持</td><td align="center">支持</td></tr><tr><td align="center">zookeeper</td><td align="center">支持</td><td align="center">支持</td></tr><tr><td align="center">DNS</td><td align="center">支持</td><td align="center">不支持</td></tr></tbody></table><p>此外,这两个项目都非常积极与一些周边项目进行了集成与合作。比如 Rancher、KubeSphere 等。</p><p>从生态合作角度来看,APISIX Ingress 比 Traefik 提供了更为广泛的集成能力,尤其是与基础组件。因此在进行技术选型时,可以结合当前自己所用的基础组件的情况进行权衡。</p><h2>来自用户的声音</h2><p>在今年,我们也看到了很多来自用户的声音,他们开始在业务架构中用上了 APISIX Ingress。比如<a href="https://link.segmentfault.com/?enc=7n8bMCeecVoLdez64CLkcg%3D%3D.LRNZaikGgfVP%2FnYuH7IzTqfUM7P%2FVvT7WlbYhgWwfeeEI917A9E1Oep6ohh2u57fSd8oYQp1hnSBvV7B%2FCiN2xlH7uQtU6VEd4APoUoyQL8%3D" rel="nofollow">地平线使用 APISIX Ingress 替换了 Traefik</a>,主要是考虑如下方面:</p><ul><li>通过 Annotation 增加的配置不易重用;</li><li>Traefik 中默认的行为与 NGINX 中不同,用户在使用时候会产生困惑;</li></ul><p>在切换为 Apache APISIX Ingress 后,得益于 APISIX Ingress 丰富的插件生态,绝大多数需求均可通过内置插件满足。并且插件的配置可直接通过 APISIX Ingress 的 ApisixRoute 资源进行定义,比较直观。也可以通过 ApisixPluginConfig 进行插件模板的配置,在其他的 ApisixRoute 资源中进行引用。</p><p><img src="/img/remote/1460000043143463" alt="image (8).png" title="image (8).png"></p><p>APISIX Ingress 的数据面性能更佳,能高效地应对日益增长的业务流量,而不会陷入性能瓶颈。</p><p>除地平线以外,包括<a href="https://link.segmentfault.com/?enc=6wQEu6jH4Ms%2FrDxY%2BqCfnQ%3D%3D.bqGbOsD9cRUAee2QxyWmVrBCEjtIRviYCOchML1qTMg%3D" rel="nofollow">少年得到</a>,<a href="https://link.segmentfault.com/?enc=ldEFKB5SOqBdcINApLCncQ%3D%3D.yQgnKuDeeJeMRmI%2F21njdvdVRSTVRf%2BoV2ClRL6DGco%3D" rel="nofollow">观为智慧</a>等公司也都使用 APISIX Ingress 替换了 Traefik,更多用户案例请参考<a href="https://link.segmentfault.com/?enc=%2BsLlIrn2nR7va42k7tbNOQ%3D%3D.HG3Kvar9mhTd7YmOF8eWATYoMdk3md2DlDWoUPo1xGm%2BOj3ki3W%2BG3QjeltvQdXr" rel="nofollow">用户案例</a>。</p><p>此外,Apache APISIX 社区非常活跃,在 GitHub 和 Slack 等频道上都会快速响应。也期待各位在社区积极进行反馈与讨论。</p><h2>总结</h2><p>本文从协议支持、可扩展性和生态等方面对比了 Apache APISIX Ingress 和 Traefik。从内容中也可以看到,APISIX Ingress 在可扩展性和生态集成方面有一定的优势,用户可以更容易地对 APISIX Ingress 进行扩展,以及与一些基础组件进行集成。</p><p>希望本文可以为正在选型 Kubernetes Ingress Controller 产品的用户提供一些帮助。</p>
马斯克都不懂的 GraphQL,API 网关又能对其如何理解?
https://segmentfault.com/a/1190000043098217
2022-12-16T14:31:55+08:00
2022-12-16T14:31:55+08:00
API7_技术团队
https://segmentfault.com/u/api7
1
<blockquote>作者,罗泽轩。API7.ai 技术专家、Apache APISIX PMC 成员</blockquote><p><a href="https://link.segmentfault.com/?enc=np3UYl%2BhSbiuAIiPJE39FA%3D%3D.MC85NW682QwMKOXvCMzxCbZrB2kRwbEoQ%2F%2Ba4Jk9ggnFexSenrE%2BW3%2FtbqbLrKb1" rel="nofollow">原文链接</a></p><p>上个月马斯克评论 Twitter App 滥用 RPC 后,与一些 Twitter 的技术主管发生了矛盾 —— 直言马斯克不懂技术。那这个马斯克都不懂的 GraphQL 到底是什么?</p><p><img src="/img/remote/1460000043098219" alt="image (3).png" title="image (3).png"></p><h2>什么是 GraphQL?它有多流行?</h2><p>GraphQL 是一套由 Facebook 在 2015 年发布的一套面向 API 的查询操作语言。相比于其他的 API 设计方式,GraphQL 允许客户端根据事先约定的数据结构组建查询语句,由服务端解析这一语句并只返回所需的内容。这么一来,GraphQL 在提供丰富性和灵活性的同时,避免了冗余数据带来的性能损耗。</p><p>GraphQL 的这一特性,让它在需要跟许多复杂数据对象打交道的应用场景里大行其道,成为该环境下的不二之选。</p><p>2018 年 GraphQL 完成了规范的制定工作,并推出了稳定版本。同年,Facebook 将 GraphQL 项目捐献给了 Linux 基金会下属的 GraphQL 基金会。自那以后,GraphQL 已经在许许多多的开源项目和商业机构中落地。到目前为止,市面上已经有了多个 GraphQL 的主流客户端实现。而服务端的实现遍布各大服务端编程语言,甚至连一些小众编程语言如 D 和 R 都有对应的实现。</p><h2>GraphQL 的一些真实场景和挑战</h2><p>最为知名的采用 GraphQL 的例子,莫过于 GitHub 的 GraphQL API 了。</p><p>在拥抱 GraphQL 之前,GitHub 提供了 REST API 来暴露千千万万托管项目所产生的丰富数据。GitHub 的 REST API 是如此的成功,以致于它成为了人们设计 REST API 时竞相模仿的典范。</p><p>然而随着数据对象的变多和对象内字段的变大,REST API 开始暴露出越来越多的弊端。在服务端,由于每次调用都会产生大量的数据,GitHub 为了降低成本不得不对调用频率设置严格的限制。</p><p>而在开发者这边,他们则不得不与这一限制做斗争。因为虽然单次调用会返回繁多的数据,但是绝大部分都是无用的。开发者要想获取某一特定的信息,往往需要发起多个查询,然后编写许多胶水代码把查询结果中有意义的数据拼接成所需的内容。在这一过程中,他们还不得不带上“调用次数”的镣铐。</p><p>所以 GraphQL 的出现,立刻就让 GitHub <a href="https://link.segmentfault.com/?enc=Y1%2FSVOvpfRF0NLAtJ1dukw%3D%3D.nhjc3posgsMSsCl49LKAaYbM2MaqoFSuDhXihvw6rwCxVJSRgN%2BMuoSsiOrDF8NUwlrJ03ejCGq2mT3JV91oSs2MZUIfZDwBhuYcS9diYL2jiNCLu4tpibe2Pbsus0vT" rel="nofollow">皈依</a>了。GitHub 成为了 GraphQL 的使者保罗,为万千开发者传递福音。目前 GraphQL API 已经是 GitHub API 的首选。从<a href="https://link.segmentfault.com/?enc=5TzMx2%2BGs1OCioduP2cy6Q%3D%3D.2xGHmQDVMY%2FAqxdrRgOYAkpFTSQJK7hJIeGJkyqUPqS1Ju1Eo8q8XYUFKcALKllxSgFJ0ZgxyxFNxUl8ydobxA%3D%3D" rel="nofollow">第一次宣布对 GraphQL 的支持</a>之后,GitHub 每一年都会<a href="https://link.segmentfault.com/?enc=spd3dp%2FAGfYnSUj7KVziAw%3D%3D.Yeuxhl1fV6UB9LLR2Fyig5IX4bXSDTVtmTRL2M35xfk%3D" rel="nofollow">发几篇关于 GraphQL 的文章</a>。为了让开发者能够迁移到 GraphQL 上来,GitHub 专门写了个交互式查询应用,<a href="https://link.segmentfault.com/?enc=%2F%2BT9ulYrdNMAvuMNdQqbFw%3D%3D.538O5%2FCIlhazmcE7FnHeVRRVDuuU15WnzBK5FwtQoilFFxpDSi6AXu5N8UCyeNKjTuu4%2FcZFx4qRoHAsUoeHbYC8xrHSkUN6jiddLzIEqkuXbqL9coovHRK8PS7FnMSfPeMPUlZgZzzwjdHmKtfEtZeXtftu1B2MJfTVZoK2O8oTijXXDWu%2F9vIM%2B8AHSq1HsI5WGGZFQvHE8yNmSbB%2BwJumOTQmvkuaWq9DgxDGraN4nHIgJoFlNfyj0E%2FONNvwWcH5l%2FLcsvLpXmH0bVC%2FMQu4XHFDU35VmOuNAdg60zE%3D" rel="nofollow">开发者可以通过这个应用学习怎么编写</a> GraphQL。</p><p>然而 GraphQL 并非灵丹妙药。就在最近,<a href="https://link.segmentfault.com/?enc=NRhN8JRBfS8C7IuhjEmc6A%3D%3D.wdzs6lnsj0bE1VQH2x%2F46g9QRpaJPlINuLJ21UXM0mpiSHjnGFfDtSeDX%2FANoE%2FNv3vUNMMo310t8wG9xDZ8JrZjriJvvQC4n%2FMmasSRv2WJvzdqbgvXqfkIYHdPZWNk" rel="nofollow">GitHub 废弃自己 package API 的 GraphQL 实现</a>。许多人也开始<a href="https://link.segmentfault.com/?enc=uvRoiDS2zQOrdeEOSKlsGQ%3D%3D.HOvUSudTd3r0nB%2Fpx%2B7Rdhbj6DB123V3eLR5kmFHlS1tcDn06fYNUicwlzS2UnZP15tnZakvsVHvh14dN780DyeGQsRqLiGFWcIyCh6BWMo%3D" rel="nofollow">热议 GraphQL 的一些缺点</a>。</p><p>GraphQL 的许多问题源自于它跟 HTTP 标准的结构差别较大,没办法简单地将 GraphQL 的一些概念映射到诸如 HTTP path/header 这样的结构中。把 GraphQL 当作普通的 HTTP API 来处理,需要额外的开发工作。如此一来,开发者如果要管理自己的 GraphQL API,就必须采用支持 GraphQL 的 API 网关才行。</p><h2>APISIX 现在对 GraphQL 的支持</h2><p>Apache APISIX 是一个动态、实时、高性能的 API 网关,提供负载均衡、动态上游、灰度发布、精细化路由、限流限速、服务降级、服务熔断、身份认证、可观测性等数百项功能。作为 Apache 的顶级项目,APISIX 一直致力于周边生态的扩展与跟进。</p><p>APISIX 目前支持通过 GraphQL 的一些属性进行动态路由。通过该能力,我们可以只接受特定的 GraphQL 请求,或者让不同的 GraphQL 转发到不同的上游。</p><p>以下面的 GraphQL 语句为例:</p><pre><code class="plain"> query getRepo {
owner {
name
}
repo {
created
}
}</code></pre><p>APISIX 会提取 GraphQL 以下三个属性,用在路由当中:</p><ul><li>graphql_operation</li><li>graphql_name</li><li>graphql_root_fields</li></ul><p>在上面的 GraphQL 语句中:</p><ul><li><code>graphql_operation</code> 对应 <code>query</code></li><li><code>graphql_name</code> 对应 <code>getRepo</code></li><li><code>graphql_root_fields</code> 对应 <code>["owner", "repo"]</code></li></ul><p>让我们来创建一个路由,展示下 APISIX 对 GraphQL 的精细化路由能力。</p><pre><code class="plain">curl http://127.0.0.1:9180/apisix/admin/routes/1 \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d '
{
"methods": ["POST"],
"uri": "/graphql",
"vars": [
["graphql_operation", "==", "query"],
["graphql_name", "==", "getRepo"],
["graphql_root_fields", "has", "owner"]
],
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:2022": 1
}
}
}'</code></pre><p>接下来使用带有 GraphQL 语句的请求去访问:</p><pre><code class="plain">curl -i -H 'content-type: application/graphql' \
-X POST http://127.0.0.1:9080/graphql -d '
query getRepo {
owner {
name
}
repo {
created
}
}'
HTTP/1.1 200 OK
...</code></pre><p>我们可以看到请求到达了上游,这是因为查询语句匹配了全部三个条件。 反之,如果我们使用不匹配的语句来访问,比如不包含 <code>owner</code> 字段:</p><pre><code class="plain">curl -i -H 'content-type: application/graphql' \
-X POST http://127.0.0.1:9080/graphql -d '
query getRepo {
repo {
created
}
}'
HTTP/1.1 404 Not Found
...</code></pre><p>则不会匹配对应的路由规则。</p><p>接下来,我们可以另外创建一个路由,让不包含 <code>owner</code> 字段的语句路由到别的上游:</p><pre><code class="plain">curl http://127.0.0.1:9180/apisix/admin/routes/2 \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d '
{
"methods": ["POST"],
"uri": "/graphql",
"vars": [
["graphql_operation", "==", "query"],
["graphql_name", "==", "getRepo"],
["graphql_root_fields", "!", "has", "owner"]
],
"upstream": {
"type": "roundrobin",
"nodes": {
"192.168.0.1:2022": 1
}
}
}'
curl -i -H 'content-type: application/graphql' \
-X POST http://127.0.0.1:9080/graphql -d '
query getRepo {
repo {
created
}
}'
HTTP/1.1 200 OK
..</code></pre><h2>展望 APISIX 未来对 GraphQL 的支持</h2><p>除了动态路由之外,APISIX 在未来也可能会根据 GraphQL 的具体字段推出更多的操作。比如说,GitHub 的 GraphQL API 有<a href="https://link.segmentfault.com/?enc=cgCZ5q5DvkIYiZjkPTaRpA%3D%3D.av6dWV3SeVfUdQU%2FsxfK0d%2FQu0IB9FOBHoeD%2FxLeu7bCiPjFhqnDEaQZ4pQKuTIfX%2BYSS0C61cYqcYDw%2FZ6dglB9NIrRc9eFzoCA8WTwCzM%3D" rel="nofollow">专门一套针对限流的计算公式</a>,我们也可以应用类似的规则来把单个 GraphQL 请求转成相应的“虚拟调用”次数,来完成 GraphQL 专属的限流工作。</p><p>当然,我们也可以换个思路解决问题。即应用自身还是提供 REST API,由网关在最外层把 GraphQL 请求转成 REST 请求,把 REST 响应转成 GraphQL 响应。这种方式提供的 GraphQL API 无需开发专门的插件,就可以完成诸如 RBAC、限流、缓存等功能。</p><p>从插件角度来看,它就是个平平无奇的 REST API。从技术角度上讲,这个思路并不难实现。毕竟在 2022 年的现在,REST API 也会提供 OpenAPI spec 来作为 schema,无非是 GraphQL schema 和 OpenAPI schema 之间的互转,外加 GraphQL 特有的字段筛选罢了(当然,我必须承认,我并没有亲自实践过,或者在一些细节上存在尚待克服的挑战)。</p><p>细心的读者会发现,这种方式转换得来的 GraphQL API,每次只能操作一个模型,显然无法满足 GraphQL 灵活性的要求,无非是披着 GraphQL 外衣的 REST API。<strong>且慢,我还没有把话说完呢!</strong>GraphQL 有一个叫 <a href="https://link.segmentfault.com/?enc=4wQ8XqBhU7oVLqVbMtH7ww%3D%3D.PXxvaMErv0nP8PjBznApysRJbP%2FjiRF9F9azzucsVlbig441Ter58n2y4ueMLr%2B5lUjcL6Tf9AnDwhE%2B2C5xgDGkuOkeNhg8Y6LqP3M%2FL%2Fs%3D" rel="nofollow">schema stitch</a> 的概念,允许实现者把多个 schema 组合在一起。</p><p>举个例子,现在有两个 API。一个叫 GetEvent,另一个叫 GetLocation。他们返回的类型分别是 Event 和 Location。</p><pre><code class="plain">type Event {
id: string
location_id: string
}
type Location {
id: string
city: string
}
type Query {
GetEvent(id: string): Event
GetLocation(id: string): Location
}</code></pre><p>我们可以加一个配置,由这两个 API 组合成新的 API 叫 <code>GetEventWithLocation</code>。新的 API 是这样的:</p><pre><code class="plain">type EventWithLocation {
id: string
location: Location
}
type Query {
GetEventWithLocation(id: string): EventWithLocation
}</code></pre><p>整体 schema stitch 的过程都由网关来完成。在上面的例子中,网关会把 API 拆分成两个,先调用 GetEvent 得到 <code>location_id</code>,再调用 GetLocation 得到组合后的数据。</p><p>总而言之,通过 REST 转 GraphQL,每个 REST API 可以变成对应的 GraphQL 模型;再借助 schema stitch,可以把多个模型组合成一个 GraphQL API。</p><p>这样一来,我们就能在现有的 REST API 上构建起丰富灵活的 GraphQL API,且在 REST API 的粒度上完成具体的插件管理。这一设计顺带解决了部分 API 编排的问题。就像上面的例子中,我们把一个 API 的输出(Event.location_id)作为另一个 API 的输入(Location.id)。</p><h2>关于 API7.ai 与 APISIX</h2><p>API7.ai(<a href="https://link.segmentfault.com/?enc=SI7KZBy7zGoeZtRIf7NOxw%3D%3D.TC6F6OriG1zP9X1ZfzzUHhyDwx66WfgqEj%2FUrmrG08g%3D" rel="nofollow">支流科技</a>)是一家提供 API 处理和分析的开源基础软件公司,于 2019 年开源了新一代云原生 API 网关 -- APISIX 并捐赠给 Apache 软件基金会。此后,API7.ai 一直积极投入支持 Apache APISIX 的开发、维护和社区运营。与千万贡献者、使用者、支持者一起做出世界级的开源项目,是 API7.ai 努力的目标。</p>
APISIX Ingress 如何支持自定义插件
https://segmentfault.com/a/1190000043089710
2022-12-15T16:05:54+08:00
2022-12-15T16:05:54+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<p>摘要:本篇主要介绍了 Ingress 资源相关的语义,以及如何对 Ingress 资源进行能力的扩展。</p><blockquote>作者:张晋涛,API7.ai 云原生技术专家,Apache APISIX PMC 成员,Apache APISIX Ingress Controller 项目维护者。</blockquote><h2>Ingress 和 Ingress controller</h2><p>Kubernetes 中的 Ingress 是一种资源对象,用于定义如何从 Kubernetes 集群外访问到 Kubernetes 集群内的服务,其中包含了具体的访问规则,通常情况下客户端使用 HTTP/HTTPS 协议进行访问。</p><p>客户端可按照 Ingress 资源定义的规则,将客户端请求路由到 Kubernetes 集群中的服务或具体的 Pod中。</p><p><img src="/img/remote/1460000043089712" alt="3df3234c-7ae7-48de-8dbb-0449226e4f2e.png" title="3df3234c-7ae7-48de-8dbb-0449226e4f2e.png"></p><p>以下是一个 Ingress 资源的示例:</p><pre><code class="yaml">apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: apisix-gateway
spec:
rules:
- host: apisix.apache.org
http:
paths:
- backend:
service:
name: apisix-gateway
port:
number: 80
path: /
pathType: Exact</code></pre><p>上述示例中包含了以下内容:</p><ul><li><strong>metadata.name</strong>:Ingress 资源的名称</li><li><strong>spec.rules[].host</strong>:外部访问使用的域名</li><li><strong>spec.rules[].http.paths[].backend</strong>:定义了 Kubernetes 集群中服务的相关信息</li><li><strong>spec.rules[].http.paths[].path</strong>:定义了外部服务访问 Kubernetes 集群中服务时使用的路径</li><li><strong>spec.rules[].http.paths[].pathType</strong>:定义了外部服务访问 Kubernetes 集群中服务时路径的匹配规则<br>从上述内容可以看到,<strong>Ingress 资源的语义是相对比较简单的。</strong></li></ul><p>Ingress 仅仅是 Kubernetes 中的一种资源定义,它本身不具备任何流量处理能力。要让 Ingress 资源生效,则必须要有 controller 来处理这些 Ingress 资源,通常这样的 controller 我们称之为 Ingress controller。</p><p>Ingress controller 会持续地监控或监听 Kubernetes 集群中 Ingress 资源的变化,并根据 Ingress 资源中定义的规则,转换为其数据面中的代理规则,并由数据面来实际的承载流量。</p><p><strong>在实际的生产环境中,客户端访问的需求是多种多样的。比如最常见的认证、路由重写等能力,通过 Ingress 资源是无法直接进行描述的。那么这些需求要如何满足呢?</strong></p><h2>Ingress-NGINX 如何支持扩展功能</h2><p>首先我以 Kubernetes 社区的 Ingress-NGINX controller 为例,介绍如何在其中使用扩展功能。</p><p>在 Ingress-NGINX 项目中,可以为 Ingress 资源增加一些 Annotation 来描述其需要使用的扩展能力。比如使用如下配置便可开启 cors 能力。</p><pre><code class="plain">apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-origin: https://foo.com,https://bar.com
nginx.ingress.kubernetes.io/cors-allow-headers: x-foo-1,x-foo-2
nginx.ingress.kubernetes.io/cors-allow-methods: GET,POST,PUT
name: nginx-ingress
spec:
rules:
- host: kubernetes.github.io
http:
paths:
- path: /ingress
pathType: Exact
backend:
service:
name: nginx
port:
number: 80</code></pre><p>这种方式仅仅需要为 Ingress 资源添加 Annotations 的配置即可,相对简单。但需要注意,使用这种模式需要 Ingress-NGINX controller 已经完成了对该 Annotations 的完整支持,否则该配置是无效的。</p><p>如果需要其他的一些 Ingress-NGINX controller 尚未实现的能力,则需要对其进行二次开发。</p><h2>在 APISIX Ingress 中使用插件</h2><p>相较于 Ingress-NGINX controller,APISIX Ingress 使用 APISIX 作为数据面,APISIX 是一个高性能的全动态 API 网关。所有的配置变更都是动态进行的,无需重启,所以对业务流量不会造成任何影响。</p><p>在 Apache APISIX Ingress 中可以通过使用插件,来满足用户各种流量处理的需求和具体场景。当前有 80+ 插件开箱即用,当然用户也可以开发自定义插件来进行能力的扩展。</p><p><img src="/img/remote/1460000043089713" alt="output.png" title="output.png"></p><p>目前,在 Apache APISIX 中支持多种方式进行自定义插件的开发:</p><ul><li>使用 Lua 进行插件的开发,这类插件会在 APISIX 内部运行;</li><li>使用其他语言进行插件的开发,这种机制叫作 Plugin Runner,利用该机制开发的插件属于 external-plugin。<br>关于 APISIX 插件的开发,可参考官方文档:</li><li>插件开发 <a href="https://link.segmentfault.com/?enc=MJ5ald0%2B8N8krbD0nIqwyQ%3D%3D.42HRzd6GYm27dDcU%2BMUnN4m4w8FL21iCe5FyOlIdwuXxmu6VlyHJiMkmyZGvYwiEvzrCvZ1lPec3e4HLkR4jMQ%3D%3D" rel="nofollow">https://apisix.apache.org/docs/apisix/plugin-develop/</a></li><li>External Plugin 开发:<a href="https://link.segmentfault.com/?enc=DRTmxLTIiVFnX4lhOpKA%2Bw%3D%3D.1IR8no7a5eV%2FYp5RJB7mkqVW63F9JKe3vR9g%2FKnnN%2F4vWdhja3Bqk7TUpyBZg71sP38BaZG5f6ywkVvzKf8MqA%3D%3D" rel="nofollow">https://apisix.apache.org/docs/apisix/external-plugin/</a><br>了解了 APISIX 的插件开发模式后,接下来将介绍 3 种在 APISIX Ingress 中使用 Lua 语言开发插件的方式。</li></ul><h3>方式一:纯 CRD 模式</h3><p>APISIX Ingress controller 支持自己设计的一套 CRD 规范,你可以直接在路由规则中开启插件(无论是内置插件还是自定义插件),例如:</p><pre><code class="yaml">apiVersion: apisix.apache.org/v2beta3
kind: ApisixRoute
metadata:
name: httpbin-route
spec:
http:
- name: rule1
match:
hosts:
- apisix.apache.org
paths:
- /apisix-ingress
backends:
- serviceName: apisix-gateway
servicePort: 80
plugins:
- name: cors
enable: true
config:
allow_origins: http://foo.bar.org
allow_methods: "GET,POST"
max_age: 3600
expose_headers: x-foo,x-baz
allow_headers: x-from-ingress
allow_credential: true</code></pre><p>通过上述配置可以创建路由规则,并且在此路由中开启 <code>cors</code> 插件。</p><p><strong>这是 APISIX Ingress 中最原生支持的方式,这种方式也与 APISIX 更加贴合。同时,用户新增自定义插件后,APISIX Ingress 也无需进行任何二次开发,可直接使用。</strong></p><h3>方式二:Ingress Annotations 模式</h3><p>由于 Ingress 资源的语义有限,所以通常情况下我们可以通过 <code>annotations</code> 为资源对象扩展一些信息,这也是最常见的对 Ingress 能力扩展的方式。例如:</p><pre><code class="yaml">apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: apisix
k8s.apisix.apache.org/enable-cors: "true"
k8s.apisix.apache.org/cors-allow-origin: https://foo.com,https://bar.com
k8s.apisix.apache.org/cors-allow-headers: x-foo-1,x-foo-2
k8s.apisix.apache.org/cors-allow-methods: GET,POST,PUT
name: apisix-ingress
spec:
rules:
- host: apisix.apache.org
http:
paths:
- path: /apisix-ingress
pathType: Exact
backend:
service:
name: apisix-gateway
port:
number: 80</code></pre><p>上述配置在 Ingress 资源中增加了 cors 相关的一些信息。APISIX Ingress controller 可以识别这些信息,并将这些信息转换为数据面中 cors 的配置,进而完成对 Ingress 资源的扩展。</p><p><strong>但是这种模式下,需要确保在 APISIX Ingress controller 中已经实现了对这些 Annotations 的处理逻辑,如果尚未实现,则需要进行一些二次开发。</strong></p><p>如果需要进行二次开发,可参考<a href="https://link.segmentfault.com/?enc=S8pnurMYcAcwSgfB23Phpg%3D%3D.BHlIIUssAMlx1ZSjbBF0Kc%2FFVXq2Y8IOBgkw6GjnivTFNuNdUPcmom5AU1FIFRVuNHqrCbBphVdVTA9UeEqLyg%3D%3D" rel="nofollow">《如何进行 APISIX Ingress controller 的开发》 </a>。</p><h3>方式三:CRD + Ingress Annotations 模式</h3><p>除以上两种方式外,在 APISIX Ingress 中也可以通过 CRD + Ingress Annotations 的方式进行扩展,例如:</p><pre><code class="yaml">apiVersion: apisix.apache.org/v2
kind: ApisixPluginConfig
metadata:
name: cors-plugin
spec:
plugins:
- name: cors
enable: true
config:
allow_origins: http://foo.bar.org
allow_methods: "GET,POST"
max_age: 3600
expose_headers: x-foo,x-baz
allow_headers: x-from-ingress
allow_credential: true
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: apisix
k8s.apisix.apache.org/plugin-config-name: cors-plugin
name: apisix-ingress
spec:
rules:
- host: apisix.apache.org
http:
paths:
- path: /apisix-ingress
pathType: Exact
backend:
service:
name: apisix-gateway
port:
number: 80</code></pre><p>通过上方的这段配置,可以单独创建名为 <code>cors-plugin</code> 的插件配置,并通过 Ingress 资源的 <code>k8s.apisix.apache.org/plugin-config-name: cors-plugin</code> 对其进行引用。通过这种操作的实际效果与前两个方式基本类似,都会在对应的路由上开启 <code>cors</code> 插件。</p><p>在这种模式下,<strong>用户的插件配置可以作为独立资源,并且可以被多个 Ingress 资源共享。同时,也无需进行任何二次开发。</strong></p><h2>总结</h2><p>本篇主要介绍了 Ingress 资源相关的语义,以及如何对 Ingress 资源进行能力的扩展。在 Ingress-NGINX 中可以通过 Annotations 的方式进行能力的扩展,但在 Apache APISIX Ingress 中可以通过三种模式进行配置。且其中两种对于用户自己开发的自定义插件而言,是无需进行任何二次开发的,进而满足用户更多的场景和需求。</p>
聚焦人机交互智能应用领域,APISIX 在希沃网关的应用与实践
https://segmentfault.com/a/1190000043057622
2022-12-13T14:07:21+08:00
2022-12-13T14:07:21+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<blockquote>分享嘉宾简海青,视源股份运维负责人。</blockquote><p>视源股份(CVTE)自成立以来,依托在音视频技术、人机交互、应用开发、系统集成等电子产品领域的软硬件技术积累,建立了教育数字化工具及服务提供商希沃(seewo)、智慧协同平台 MAXHUB 等多个业内知名品牌。其中希沃从 2012 年到 2021 年连续 10 年蝉联中国交互智能平板行业市占率桂冠,已成为名副其实的行业标杆企业。</p><p><strong>随着技术的飞速发展,在人际交互智能领域,业务需求也对架构迭代有了更高的要求。为了应对日趋成熟及快速增长的业务现状,希沃又是如何在网关层面进行跟进的呢?</strong></p><h2>网关往期迭代与痛点</h2><p>希沃网关的发展经历了四个版本的迭代。2013 年公司开始尝试互联网业务,那时候采用了 OpenResty + NGINX 静态配置的方式搭建了最初的网关,开发人员通过 SCP(Secure Copy)进行发布。与此同时一个比较严重的问题就是,每次上线发布都需要运维人员的协助才能保证平滑上线。</p><p>随着业务的发展和人员的扩充,2016 年我们开发了第二代发布系统和相关迭代网关。这次是基于 OpenResty 集成了 upsync 模块,同时配合 Consul 来进行服务发现。第二代的系统解决了上一代开发人员无法独立发布上线的问题,但仍需要运维协助才能进行扩容。</p><p>之后公司业务开始了迅猛发展,开始对网关以及产品的弹性扩缩能力有了更高的要求。2018 年我们基于 K8s 开发了第三代系统。考虑到仍有部分应用遗留在数组机上,所以整个网关架构是在 K8s 上使用 Ingress NGINX 来当作第二层的网关,第一层网关仍是 OpenResty 配合的双层网关架构。这种情况下虽然解决了前代发布扩容等自助问题,但又引入了新的麻烦。</p><p>业务的快速扩充致使对于整体稳定性的要求越来越高。采用这种双层网关架构后,一层 NGINX reload 和二层网关的路由变更,都会造成长连接断开。这对于一些长连接使用场景会影响较大,比如软件需要获取老师的授课状态时连接突然断开,状态获取中断从而影响授课。</p><p><img src="/img/bVc34Ff" alt="" title=""></p><p>本身双层架构就会带来成本层面的一些增加。同时,从上图的网关流量拓扑图可以看到,除上述遗留问题外也还存在一些架构上的痛点:</p><ul><li>在双层网关架构下,不管是在第一层网关添加域名、修改配置或者添加一些特殊规则等,都需要 reload NGINX。</li><li>同时从整体架构来看,组件的配合对于流量控制层面来说比较差。尤其是目前我们的业务用户体量已达到千万级别,一旦客户端出现不可规避的异常,就有可能出现侵蚀服务端的情况,这种时候如果在网关层面没有一定的流量控制能力,对于后端来说将会造成非常严重的雪崩。<br>因此,基于上述迭代遗留问题和架构痛点,在 2022 年我们引入了 APISIX 来解决上述问题。同时借助 APISIX,也加强了在网关层面对于流量的控制能力。</li></ul><p>但是在迁移 APISIX 的过程中,也会存在一些已知挑战。比如:</p><ul><li>⼀层 NGINX 域名多,定制化规则复杂。目前我们的业务中有 700+ 域名,同时还存在非常多的定制化配置,比如重定向、黑白名单等,这些都需要适配 APISIX 的插件。</li><li>由于历史遗留问题,⼀层 NGINX 和二层 Ingress 网关还是⼀对多的关系,对于后续的流量切换是不是会很复杂,这也是一个待解决问题。</li><li><p>内部存在的双层 DNS 架构。目前 DNS 解析主要用于处理公网和服务器内部的解析,所以对于后续的方案我们更希望是一个能方便回滚同时可以优化内网调用性能的。</p><h2>迁移 APISIX 后架构调整</h2></li></ul><p>面对上述已知的挑战,在迁移过程中主要进行了以下三个角度的操作。由于整个迁移过程没有涉及到研发内容,所以全部都是由运维人员实施的。</p><p>在迁移过程中,首先要做的就是 APISIX 路由的生成。基于原本的架构,我们去掉了一层特殊功能,比如 rewrite、set-header 等。弱化一层网关的转发,把所有功能都集中在二层的 Ingress 上,然后基于 Ingress 去生成 APISIX 的路由。同时在 NGINX 配置的基础上适配 APISIX 的插件。</p><p>路由生成后,就需要去校验整个转发过程是否正确。我们基于 goreplay 的录制回放来验证路由转发的正确性,同时通过自动化脚本来验证插件功能是否正常。在功能校验通过的情况下,我们会继续验证 APISIX 在性能层面是否满足内部需求。因此,在性能压测过程中我们自研了 elastic-apm 插件,同时针对部分影响 QPS 的插件进行了性能优化。</p><p>处理完功能跟性能相关的问题后,最大的挑战就是流量切换了,因为流量切换将直接关乎生产质量。</p><p><img src="/img/bVc34Fg" alt="" title=""></p><p>虽然前边我们已经使用了 goreplay 进行流量录制回放,但我们仍然需要一个比较可靠的回滚方案。假设流量切换造成了异常,比如转发异常或者是 APISIX 出现崩溃时,能够进行快速回滚操作。基于此,我们首先切换了公网流量,因为公网是通过 WAF 回源到 SLB 来进行流量切换的,这时如果我们切换到 APISIX,就可以很方便地去修改回源地址来将整个流量进行回滚,如上图标注「切换」字样所示。这个异常情况下的流量切换过程基本是在秒级别,可能 10 秒内就把所有流量都切回来了。</p><p>完成了公网流量切换的情况下,顺利运行了几天,我们就通过 APISIX 将内网流量也进行了变更,然后整个生产上的切换就全部完成了。但是这个上线过程中,其实我们还是遇到了一些问题的。</p><h2>迁移过程中的问题与解决方案</h2><h3>Prometheus 插件转发延迟</h3><p>这个是在我们内网测试环境中发现的一个问题。由于我们的内网是 all-in-one 的测试环境,所有部门都使用同一个 APISIX 的入口,所以路由规则非常多,达到 4000+。这样就会导致每次拉取 Prometheus 插件时, metrics ⼤小达到 80M+,造成单个 worker 进程跑满,从而造成 worker 的转发延迟。</p><p><img src="/img/bVc34Fh" alt="" title=""></p><p>这个问题是目前 APISIX 开源版本存在的一个现象,主要是因为业务流量和内部 API 流量(比如Prometheus metrics 和 Admin API)都共用 worker 造成的。我们在之前是针对 Prometheus 插件进行了修改,其中延迟相关的 metrics 占用了 90%以上(如上图所示),所以我们将这部分采集去掉了。去掉这部分后,业务层面还是满足了我们的监控使用需求,并未造成影响。</p><p>不过最近我们针对这个问题又有了新的方案,目前还处于 demo 阶段。这套新方案是对 NGINX 源码进行修改,通过多启动⼀个或多个 worker 进程(isolation process) 来专⻔监听特定端口的请求(比如 Prometheus、Admin API、Control API 等),不监听处理正常业务端口请求。其它的 worker 进程则取消监听上述端口,只处理正常业务端口请求,来确保 APISIX 内部请求和正常业务请求间不会互相影响。</p><p><img src="/img/bVc34Fj" alt="" title=""></p><h3>默认路由匹配异常</h3><p><img src="/img/bVc34Fl" alt="" title=""></p><p>在上线 APISIX 后,我们发现域名并没有走精确匹配模式,而是采用了通配符匹配,这跟 NGINX 的域名最长匹配是不一致的。为此,我们通过更换路由策略,将 URL 方式改成了 host+URL 的方式,解决了该问题。</p><p>但关于 APISIX 基于 URL 路由策略作为默认路由的问题,大家可以在自己的生产环境中进行压测后再决定是否保留。</p><p>假如你的生产场景中属于 URL 特别多、域名特别少的类型,那 APISIX 这种默认路由策略是完全 OK 的。但在我们的业务场景下,并没有这么多 URL,所以采用 host+URL 的方式是更满足我们的性能需求。</p><h3>默认自动绑核问题</h3><p>在云原生的背景下,大部分用户都会选择将 APISIX 部署在容器中使用。但 APISIX 在默认配置下会进行自动绑核,这样就会导致在容器化场景下,可能只会用到前几个核心,造成前几个核心跑满而后几个核心仍处于空闲的状态。虽然 CPU 使用率不高,但是会使 APISIX 转发出现延迟。</p><p><img src="/img/bVc34Fm" alt="" title=""></p><p>不过 APISIX 社区最近已经开始调整这个配置,将 <code>worker_cpu_affinity</code> 配置的默认值从 <code>true</code> 改为了 <code>false</code>。因此这个问题目前在 APISIX 版本中已经解决了。</p><h3>版本升级兼容问题</h3><p>在上线 APISIX 的过程中,我们还发现在较老的系统或 OpenSSL 库中,它的 ssl_ciphers 和服务端默认值无交集,从而造成 SSL 握手失败。</p><p>针对这个问题,我们建议大家在上线 APISIX 之前,先通过一些 SSL 工具先去探测一下当前旧网关与 APISIX 网关的 SSL 握手交集是否正确或满足使用场景,然后再进行规模化的迁移调整。</p><p>除此之外,在 APISIX 发布 2.15 LTS 版本后,我们就在内网进行了升级,但是升级后就发现了一些路由匹配相关的问题。</p><p>因为从旧版本升级到新版本时,存在一些兼容性问题,导致 redirect 插件参数 <code>http_to_https</code> 为<code>true</code> 时,参数 <code>http_to_https</code> 和 <code>append_query_string</code> 校验失败,进而路由加载失败,导致路由丢失。这种情况下就会在路由匹配时出现 404 或者转发到其他上游的情况。</p><p>目前这个问题已经在 APISIX 的 master 分支中解决了,但是并没有针对 2.15 版本进行单独解决,所以大家在使用该版本时也需要留意这部分问题。</p><h2>应用 APISIX 的收益及展望</h2><p>虽然前边提到了一些我们在上线 APISIX 过程中遇到的问题,但是在应用 APISIX 之后,给公司业务层面还是带来了很多价值的。比如:</p><ul><li><strong>运维效率提升。</strong> 使用 APISIX 后,再也不存在 reload 相关的烦恼,可以做到随时更新路由和证书等,给开发人员带来了操作上的便利。</li><li><strong>流量控制能力提升。</strong> 使用 APISIX 后,我们在熔断和限流方面都得到了提升,从而加强了在流量管控层面的能力,进一步稳固了整个业务核心流程。</li><li><strong>自研插件,增强网关能力。</strong> 得益于 APISIX 的强拓展性和自身插件性能的优异,我们也会更主动地去开发一些插件。比如我们在 APISIX 上集成了统一鉴权的能力,新业务无需单独对接鉴权系统,加快了产品迭代流程。</li></ul><p><img src="/img/bVc34Fn" alt="" title=""></p><ul><li><strong>去掉了冗余的一层 NGINX,实现降本增效。</strong> 之前的双层网关架构中,一层的 NGINX 对于开发人员并不透明。现在将双层网关合并为一层后,开发人员可以很清晰地看到架构中一些关于路由或插件等配置,对于排查问题来说更加方便快捷。<br>在后续使用 APISIX 的规划中,我们还是会继续在网关层面进行增强。比如开发针对内部业务的自研插件,提供多租户的能力,或者是将 API 管理的功能带到网关层面等等。</li></ul><p>当然在这个过程中,我们也在积极回馈社区。目前已在社区中贡献了 8 个 PR,帮忙完善和修复了一些生态插件相关的问题。比如完善 batch_request 支持自定义 uri、为 hmac-auth 插件提供请求 body 校验等功能。</p><p>希望在后续的实践过程中,我们可以更全面地使用和发挥 APISIX 的特性,更加积极地探索 APISIX 的使用场景。期待未来有更多的共建功能上线。</p>
如何基于 APISIX 迭代数字智联平台
https://segmentfault.com/a/1190000043030989
2022-12-09T15:54:43+08:00
2022-12-09T15:54:43+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<blockquote>分享嘉宾:沈巍,浙大网新研发总监。</blockquote><p>网新电气成立于 2011 年,属于浙大网新旗下,为绿色智慧交通系统解决方案的提供商。业务范围包括铁路、隧道、城市智能交通、航空、高速公路等行业。整个高铁信息化的业务分布占到了全国市场的 20% 以上,比如参与建设了杭黄铁路、合肥南站等客服信息化系统;杭州文一路隧道、杭州紫之隧道等监控系统;首都机场物联网平台、数据交换平台等系统。</p><p>在城市智能交通的覆盖范围下,很多场景对于流量处理和系统稳定性都要求极高。为了应对这种城市级别的数字智能要求,对于基础架构的组件选择也是重中之重。本文将从数字化平台应用的角度,带来与 API 网关 Apache APISIX 的相遇相熟和应用,希望对仍在网关选型过程中的企业或用户有所帮助。</p><h2>数字大桥智联平台应用场景</h2><p><img src="/img/remote/1460000043030991" alt="https://static.apiseven.com/2022/12/09/639293149c4bc.png" title="https://static.apiseven.com/2022/12/09/639293149c4bc.png"></p><p>在数字领域的业务实践中,我们打造了网新数字大桥智联平台。数字大桥智联平台是一个非常庞大的交通领域管控系统,涵盖了整个交通领域比如结构化安全、病害检测、日常维养巡检、数字孪生与建模、交通应急指挥以及各类评估算法等等。</p><p>下图为网新数字大桥智联平原本的架构模式,整个系统主要分为四个部分。</p><p><img src="/img/remote/1460000043030992" alt="https://static.apiseven.com/2022/12/09/63929314aa6c5.png" title="https://static.apiseven.com/2022/12/09/63929314aa6c5.png"></p><p>最底层属于采集层,主要负责采集各类数据。目前采集数据的种类比较多,比如温度传感器会采集到路面的一些温度;结构层面的力学传感器会检测震动数据;GPS 传感器则会采集位移情况;无人机监测路面状态等等,所以整体监测类型的数据量也是非常大的。</p><p>在此之上,在中间层我们还构建了一个数据中枢,它可以把所有的业务数据都汇总到一起,然后再提供业务的 API 给各个业务系统使用。这个项目最大的一个特点,就是参与的公司很多(大概 30+),这些公司都会参与进来进行各自的业务系统搭建。</p><p>在业务层中,需要在同一套用户体系下访问业务系统,主要是给业务用户使用。在实际实践过程中,不同业务系统之间往往存在交叉访问。随着业务系统的增多,整个服务对外提供的 IP 和端口也会增多,一些问题开始应运而生。</p><h3>痛点与现状</h3><p>随着实践的增多,我们发现上述架构开始呈现如下问题:</p><ol><li>不同系统之间 API 鉴权各异,大量重复开发;</li><li>不同系统对 API 的安全管理程度不同;</li><li>缺少统一的 API 路由;</li><li>缺少统一 API 监控等。</li></ol><p>基于上述需求痛点,引申出了一个结论方向:即我们需要构建一个 API 网关。API 网关对于我们现阶段的系统而言,是一个<strong>刚需</strong>的组件。在这个过程中,我们也开始考虑如果引入 API 网关后会有什么优点。</p><p><img src="/img/remote/1460000043030993" alt="https://static.apiseven.com/2022/12/09/63929315c57c8.png" title="https://static.apiseven.com/2022/12/09/63929315c57c8.png"></p><p>比如之前整个业务系统会暴露大量的服务端口,因为系统很多都需要暴露到外部去使用。引入 API 网关后,或许整个系统可以只暴露一个 <code>443</code> 端口,其他全部在内网访问即可,这样整个系统的安全性就更高。</p><p>其次 API 鉴权如果都放到 API 网关来做的话,那整个功能的实现就非常简单了,同时还降低了各个业务系统之间互相调用的开发工作量,避免重复开发。同理对于 API 的路由和监控也一样,都可以让整体业务更好地管理。</p><p>最后就是内网如果走 HTTP 的话,性能其实会更好。因为之前架构中,各个业务系统之间的互相调用,为了保证安全性所以选择了 HTTPS 模式,在一定程度上影响了整体性能。</p><p>所以总体来看,加入 API 网关这个组件,对于我们整体业务的提升是比较明显的。</p><h2>网关层为何选择 APISIX</h2><h3>自研初尝试</h3><p>在选型 API 网关之前,其实我们自己也尝试过一些操作。因为整个团队对 Java 技术栈和 Spring 框架都比较熟,所以就选择基于 Zuul 开发了一个 API 网关使用。当时这套网关对于 HTTP 场景也基本够用。随着后续继续使用,就有新的需求开始产生。比如:</p><ul><li><strong>需要对 WebSocket 的鉴权进行支持。</strong>很多实时类的告警信息需要通过 WebSocket 进行推送,如果需要支持这类信息,还需另行开发;同时 Zuul 对 WebSocket 的支持性不是很好,想要对 WebSocket 进行鉴权更是难上加难。</li><li><strong>需要路由支持 HLS 和 FLV 等流媒体协议。</strong>由于本身业务实践场景中,对于视频处理的需求比较大,尤其是面向 ToB 和 ToG 的业务场景,视频监控是一个很重要的系统。因此如果视频处理走网关的话,就需要支持 HLS 和 FLV 流媒体协议(两种都基于 HTTP),同时视频对于延迟性和流量要求都是很高的,对网关压力极大。</li><li><strong>BIM 模型展示,基于 3D Tiles 协议需要在短时间下载数 GB 的数据。</strong>在前文提到的数字孪生场景下,BIM 建模非常火。因此整个模型体量是非常大的,大概几百个 G 。尽管 3D Tiles 协议支持动态加载能力,但是在短时间内需要完成的流量依旧很庞大,对于网关来说压力就更大了。</li><li><strong>路由需要动态加载,即时生效。</strong>在动态加载层面,Zuul 没法满足我们即时生效的需求。即便是我们进行二次开发,也会加大额外的工作量,同时还并不能保证可以完美地实现这种效果。</li></ul><h3>调研选型主流网关</h3><p>在自研道路上跌跌撞撞之后,我们开始对市场中主流的网关产品进行调研。当时我们调研了以下几个网关,调研完之后发现每个产品各自的优缺点都比较明显,但是都不能很好地契合我们的场景。比如 NGINX 无法做动态路由,Java 技术栈相关的性能又跟不太上等。</p><p><img src="/img/remote/1460000043030994" alt="https://static.apiseven.com/2022/12/09/6392931701477.png" title="https://static.apiseven.com/2022/12/09/6392931701477.png"></p><p>调研之后,我们并没有找到适合自己的产品,也一直都在观望和困惑中。但后来在选型过程中,无意间看到了由开源中国举办的 「2021 年年度中国开源项目」评选活动。因为我觉得这里面肯定有很多优秀的项目,于是抱着学习的心态就一个个点进去了解了一下,然后就看到了 API 网关产品 —— Apache APISIX。</p><p><img src="/img/remote/1460000043030995" alt="https://static.apiseven.com/2022/12/09/63929313912bb.png" title="https://static.apiseven.com/2022/12/09/63929313912bb.png"></p><p>后来跟随 APISIX 的各种介绍,包括 GitHub 上的提问、社区官网上的技术文档以及 B 站上的一些用户案例和技术实践分享等等,我发现整个社区都非常有活力。比如在 GitHub 上有接近 3600 个 issue 而且已经解决了 3200 个左右,解决率高达 89%,这个数量占比还是非常可观的;同时有些社区反馈渠道也允许中文提问,这对于中国开发者而言非常友好。</p><p>同时 Apache APISIX 项目在全网有较多的参考资料。APISIX 社区在 B 站和 Youtube 上都有非常多的视频分享,包括用户案例和一些技术 tutorials(教程)。我曾经花了两天时间把 APISIX的 B 站视频全都看了一遍,这个过程中我发现有很多企业类似的案例都跟我们的需求场景很类似,包括一些实践过程中踩的坑。这些对于我们而言,很多是可以直接拿来借鉴的真实经验,对于后续的架构更新有非常高的价值参考。</p><p>这些细节,都让我们对于产品的后续使用有了十足的安全感,最终我们选择了将 Apache APISIX 作为 API 网关进行后续架构和业务的迭代。</p><h2>应用 APISIX 的业务实践</h2><h3>让 K8s 中部署更可靠</h3><p><img src="/img/remote/1460000043030996" alt="https://static.apiseven.com/2022/12/09/6392931729609.png" title="https://static.apiseven.com/2022/12/09/6392931729609.png"></p><p>目前我们是将 APISIX 部署在 K8s 上使用,整个用户侧的业务系统都是先经过 API 网关,然后再由 API 网关在路由上分发到各个业务服务,中间都是通过内部域名进行传递。</p><p>在高性能的体现上,我们采取了 K8s 多实例的方案。比如说性能要求好,就可以给它配 10 个实例;性能要求差一些,就配 5 个实例。</p><p>同时域名也是我们很在意的点。因为按照前文所提到的预想规划,我们只希望暴露一个 <code>443</code> 的端口出去,其他都通过 K8s 内部的域名进行整体服务的运转。在刚开始使用 APISIX 时我还有些担心,万一它只支持 IP 地址跟端口,那就很麻烦了。</p><p>后来经过测试发现效果也非常好。如下图左侧为 APISIX Dashboard,右侧是服务域名(K8s 服务域名)。这里就可以直接配置 K8s 的域名+一个端口,这样就可以直接完成域名的转发。这对整个系统的可靠性跟安全性都有了非常强力的功能支持。</p><p><img src="/img/remote/1460000043030997" alt="https://static.apiseven.com/2022/12/09/639293138baeb.png" title="https://static.apiseven.com/2022/12/09/639293138baeb.png"></p><h3>自定义服务轻松对接鉴权</h3><p>单点登录与鉴权也是我们业务系统中非常重要的一部分。因为我们有大量的业务和用户需要进行管理,所以这一块肯定是由各自业务系统进行的,因此我们也自研了单点登录服务。</p><p>由于自研服务系统,所以相关 token 并不通用,其中就会携带一些业务特定的自定义信息。因此我们也害怕自己的 token 无法被 APISIX 鉴权相关功能所识别,因为 APISIX 提供的 token 是我们内部无法使用的。</p><p><img src="/img/remote/1460000043030998" alt="https://static.apiseven.com/2022/12/09/6392931354abd.png" title="https://static.apiseven.com/2022/12/09/6392931354abd.png"></p><p>但在后续的实践过程中我们发现,这种想法是多虑的。在实际生产过程中,只需把我们业务的 token 生产加密算法与 APISIX 的加密配置保持同步,就可以利用 APISIX 实现完整功能的鉴权。如上图所示,自研服务产生的 token 通过 APISIX 的 <code>jwt-auth</code> 插件完成了一整个服务鉴权。</p><h3>针对业务的 etcd 处理</h3><p>作为 APISIX 架构中的重要组件,etcd 对于我们的研发团队来说确实是一个挑战。由于本身对 etcd 并不熟悉,所以我们在 K8s 环境中部署 etcd 集群折腾了很久。虽然在学习成本上有些折腾,但也意外发现了一个惊喜。</p><p>我们发现,APISIX 在 etcd 宕机后,非重启情况下还可以正常工作。等 etcd 恢复后, APISIX 会自动重连。基于此,我们提出了一个更贴合实际业务场景的方案。</p><p>因此我们放弃了配置 etcd 集群,而是只配置了一个 etcd 用来支撑整个 APISIX。只需要把 etcd 的数据备份好,重启完 APISIX 也能正常连接运作。实际运行起来的效果也是很不错的,至少对于我们的业务场景是够用的。同时因为 etcd 只部署了一个,所以后续的运维工作量也减少了很多。</p><h3>开箱即用的 API 监控</h3><p>对于我们的业务来说,是属于强依赖于系统集成的项目,因此业务系统之间互相调用 API 的环节就非常多。如果某些系统之间的调用 API 出现了问题,就会导致不同业务部门互相扯皮。为了应对这种环节,就需要有相应的监控系统进行排查。</p><p>APISIX 官方其实已经提供了一套完整的监控解决方案「APISIX+Prometheus+Grafana」。利用自带的 <code>prometheus</code> 插件,可以非常方便地与 Prometheus 进行集成,并实现请求状态码、实时带宽、etcd 可用性、TCP 连接指标和节点等信息的监控和管理。</p><p><img src="/img/remote/1460000043030999" alt="https://static.apiseven.com/2022/12/09/63929319077ae.png" title="https://static.apiseven.com/2022/12/09/63929319077ae.png"></p><p>依靠 APISIX 这套监控方案可以很容易地定位出哪部分调用 API 出现了问题,进行快速定位,避免了各业务系统之间集成对接时所产生的一些问题。</p><h2>总结与展望</h2><p>总体来看,我们接触 APISIX 的时间并不算长。2021 年 10 月份刚知道这个产品,在测试和需求检验等角度折腾了两三个月后,开始尝试部署到生产环境中,现在也是随着官方版本的更新逐渐迭代。</p><p>从我个人角度和业务运行的体验来看,APISIX 作为网关已经是一个很成熟的产品了。在使用的这段时间里,也逐渐从对这个产品的各种担心到真正上手后的惊喜回馈:</p><ul><li>一开始曾担心配置上游时,无法使用 K8s 的服务域名;使用过发现,原来完美支持服务域名访问。</li><li>基于 APISIX 实现了业务高可用方案,依赖 K8s 云平台实现多实例+自动伸缩+健康监测+预警告警等功能。</li><li>对于系统集成项目,APISIX 完美解决了 API 的管理、 鉴权、路由问题。</li><li>其配置的热更新特性,极大避免了“因为更新导致服务无法正常访问”。</li><li>作为一个开箱即用的网关产品,让开发人员可以更专注于业务代码。</li></ul><p>当然,在实践过程中,我们也基于这些操作提出了一些业务层面对于 APISIX 的功能需求,比如:</p><ul><li>能否支持除 etcd 外的其他数据持久化方式,比如 MySQL、PostgreSQL 等。可让我们在熟悉的数据存储领域能够更好地应用与排查问题;</li><li>APISIX Dashboard 能否支持一键数据备份与还原。<br>整体来看,在使用 APISIX 的过程中,对于开发人员的学习成本可能高了一些,但是产品服务于业务中所体现出来的整体效果是完全惊喜的。后续我们也计划在更多系列的业务产品中引入 APISIX,作为网关组件让我们的业务产品可以发挥更稳定更高效的价值。</li></ul>
Apache APISIX 玩转 Tongsuo 国密插件
https://segmentfault.com/a/1190000043030825
2022-12-09T15:39:11+08:00
2022-12-09T15:39:11+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<p>本文通过解读国密的相关内容与标准,呈现了当下国内技术环境中对于国密功能支持的现状。并从 API 网关 Apache APISIX 的角度,带来有关国密的探索与功能呈现。</p><blockquote>作者:罗泽轩,Apache APISIX PMC</blockquote><h2>什么是国密</h2><p>顾名思义,<strong>国密就是国产化的密码算法</strong>。在我们日常开发过程中会接触到各种各样的密码算法,如 RSA、SHA256 等等。为了达到更高的安全等级,许多大公司和国家会制定自己的密码算法。国密就是这样一组由中国国家密码管理局制定的密码算法。在国际形势越发复杂多变的今天,密码算法的国产化替代,在一些领域已经成为了一股势不可挡的潮流。</p><p><strong>国密的官方名称为国家商用密码,简称商密,拼音缩写是 SM</strong>。这也是国密标准中 SM2/3/4/7/9 等算法名称的来源。国密算法的命名方式非常简单直接,就像“绵阳九所”、“二机部”一样,都是“分类+序号”的组合。其中 SM1 和 SM4 是对称算法,对标 AES;SM2 是非对称算法,对标 RSA、ECDSA;SM3 是摘要算法,对标 MD5;等等。由于本文并非涉及到国密的实现细节,所以不会讲得非常细,也就科普下国密算法的分类。</p><p>在基础的国密算法之上,我们可以构造一个国密的垂直生态,比如实现国密算法的硬件、提供国密支持的密码库、加入国密流程的 TLS 握手协议等等。正如安全需要纵深防御一样,基于国密的信任链也需要有全软件栈上的支持。</p><p>因此,当我们谈论国密支持时,并不仅仅单独指可以用某一种国密算法进行加解密,而是指嵌合入国密的生态,支持某种国密的应用场景。</p><h2>国密的应用场景</h2><p>作为国家密码管理局制定的密码算法,国密广泛应用于电子政务(包括国家政务通、警务通等重要领域)、信创及金融业的各个应用领域。</p><ul><li><strong>政府和金融的身份认证终端</strong>。依照现行有关规定,许多涉及政府和金融的身份认证终端(诸如 USBKey、智能 IC 卡、银行卡终端等)都需要提供对国密的支持。</li><li><strong>国产开源操作系统</strong>。许多主打国产替代的开源操作系统,会提供基于国密的安全加固功能。比如龙蜥操作系统 (Anolis OS) 提到自己实现了全栈国密能力;OpenEuler 也在做国密相关的一些功能,比如基于国密数字证书扩展了 EFI 的数字签名。</li><li><strong>信创产品</strong>。还有许多做信创生意的厂商,围绕国密推出符合相关标准的产品。例如支持使用国密算法做数字签名的 PDF 工具、支持国密接入标准的音视频软件等等。</li><li><p><strong>基于国密 TLS 协议的生态</strong>。也是在日常开发中接触得最多的。譬如各种国产 CA 厂商、支持了国密TLS的许许多多密码库和浏览器,以及国密接入的VPN和网关等等。</p><h2>APISIX 对国密的探索与支持</h2></li></ul><p>Apache APISIX 是一个动态、实时、高性能的 API 网关,提供负载均衡、动态上游、灰度发布、精细化路由、限流限速、服务降级、服务熔断、身份认证、可观测性等数百项功能。作为一个发迹于国内环境的 API 网关,APISIX 自然需要考虑下如何接入国密的生态圈。</p><p>由于国密更多地用在国内环境中,尚未被 OpenSSL 等国际主流项目所完全接纳。如果要使用国密的功能,就涉及到更换 APISIX 默认的 OpenSSL 库为其他 SSL 库。</p><p>为此,我们考察了以下的项目:</p><ul><li>GMSSL:北京大学开源的项目。原版基于 OpenSSL 1.0.2 修改而来。新的 GMSSL 3.0 基本上是重新开发,跟 OpenSSL 的目录结构差别很大。</li><li>gm-BoringSSL:个人开源项目,在 BoringSSL 上增加国密支持。已有两年未改动。</li><li>TaSSL:北京江南天安科技有限公司开源的项目。基于 OpenSSL 1.1.1 修改而来。</li><li>Tongsuo:蚂蚁集团开源的项目。基于 OpenSSL 3.0 修改而来,项目前身是 BabaSSL,现已 <strong>改名为铜锁/Tongsuo</strong>。<br>由于 GMSSL 3.0 并不基于 OpenSSL,即使能保证 API 兼容,也没办法确保能 100% 替换现有 OpenSSL 的行为,所以被首先排除。其次 gm-BoringSSL 疏于维护,也被排除。</li></ul><p>在 TaSSL 和 Tongsuo 之中,我倾向于选择 Tongsuo[1]。因为 Tongsuo 在标准上拥有更强的话语权,比如 RFC 8998(TLS 1.3 中支持 SM 套件)就是由 Tongsuo 的开发者制定的。TaSSL 则是每出一个版本,就公布一个新的仓库。比如前一个 版本[2],感觉不太靠谱。</p><p>对于选择 Tongsuo,我个人存在一个顾虑,就是他目前基于 OpenSSL 3.0 的版本还没有发布正式的 Release 版本(第一个版本预计在 2023 年 2 月发布)。由于 Tongsuo 当前还没有一个固定的版本,因此社区决定先把国密相关的功能独立出来,以插件形式存在,有相关需求时可单独启用。</p><p>目前已在插件层面实现了服务端一侧国密双证书的支持,感兴趣的读者可以在官网查看 <code>gm</code> 插件介绍文档 插件介绍文档[3],自行完成 APISIX 的编译和对应插件的安装配置工作。当然,如果想即刻预览该插件的使用过程,也可以直接参考下文内容。</p><h2>快速参考:APISIX 国密插件的使用 </h2><h3>启用插件</h3><p><strong>插件要求 Apache APISIX 运行在编译了 Tongsuo 的 APISIX-Base 上</strong>。</p><p>首先需要安装 Tongsuo (此处我们选择编译出 Tongsuo 的动态链接库):</p><pre><code class="bash"># TODO: use a fixed release once they have created one.
# See https://github.com/Tongsuo-Project/Tongsuo/issues/318
git clone https://github.com/api7/tongsuo --depth 1
pushd tongsuo
./config shared enable-ntls -g --prefix=/usr/local/tongsuo
make -j2
sudo make install_sw</code></pre><p>其次需要构建 APISIX-Base,让它使用 Tongsuo 作为 SSL 库:</p><pre><code class="bash">export OR_PREFIX=/usr/local/openresty
export openssl_prefix=/usr/local/tongsuo
export zlib_prefix=$OR_PREFIX/zlib
export pcre_prefix=$OR_PREFIX/pcre
export cc_opt="-DNGX_LUA_ABORT_AT_PANIC -I${zlib_prefix}/include -I${pcre_prefix}/include -I${openssl_prefix}/include"
export ld_opt="-L${zlib_prefix}/lib -L${pcre_prefix}/lib -L${openssl_prefix}/lib64 -Wl,-rpath,${zlib_prefix}/lib:${pcre_prefix}/lib:${openssl_prefix}/lib64"
./build-apisix-base.sh</code></pre><p>该插件默认是禁用状态,你需要将其添加到配置文件<code>./conf/config.yaml</code> 中才可以启用它:</p><pre><code class="yaml">plugins:
- ...
- gm</code></pre><p>由于 APISIX 的默认 cipher 中不包含国密 cipher,所以我们还需要在配置文件 <code>./conf/config.yaml</code> 中设置 cipher:</p><pre><code class="yaml">apisix:
...
ssl:
...
# 可按实际情况调整。错误的 cipher 会导致 “no shared cipher” 或 “no ciphers available” 报错。
ssl_ciphers: HIGH:!aNULL:!MD5</code></pre><p>配置完成后,重新加载 APISIX,此时 APISIX 将会启用国密相关的逻辑。</p><h2>测试插件</h2><p>在测试插件之前,需要准备好国密双证书。Tongsuo 提供了生成 【SM2 双证书】 的 教程[4]。</p><p>在下面的例子中,我们将用到如下的证书:</p><pre><code class="plain"># 客户端加密证书和密钥
t/certs/client_enc.crt
t/certs/client_enc.key
# 客户端签名证书和密钥
t/certs/client_sign.crt
t/certs/client_sign.key
# CA 和中间 CA 打包在一起的文件,用于设置受信任的 CA
t/certs/gm_ca.crt
# 服务端加密证书和密钥
t/certs/server_enc.crt
t/certs/server_enc.key
# 服务端签名证书和密钥
t/certs/server_sign.crt
t/certs/server_sign.key</code></pre><p>此外,还需要准备 Tongsuo 命令行工具。</p><pre><code class="plain">./config enable-ntls -static
make -j2
# 生成的命令行工具在 apps 目录下
mv apps/openssl ..</code></pre><p>你也可以采用非静态编译的方式,不过就需要根据具体环境,自己解决动态链接库的路径问题了。以下示例展示了如何在指定域名中启用 <code>gm</code> 插件。<br>要在指定域名中启用 <code>gm</code> 插件的功能,需要先创建对应的 SSL 对象:</p><pre><code class="python">#!/usr/bin/env python
# coding: utf-8
import sys
# sudo pip install requests
import requests
if len(sys.argv) <= 3:
print("bad argument")
sys.exit(1)
with open(sys.argv[1]) as f:
enc_cert = f.read()
with open(sys.argv[2]) as f:
enc_key = f.read()
with open(sys.argv[3]) as f:
sign_cert = f.read()
with open(sys.argv[4]) as f:
sign_key = f.read()
api_key = "edd1c9f034335f136f87ad84b625c8f1"
resp = requests.put("http://127.0.0.1:9180/apisix/admin/ssls/1", json={
"cert": enc_cert,
"key": enc_key,
"certs": [sign_cert],
"keys": [sign_key],
"gm": True,
"snis": ["localhost"],
}, headers={
"X-API-KEY": api_key,
})
print(resp.status_code)
print(resp.text)</code></pre><p>然后将上面的脚本保存为 <code>./create_gm_ssl.py</code>,运行以下命令:</p><pre><code class="plain">./create_gm_ssl.py t/certs/server_enc.crt t/certs/server_enc.key t/certs/server_sign.crt t/certs/server_sign.key</code></pre><p>输出结果如下:</p><pre><code class="plain">200
{"key":"\/apisix\/ssls\/1","value":{"keys":["Yn...</code></pre><p>完成上述准备后,可以使用如下命令测试插件是否启用成功:</p><pre><code class="plain">./openssl s_client -connect localhost:9443 -servername localhost -cipher ECDHE-SM2-WITH-SM4-SM3 -enable_ntls -ntls -verifyCAfile t/certs/gm_ca.crt -sign_cert t/certs/client_sign.crt -sign_key t/certs/client_sign.key -enc_cert t/certs/client_enc.crt -enc_key t/certs/client_enc.key</code></pre><p>其中,<code>./openssl</code> 是上文提到的 Tongsuo 命令行工具,<code>9443</code> 是 APISIX 默认的 HTTPS 端口。<br>如果一切正常,可以看到连接已经建立了起来,并输出如下信息:</p><pre><code class="plain">...
New, NTLSv1.1, Cipher is ECDHE-SM2-SM4-CBC-SM3
...</code></pre><h3>禁用插件</h3><p>如果不再使用此插件,可将 <code>gm</code> 插件从 <code>./conf/config.yaml</code> 配置文件中移除,然后重启 APISIX 或者通过插件热加载的接口触发插件的卸载。</p><h2>相关链接 </h2><p>如果你对该功能或者插件感兴趣,欢迎在随时在社区进行交流。</p><p>[1] <a href="https://link.segmentfault.com/?enc=7ZGqaHp0h1wsIG3BRhlv3w%3D%3D.zMtjtn3gucKtRti3o15w9FtFOTjZ2TXrBcjwCEQTISHAsQ95GUcJyOedhHT6BMKw" rel="nofollow">https://github.com/Tongsuo-Project/Tongsuo</a></p><p>[2] <a href="https://link.segmentfault.com/?enc=%2FQZ%2FxVNoMge9QlPtyVC%2FQQ%3D%3D.JB41prN07lzy9j8oVb52pmjjxFtpFRMFK%2BSF%2FDNuwbicS8e7%2BOzYf1agtmmJqq%2Fw" rel="nofollow">https://github.com/jntass/TASSL-1.1.1k</a></p><p>[3] <a href="https://link.segmentfault.com/?enc=r5eC2nQfyhlAWDo6Tdd8wg%3D%3D.Aw9CUflJ9bxARfS39%2Bovd3Lxo91H8qs6%2BzeQbcAxtcv0qt9te104Z4Y1tfibwcZ3R02%2F%2FxcHMsFlqIBZbCVyUA%3D%3D" rel="nofollow">https://apisix.apache.org/zh/docs/apisix/next/plugins/gm/</a></p><p>[4] <a href="https://link.segmentfault.com/?enc=5LBPSd8U8iakeRCgCZItwQ%3D%3D.%2Ft%2B2IE9YeE5SnVlIvwXmfGMxDiv%2F53yXk0BCOpAtf181%2F1b01zhMZdiIcyPN%2B058" rel="nofollow">https://www.yuque.com/tsdoc/ts/sulazb</a></p>
APISIX 在君润人力云原生平台的架构实践
https://segmentfault.com/a/1190000042976277
2022-12-05T13:51:48+08:00
2022-12-05T13:51:48+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<blockquote>讲师:袁鹏,一页科技架构师<br>摘要: 君润人力采用多套 Apache APISIX 集群来满足自研服务平台的功能需求。</blockquote><p>君润人力成立于 2019 年,是一家以科技驱动的人力资源解决方案服务商,依托行业领先的科技水平和服务能力,专注于为客户提供一站式人力资源服务。自研数十家人力资源服务平台,深度链接 B 端企业和 C 端用户,构建数字化人力资源服务生态。</p><p>本文从君润人力业务快速扩张的背景入手,重点介绍开源 API 网关 Apache APISIX 对其自研平台系统架构的多样化应用场景支持,共有四大线上实战案例,希望对仍在网关选型过程中的企业或用户有所帮助。</p><p><img src="/img/remote/1460000042976279" alt="" title=""></p><h2>君润人力自研系统架构概述</h2><p>君润人力在搭建自研服务平台架构时,首要原则就是<strong>“开放、开源、拥抱云原生”</strong>。平台基于 Kubernetes 微服务架构构建云端系统,整体架构参考下图。</p><p><img src="/img/remote/1460000042976280" alt="" title=""></p><p>右侧是君润自研服务平台,DevOps 流程依托 Git webhook、coding、Kubernetes 集群实现全自动化与无感发布,业务系统基于 PaaS 平台进行迭代开发,确保研发规范,并达成技术栈的统一。上侧是监控手段,系统集成了 sky-agent 和 arthas,满足了线上服务观测多维度的需求。与此同时,采用腾讯日志云与 Kubernetes 结合的方式将服务日志存储在云上,实现 Kubernetes 集群内无文件化服务,避免磁盘容量堆积从而影响到 Kubernetes 集群单个节点的健康。</p><p>左侧是业务系统中最重要的环节 —— 流量管理。所有流量会通过 WAF(WAF,Web 应用防火墙,负责网络安全)进入三层网络里面的第一层 CLB,主要负责流量转发;第二层就是云原生网关 APISIX,它承担了业务系统的内部服务治理;第三层则是业务系统的 Gateway 应用内部的网关,负责单系统鉴权和路由。</p><p>其中第一层的 CLB 和第二层的 APISIX 尤其重要,它是所有系统的入口,一旦出现问题,那么所有系统将无法被访问。CLB 采用的是腾讯云服务,稳定性、扩展性与抗并发性能都比较高,业务架构需要解决的是第二层云原生网关 APISIX,保证它的高可用。</p><p>APISIX-Service 被部署在 Kubernetes 集群内部,Kubernetes 集群采用的是腾讯云提供的服务,为了保证出现问题后能够快速恢复,系统外置了 etcd 集群,使数据得以保留,这是 APISIX 集群高可用的体现。</p><p>那么,如何来保证 APISIX 的高并发和高性能?这里系统利用了 Kubernetes 的机制,当 APISIX 进行路由转发时,通过 Kubernetes 的服务名 + 服务端口使它在同一个网段内跳转;因为网关服务部署在 Kubernetes 内部,依托于 Kubernetes 的特性,可以进行滚动升级,进而达到 APISIX 网关升级的无感发布。</p><p>通过该架构图不难发现,第二层是所有流量的入口,选择一个满足业务扩张需求的云原生网关,对系统架构来说至关重要,下面谈谈在网关技术选型时的主要思考。</p><h2>技术网关选型痛点</h2><ul><li><strong>数量庞大的业务系统。</strong>目前人力资源这个领域有 15+ 服务平台,生态业务多样化,面临的问题也比较多,服务请求需要频繁变更,导致需要操作和配置的路由也非常多。原来系统是基于 CLB 进行流量转发,久而久之,运维人员需要配置和操作的地方非常多,耗费了大量的人力与时间。</li><li><strong>频繁的高并发大流量。</strong>举个例子,客户集中在同一天发薪或提现大额资金。在用户数量达到大几十万时,集中进行的某些行为,如打卡、签约、领取任务或工资等,此时系统并发流量非常大,短期翻倍的情况比比皆是。</li><li><strong>个性化需求的扩张压力。</strong>APISIX 是基于 OpenResty、Nginx 和 Lua 开发的,但如果用 Lua 来开发插件,会有一定的研发投入和维护成本。<br>对于插件的支持,APISIX 已经提前做好了准备。APISIX 的官网提供了自定义插件 <code>ext-plugin</code> 来支持 Java 开发,技术栈问题迎刃而解。此外 APISIX 的生态非常好,作为国产网关产品,社区极其活跃,业内实践还特别多,在云原生网关这层来说,业内也是顶级存在。</li></ul><p>我们的团队非常开放,做完技术选型后,快速实践落地。从上线部署到服务分批次接入,耗时不到 1 个月时间。目前 99% 的服务通过 APISIX 访问,上线一年多至今零事故,稳定性非常好。下面这张图里,大家能看到 APISIX 的一些特性,红字部分是我们最看重的几点。</p><p><img src="/img/remote/1460000042976281" alt="" title=""></p><h2>APISIX 四大实践场景</h2><p>下面我们来逐一介绍 APISIX 的四个实践场景。</p><p><img src="/img/remote/1460000042976282" alt="" title=""></p><h3>路由策略</h3><p>Apache APISIX 基于 Radixtree 和 etcd 提供路由极速匹配与配置快速同步的能力。路由和插件的设计实现都满足了极速性能和超低延迟的需求。比如以下两个场景中,都表现出了不错的性能:</p><ul><li><strong>高峰期的 API 紧急停用。</strong>业务系统处在高峰期时,用户导出百万数量级的报表数据,会使 MySQL 数据库直接宕机,此时重启服务也无法解决,用户继续导出操作会持续故障,这个问题以前我们得发版才能解决;而现在只需要运维人员简单配置一番,就可以做到 API 紧急停用,通过<strong>路由优先级策略和失效策略</strong>(依托 Serverless 插件),配合使 API 接口在分钟级下线,从而保证服务的稳定。</li><li><strong>业务系统较多。</strong>其中 SaaS 系统需要支持客户自定义域名访问,我们采用了<strong>泛域名匹配</strong>,做到一次配置,全局通用。<br>下图是君润人力 2022 年度路由增长趋势图。可以看到,不论前端还是后端路由,<strong>在引入 APISIX 之后,都实现了三倍或以上的数量增长</strong>。</li></ul><p><img src="/img/remote/1460000042976283" alt="" title=""></p><h3>安全控制</h3><p>我们基于 APISIX 做了双层网关架构,在 APISIX 上隔离出一个逻辑网关,用户访问 CLB 进来 APISIX 再转发到业务系统。如果用户使用的这个功能需要用到 PaaS,就会通过 PaaS 服务网关进行访问,此时的 PaaS 服务网关就是一个逻辑网关。</p><p><img src="/img/remote/1460000042976284" alt="" title=""></p><p>我们在 Kubernetes 内部封闭了一个区域,即 PaaS 平台,里面包含大量的基础服务。利用 APISIX 网关的特性:熔断、安全、身份识别,使上层业务系统访问 PaaS 服务都需要通过 PaaS 网关。</p><h3>流量管理</h3><p>由于业务系统数量较多, 核心服务(SSO、PaaS 服务和发薪服务)可用性要求高, 这些服务的流量管控需要依赖 APISIX 提供的流量管理灰度策略。</p><p><img src="/img/remote/1460000042976285" alt="" title=""></p><p>为此,系统内部采用了两种方式进行。一是基于标签灰度。核心服务上到生产环境之前,测试人员先在灰度环境验证。我们会将测试用户流量转发到灰度服务,进行生产环境验证,验证通过后再基于权重切入流量,观察一段时间,没问题后再把全部流量切换到新版本;二是生产环境内。相同的服务并存多个版本,不同的业务系统访问不同的版本时,基于标签进行路由转发。</p><h3>日志管理</h3><p>从下图的架构模式可以看出,APISIX 和 Pod 服务都基于 Kubernetes 架构,所有后端路由都被绑定在同一服务上,在 APISIX 服务上配置的 Kafka 插件用来采集日志数据,同时配置了 Skywalking 监控程序性能。根据 RequestId 和 TraceId 在 Skywalking 和日志云,可以观测到整个调用链路,并查看每个环节的日志记录和 API 请求耗时。</p><p><img src="/img/remote/1460000042976286" alt="" title=""></p><p>从目前观测到的数据来看,系统每天都有上千万次的 API 请求,平均每天产生的日志数据达到 30G ,日志总量达到 TB 级。</p><h2>使用 APISIX 的经验与展望</h2><p>在使用 APISIX 的过程中,我们总结了一些经验之谈,在这里分享给对 APISIX 感兴趣的朋友们。</p><p><strong>构建基础镜像需要拉取国外资源。</strong>APISIX 需要部署在 Kubernetes 内部,内部会进行一定的二次开发和源码编译,这时需要到 GitHub 上拉取资源,目前官方提供的 Docker 镜像有一部分需要拉取国外资源,在进行本地开发和线上部署时,环境调试相对麻烦。</p><p><strong>调试环境部署有要求。</strong>自定义插件 <code>java-plugin-runner</code> ,需要基于 runner.sock 进行 RPC 通讯,现有案例较少,调试起来有一定困难,它需要和 APISIX-Service 在同一个镜像内。</p><p><strong>每天产生大量的日志记录到本地。</strong>刚刚发布生产时,我们发现就算只开启 error 级别日志,每天的增长数量都非常大,导致 Kubernetes 集群中一个节点磁盘告警。后面打包镜像时将日志由文件记录改变为输出至控制台,收集至云日志服务 CLS 存储记录分析,实现本地无文件化存储。</p><p>当然,以上都属于前期准备工作上的必经之路,在正式投入使用后,APISIX 给我们带来的收益远远大于期望,总体有以下三点:</p><ol><li><strong>对业务发展起到了强有力的支撑。</strong>使用 APISIX 后,系统功能更加丰富,性能更加强劲。APISIX 对 API 服务提供了多种可观测性和安全防护手段,可以支持我们每天千万次流量的访问。</li><li><strong>助力研发交付效率。</strong>比如原来配置 DNS 解析需要 10 分钟才能生效,而现在通过泛域名配置,几秒钟就能生效;因为原先需要在 CLB 和 Nginx 两个地方手动修改配置,而我们有 10 多个系统、100 多个服务,需要配置的点很多,应用了 APISIX 的泛域名配置后,现在只需要在控制面板上修改,极大地减少 DevOps 工作量。</li><li><strong>大幅降低成本。</strong>LB (负载均衡)成本的变化。LB 服务数量由 200+ 缩减到了 10+,大大降低了系统维护成本。<br>后期我们还会有一系列需要借助 APISIX 云原生网关达成的功能开发包括但不限于:集成 Sentinel 使服务具备热插拔动态限流功能、开发多维度流量控制、风控识别功能升级、分层治理和全链路日志分析等等。届时将采用多套 APISIX 集群来满足自研服务平台的功能需求。</li></ol><p>总结下来,使用 APISIX 云原生网关给君润人力服务平台带来了非常大的帮助,使我们能轻松应对多样化的复杂场景,打造趋于完美的数字化人力资源服务生态。</p>
译文 | A poor man's API
https://segmentfault.com/a/1190000042955947
2022-12-02T10:14:21+08:00
2022-12-02T10:14:21+08:00
API7_技术团队
https://segmentfault.com/u/api7
2
<blockquote>作者:Nicolas Fränkel<br>翻译:Sylvia<br><a href="https://link.segmentfault.com/?enc=xfLx2A1NGxNKfDdtXfda3g%3D%3D.3lhMVF%2BCZBL2B0kZsnq0xP2BZSl4s0gk4BbtaVpgUGFSavqRTYnT%2F9UZRYwIshmM" rel="nofollow">https://blog.frankel.ch/poor-man-api/</a></blockquote><p>在 API 日渐流行的年代,越来越多的非技术人员也希望能从 API 的使用中获利,而创建一套成熟的 API 方案需要时间成本和金钱两方面的资源加持。在这个过程中,你需要考虑模型、设计、REST 原则等,而不仅仅是编写一行代码。</p><p>如何打造一个具有高性价比且能持续迭代的产品,成为越来越多技术团队的目标。<strong>本文将展示如何在不编写任何代码的情况下,简单实现一个 API 实践。</strong></p><h2>方案初试</h2><p>该解决方案主要使用的是 PostgreSQL 数据库,PostgreSQL 是一个开源 SQL 数据库。同时我们没有编写 REST API,而是使用了 PostgREST 组件。</p><p><a href="https://link.segmentfault.com/?enc=h4GwMO9ON6DzasjE1MkXag%3D%3D.oGxYYxfBys7SfGuctcwcMNoDQPubqCyWzFLncpaMt0U%3D" rel="nofollow">PostgREST</a> 是一个独立的 Web 服务器,它可以将 PostgreSQL 数据库直接转换为 RESTful API。如果你想了解 PostgREST 的使用方法,可以参考<a href="https://link.segmentfault.com/?enc=NFFFAQ%2BfYrf74H7EgRqTwA%3D%3D.SH5fjE7eg%2FEtKEVLTOQqxWN%2BQdJN1Jqht3YFT0Q1Tmzrc11Gh2zxpIEg5xbSi4ExQIZf0oHyt%2BP%2Bc3yFE1eiRA%3D%3D" rel="nofollow">入门指南</a>文档,内容非常全面且开箱即用。</p><p>接下来,我们将它应用到一个简单的示例中。</p><h3>具体步骤</h3><blockquote>以下过程你可以在 <a href="https://link.segmentfault.com/?enc=XOeJtOZy3Qaky%2FCNVveJLw%3D%3D.bTkYkhjprYpmwnlelFYfjzHeaCQnNHgOU7fRRJ%2B9UxZL3NpXVQ1kaooz706soBd8" rel="nofollow">GitHub</a> 上找到完整源代码。<br>下方展示了一个通过 CRUD API 公开的 product 表。</blockquote><p><img src="/img/bVc4oX0" alt="" title=""></p><p>由于我没有找到任何现成的 Docker 镜像,所以我单独创建了一份新的 Dockerfile。其中主要涉及依赖项的安装和参数化数据生成。</p><p><strong><em>Dockerfile</em></strong></p><pre><code class="dockerfile">FROM debian:bookworm-slim
ARG POSTGREST_VERSION=v10.1.1
ARG POSTGREST_FILE=postgrest-$POSTGREST_VERSION-linux-static-x64.tar.xz
RUN mkdir postgrest
WORKDIR postgrest
ADD https://github.com/PostgREST/postgrest/releases/download/$POSTGREST_VERSION/$POSTGREST_FILE \
.
RUN apt-get update && \
apt-get install -y libpq-dev xz-utils && \
tar xvf $POSTGREST_FILE && \
rm $POSTGREST_FILE</code></pre><p>之后,Docker 镜像在 <code>/postgrest</code> 文件夹中会包含一个名为 <code>postgrest</code> 的可执行文件。这里可以通过 Docker Compose 来部署:</p><p><strong><em>docker-compose.yml</em></strong></p><pre><code class="yaml">version: "3"
services:
postgrest:
build: ./postgrest
volumes:
- ./postgrest/product.conf:/etc/product.conf:ro
ports:
- "3000:3000"
entrypoint: ["/postgrest/postgrest"]
command: ["/etc/product.conf"]
depends_on:
- postgres
postgres:
image: postgres:15-alpine
environment:
POSTGRES_PASSWORD: "root"
volumes:
- ./postgres:/docker-entrypoint-initdb.d:ro</code></pre><p>接下来可以执行以下命令,查询前文提到的 <code>product</code> 表:</p><pre><code class="shell">curl localhost:3000/product</code></pre><p>得到如下结果反馈:</p><pre><code class="json">[{"id":1,"name":"Stickers pack","description":"A pack of rad stickers to display on your laptop or wherever you feel like. Show your love for Apache APISIX","price":0.49,"hero":false},
{"id":2,"name":"Lapel pin","description":"With this \"Powered by Apache APISIX\" lapel pin, support your favorite API Gateway and let everybody know about it.","price":1.49,"hero":false},
{"id":3,"name":"Tee-Shirt","description":"The classic geek product! At a conference, at home, at work, this tee-shirt will be your best friend.","price":9.99,"hero":true}]</code></pre><h2>方案优化</h2><p>尽管上文提到的这套解决方案有效,但仍存在很大的改进空间。比如数据库用户不能更改数据、实际操作中每个人都可以访问相关数据等。这对于与产品相关的数据来说,可能不是一个大问题,但如果是医疗数据呢?</p><p>PostgREST 的官网<a href="https://link.segmentfault.com/?enc=BmZmzr6ZchOqT8Ow57EezQ%3D%3D.Jf6kFVBbFHM5IeJ5ge%2FdbdJpbjDT4qkrhEstbIv8dthLmq4qiRkMqRMxv3drK9%2Bh" rel="nofollow">使用文档</a>中提到了这一点,并明确提出:建议用户使用反向代理。</p><p>提到反向代理,就不得不将目光转向到 API 网关行列。与 NGINX 不同,这里我选取了<strong>开源领域非常活跃的 API 网关产品 — Apache APISIX</strong>。APISIX 是一个动态、实时、高性能的 API 网关,提供了负载均衡、动态上游、灰度发布、精细化路由、限流限速、服务降级、服务熔断、身份认证、可观测性等数百项功能。</p><p>首先,我们可以在 Docker Compose 文件中补充 APISIX 相关信息,包括 APISIX 及其依赖的存储 etcd,而 etcd 主要用于存储 APISIX 的路由、插件等配置信息。</p><p><strong><em>docker-compose.yml</em></strong></p><pre><code class="yaml">version: "3"
services:
apisix:
image: apache/apisix:2.15.0-alpine
volumes:
- ./apisix/config.yml:/usr/local/apisix/conf/config.yaml:ro
ports:
- "9080:9080"
restart: always
depends_on:
- etcd
- postgrest
etcd:
image: bitnami/etcd:3.5.2
environment:
ETCD_ENABLE_V2: "true"
ALLOW_NONE_AUTHENTICATION: "yes"
ETCD_ADVERTISE_CLIENT_URLS: "http://0.0.0.0:2397"
ETCD_LISTEN_CLIENT_URLS: "http://0.0.0.0:2397"</code></pre><p>然后将 APISIX 配置为 <code>postgrest</code> 的代理进行调用。</p><pre><code class="shell">curl http://apisix:9080/apisix/admin/upstreams/1 -H 'X-API-KEY: 123xyz' -X PUT -d '
{
"type": "roundrobin",
"nodes": {
"postgrest:3000": 1
}
}'
curl http://apisix:9080/apisix/admin/routes/1 -H 'X-API-KEY: 123xyz' -X PUT -d '
{
"uri": "/*",
"upstream_id": 1
}</code></pre><p>现在再来查询端点,会得到与上文一致的返回结果。</p><pre><code class="shell">curl localhost:9080/product</code></pre><h2>添砖加瓦</h2><p>虽然到目前为止,我们还没有添加任何实际项目,但准备工作已经全部就绪了。接下来就让我们为这个 API 添加一些其他功能,让其更安全有效,易于追踪。</p><h3>DDoS 保护</h3><p>API 作为一个连接属性的组件,必然要保证其过程中的传输安全。因此,在这里我们对 API 增加一些防护,让其免受 DDoS 攻击。APISIX 提供了非常多的官方插件,涉及鉴权、流量处理、监控等等。为了防止 DDoS,我们可以使用 APISIX 的 <code>limit-count</code> 插件。</p><p>在 APISIX 中使用插件时,你可以在创建特定路由时在每个路由上设置某个插件。如果你想让某个插件在每个路由上都生效,则可以使用全局规则。如下方所示,我们希望在默认情况下可以保护每个路由,所以使用全局规则设定 <code>limit-count</code> 插件。</p><pre><code class="shell">curl http://apisix:9080/apisix/admin/global_rules/1 -H 'X-API-KEY: 123xyz' -X PUT -d '
{
"plugins": {
"limit-count": {
"count": 1,
"time_window": 5,
"rejected_code": 429
}
}
}'</code></pre><p>现在,如果我们执行太多的请求,APISIX 将会保护上游。</p><pre><code class="shell">curl localhost:9080/product</code></pre><pre><code class="xml"><html>
<head><title>429 Too Many Requests</title></head>
<body>
<center><h1>429 Too Many Requests</h1></center>
<hr><center>openresty</center>
</body>
</html></code></pre><h3>增加鉴权</h3><p>PostgREST 还在根端提供了一个 OpenAPI endpoint。因此,我们现在有两条路由: <code>/</code> (控制 Open API 规范)和 <code>/product</code> (控制产品)。</p><p>假设我们现在需要制定一套限制访问的操作,即不允许未经授权的人访问数据。普通用户可以访问产品端信息,而管理员用户可以访问 Open API 规范和产品端信息。</p><p>APISIX 提供了几种<a href="https://link.segmentfault.com/?enc=OcxhmBWd4jOgsU%2Bu3OWnJQ%3D%3D.M7I1qWcsWL%2FKk01qMDuZz0euM76EyocmPsN6pDYG4a1IkGd4BwWBPWI5%2B3Fw8dGyVfOZ4IXV1nMZU98UyVK1cw%3D%3D" rel="nofollow">身份验证方法</a>,这些身份认证方式都可以通过插件进行实现。这里我们选取 APISIX 中最常用也是最简单的认证插件 <a href="https://link.segmentfault.com/?enc=KvpqiohFPdwjEETO%2FnICdQ%3D%3D.Jr7D0yQ8SHQPBzNe5ibh%2BFZZG2BXzixpIP6GYx6H16SWt4oq4tAT5DVG2DcZbFty44Xogh4up3PDzOCqRlR2Ag%3D%3D" rel="nofollow">key-auth</a>,它依赖于 <a href="https://link.segmentfault.com/?enc=vl9yYtXVtrNuJaGYrGLtaA%3D%3D.9sPFMs88xbw3z1YQT88AFQlO7QdKSMP%2BTu6roxWTvx1sIeAL2yGwltaaF72WN6Lqh7LWXy1NaJ3dM715ZcfxOw%3D%3D" rel="nofollow">Consumer</a>(消费者)抽象。 <code>key-auth</code> 插件的使用中需要一个特定的 header,这样插件就可以根据值数据进行反向查找,并找到其对应的 Consumer。</p><p>以下代码展示了如何新建一个 Consumer:</p><pre><code class="shell">curl http://apisix:9080/apisix/admin/consumers -H 'X-API-KEY: 123xyz' -X PUT -d '
{
"username": "admin",
"plugins": {
"key-auth": {
"key": "admin"
}
}
}'</code></pre><p>同样的,我们需要对 Consumer <code>user</code> 和 Key <code>user</code> 进行相关操作。现在可以创建一个专用路由来配置它们,以便只有来自 <code>admin</code> 的请求才能通过:</p><pre><code class="shell">curl http://apisix:9080/apisix/admin/routes -H 'X-API-KEY: 123xyz' -X POST -d '
{
"uri": "/",
"upstream_id": 1,
"plugins": {
"key-auth": {},
"consumer-restriction": {
"whitelist": [ "admin" ]
}
}
}'</code></pre><p>然后使用以下命令测试一下:</p><pre><code class="shell">curl localhost:9080</code></pre><p>发现并没有起作用。这是因为我们没有通过 API 密钥的 header 进行身份验证。</p><pre><code class="shell">{"message":"Missing API key found in request"}</code></pre><p>添加 header 后再次进行测试:</p><pre><code class="shell">curl -H "apikey: user" localhost:9080
{"message":"The consumer_name is forbidden."}</code></pre><p>发现仍然没有效果。这是因为这里 API key 为 <code>user</code> ,我们前边仅为 <code>admin</code> 也就是管理员设置了相关权限。所以如果更换为 <code>admin</code> ,就会如期返回 Open API 规范的相关信息。</p><h3>配置监控</h3><p>在软件系统中,总有一个被人们低估重要性的功能 —— <strong>可观测性</strong>。在生产环境中部署了任何组件,都需要监控其运行状况。</p><p>如今,很多服务都提供了可观测性的功能,比如 <a href="https://link.segmentfault.com/?enc=WCUFBq9VXf5FPPBn52hDig%3D%3D.8HwIAuxODc3Y2GJ5Ml%2FP2ezOC5tL2RLagcxqVsKWEKg%3D" rel="nofollow">Prometheus</a>。得益于 Prometheus 的开源属性,它被广泛应用于实践中。因此,这里我们也选用 Prometheus 进行相关数据的监控。</p><p>为了通过图表等形式显示数据,我们也同时需要依赖于 <a href="https://link.segmentfault.com/?enc=qxJyyDLyoeyVhIfwp0qGTg%3D%3D.I9mnR0PKoFuQURrL5QSlcRrmBXBFs8h7K0VQWjDgnUQ%3D" rel="nofollow">Grafana</a>。接下来,将这些组件添加到 Docker Compose 文件中。</p><p><strong><em>docker-compose.yml</em></strong></p><pre><code class="yaml">version: "3"
services:
prometheus:
image: prom/prometheus:v2.40.1
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
depends_on:
- apisix
grafana:
image: grafana/grafana:8.5.15
volumes:
- ./grafana/provisioning:/etc/grafana/provisioning
- ./grafana/dashboards:/var/lib/grafana/dashboards
- ./grafana/config/grafana.ini:/etc/grafana/grafana.ini
ports:
- "3001:3000"
depends_on:
- prometheus</code></pre><p>以上操作需注意:APISIX 的默认监控方案中自带 Grafana,因此只需从 APISIX 中获取<a href="https://link.segmentfault.com/?enc=Gbmjalc%2ByzarKkJpApMEiA%3D%3D.S0qe2%2BxOaw3B1zsnD4Md2eDBx0QQpDmbTwRaf9VGTToIBuYx%2Bup%2FURO2PneDYH3dpTSGA%2Bq8yAbBVj736WWOKmyB%2BSsNem14dL6Gx9z77Wyuu47YWgb5aH%2BWEDMRxzUujefSpVW3MoR8L8pYWZg76w%3D%3D" rel="nofollow">相关配置</a> 即可。同时将默认端口从 3000 更改为 3001 是为了避免与 PostgREST 服务发生冲突。</p><p>一旦监控基础设施到位,我们只需要指示 APISIX 以 Prometheus 期望的格式提供数据即可。可以通过配置插件和新的全局规则来实现这一目标:</p><p>config.yaml</p><pre><code class="yaml">plugin_attr:
prometheus:
export_addr:
ip: "0.0.0.0"
port: 9091</code></pre><pre><code class="bash">curl http://apisix:9080/apisix/admin/global_rules/2 -H 'X-API-KEY: 123xyz' -X PUT -d '
{
"plugins": {
"prometheus": {}
}
}'</code></pre><p>此时发送几个查询请求,并打开 Grafana 仪表板,可看到类似数据。如果运行较多请求,则会出现更丰富的数据仪表。</p><p><img src="/img/bVc4oXX" alt="" title=""></p><p><img src="/img/bVc4oXZ" alt="" title=""></p><h2>总结</h2><p>创建一个成熟的 RESTful API 是一项巨大的投资。你可以通过 PostgREST 将数据库暴露在 CRUD API 中来快速测试一个简单的 API。但是,这样的体系结构不适用于实际生产。要想使其更具实践性,就需要在 PostgREST 前设置一个 facade、一个反向代理,或者更好的 API 网关。</p><p>Apache APISIX 作为云原生 API 网关,提供了广泛的特性,从流量处理到认证授权和可观测性等。有了 APISIX,你就可以用较低的成本快速验证你的 API 需求。锦上添花的是,当你验证需求完成之后,还可以保留现有的 facade,并用自定义开发的 API 来替换 PostgREST。</p><h2>关于 API7.ai 与 APISIX</h2><p>API7.ai(<a href="https://link.segmentfault.com/?enc=%2F7GhkNb87daopBF0cY78pA%3D%3D.hETWEcCR2u%2FCnkjrEBG6X4YPggCOajBgXpz0CRnSYpw%3D" rel="nofollow">支流科技</a>)是一家提供 API 处理和分析的开源基础软件公司,于 2019 年开源了新一代云原生 API 网关 -- APISIX 并捐赠给 Apache 软件基金会。此后,API7.ai 一直积极投入支持 Apache APISIX 的开发、维护和社区运营。与千万贡献者、使用者、支持者一起做出世界级的开源项目,是 API7.ai 努力的目标。</p>
APISIX Ingress 是如何支持上千个 Pod 副本的应用
https://segmentfault.com/a/1190000042916721
2022-11-28T16:04:12+08:00
2022-11-28T16:04:12+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<blockquote>作者:容鑫,Apache APISIX Committer</blockquote><h2>在 K8s 中为什么会遇到上千个 Pod 副本的应用场景?</h2><p>在 Kubernetes 中,Pod 是最小的调度单元。应用程序实际是以 Pod 在运行的,通常情况下出于可扩展性和降低爆炸半径等方面的考虑,只会给 Pod 设置有限的资源。那么对于大流量的场景,一般都是通过水平扩容的方式进行应对。</p><p>例如电商行业在进行促销活动或秒杀抢购活动时,业务流量相对较大。为了应对这种场景,通常会设置弹性扩容。在活动进行时,服务会进行弹性伸缩直到能够承载流量,这时会基于弹性扩容的策略,为业务增加副本数,也就是 Pod 会变多。</p><p>每个 Pod 都有各自唯一的 IP ,但同时 Pod 的 IP 也不是固定的。为了及时追踪 Pod IP 的变化,从而进行负载均衡,Endpoints API 提供了在 Kubernetes 中跟踪网络端点的一种简单而直接的方法。<br>但随着 Kubernetes 集群和服务逐渐开始为更多的后端 Pod 进行处理和发送请求,比如上文提到大流量场景下,Pod 数量会被不断扩容,Endpoints API 也将变得越大。这种情况下,Endpoints API 局限性变得越来越明显,甚至成为性能瓶颈。</p><p>为了解决这个局限性问题,在 Kubernetes v1.21 的版本中引入了对 Endpointslice API 的支持,解决了 Endpoints API 处理大量网络端点带来的性能问题,同时提供了可扩展和可伸缩的能力。<br>通过下图我们可以明显看到它们之间的区别:</p><ul><li>Endpoints 在流量高峰时的变化:<br><img src="/img/bVc4eLe" alt="" title=""></li><li>Endpointslices 在流量高峰时的变化:<br><img src="/img/bVc4eLf" alt="" title=""></li></ul><p>在 Kubernetes 中,应用之间是如何进行相互访问的呢?Endpoints 和 Endpointslice 具体区别又是什么?和 Pod 有着什么样的关系?APISIX Ingress 中为什么要支持这些特性,以及如何进行安装和使用?本文将着重介绍这些问题。</p><h2>Kubernetes 中如何访问应用</h2><p>在 Kubernetes 中,每个 Pod 都有其自己唯一的 IP 地址。通常情况下,Service 通过 selector 和一组 Pod 建立关联,并提供了相同的 DNS 名,并可以在它之间进行负载均衡。Kubernetes 集群内不同应用之间可通过 DNS 进行相互访问。</p><p>在 Service 创建时,Kubernetes 会根据 Service 关联一个 Endpoints 资源,若 Service 没有定义 selector 字段,将不会自动创建 Endpoints。</p><h3>什么是 Endpoints</h3><p>Endpoints 是 Kubernetes 中的一个资源对象,存储在 etcd 中,用来记录一个 Service 对应一组 Pod 的访问地址,一个 Service 只有一个 Endpoints 资源。Endpoints 资源会去观测 Pod 集合,只要服务中的某个 Pod 发生变更,Endpoints 就会进行同步更新。<br>比如部署 3 个 httpbin 副本,查看 Pod 的情况,包括 IP 信息。</p><pre><code class="shell">$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
httpbin-deployment-fdd7d8dfb-8sxxq 1/1 Running 0 49m 10.1.36.133 docker-desktop <none> <none>
httpbin-deployment-fdd7d8dfb-bjw99 1/1 Running 4 (5h39m ago) 23d 10.1.36.125 docker-desktop <none> <none>
httpbin-deployment-fdd7d8dfb-r5nf9 1/1 Running 0 49m 10.1.36.131 docker-desktop <none> <none></code></pre><p>创建 httpbin 服务,并查看应 Endpoints 端点情况。</p><pre><code class="shell">$ kubectl get endpoints httpbin
NAME ENDPOINTS AGE
httpbin 10.1.36.125:80,10.1.36.131:80,10.1.36.133:80 23d</code></pre><p><strong>从上述示例可以看到,Endpoints 中 httpbin 资源对象的所有网络端点,分别对应了每个 Pod 的 IP 地址。</strong></p><p>当然, Endpoints 也有它的一些不足之处,比如:</p><ol><li>Endpoints 具有容量限制,如果某个 Endpoints 资源中端口的个数超过 1000,那么 Endpoints 控制器会将其截断为 1000。</li><li>一个 Service 只有一个 Endpoints 资源,这意味着它需要为支持相应服务的每个 Pod 存储 IP 等网络信息。这导致 Endpoints 资源变的十分巨大,其中一个端点发生了变更,将会导致整个 Endpoints 资源更新。当业务需要进行频繁端点更新时,一个巨大的 API 资源被相互传递,而这会影响到 Kubernetes 组件的性能,并且会产生大量的网络流量和额外的处理。</li></ol><h3>什么是 Endpointslices</h3><p>Endpointslices 为 Endpoints 提供了一种可扩缩和可拓展的替代方案,缓解处理大量网络端点带来的性能问题,还能为一些诸如拓扑路由的额外功能提供一个可扩展的平台。<strong>该特性在 Kubernetes v1.21+ 的版本中已提供支持。</strong></p><p>EndpointSlice 旨在通过分片的方式来解决此问题,并没有使用单个 Endpoints 资源跟踪服务的所有网络端点,而是将它们拆分为多个较小的 EndpointSlice。</p><p>默认情况下,控制面创建和管理的 EndpointSlice 将包含不超过 100 个端点。 你可以使用 <a href="https://link.segmentfault.com/?enc=TGjHbqnzb5DAWtPeRK0GGw%3D%3D.5yDAQd3rBNWPJhh8yFgjcRu9%2BsVwJe2cnPHKnYCIMtuj3fdQOfvGy7l9VUu3%2Bu7XiGVTD10XDL25n8tOYmIGVuK%2B6qrkh4Cm1XW02aA4DcJa7cqCKuVJyK3eXW%2BSPq9owOg36W%2BL22Ey78CyaZbV6Q%3D%3D" rel="nofollow">kube-controller-manager</a> 的 <code>--max-endpoints-per-slice</code> 标志设置此值,其最大值为 1000。</p><h3>为什么需要 Endpointslices</h3><p>首先,我们考虑具有 2000 个 Pod 的服务它最终可能具有 1.0 MB 的 Endpoints 资源。在生产环境中,如果该服务发生滚动更新或节点迁移,那么 Endpoints 资源将会频繁变更。</p><p>想象一下,如果滚动更新会导致全部 Pod 都被替换,<a href="https://link.segmentfault.com/?enc=QS2KTwoW1%2F09kFzNhEOvyQ%3D%3D.SGSSgjii6Zi7ai1HNoVTm2BQEhG7XzGCBH2wdhzxPClTBHLQVXW%2BvDoZCbNZ0r9RMpdPmMeWrv0l%2BY%2BM%2FW%2Ba9Q%3D%3D" rel="nofollow">由于 etcd 具有最大请求大小限制</a>,Kubernetes 对 Endpoints 最大容量限制为 1000,如果网络端点数量超出了 1000,那么多出来的网络端点,将不会被 Endpoints 资源记录。</p><p>当然也可能因为一些需求,需要多次进行滚动更新,那么这个巨大的 API 资源对象将会 Kubernetes 组件中来回传递,极大影响了 Kubernetes 组件的性能。</p><p>如果使用了 Endpointslices,假设一个服务后端有 2000 个 Pod。如果将配置修改为每个 Endpointslices 存储 100 个端点,最终将获得 20 个 Endpointslices。添加或删除 Pod 时,只需要更新其中 1 个 Endpointslice 资源即可,这样操作后,可扩展性和网络可伸缩有了很大的提升。</p><p>比起在流量高峰时,服务为了承载流量,扩容出大量的 Pod,Endpoints 资源会被频繁更新,两个使用场景的差异就变得非常明显。更重要的是,既然服务的所有 Pod IP 都不需要存储在单个资源中,那么我们就不必担心 etcd 中存储的对象的大小限制。</p><h3>Endpoints VS Endpointslice</h3><blockquote>因为 Endpointslice 是在 Kubernetes v1.21+ 的版本得到支持,所以该结论是基于 Kubernetes v1.21+ 版本。</blockquote><p>通过上文的描述我们总结一下两种资源的适用情况。</p><p>Endpoints 适用场景:</p><ul><li>有弹性伸缩需求,Pod 数量较少,传递资源不会造成大量网络流量和额外处理。</li><li>无弹性伸缩需求,Pod 数量不会太多。哪怕 Pod 数量是固定,但是总是要滚动更新或者出现故障的。</li></ul><p>Endpointslice 适用场景:</p><ul><li>有弹性需求,且 Pod 数量较多(几百上千)。</li><li>Pod 数量很多(几百上千),因为 Endpoints 网络端点最大数量限制为 1000,所以超过 1000 的 Pod 必须得用 Endpointslice。</li></ul><h2>在 APISIX Ingress 中的实践</h2><p>APISIX Ingress Controller 是一个 Ingress 控制器的实现。可以将用户配置的规则转换为 Apache APISIX中的规则,从而使用 APISIX 完成具体的流量承载。</p><p>在具体实现过程中,APISIX Ingress 通过 watch Endpoints 或 Endpointslice 资源的变化,从而让 APISIX 能够对 Pod 进行负载均衡和健康检查等。为了能够支持 Kubernetes v1.16+ 的版本,APISIX Ingress 在安装时,默认使用 Endpoints 的特性。</p><p><strong>如果你的集群版本为 Kubernetes v1.21+,在安装 APISIX Ingress 时,需要指定 <code>watchEndpointSlice=true</code> 标志来开启 Endpointslice 特性的支持。</strong></p><blockquote>注意:在集群的版本为 Kubernetes v1.21+ 时,我们推荐使用 Endpointslice 特性。否则当服务中 Pod 数量超过 <code>--max-endpoints-per-slice</code> 标志设定的值时,由于 APISIX Ingress watch 的是 Endpoints 资源对象,则会导致配置的丢失。开启此特性,就不额外关注 <code>--max-endpoints-per-slice</code> 的值。</blockquote><p>以下将为你简单介绍如何在 APISIX Ingress 中应用 Endpointslice 特性。</p><h3>创建包含 20 个副本的 Service</h3><p>使用 <code>kubectl apply -f httpbin-deploy.yaml</code> 命令,在 Kuernetes 中部署 httpbin 应用服务,并创建 20 个 Pod 副本。具体 <code>htppbin-deploy.yaml</code> 文件配置可参考下方代码。</p><pre><code class="yaml"># htppbin-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpbin-deployment
spec:
replicas: 20
selector:
matchLabels:
app: httpbin-deployment
strategy:
rollingUpdate:
maxSurge: 50%
maxUnavailable: 1
type: RollingUpdate
template:
metadata:
labels:
app: httpbin-deployment
spec:
terminationGracePeriodSeconds: 0
containers:
- livenessProbe:
failureThreshold: 3
initialDelaySeconds: 2
periodSeconds: 5
successThreshold: 1
tcpSocket:
port: 80
timeoutSeconds: 2
readinessProbe:
failureThreshold: 3
initialDelaySeconds: 2
periodSeconds: 5
successThreshold: 1
tcpSocket:
port: 80
timeoutSeconds: 2
image: "kennethreitz/httpbin:latest"
imagePullPolicy: IfNotPresent
name: httpbin-deployment
ports:
- containerPort: 80
name: "http"
protocol: "TCP"
---
apiVersion: v1
kind: Service
metadata:
name: httpbin
spec:
selector:
app: httpbin-deployment
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
type: ClusterIP</code></pre><h3>通过 APISIX Ingress 代理</h3><p>第一步,使用 Helm 安装 APISIX Ingress。<br>通过 ·--set ingress-controller.config.kubernetes.watchEndpointSlice=true` 开启 Endpointslice 特性的支持。</p><pre><code class="shell">helm repo add apisix https://charts.apiseven.com
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
kubectl create ns ingress-apisix
helm install apisix apisix/apisix \
--set gateway.type=NodePort \
--set ingress-controller.enabled=true \
--namespace ingress-apisix \
--set ingress-controller.config.apisix.serviceNamespace=ingress-apisix \
--set ingress-controller.config.kubernetes.watchEndpointSlice=true </code></pre><p>第二步,使用 CRD 资源进行代理。注意,APISIX Ingress 中对 Endpoints 和 Endpointslice 特性的支持,使用者是感知不到的,它们的配置都是一致的。</p><pre><code class="yaml">apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
name: httpbin-route
spec:
http:
- name: rule
match:
hosts:
- httpbin.org
paths:
- /get
backends:
- serviceName: httpbin
servicePort: 80</code></pre><p>第三步,进入 APISIX Pod 中查看。可以看到在 APISIX 中的 upstream 对象 nodes 字段包含了 20 个 Pod 的 IP 地址。</p><pre><code class="shell">kubectl exec -it ${Pod for APISIX} -n ingress-apisix -- curl "http://127.0.0.1:9180/apisix/admin/upstreams" -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1'</code></pre><pre><code class="json">{
"action": "get",
"count": 1,
"node": {
"key": "\/apisix\/upstreams",
"nodes": [
{
"value": {
"hash_on": "vars",
"desc": "Created by apisix-ingress-controller, DO NOT modify it manually",
"pass_host": "pass",
"nodes": [
{
"weight": 100,
"host": "10.1.36.100",
"priority": 0,
"port": 80
},
{
"weight": 100,
"host": "10.1.36.101",
"priority": 0,
"port": 80
},
{
"weight": 100,
"host": "10.1.36.102",
"priority": 0,
"port": 80
},
{
"weight": 100,
"host": "10.1.36.103",
"priority": 0,
"port": 80
},
{
"weight": 100,
"host": "10.1.36.104",
"priority": 0,
"port": 80
},
{
"weight": 100,
"host": "10.1.36.109",
"priority": 0,
"port": 80
},
{
"weight": 100,
"host": "10.1.36.92",
"priority": 0,
"port": 80
}
... // 以下省略 13 个节点
// 10.1.36.118
// 10.1.36.115
// 10.1.36.116
// 10.1.36.106
// 10.1.36.113
// 10.1.36.111
// 10.1.36.108
// 10.1.36.114
// 10.1.36.107
// 10.1.36.110
// 10.1.36.105
// 10.1.36.112
// 10.1.36.117
],
"labels": {
"managed-by": "apisix-ingress-controller"
},
"type": "roundrobin",
"name": "default_httpbin_80",
"scheme": "http"
},
"key": "\/apisix\/upstreams\/5ce57b8e"
}
],
"dir": true
}
}</code></pre><p>上述信息与 Endpointslice 中的网络端点相对应。</p><pre><code class="yaml">addressType: IPv4
apiVersion: discovery.k8s.io/v1
endpoints:
- addresses:
- 10.1.36.92
...
- addresses:
- 10.1.36.100
...
- addresses:
- 10.1.36.104
...
- addresses:
- 10.1.36.102
...
- addresses:
- 10.1.36.101
...
- addresses:
- 10.1.36.103
...
- addresses:
- 10.1.36.109
...
- addresses:
- 10.1.36.118
...
- addresses:
- 10.1.36.115
...
- addresses:
- 10.1.36.116
...
- addresses:
- 10.1.36.106
...
- addresses:
- 10.1.36.113
...
- addresses:
- 10.1.36.111
...
- addresses:
- 10.1.36.108
...
- addresses:
- 10.1.36.114
...
- addresses:
- 10.1.36.107
...
- addresses:
- 10.1.36.110
...
- addresses:
- 10.1.36.105
...
- addresses:
- 10.1.36.112
...
- addresses:
- 10.1.36.117
...
kind: EndpointSlice
metadata:
labels:
endpointslice.kubernetes.io/managed-by: endpointslice-controller.k8s.io
kubernetes.io/service-name: httpbin
name: httpbin-dkvtr
namespace: default
ports:
- name: http
port: 80
protocol: TCP</code></pre><h2>总结</h2><p>本文介绍了 Kubernetes 需要部署大量 Pod 的场景和遇到的问题,对比了 Endpoints 和 Endpointslice 之间区别,以及如何安装 APISIX Ingress 来使用 Endpointslice 的特性。</p><p>如果你的集群版本为 Kubernetes v1.21+,推荐在安装 APISIX Ingress 时开启 Endpointslice 特性支持,这样就不用关注 <code>--max-endpoints-per-slice</code> 标志设定的值,从而避免配置丢失。</p>
为什么 NGINX 的 reload 不是热加载?
https://segmentfault.com/a/1190000042898363
2022-11-25T10:34:32+08:00
2022-11-25T10:34:32+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<blockquote>作者 刘维</blockquote><p>这段时间在 Reddit 看到一个讨论,为什么 NGINX 不支持热加载?乍看之下很反常识,作为世界第一大 Web 服务器,不支持热加载?难道大家都在使用的 <code>nginx -s reload</code> 命令都用错了? 带着这个疑问,让我们开始这次探索之旅,一起聊聊热加载和 NGINX 的故事。</p><h2>NGINX 相关介绍</h2><p>NGINX 是一个跨平台的开源 Web 服务器,使用 C 语言开发。据统计,全世界流量最高的前 1000 名网站中,有超过 40% 的网站都在使用 NGINX 处理海量请求。</p><p>NGINX 有什么优势,导致它从众多的 Web 服务器中脱颖而出,并一直保持高使用量呢?</p><p>我觉得核心原因在于,NGINX 天生善于处理高并发,能在高并发请求的同时保持高效的服务。相比于同时代的其他竞争对手例如 Apache、Tomcat 等,其领先的事件驱动型设计和全异步的网络 I/O 处理机制,以及极致的内存分配管理等众多优秀设计,将服务器硬件资源压缩到了极致。使得 NGINX 成为高性能 Web 服务器的代名词。</p><p>当然,除此之外还有一些其他原因,比如:</p><ul><li>高度模块化的设计,使得 NGINX 拥有无数个功能丰富的官方模块和第三方拓展模块。</li><li>最自由的 BSD 许可协议,使得无数开发者愿意为 NGINX 贡献自己的想法。</li><li>支持热加载,能保证 NGINX 提供 7x24h 不间断的服务。</li></ul><h2>关于热加载</h2><p>大家期望的热加载功能是什么样的?我个人认为,首先应该是用户端无感知的,在保证用户请求正常和连接不断的情况下,实现服务端或上游的动态更新。</p><p>那什么情况下需要热加载?在如今云原生时代下,微服务架构盛行,越来越多的应用场景有了更加频繁的服务变更需求。包括反向代理域名上下线、上游地址变更、IP 黑白名单更新等,这些都和热加载息息相关。</p><p>那么 NGINX 是如何实现热加载的?</p><h2>NGINX 热加载的原理</h2><p>执行 <code>nginx -s reload</code> 热加载命令,就等同于向 NGINX 的 master 进程发送 HUP 信号。在 master 进程收到 HUP 信号后,会依次打开新的监听端口,然后启动新的 worker 进程。</p><p>此时会存在新旧两套 worker 进程,在新的 worker 进程起来后,master 会向老的 worker 进程发送 QUIT 信号进行优雅关闭。老的 worker 进程收到 QUIT 信号后,会首先关闭监听句柄,此时新的连接就只会流进到新的 worker 进程中,老的 worker 进程处理完当前连接后就会结束进程。</p><p><img src="/img/bVc39Zc" alt="" title=""></p><p>从原理上看,NGINX 的热加载能很好地满足我们的需求吗?答案很可能是否定的,让我们来看下 NGINX 的热加载存在哪些问题。</p><h2>NGINX 热加载的缺陷</h2><p><strong>首先,NGINX 频繁热加载会造成连接不稳定,增加丢失业务的可能性。</strong></p><p>NGINX 在执行 reload 指令时,会在旧的 worker 进程上处理已经存在的连接,处理完连接上的当前请求后,会主动断开连接。此时如果客户端没处理好,就可能会丢失业务,这对于客户端来说明显就不是无感知的了。</p><p><strong>其次,在某些场景下,旧进程回收时间长,进而影响正常业务。</strong></p><p>比如代理 WebSocket 协议时,由于 NGINX 不解析通讯帧,所以无法知道该请求是否为已处理完毕状态。即使 worker 进程收到来自 master 的退出指令,它也无法立刻退出,而是需要等到这些连接出现异常、超时或者某一端主动断开后,才能正常退出。</p><p>再比如 NGINX 做 TCP 层和 UDP 层的反向代理时,它也没法知道一个请求究竟要经过多少次请求才算真正地结束。</p><p>这就导致旧 worker 进程的回收时间特别长,尤其是在直播、新闻媒体活语音识别等行业。旧 worker 进程的回收时间通常能达到半小时甚至更长,这时如果再频繁 reload,将会导致 shutting down 进程持续增加,最终甚至会导致 NGINX OOM,严重影响业务。</p><pre><code class="plain"># 一直存在旧 worker 进程:
nobody 6246 6241 0 10:51 ? 00:00:00 nginx: worker process
nobody 6247 6241 0 10:51 ? 00:00:00 nginx: worker process
nobody 6247 6241 0 10:51 ? 00:00:00 nginx: worker process
nobody 6248 6241 0 10:51 ? 00:00:00 nginx: worker process
nobody 6249 6241 0 10:51 ? 00:00:00 nginx: worker process
nobody 7995 10419 0 10:30 ? 00:20:37 nginx: worker process is shutting down <= here
nobody 7995 10419 0 10:30 ? 00:20:37 nginx: worker process is shutting down
nobody 7996 10419 0 10:30 ? 00:20:37 nginx: worker process is shutting down </code></pre><p>从上述内容可以看到,通过<code>nginx -s reload</code>方式支持的“热加载”,虽然在以往的技术场景中够用,但是在微服务和云原生迅速发展的今天,它已经捉襟见肘且不合时宜。<br>如果你的业务变更频率是每周或者每天,那么 NGINX 这种 reload 还是满足你的需求的。但如果变更频率是每小时、每分钟呢?假设你有 100 个 NGINX 服务,每小时 reload 一次的话,就要 reload 2400 次;如果每分钟 reload 一次,就是 864 万次。这显然是无法接受的。 </p><p>因此,我们需要一个不需要进程替换的 reload 方案,在现有 NGINX 进程内可以直接完成内容的更新和实时生效。</p><h2>在内存中直接生效的热加载方案</h2><p>在 Apache APISIX 诞生之初,就是希望来解决 NGINX 热加载这个问题的。</p><p>Apache APISIX 是基于 NGINX + Lua 的技术栈,以 ETCD 作为配置中心实现的云原生、高性能、全动态的微服务 API 网关,提供负载均衡、动态上游、灰度发布、精细化路由、限流限速、服务降级、服务熔断、身份认证、可观测性等数百项功能。</p><p>使用 APISIX 你不需要重启服务就可以更新配置,这意味着修改上游、路由、插件时都不用重启。既然是基于 NGINX,APISIX 又是如何摆脱 NGINX 的限制实现完美热更新?我们先看下 APISIX 的架构。</p><p><img src="/img/bVc39Zd" alt="" title=""></p><p>通过上述架构图可以看到,之所以 APISIX 能摆脱 NGINX 的限制是因为它把上游等配置全部放到 APISIX Core 和 Plugin Runtime 中动态指定。</p><p>以路由为例,NGINX 需要在配置文件内进行配置,每次更改都需要 reload 之后才能生效。而为了实现路由动态配置,Apache APISIX 在 NGINX 配置文件内配置了单个 server,这个 server 中只有一个 location。我们把这个 location 作为主入口,所有的请求都会经过这个 location,再由 APISIX Core 动态指定具体上游。因此 Apache APISIX 的路由模块支持在运行时增减、修改和删除路由,实现了动态加载。所有的这些变化,对客户端都零感知,没有任何影响。</p><p>再来几个典型场景的描述。</p><p>比如增加某个新域名的反向代理,在 APISIX 中只需创建上游,并添加新的路由即可,整个过程中不需要 NGINX 进程有任何重启。再比如插件系统,APISIX 可以通过 ip-restriction 插件实现 IP 黑白名单功能,这些能力的更新也是动态方式,同样不需要重启服务。借助架构内的 ETCD,配置策略以增量方式实时推送,最终让所有规则实时、动态的生效,为用户带来极致体验。</p><h2>总结</h2><p>NGINX 的热加载在某些场景下会长时间存在新旧两套进程,导致额外消耗资源,同时频繁热加载也会导致小概率业务丢失。 面对当下云原生和微服务的技术趋势下, 服务变化更加的频繁,控制 API 的策略也发生了变化,导致我们对热加载的需求提出了新需求,NGINX 的热加载已经不能满足实际业务需求。</p><p>现在是时候切换到更贴合云原生时代并且更完善的热加载策略、性能表现卓越的 API 网关——Apache APISIX,从而享受动态、统一管理等特性带来的管理效率上的极大提升。</p>
最新出炉!开源 API 网关的性能对比:APISIX 3.0 和 Kong 3.0
https://segmentfault.com/a/1190000042838576
2022-11-17T15:42:26+08:00
2022-11-17T15:42:26+08:00
API7_技术团队
https://segmentfault.com/u/api7
1
<h2>背景</h2><p>云原生时代下,企业逐渐向云上迁移,越来越多的应用和服务都在进行容器化改造,服务之间的流量也开始爆发性的增长。为了能高效地管理这些规模庞大的 API,API 网关开始在技术领域大展身手。</p><p>用户除了需要 API 网关提供请求代理、熔断限流、审计监控等常规能力外,更多开始关注云原生兼容性、支撑场景的多样性,以及更好的性能及稳定性。在这样的背景下,以 Apache APISIX 和 Kong 等为代表的云原生 API 网关项目得到了越来越多开发者的青睐。</p><p>Apache APISIX 是一个云原生、高性能、可扩展的 API 网关,由深圳支流科技捐赠给 Apache 基金会,并于 2020 年 7 月从 Apache 孵化器毕业, 成为 Apache 软件基金会顶级项目。APISIX 基于 NGINX 和 etcd 来实现,和传统 API 网关相比,APISIX 具备动态路由和插件热加载,特别适合云原生架构下的 API 管理。</p><p><img src="/img/bVcTK30" alt="" title=""></p><p>Kong 也是一款高可用、易扩展的开源 API 网关项目。通过提供代理、路由、负载均衡、身份验证等功能,在微服务与传统 API <br>领域提供网关层面的支持。</p><p>2022 年秋季,Kong 与 Apache APISIX 相继发布了最新的 3.0 版本。其中,Apache APISIX 3.0 重点在生态和架构层面进行了创新与迭代,致力让所有用户都能利用 APISIX 发挥更优秀的价值。Kong 3.0 则在新版本中更加侧重政府、金融业以及对安全合规更关注的大型企业,整体涉及在合规、易用性、功能与性能等方面进行了拓展。</p><p>作为开源微服务网关领域的优秀作品,在二者几乎同一时间发布 3.0 版本之际,我们对两个产品进行了一次性能测试,方便读者在选择和使用这两个网关产品时,对其最新版本的性能表现上有更加清晰的认知。</p><h2>测试环境与方式</h2><p>以下为本次进行测试的方式及环境数据,测试结果仅针对以下环境、机器及特定版本等。<br>同时本次测试使用 Docker 部署 APISIX 和 Kong 时,将使用 Docker 的 host 网络模式,避免网络原因影响测试结果。以下为其他相关测试配置信息。</p><h3>请求拓扑图</h3><p>以下是测试链路的拓扑图,压力测试工具使用 wrk2,上游服务使用 OpenResty。</p><p><img src="/img/remote/1460000042838578" alt="在这里插入图片描述" title="在这里插入图片描述"></p><h3>相关服务器与软件信息</h3><p>本次测试将在云服务器上进行,服务器配置为 Standard D8s v3 (8 核心虚拟 CPU,32 GiB 内存) 。所有测试相关组件均部署在这台服务器上,具体服务器环境信息如下表所示。</p><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td><strong>服务器环境信息</strong></td></tr><tr><td><strong>名称</strong></td><td><strong>配置</strong></td></tr><tr><td>os version</td><td>Debian 10 Buster</td></tr><tr><td>ulimit -n</td><td>65535</td></tr></tbody></table><p>测试中所涉及到的软件版本信息如下表所示。</p><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td><strong>软件名称</strong></td><td><strong>版本信息</strong></td></tr><tr><td>Docker</td><td>20.10.18, build b40c2f6</td></tr><tr><td>APISIX</td><td>3.0.0</td></tr><tr><td>Kong</td><td>3.0.0</td></tr><tr><td>Upstream</td><td>OpenResty 1.21.4.1</td></tr><tr><td>Test tool</td><td>wrk2</td></tr></tbody></table><h2>部署细节</h2><p>我们选择 wrk2 作为性能测试工具,选择 OpenResty 作为模拟上游。用 Docker 来部署 APISIX 与 Kong,并且都启用二者的声明式配置。</p><p>在测试时,只开启一个 1 个 worker 进程,这样测试结果会比较直观。正常来说,在实际使用中多个 worker 的负载能力相比 1 个 worker 来说,其性能接近线性增长。部署脚本与测试脚本可参考 <a href="https://link.segmentfault.com/?enc=mX0ZUv0cdvbn8DfM8awonQ%3D%3D.vgs3%2B2l9znsoWhhzATFPliTV%2BpWO9vgjEk%2BCR1OVJfWi%2FR%2F0OrE1kTe3880As0u9" rel="nofollow">https://github.com/api7/apisix-benchmark</a>。</p><blockquote>注意:在测试过程中,APISIX 关闭了 <code>proxy-cache</code> 和 <code>proxy-mirror</code> 插件。这是因为这两个插件的启用将会影响 APISIX 4% 左右的性能(benchmark 相关文档中有提及),因此在本次测试中进行了关闭。</blockquote><h2>多场景测试与性能对比</h2><h3>场景一:1 条路由,不启用任何插件</h3><p>场景一旨在测试纯代理场景。因此只设置 1 条路由,不启用任何插件,测试 APISIX 与 Kong 在该场景下的性能差异。</p><p>APISIX 配置如下:</p><pre><code class="yaml">routes:
-
uri: /hello
upstream:
nodes:
"127.0.0.1:1980": 1
type: roundrobin
#END</code></pre><p>Kong 配置如下:</p><pre><code class="yaml">_format_version: "3.0"
_transform: true
services:
- name: hello
url: http://127.0.0.1:1980
routes:
- name: hello
paths:
- /hello</code></pre><h4>性能对比</h4><p>在该场景下共进行了 10 轮测试,QPS 如下折线图所示(以 QPS 指标来评价性能)。</p><p><img src="/img/remote/1460000042838579" alt="在这里插入图片描述" title="在这里插入图片描述"></p><p>从图中可以看到,在纯代理场景下,APISIX 3.0 的性能表现优于 Kong 3.0 之上。APISIX 3.0 的 10 轮 QPS 的平均值为 14104,Kong 3.0 的 10 轮 QPS 的平均值是 9857。<strong>相比之下,APISIX 3.0 的性能是 Kong 3.0 的 140%</strong>。</p><h3>场景二:1 条路由 + 1 个插件(限流)</h3><p>限流是网关产品的主要使用场景之一,因此在场景二中,我们配置了 1 条路由与 1 个限流插件来满足测试要求。</p><blockquote>注意:该场景主要测试网关在限流场景下的性能,其中对限流插件的配置进行了较高的限制,避免触发实际的限流动作。</blockquote><p>APISIX 配置如下:</p><pre><code class="yaml">routes:
-
uri: /hello
upstream:
nodes:
"127.0.0.1:1980": 1
type: roundrobin
plugins:
limit-count:
count: 999999999
time_window: 60
rejected_code: 503
key: remote_addr
#END</code></pre><p>Kong 配置如下:</p><pre><code class="yaml">_format_version: "3.0"
_transform: true
services:
- name: hello
url: http://127.0.0.1:1980
routes:
- name: hello
paths:
- /hello
plugins:
- name: rate-limiting
config:
minute: 999999999
limit_by: ip
policy: local</code></pre><h4>性能对比</h4><p>依旧是进行了 10 轮测试,QPS 如下折线图所示(以 QPS 指标来评价性能)。<br><img src="/img/remote/1460000042838580" alt="在这里插入图片描述" title="在这里插入图片描述"></p><p>从上述对比图中可以看到,在启用「限制请求数量类」的插件后,<strong>APISIX 3.0 与 Kong 3.0 的 QPS 都下降明显,但是 Kong 3.0 的 QPS 下降幅度更大</strong>。APISIX 3.0 的 10 轮 QPS 的平均值是 9154,Kong 3.0 的 10 轮 QPS 的平均值是 4810,相比之下,APISIX 3.0 的性能是 Kong 3.0 的 <strong>190%</strong>。</p><h3>场景三:1 条路由 + 2 个插件(限流+鉴权)</h3><p>除上述提到的限流功能外,鉴权场景也是网关的主要使用场景之一。因此场景三将两个重要的功能合二为一,配置了 1 条路由的同时,绑定了限流插件和鉴权插件。该场景涵盖了限流与鉴权功能的同时,还在请求路径中实现了多个插件一起配合工作,覆盖了网关实际使用的经典场景。</p><p>APISIX 配置如下:</p><pre><code class="yaml">routes:
-
uri: /hello
upstream:
nodes:
"127.0.0.1:1980": 1
type: roundrobin
plugins:
key-auth:
limit-count:
count: 999999999
time_window: 60
rejected_code: 503
key: remote_addr
consumers:
- username: jack
plugins:
key-auth:
key: user-key
#END</code></pre><p>Kong 配置如下:</p><pre><code class="yaml">_format_version: "3.0"
_transform: true
services:
- name: hello
url: http://127.0.0.1:1980
routes:
- name: hello
paths:
- /hello
plugins:
- name: rate-limiting
config:
minute: 999999999
limit_by: ip
policy: local
- name: key-auth
config:
key_names:
- apikey
consumers:
- username: my-user
keyauth_credentials:
- key: my-key</code></pre><h4>性能对比</h4><p><img src="/img/remote/1460000042838581" alt="在这里插入图片描述" title="在这里插入图片描述"></p><p>从上述结果折线图中可以看到,APISIX 3.0 在启用 <code>limit-count</code> 和 <code>key-auth</code> 插件后,10 轮 QPS 的平均值为 8933,相比只启用 <code>limit-count</code> 插件时的 QPS 平均值 9154,只有<strong>略微下降(约为 2.4%)</strong>。<br>而 Kong 3.0 在启用 <code>rate-limiting</code> 和 <code>key-auth</code> 插件后,10 轮 QPS 的平均值为 3977,相比只启用 <code>rate-limiting</code> 插件时 QPS 平均值 4810,<strong>下降非常明显(约为 17%)</strong>。<br>在该场景下对比 10 轮平均 QPS,APISIX 3.0 的性能是 Kong 3.0 的 <strong>220%</strong>。</p><h3>场景四:5000 条路由</h3><p>该方案使用脚本生成了 5000 条不重复的路由,测试时只命中其中一条路由。该场景主要是测试 APISIX 与 Kong 进行路由匹配时的性能。</p><h4>性能对比</h4><p><img src="/img/remote/1460000042838582" alt="在这里插入图片描述" title="在这里插入图片描述"></p><p>同样是进行 10 轮测试,结果如上述折线图所示。</p><p>从图中可以看到,在该场景下,APISIX 3.0 的 10 轮 QPS 的平均值为 13787,Kong 3.0 的 10 轮 QPS 的平均值为 9840。相比之下,APISIX 3.0 的性能是 Kong 3.0 的 <strong>140%</strong>,与场景一测试环境下的效果对比类似。</p><h2>结论</h2><p>从上述几组测试场景的结果来看:</p><ul><li><strong>当不在路由上绑定插件时</strong>,多路由匹配与单路由纯代理场景下,APISIX 3.0 的整体表现性能为 Kong 3.0 的 <strong>140% 左右</strong>;</li><li><strong>当在路由上绑定插件时</strong>,APISIX 3.0 的性能为 Kong 3.0 的 <strong>200%</strong> 左右(有近一倍的性能提升)。</li></ul><p>因此在不同场景的性能表现上,APISIX 3.0 整体性能相比 Kong 3.0 而言,仍然保持着较大的优势。如果你对上述两个网关的使用场景有更多使用上的心得,也欢迎随时交流。</p><h2>关于 API7.ai 与 APISIX</h2><p>API7.ai(<a href="https://link.segmentfault.com/?enc=Di2rpReEGvBA3J%2FwgNcOjw%3D%3D.KUl6lb6GUgFjJnS7DQEN4AijrmpU1HuAvlmzqsYvU%2F4%3D" rel="nofollow">支流科技</a>)是一家提供 API 处理和分析的开源基础软件公司,于 2019 年开源了新一代云原生 API 网关 -- APISIX 并捐赠给 Apache 软件基金会。此后,API7.ai 一直积极投入支持 Apache APISIX 的开发、维护和社区运营。与千万贡献者、使用者、支持者一起做出世界级的开源项目,是 API7.ai 努力的目标。</p>
微服务中的服务发现是什么?
https://segmentfault.com/a/1190000042838102
2022-11-17T14:26:31+08:00
2022-11-17T14:26:31+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<blockquote>作者:罗泽轩<br>摘要:本文通过服务发现的相关背景和 APISIX 对于服务发现的应用与实践,来介绍微服务中的服务发现内容。</blockquote><p>在互联网刚开始出现的年代,人们要想访问某个在线服务,需要输入一长串的 IP 地址。IP 地址虽然不长,但是作为一串无意义的数字,要求记住特定服务的特定地址还是很考验人的记忆力。所以后来人们就发明了域名系统。每个在线服务会到域名商注册一个域名,然后通过 DNS 建立域名和具体 IP 的联系。这样一来,人们只需要输入一个好记的域名,就能访问到具体 IP 上的在线服务。这就是最早的服务发现。</p><p>当一个公司内部的服务数到了一定的规模(比如在做了微服务拆分之后),也会遇到 IP 实在不好记的问题。这时候就需要有一套服务发现系统。公司里面各个服务在该系统上注册,然后想要访问这些服务的其他服务会从该系统上查询对应的 IP 地址,这样就不需要让某个服务“记住”复杂多变的 IP 地址了。</p><p><img src="/img/bVc3Ujd" alt="" title=""></p><p>图1:IP 地址的变更,会让访问者无所适从</p><p><img src="/img/bVc3Ujc" alt="" title=""></p><p>图2:通过引入域名系统作为服务发现机制,现在可以灵活对待 IP 的变更了</p><h2>常见服务发现系统介绍</h2><p>作为一个服务发现系统,它需要满足至少四个功能:</p><ol><li>提供注册的 API。</li><li>提供查询的 API。</li><li>高可用。毕竟服务发现系统是整个系统的神经,不能麻痹甚至瘫痪。</li><li>生态。众所周知,程序员是一群很懒惰的人,最好希望有个库,引入进来就能跟 API 完成对接。</li></ol><p>下面让我们来看看市面上主流的几个开源服务发现系统:</p><h3>Consul</h3><p><a href="https://link.segmentfault.com/?enc=PuMSwx9mtg%2BhPUgDNuacnQ%3D%3D.3gSRKoZIzH4s19LXhTkUnwFkCbRMEAJU5aiai7Ov4PEvpTavJF5Pl0q4l8eQhvi8" rel="nofollow">Consul</a> 是由著名的开源项目孵化公司 Hashicorp 开发的服务发现系统。作为一个在 2014 年 4 月 17 日就发布了第一版的老牌软件,Consul 有着最为丰富的生态,甚至有第三方开发者为它开发了 Haskell 的 SDK。大部分 Consul 的 SDK 只是对其 HTTP API 的封装,所以其实开发量并不大。</p><p>Consul 支持通过 HTTP API 来完成服务的注册和查询。它支持在查询时通过 HTTP long polling 来实现及时的数据推送,避免轮询。此外 Consul 也支持通过 DNS 的方式查询对应服务的实例。</p><p>Consul 的部署方式比较有趣。Consul 的每个实例叫做 agent,它既可以是客户端,也可以是服务端。在客户端,Consul 会维持一个客户端的状态;对于服务端, Consul 通过一致性算法 Raft,支持分布式部署,以此来实现高可用。</p><h3>Eureka</h3><p><a href="https://link.segmentfault.com/?enc=M09qRvoa8c7lOWP3PZ%2B7rw%3D%3D.0MEhwdL6RK%2FAIE5WxDIHpfDeECZZHGdPWL9VxNpRPn8zKRrI8c%2F93PA4alLEgrMA" rel="nofollow">Eureka</a> 是由 Netflix 开源出来的项目。它也是相当地古老 —— 有迹可循的<a href="https://link.segmentfault.com/?enc=Y6UCMJ6I70%2B0JjBqKN0mZg%3D%3D.ItqsWApiuTabLzLgWh2PQJ5ZyrNwZDZQbFWEx5bgdjlOYqkok1rQmsYkAM4HS%2Fam2E7d%2FJLjEiKtQGzIQgTfl6koYXyaCSwKwVwX%2F%2BmglHPFPfyttgGuIJgpLvgO2pJk" rel="nofollow">提交</a>可以追溯到 2012 年。不过这个项目已经有 1 年不维护了。许多用户迁移到下文会提到的 Nacos 上面。</p><p>Eureka 支持通过 HTTP API 和 Java SDK 来交互。许多 Eureka 的用户,其实是通过 Spring Cloud 等 Java 生态圈里的项目带进来的。Eureka 高可用的设计,如果要用 CAP 的术语来描述,属于 AP,即允许网络分裂时客户端看到过期的数据,避免因为网络问题出现次生灾害。</p><h3>Nacos</h3><p><a href="https://link.segmentfault.com/?enc=5kC5%2BqAhAt6bxsO9rYVzZQ%3D%3D.l7QL5bdh20glzAGhcuj36%2F9MBbvpkNJ2TTtJYkg2YvwTMdWsKLGgxmLeFlW0CYH5" rel="nofollow">Nacos</a> 是由阿里巴巴开发的服务发现系统。其名称来自于 Naming and Configuration Service 前几个字母的汇总。自 2018 年 7 月 20 日发布了 0.1.0 版本以来,Nacos 目前已经发展到了 2.1 版本。</p><p>就像阿里巴巴的许多开源项目一样,Nacos 在中国的 Java 开发者中相当流行,其知名度甚至比 Eureka 还要大不少。</p><p>它支持通过 HTTP API 和 Java/Go/Python/NodeJS/C# 等 SDK 来完成服务的注册和查询。目前,Nacos 开发者也在开发基于 gRPC 的新 API。对于 HTTP API,目前 Nacos 只支持通过轮询的方式来获取服务列表。所以 Nacos 官方更加推荐采取 SDK 的方式。其 SDK 的实现,是轮询 + 基于 UDP 的推送两种方式,因此会有更好的实时性。另外 Nacos 也在开发基于 gRPC 的新 API,该 API 将会引入服务端推送的能力,对于那些不能接入 SDK 的系统将是一大利好。</p><p>Nacos 的高可用,一方面是客户端 SDK 中提供了持久化的能力,另外服务端通过 Raft 和 Distro 两套协议实现了一致性。</p><h2>常见对接方式以及各自优缺点</h2><p>抛开私有协议不谈,服务发现的对接方式可以分成三类:</p><ol><li>HTTP 轮询</li><li>DNS</li><li>HTTP long poll 或 gRPC server stream</li></ol><p>HTTP 轮询胜在实现简单,不过并不具有实时性。</p><p>DNS 的性能开销是最小的。由于 DNS cache 的存在,DNS 也并不具有实时性。DNS 对比于其他方式,有个好处在于它是一套广为接受、实现无关的标准。但是凡事总有两面性,这意味着服务发现系统无法给 DNS 响应增加一些额外的字段,除非活用 DNS 响应里的 Additional 字段,但是这么一来就需要客户端特别的处理。</p><p>HTTP long poll 或 gRPC server stream 是三者中最有实时性的,另外由于他们都是基于 HTTP 的,所以可以方便地自定义响应。而缺点就是客户端实现起来相对有难度。</p><h2>APISIX 如何对接服务发现系统</h2><p>作为一个云原生的网关,APISIX 支持从服务发现系统中获取上游节点,而且是同时支持在数据面和控制面上对接服务发现系统。</p><h3>数据面</h3><p>APISIX 在数据面上支持跟 DNS、Eureka、Consul(KV 模式)、Nacos、k8s 对接。</p><p>在对接 DNS 服务发现,APISIX 会通过 DNS 的 SRV 或 A/AAAA 记录获取某个服务具体的上游节点。当请求访问上游时,会先尝试从 DNS cache 中获取,如果没有,则发起 DNS 查询,得到对应记录里面的具体 IP 地址。</p><p>至于其他的服务发现类型,则是在后台同步的。当请求访问上游时,从当前同步到的数据里获取服务名对应的那一部分数据。对于 k8s 和 Consul KV,由于他们支持 HTTP long polling,所以我们可以通过该方式实时获取变更的 IP 地址。而对于 Eureka 和 Nacos,目前我们只是通过轮询的方式来获取数据。</p><h3>控制面</h3><p>APISIX 同时还支持在控制面上做服务发现。我们正在开发 <a href="https://link.segmentfault.com/?enc=BZRopLo0Xw9W6wtwuipHJA%3D%3D.UtDx2ZpEeXIb6VvVD89nYDEwWjQ1PecwRDPSaSMh7CggPIsVPxWJTBip62TDcRxd" rel="nofollow">apisix-seed</a>,该项目会从服务发现系统中同步数据到 etcd 中,这样数据面就能从 etcd 中同步最新的上游节点了。</p><p>目前我们已经在控制面上实现了对 Nacos 和 Zookeeper 的支持。由于在控制面上的服务发现支持,是通过官方提供的 SDK 实现的,所以可以拥有普通的 HTTP 对接所没有的优势。比如在 apisix-seed 的 Nacos 实现中,我们支持基于 UDP 的推送,所以数据的时效性要比 HTTP 轮询较好。</p><h2>APISIX 支持服务发现有哪些场景优势</h2><p>通过在网关上直接集成服务发现,可以大大简化服务上线的工作量。配置 APISIX 来对接你的服务发现系统,然后剩下的事就让 APISIX 替你操心好了。</p><p>举个例子,假设你的公司正在使用 Nacos 来作为服务发现系统,那么你只需要配置 APISIX,启用 Nacos 服务发现功能,后面只要把服务名配置到 APISIX 的上游,APISIX 就会自动获取该上游对应的具体 IP 节点。</p><p>这一优势在需要在网关迁移时,比如从 Spring Cloud Gateway 迁移到 APISIX 上来,可以明显减少所需的工作量。如果过去用的 Spring Cloud Gateway 使用了 Eureka 抑或 Nacos 来做服务发现,那么迁移到新系统时,仅需在 APISIX 里面启用对 Eureka 或 Nacos 的支持,即可完成过渡工作。 </p><p>下图是「还呗」在这方面的技术实践,替换 Spring Cloud Gateway 是为了进一步提升稳定性、强监管以及准确性和有效性:</p><p><img src="/img/bVc3Uja" alt="" title=""></p><p>使用 APISIX 之前</p><p><img src="/img/bVc3Ujb" alt="" title=""></p><p>使用 APISIX 之后</p><blockquote>引用「还呗」原文:<br>作为企业来说,成本优先依旧是需要考虑的原则。野蛮生长阶段可能只需要尽快实现业务,而在目前大环境下,预算范围之内肯定是成本优先。这样的话,效率和成本永远只能保住一项。因此在成本有限的情况下,企业就会少谈技术的先进性。技术人员在选型的过程中,就不会考虑当下选择的这个技术对团队有多大冲击、对现有的运维和架构带来多少收益等等,更多是从成本角度考虑。</blockquote><p>此外,APISIX 支持同时配置多个服务发现。许多公司由于历史原因,可能会存在多套服务发现系统。比如就我知道的情况,有的公司会同时有旧的 Eureka 服务发现和新的 Nacos 服务发现。APISIX 只需要同时启用 Eureka 和 Nacos,就可以应对这种情况。</p><p>如果你现在是直接在 APISIX 上配置上游节点,也可以考虑另外部署服务发现系统,并改由服务发现系统来存储具体的节点配置。将上游节点的配置,由 APISIX 移动到专门的服务发现系统,好处在于客户端可以自己来完成服务的注册,而且专门的服务发现系统往往会提供额外的功能,如更丰富的健康检查等。</p><p>未来,我们也会在 APISIX Ingress Controller 上支持集成各种服务注册发现组件,便于用户使用。到那时,用户将不仅仅能在 APISIX Ingress Controller 上指定 K8s service 的 endpoints 作为上游节点,还能够把服务发现得到的节点整合进来。</p>
基金交易场景下,如何利用 Apache APISIX 来稳固 API 安全
https://segmentfault.com/a/1190000042830462
2022-11-16T14:58:05+08:00
2022-11-16T14:58:05+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<blockquote>王晔倞,Apache APISIX Committer,公众号「头哥侃码」作者。</blockquote><h2>背景介绍</h2><p><strong>金融领域的企业中,安全是非常重中之重的因素。</strong> 通常各类金融企业都会花费大量成本去采购安全相关的设备和硬件,基金管理相关企业更是如此。</p><p>根据相关国内基金管理行业发展现状分析报告中可以看到:“我国基金管理行业在经历野蛮生长、严监管规范以及次贷危机冲击等阶段后,目前已经形成了监管规范化不断加强、公司格局完善的局面。在大众投资理念转变的背景下,基金管理行业规模稳步增长,行业发展空间有望持续扩大。”</p><p>从现实数据来看,大环境和人们理财意识的逐渐增强,使得基金行业在业务发展的过程中,对于技术业务上的呈现也开始有着更高的追求。</p><p>比如稳定性,这里说的稳定性并不是应对类似双十一那种突增流量时的业务表现,而是保证业务长久线上的稳定性和持续性。<br>其次就是有效性和准确性,它与稳定性是相辅相成的。因为金融领域有着非常严格的交易时间,尤其是证券和基金行业。大量的交易都会发生在固定的时间段内,因此相关时间内的有效性和准确性是必须要保障的。即业务场景下允许系统慢,但不允许崩。</p><p>最后就是开头内容中提到的严监管规范,也就是政策层面的监管强度。因为行业的特殊性,在业务安全和企业治理中,很多时候它是需要考虑行业色彩和大环境的。</p><p>得益于这些业务色彩,我们也会对基金行业的一些业务架构产生兴趣,比如他们是运用哪些方式来保障高监管要求下的业务安全。在这里,我们选取了目前使用 APISIX 的一家基金行业用户,带来他们的业务网关架构演进与基于 APISIX 进行的业务安全实践细节。</p><h2>行业现状与痛点</h2><p>这家公司是从 2012 年开始搭建相关交易系统,API 规模大概在 12000+ 数量,包含 PaaS、BaaS、两地三中心、安全、运维、中间件和 DevOps 平台等等。公司业务的整个系统架构演进大体经历了三个阶段。</p><p>在业务刚起步阶段(1.0 时代),业务架构还非常单一,基本是能应对基金买卖的简单场景即可,属于单体架构模式。随着后续业务的扩充和国内市场环境的影响,架构也随之进行了初步的更新迭代。在这个阶段下,开始对业务进行了简易分割。<br><img src="/img/bVc3SjH" alt="" title=""></p><p>上图就是当时最简单的架构模式,相信大家对这种架构类型都比较熟悉。即有一套简单的前端配置,包括应用、网站等等。从整体来看,业务模型比较简单,因为只需搭建好交易体系即可,搭配上完整的支付业务和鉴权服务。后端主要是去维护相关的基金交易数据,然后存储到数据库里。</p><p>当然这里的鉴权服务并不是指网关层面的呈现,而是基金业务与其他第三方基金公司之间的连接鉴权。比如跟外部的基金业务对接时,需要进行双方数据的对接,这种时候就需要有一些 Token 去进行鉴别和限制等等。</p><p>当时还没有网关的概念。在这个架构下类似现在网关概念的就是图中的 HSB(服务总线),它其实就是网关的前身,主要用来控制南北向流量。</p><p>人们总说,在公司业务或者行业发展过程中,总会出现一些节点来加速或者改变某个行业的进程。2015 年国内股市的业务爆发,也导致了基金业务公司开始进行业务扩展。市场方向的快速推动下,业务也开始加速前进。</p><p>这种情况下,该基金公司的后端业务就需要进行大量的变动,开始演进到如下图所示的架构类型(2.0 时代)。</p><p><img src="/img/bVc3SjJ" alt="" title=""></p><p>如上图红线圈出来的地方,可以明显看到,之前的服务总线模块开始变得复杂,各种业务组件纷纷出现。之所以变得复杂,是因为在业务野蛮生长阶段时,它会因为一些需求现状各自进行产出。</p><p>举个最简单的例子,就像各地的健康码系统一样,各省都不一样,甚至有的同一个省的不同地市还会有单独的系统。因为在产出过程中,我们会发现「自己造自己家的烟囱」是最快最快速的方式。</p><p>那么这种情况下就衍生出另外一个问题,也就是在系统发展过程中,一定会伴随所谓的技术债务问题。就是不同负责人在位时,所选择的技术栈或系统模型各不相同。</p><p>这个过程中就会出现一些业务库的拼装,因为当下的服务场景开始变得复杂。所有的这些新增动作等都需要通过后台系统的组合,来完成前端的某一个动作。因此这种组合的过程中将,势必要用到很多种类的组件以及经历几次产品更替,系统就开始变得复杂和繁重。</p><p>上述业务现状的演变下,不少问题就开始涌现。如下图所示,从三个层面进行了一些汇总。<br><img src="/img/bVc3SjL" alt="" title=""></p><p>当然除了上述提到的这些在外,在业务层面还会有其他一些问题。比如:</p><ul><li><strong>技术成本开始变高,整体性能有所影响。</strong> 网关技术栈的不统一,导致需要同时维护多种协议与微服务框架,开发维护成本较高;随着业务的规模越来越大,像大部分金融企业都选择基于 Java 做中间件,在处理大流量 QPS 时所需的服务器资源越来越多,整体性能冗杂。</li><li><strong>微服务框架的侵入性强,存在安全隐患。</strong> 当下无论是 API 治理、审计还是鉴权,基本都依赖微服务框架和 SDK 来进行。因此每次版本的更新,不仅带来运维风险,还容易引发大量内部业务矛盾。</li><li><strong>缺少更高阶功能的需求满足。</strong> 如果一个架构中,业务系统网关都是自研的,那么任何功能都要从 0 进行开发编写,这个过程中需要考虑一些时间成本问题,同时在业务层面可能只会实现相对简单的功能,比如缺少服务发现和部分监控指标等功能。这对于后续的业务发展就会造成一些技术上的瓶颈。</li></ul><h2>技术选型与架构更新</h2><p>在后续业务发展过程中,这家基金公司在 2020 年时开始对网关产品进行单独选型。</p><p>在 2018 年左右时,Spring Cloud 其实非常盛行,金融行业内有很多企业都开始使用 Spring Cloud Gateway。同时金融行业的架构中,大多都是基于 Java 进行的,所以开发人员中大多都是熟悉 Java 的。</p><p>而当时这家基金公司并没有跟随行业的普遍方案,去选择 Spring Cloud Gateway,而是最终确定了Apache APISIX 作为他们的网关。之所以没有选择 Spring Cloud Gateway,主要原因如下图所示。<br><img src="/img/bVc3SjT" alt="" title=""></p><p>当然除了上述原因外,还有一些实际大环境的因素。2019 年左右,P2P 事件开始曝光,很多 P2P 公司倒闭。当时整个金融领域尤其是基金交易领域,发生了翻天覆地的变化。各家企业都开始进行成本压缩,所以在架构选型中,还需要面对成本压缩的考虑因素,去进行选型。</p><p>因为公司要压成本,同时也开始要求把一些非交易系统放置到云上。作为技术人员,这种情况下就会考虑如何让上云时更方便更高效,因此面临这种场景时,技术栈能被统一地越少越好。所以他们就开始往云原生方向的网关产品去观望。最终,结合业务表现和技术栈统一相关的成本因素,最终选择了 APISIX。<br><img src="/img/bVc3SjV" alt="" title=""></p><p>基于 APISIX,该基金公司的业务架构更新成了如下图所示的全新模块(3.0 时代),这其中将架构分成了前中后模式,并对代码进行了分层。</p><p><img src="/img/bVc3SjX" alt="" title=""></p><p>从外部进入的流量(南北向流量)经过 APISIX 后,可以进行一些安全控制、流量管控和准入控制(比如灰度)等。而在应用层与业务层以及业务层与基础层中间,会用 APISIX 来解决东西向的流量。由于该基金公司当时大部分的系统是运行在 VMWare 虚拟机上,只有测试的交易系统是跑到 K8s 上的,所以他们的 CI/CD 比较复杂,因此 APISIX 在这里处理东西向流量时,主要是进行统一入口、监控报警、分流和鉴权相关操作。</p><h2>基于 APISIX 的业务安全实践</h2><h3>微服务治理</h3><p>微服务主要解决东西向流量。APISIX 在进行整个微服务治理的过程中,主要会帮助企业解决统一入口、API 配置管理、分流鉴权、服务监控、协议转换等问题,具有分布式和可拓展的特性。</p><p>前文我们提到过该公司业务架构中也存在比如鉴权模块,但当时他们用 Java 的一些扩展包进行了自研。但发展到后期,业务开始进行统一时,各个平台都需要对接进来,问题就开始出现了。</p><p>因为除了产品、中间件等等业务平台,还包括账户中心等等,这些之间的相互联动,<strong>不止需要进行HTTPS 的相关加密,还会存在一些单点必要需求等。</strong></p><p>为了满足这些业务间联动和相关加密处理需求等,该基金公司就利用 APISIX 在中间件部分(APISIX 作为网关是其中一部分)作为单点入口,去处理这些联动和安全层面的需求对接,如下图所示。</p><p><img src="/img/bVc3Sj0" alt="" title=""></p><p>其中在服务治理层,会涉及到一些协议转换,而 APISIX 具备成熟的服务治理框架去对接 Dubbo 以及进行 MQ 服务之间解耦。APISIX 的基础协议支持的类型非常多,包括 HTTPS、MQTT、Dubbo、gRPC 和 WebSocket 等多种类型。<br>比如在实际使用时,可以通过 APISIX 内置的 dubbo-proxy 插件来实现代理 Dubbo 协议,无需再进行相关配置的从 0 到 1,而是直接开箱即用,轻松地将 Dubbo Service 发布为 HTTP 服务。</p><h3>认证授权</h3><p>身份认证在日常生活当中是非常常见的一项功能,大家平时基本都会接触到。比如用支付宝消费时的人脸识别确认、公司上班下班时的指纹/面部打卡以及网站上进行账号密码登录操作等,其实都是身份认证的场景体现。<strong>可以说身份认证是保证基金交易安全稳定运行的重要因素之一。</strong></p><p>之前该公司是利用 Java 的一些自研组件或者程序逻辑等来作为「类似网关作用」进行相关的认证授权。但整体会出现负载压力大,且不支持流量控制和灰度等能力,同时还缺少相关项目维护人员。</p><p>在使用 APISIX 后,他们开始利用 APISIX 去提供认证和授权的相关能力,来帮助自家业务进行统一管理和高效运维。目前 APISIX 所支持的插件也已达到 80+,其中也内置不少认证鉴权相关的插件。<br><img src="/img/bVc3Sj2" alt="" title=""></p><p>比如借助 APISIX 内置的 jwt-auth 插件或 openid-connect 插件,在业务内部和外部之间进行一些认证交互,实现如下一些场景:</p><ul><li>应用级:内部应用通过应用级 AT 到账号系统,然后根据用户 ID 等数据直接查询用户的信息,无需用户授权;</li><li>用户级:用户授权应用,应用通过用户授权生产的用户级 AT,到账号查询用户的 openid、unionid 和头像等用户信息。</li></ul><h3>SSL 证书</h3><p>在系统建设的初期,该公司架构基本都是基于 HTTP 协议或一些自定义协议进行互相调用。但是通过 HTTP 协议传输的是明文数据,不会对数据进行加密。<strong>但在基金交易系统中,无论是因为政策监管还是安全需要,即使在内网也需要通过 HTTPS 访问数据。</strong></p><p>在之前的安全功能呈现上,该公司都是直接采购安全类产品进行防护,但是使用过程中必然少不了三方的维护等环节。在架构演进过程中引入 APISIX 后,刚好解决了该场景下的一些需求。从而方便根据业务需要进行灵活调整,同时在成本层面抛弃防火墙等产品,优化了运维流程,最重要的是也满足了相关监管需求。</p><p>比如 APISIX 在 SSL 证书功能上就支持单一域名、泛域名、多域名和单域名多证书的多种场景。</p><p><img src="/img/bVc3Sj3" alt="" title=""></p><p>APISIX 支持通过 TLS 扩展 SNI 实现加载特定的 SSL 证书,以实现对 HTTPS 的相关支持。启用该特性后,将允许客户端在服务器端向其发送证书之前向服务器端发送请求的域名,服务器端将会根据客户端请求的域名选择合适的 SSL 证书发送给客户端。</p><p>所以在安全相关的功能准备中,很多东西都是顺应着大环境的要求和一些架构更新过程中的业务统一而进行的。并不是说因为有了 APISIX 这种类型的云原生网关,才开始去重视业务上的安全问题,而是说有了 APISIX 网关,可以让企业业务安全更高效更简易地进行管理和操作。</p><h2>总结</h2><p>以上就是从基金交易业务的场景下,带来了泛金融行业在进行业务架构迭代过程中的变更与相关安全实践。在业务机构更新的过程中,如果没有类似 APISIX 这种网关中间件的能力加持,或许就没法轻易地满足业务上的高速发展,也没有办法轻易解决行业野蛮增长过程之后的复杂技术债问题。</p><p>所以不管是企业治理还是稳固业务安全,选择一个健康持续发展的中间件产品,是非常有利于业务架构的升级与后续拓展的,同时可以最大限度地帮助企业去解决技术债务。为什么选择技术债务这个点,因为它是我们在社区通过一些企业用例反馈之后,发现的一些企业选择 APISIX 的痛点之一。</p><p>纵观该基金企业的整个系统演化过程,都是用业务去推动选型。通过对性能、可拓展性以及安全等层面,Apache APISIX 都用更实际的数据和效果证明了它作为网关和中间件属性的作用,在保证性能的同时,也为金融行业的业务安全带来了最稳定的保障。</p>
实践一年之久,vivo 如何基于 APISIX 进行业务基础架构的演进
https://segmentfault.com/a/1190000042792309
2022-11-11T14:54:21+08:00
2022-11-11T14:54:21+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<blockquote>分享嘉宾赵旭,vivo 互联网架构师,主要专注于基础架构研发方向。</blockquote><p>Apache APISIX 在这两年已经受到了国内很多知名企业的信赖,并纷纷开始在实际生产环境中应用 APISIX。在这个过程中也包揽了很多不同行业的企业用户,比如金融行业的众安保险和安信证券,国产头部车企的吉利和小鹏汽车。其实在国产手机领域中,也有一些头部企业用户在使用,比如 vivo。</p><p>vivo 是从去年年中开始在业务生产中正式使用 APISIX 来替换之前传统的 NGINX。目前 APISIX 在 vivo 业务架构的实践和场景支持上都表现得十分出色:</p><ul><li><strong>高可用性:</strong> 上线至今没有出现重大故障,系统可用性超过 99.99%;</li><li><strong>高性能:</strong> 承载较大线上流量,服务于较多业务。线上目前转发流量接近百万级 QPS,目前仍处于持续增长的过程中;</li><li><strong>功能丰富:</strong> 基本覆盖了常见的 NGINX 代理场景,50% 的业务已经迁移到 APISIX 集群;</li><li><strong>支撑了云原生的建设和发展:</strong> 有效支撑和推动了公司容器化进展,支撑容器平台的物理机器已有万级规模,40% 的业务已经从物理机虚拟机迁移到容器平台。</li></ul><h2>基于 APISIX 架构设计与定制调整</h2><p><img src="/img/bVc3Iov" alt="" title=""></p><p>从上图架构中可以看到,在网关层面我们已经实现了“四层+七层”的代理模式。其中四层网关我们是用 LVS+DPDK 的方式来实现的,流量从外网进入四层网关后会转换到七层网关。而七层网关就是 APISIX 集群,目前这套 APISIX 集群已经实现了容器、物理机、虚拟机等容器混合流量部署的接入功能。</p><p>图中红色部分,则是我们正在使用的一些自研和开源项目,并借助这些项目实现整个流量转发体系。另外在架构中,我们还实现了 APISIX 集群拆分的管理功能。</p><p>这套架构基于 APISIX 开源版本我们也进行了一些调整,主要是在控制面和数据面进行了更贴合内部业务的改造。</p><h3>配置管理与发布改造</h3><p><img src="/img/bVc3IoB" alt="" title=""></p><p>当业务从 APISIX 变更平台配置好相关数据后,变更平台会通过 RPC notify 的方式发送给 Manager API。之后 Manager API 会通过 RPC notify 方式发送给部署在 APISIX 集群中的 apisix-agent。然后 APISIX 会通过 privilege 特权进程,定时轮询 apisix-agent 去批量获取变更任务。</p><p>之后 APISIX 通过共享队列的方式,由特权进程通知给各个实际的 worker 来实现内存上的变更。变更完成后,worker 会将相关的配置信息进行一个落盘处理。当整个任务完成后, APISIX 会把相关结果通知传送给 apisix-agent 并传递给 Manager API。</p><p>本身这套流程是一个异步查询,对于配置是否完全生效或者任务是否成功,都是通过轮询接口进行查询。可以看到上述流程中,我们对开源 APISIX 进行了如下的变动:</p><p><strong>去掉 etcd 组件,自研实现了基于 APISIX 的 A6 配置管理系统。</strong></p><p>之所以去掉 etcd 组件,是因为 etcd 本身还是一个偏内存的数据库,不适用于多维度资源的查询;同时对于我们本身业务而言,etcd 的修改事件模式(主要是删除)具有不可重入性,一旦删除相关资源,就无法再进行重复性操作;最后就是考虑到单独增加一个系统组件(且处于核心地位),在大量 watch client 下的系统维护复杂度会大量增加,因此在我们的改造架构中删除了 etcd。</p><p><strong>自研 apisix-agent 组件。</strong></p><p>在引入 APISIX 之前,我们团队对于 OpenResty 和 Lua 的使用经验基本为 0。所以考虑到已经去掉 etcd 的前提下,我们团队确实不具备能够改造 APISIX 核心底层技术的能力,所以通过自研 apisix-agent 组件,来简化现有 APISIX 的一些能力,降低我们内部使用上的一些复杂度,从而达到复杂性和稳定性之间的权衡。</p><p><strong>实现 APISIX 资源配置文件本地落盘。</strong></p><p>这一点主要是我们在设计时,希望数据面可以独立运行而不是去依赖于一个控制中心,减少连带影响。这样的话,当 APISIX 启动时就可以从配置中心全量拉取,也可以直接从本地落盘的文件目录进行拉取。在这种方案下,大大提高了数据面的独立性与系统的健壮性,同时对于问题的排查也带来了更清晰的路径展示。</p><p><strong>增加“变更任务、结果回调”的上报机制。</strong></p><p>整个流程的后半段,其实都是在执行这种上报机制。之所以制作这种逻辑,是为了保障像路由和上游等核心资源的时效性。一旦出现那种你以为资源下发成功但实际没成功的现状,就必然会导致一段时间的流量影响。这套逻辑就可以保证 APISIX 上所有的 worker 都把需要的资源变动更新到了对应内存中。</p><h3>集群拆分管理改造</h3><p><img src="/img/bVc3IoG" alt="" title=""></p><p>APISIX 开源版本中是不支持集群管理功能的,基本上所有业务都共用一个 etcd。但是对于我们这种中大型企业来说,业务场景与业务逻辑都十分复杂。在这种情况下如果使用这种大一统的集群方式,在业务运行过程中很容易出现不可控的场景。其次,随着业务复杂度的提升,有些业务就不可避免地会用到更复杂的插件,一旦数量变多,对于整个集群的性能也会造成一定影响。</p><p>基于以上考虑,我们决定将 APISIX 进行集群拆分化的管理,从而实现业务和服务集群的精细化管理。这样就可以实现不同的域名或业务通过映射管理等各种方式,分配给不同的 APISIX 集群,从而实现 APISIX 集群上的配置隔离。同时还能控制故障域,对于业务复杂度也能起到一个有效的支撑。</p><p>如果业务过于复杂,就可以给它单独分配集群,不会影响其他业务。有效降低了 APISIX 非转发层面所造成集群负载,包括配置下发、高频容器节点变更以及健康检查所带来的负载影响。</p><h3>新增 Intel QAT HTTPS 加速卡</h3><p>根据国家工信部的相关要求,外网流量必须要走 HTTPS 协议。作为一个基于 TLS 的 HTTP 加密协议,HTTPS 的加解密过程对于 CPU 的负担是比较大的。</p><p>我们内部进行过一个统计,如果纯转发 HTTP 和 HTTPS 两种,同样的路由和其他相关配置齐平的情况下,HTTPS 只能承载大概 HTTP 的 1/8 到 1/10 左右。因此 HTTPS 的加解密对于性能影响还是非常大的,因此我们重新编译了 OpenResty 模块。</p><p>主要操作就是在 NGINX 代码中 Patch 了 Intel QAT 模块,通过 SSL 异步解密方式,将加解密的过程交给了 QAT 卡进行。从而释放了 CPU,增大了单机 HTTPS 承载的 QPS。通过引入这套模块,单机 HTTPS 承载量提升了一倍左右。</p><h2>落地 APISIX 的问题与解决方案</h2><h3>支持容器化,自研 Ingress 组件</h3><p>为了让这套架构符合公司的容器化发展,我们还自研了一套内部的 K8s-ingress 组件,这个组件的作用就是将 K8s 节点的变更通知到 APISIX 中。<br>在之前公司内部已经利用 Calico 方案实现了容器与 IDC 机房的网络互通。在这种情况下,如何快速稳定地将变更通知到 APISIX 进行流量转发,是接入 K8s 的重要一环。</p><p>因此我们在调研过一些 K8s Ingress 实现方案之后,基于公司的业务场景和发展,最终选择了自研开发。主要考虑了以下几点:</p><ul><li><strong>适配改造的异步推送配置变更机制。</strong> 前文介绍过的异步轮询机制其实是比较复杂的,因此为了适配这套机制,我们选择了用自研组件去配合。</li><li><strong>适配多套 K8s 集群事件处理通知。</strong> 由于公司业务丰富,我们不可能只部署同一套 K8s 集群。因此从稳定性与可控管理层面因素考虑,我们在这里也进行了多集群场景的适配,保证多集群的变更能正常通知到 APISIX 中。</li><li><strong>适配复杂的业务场景。</strong> 比如满足一个 Server 监听多个端口(涉及不同协议)的场景,或者是将 Dubbo、gRPC 等其他 RPC 框架服务器接入 K8s 的场景,这些都需要单独开发一套逻辑去实现。</li><li><strong>适配内部 DevOps 等自动化场景</strong> 较特殊需求,方便业务能快速部署和启用流量等。</li></ul><h3>自研工具,降低迁移成本</h3><p>前面我们也提到了,之前公司的业务都是部署在 NGINX 集群当中,运行得也比较稳定了。所以当我们将基础架构更换到 APISIX 时,对于一些原本使用 NGINX 的业务部门来说就会存在一些抵触心理。因为一旦需要配合基础架构去更换这些底层组件,就会涉及到一些自身业务上的影响和收益等,比如工作量的增加或者系统的不稳定表现。</p><p>所以如何在公司内部去推动业务部门进行配合架构演进,也是一个非常重要的环节。我们自己的经验总结了如下几点:</p><ul><li>先找一个合作部门的业务,服务好该业务部门,利用好的效果反馈来树立标杆。同时提供技术上的人员指导和辅助,为后续的运维服务打下基础。</li><li>在本身架构搭建中,打造对于业务层面更易用的控制面系统,方便业务接入。比如前文提到的去掉 etcd 进行自研控制面,就是考虑到了这个因素。</li><li>提供从 NGINX 配置一键切换到 APISIX 配置的自动转换能力,将演进成本降低。为此我们专门开发了一套系统,业务方只需输入域名,就可以把相关 NGINX 上的服务和上游等配置信息拉取过来。然后经过简单的逻辑转换与关联后,即可自动发布。</li></ul><p>虽然做这么一套自动化配置系统困难较大,但我们还是尝试去做了并基本实现了公司内部业务的一些实际需求。比如进行 location 与 upstream 的转换与绑定、把 NGINX 关键字转换成 APISIX 插件等,目前仅把在 NGINX 上使用的一些常规配置转换成了插件使用。</p><p>当然这个转换成功率并不一定是完全契合的,因此我们在这套系统中也增加了确认环节。如果在转化后觉得并不完全适用 APISIX 的一些配置细节,还可以自行进行少量修改。从而依赖这套工具,去最大程度地降低各业务部分的迁移工作量。</p><h3>自研集成测试,解决版本升级问题</h3><p>vivo 是从 2021 年年中基于 APISIX 2.4 版本进行改造并陆续上线的,在今年 Q2 阶段已升级到 2.13 版本。在从 2.4 升级到 2.13 版本的过程中,在业务上也是遇到了一些问题。主要涉及到 APISIX Lua 和 OpenResty 相关的部分。</p><p>在 APISIX 的 Lua 部分,得益于 APISIX 模块化的组织方式,把我们自己修改的代码合并到 APISIX 高版本分支中,并不算麻烦。再通过完整的回归测试后,比较轻松地完成了升级。</p><p>而在 OpenResty 部分则踩坑比较多。APISIX 升级的过程中,OpenResty 也在升级,差不多一年升级一个版本。由于我们公司使用了较多的 Patch 包和一些非常用功能 (如 QAT 等),Patch 过多就会出现一些代码冲突等问题,导致该组件的后续升级比较费时费力。</p><p>为了解决上述问题,我们也进行了前文提到的一些控制面与数据面的改造。同时为了保证这些改造是运行完美的,就需要去进行相关的测试。但是如果是单纯利用 APISIX 的测试场景则只会顾及到 APISIX 单个模块,由于我们已经对控制面进行了比较大的改动,所以在这种情况下,我们使用了 Python 的 robot framework 插件来打造了自研的系统集成测试。同时由于 Python 语言的普适性,对于开发和运维人员也非常友好。</p><h2>APISIX 使用心得与未来计划</h2><p>《Google SRE》书中提到过,<strong>“正常运行是无数种异常情况中的一种异常”</strong> ,软件能够保持正常地运行,其实是非常困难的。在使用开源产品 Apache APISIX 一年多的时间里,我们也慢慢感悟出了一些技术使用上的心得。</p><p>在使用开源产品时,心态一定要开放,不能因为出现一点点问题就开始到处抱怨。在出现问题时,要抱着积极改进与合作的心态。虽然任何软件都不可避免出现问题,但本身作为一个 Apache 的顶级项目,至少在代码质量和流程测试中都是非常有保障的。</p><p>因此在使用中遇到问题时,可以优先考虑是否自己正确地使用了相关功能,先自己去寻找原因。如果本身没有充足的时间成本和技术实力,就可以去寻求社区的帮助。得益于 APISIX 的社区活跃,我们在社区里提的很多 issue 都及时得到了响应与跟进。</p><p>同时在使用 APISIX 的过程中,我们自己其实做了非常多的改造。因此在这个过程中,我们也非常重视测试环节,只有足够的测试才能保证代码的正常运行。</p><p>当然在使用 APISIX 的这一年多的时间里,我们也在积极探索与发现更多 APISIX 的功能场景。在后续使用计划中,我们将开拓以下方向:</p><ul><li><strong>推动 APISIX 从 HTTP 流量网关扩展到 API 网关</strong>,这点是我们公司明年的计划之一。比如使用 APISIX 将 HTTP 协议转发到后端的 Dubbo、gRPC 或者 MQ 上进行协议转换,或者使用 API 网关的一些通用功能(限流、熔断、鉴权等)。</li><li><strong>加强 NGINX 与 OpenResty 的自建能力</strong>。目前开源的 NGINX 社区已经不活跃了,对于一些功能的支持不及时,不利于公司统一接入层的未来发展趋势。同时我们也一致致力于提高公司内部对于 APISIX 的产品使用熟悉度,希望后续也能和 APISIX 社区一起协同共建一些功能。</li><li><strong>拥抱服务网格趋势</strong>。随着云原生趋势的发展,服务网格领域也开始变得热门起来。但是在我个人看来,服务网格距离大部分公司还比较遥远,目前还很难把这套东西应用起来。但如果将来有一天我们真的需要启用这一块,就需要从现在就打好基本功。所谓的基本功就是流量治理和服务治理这种基础能力,把他们建设地更好,去迎接未来技术趋势的变化。</li></ul>
API 网关 Apache APISIX 3.0 版本正式发布
https://segmentfault.com/a/1190000042791581
2022-11-11T12:15:36+08:00
2022-11-11T12:15:36+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<p>作为开源的云原生 API 网关,Apache APISIX 致力于在性能和使用体验上为开发者和用户们带来更好更优异的表现,帮助企业解决一些关于云原生和微服务技术下遇到的新问题。</p><p><img src="/img/bVcYu7C" alt="" title=""></p><p>在 9 月底,Apache APISIX 发布了 3.0.0-beta 预览版,为用户们提前带来了一些新的功能体验。<strong>今天,APISIX 正式发布了 3.0.0 版本,将产品从体验和功能角度,带到了新一轮的进程中。</strong> 经过迭代的 3.0.0 正式版与此前 3.0.0-beta 预览版相比:</p><ul><li>新增了 Consumer Group,可以更方便地管理消费者;</li><li>支持配置 DNS 解析域名类型的顺序;</li><li>新增 AI 平面,更智能化地对配置与流量进行分析与呈现;</li><li>对多个现有生态插件进行更细致的优化。</li></ul><p>除了以上技术层面的细节改动外,还有很多新的功能特性与生态扩展细节均在下文中为大家呈现。可以说这次的版本迭代,真正做到了 <strong>“性能更强更智能,生态更广更多样”。</strong></p><p>如果你想立刻体验 APISIX 3.0 正式版本,可以即刻前往 <a href="https://link.segmentfault.com/?enc=RtAiwsOvVXU%2B8UXBLTNfTA%3D%3D.dpRymAmMQ8zpu4IS7%2BbRZlTBpNTU8EWfanrrwpAkQ00%3D" rel="nofollow">官网</a> 进行下载与使用。</p><h2>APISIX 3.0 新增亮点汇总</h2><h3>1. 全面支持 ARM64</h3><p>目前 ARM64 对于云厂商来说,已成为一个非常主流的服务器架构选型。从 AWS Graviton、GCP Tau T2A 再到华为鲲鹏等系列产品,可以看到各家云厂商都开始推出了基于 ARM 架构的服务器。</p><p><img src="/img/bVc3IcR" alt="" title=""></p><p>目前从数据来看,Arm 架构的服务器在性价比层面的表现略优于 X86。为了顺应时代技术潮流,APISIX 也在 ARM64 上做了全面的 CI 回归。保证用户在 ARM 架构中运行 APISIX 时,依旧可以顺畅运行各种功能。</p><h3>2. 新增 gRPC 客户端</h3><p>在 3.0 版本中,将新增一个 core.grpc 模块。如果你熟悉 NGINX 和 OpenResty 的话,就知道这两者对于 gRPC 的支持相当有限,仅停留在执行反向代理或负载均衡这样的基础功能上。<br>而 APISIX 在目前 2.x 版本中就已经实现了 gRPC 和 HTTP 协议的转换。在 3.0 版本中,将通过新增 gRPC 客户端的方式,允许开发者直接调⽤第三⽅的 gRPC 服务,⽆需引⼊额外的组件或要求服务提供⽅额外使⽤ HTTP 接口,将使用过程大大简捷化。</p><h3>3. 重新设计 Admin API</h3><p>目前在使用 APISIX 时,你可能会发现 APISIX 的响应体中掺杂了很多没有意义的数据,比如一些 etcd 的返回值,没有进行任何剪裁就直接传送给了客户端。同时目前整个响应体的架构设计也并不完善,存在一些冗余字段。</p><p>在 APISIX 3.0 版本中,重新设计了响应体结构,新的格式可以让整个请求格式和返回体都更加的 Restful 化,从而让用户更加方便地使用新版本的 Admin API。当然该过程也允许通过参数来控制使用哪个版本的 Admin API,不用害怕升级后兼容不了之前的版本。</p><h3>4. DP 和 CP 分离</h3><p>APISIX 在最近一两年内出现了多个安全相关的漏洞,大多数漏洞的根本原因都是因为 APISIX 在默认部署模式下,将数据面与控制面部署在一起了。一旦数据面上存在安全漏洞,攻击者就可以通过数据面直接侵入控制面,从而影响到其他所有的数据面。</p><p>因此在 3.0 版本中,新增了部署模式配置 deployment,默认属性为 traditional,也就是数据面与控制面部署在一起。当然,新配置模式还是更建议大家将属性设置为 data_plane 或 control_plane,从而实现数据面与控制面的完全分离。</p><p>在完全分离后,不仅能解决上述安全隐患,还能更好地在数据面和控制面中分别进行功能的迭代而互不影响。</p><h3>5. 新增 AI 平面</h3><p><strong>在数据平面和控制平面之外,Apache APISIX 新增了 AI 平面。</strong> 通过对于 API 流量和配置的学习与分析,减轻开发者和维护者的使用和运维压力。比如以下两个场景就可以通过 AI 平面进行自动优化:</p><ul><li>发现没有身份认证的 API,并给出风险提示;</li><li>对于只配置了身份认证等 Access 阶段插件的 API,自动跳过 log 等不必要阶段,加快处理速度。</li></ul><p>AI 平面给流量处理带来了新的可能性,在后续使用过程中,类似上游服务自动热身、安全威胁发现等都可以通过 AI 平面来进行处理。</p><h3>6. 更完善的服务发现支持</h3><p>APISIX 在现版本中,已支持集成了很多服务发现组件,比如 Zookeeper、Consul、Nacos 等。但目前这些集成都是在数据面上完成的,一旦你的数据面节点非常多,这对于后续的服务发现组件压力也是非常大的。</p><p>尤其是像 ETCD 和 ZooKeeper 这一类提供强一致性的组件,通常无法承受太大量的连接数;此外,用户还需要为 Apache APISIX 数据面配置服务发现组件的认证,如果你在使用虚拟机部署 Apache APISIX,那么你需要将认证配置同步到每一个实例。</p><p>同时在用户实际生产环境中,他们想要的不仅仅是一个简单的类似于像 Consul KV 的集成或者是 DNS 的集成,而是更希望能做到类似健康检查等更多完整功能的集成。</p><p>因此在 APISIX 3.0 中,我们通过新增一个子项目 APISIX-SEED 进行了一层抽象,实现了控制层面的服务发现支持,降低了对服务发现组件的压力。后端服务的节点将由 APISIX-SEED 组件进行更新然后同步到 ETCD,最终被 Apache APISIX 所使用。</p><p><img src="/img/bVc3IcT" alt="" title=""></p><h3>7. 新增 xRPC 框架</h3><p>APISIX 在现版本中支持代理 TCP 协议,但是有些时候,纯粹的 TCP 协议代理是不够的。用户需要的是特定应用协议的代理,比如 Redis Proxy、Kafka Proxy 等。因为有些功能必须在对该协议进行编解码之后才能实现。</p><p>因此,APISIX 在 3.0 版本中实现了一个名为 xRPC 的四层协议拓展框架,允许开发者在上面自定义特定的应用协议。基于 xRPC,开发者可以通过 Lua 代码对请求和响应进行编解码,进而在了解协议内容的基础上完成故障注入、日志上报、动态路由等功能的实现。</p><p>基于 xRPC 框架,APISIX 可以提供对若干主流应用协议的代理实现。同时用户也可以基于该框架来支持自己私有的基于 TCP 的应用协议,使其具备类似 HTTP 协议代理的 <strong>精准颗粒度</strong> 的和 <strong>更高阶的七层控制</strong> 。而在不同的协议之上,又可以去抽象一些共性因素,实现相关插件能力,让不同的协议可以共享这些能力。</p><h3>8. 支持更多四层可观测性</h3><p>APISIX 在可观测性的功能支持上一直都投入很多,几乎支持了所有的可观测性组件,比如 Zipkin、Apache SkyWalking、Datadog 等等。同时还支持了各种各样的日志组件,但这些大多都是在七层(应用层)进行的。</p><p>在 APISIX 3.0 版本中将会增加更多基于四层(传输层)的可观测性支持。比如增加了四层上对于 Prometheus 和各种日志的支持,不仅可以让用户非常轻松地观测到七层流量中哪里出了问题,也可以去发现四层的流量运作状况。</p><h3>9. 集成 OpenAPI 规范</h3><p>API 其实是一个涉及从开发、测试、上线到整个全生命周期的元素。在 APISIX 3.0 版本中,将支持标准的 OpenAPI 3.0 规范。</p><p>因此,如果你是在一些 API 设计和测试的软件上进行管理 API 的话,就可以非常方便地通过数据导出和导入,将其放置在 APISIX 中进行管理和维护。同时 APISIX 中的各种 API 也可以通过 OpenAPI 3.0 规范进行导出,然后再导入到其他系统中使用。</p><p>除此之外,在 3.0 版本中 APISIX 也支持了针对 Postman 相关自定义格式的支持(Postman Collection Format v2),实现两者之间的数据传输,从而更方便地进行集成。</p><h3>10. Gateway API 的全面支持和服务网格</h3><p>在 APISIX Ingress 的版本迭代中,已开始对 Gateway API 进行支持,最新的 1.5 版本中已基本支持了所有的 Gateway API 配置。</p><p>由于 Kubernetes Ingress 资源本身的限制,南北向场景中很多的流量管理能力无法被很好的表达出来,因此市场上大量的 Ingress Controller 解决方案都提供了自定义的 CRD,虽然这样能很好地帮助用户管理流量,但是却间接提高了迁移的成本,几乎导致用户被某个 Ingress Controller 选型锁定。因此 Kubernetes 社区在前两年开始着手制定 Gateway API 这一标准。</p><p><img src="/img/bVc3IcV" alt="" title=""></p><p>Gateway API 是一个面向角色分层的协议,通常像 AWS、GCP 这样的云厂商会充当基础设施提供者,他们会提供若干种不同可选的网关选型(GatewayClass);而网关管理员,通常会创建不同的网关实例(Gateway);更上层的开发者则只聚焦于如何创建路由来暴露自己的 API,而不关心底层的网关细节。</p><p>这种情况下就可以通过 APISIX Ingress 去使用 Gateway API 的方式进行各种配置,也就意味着你能够在各个不同的数据面进行切换。在今年年底,APISIX Ingress 将更加完整地支持 Gateway API 以及支持在四层和七层的更多能力。</p><p>与大多数服务网格方案不同,APISIX 的服务网格方案更有优势的地方是数据面(得益于 APISIX 本身的高性能),因此在控制面的选择上,更希望去兼容一些社区上已有的主流方案。最终采取了通过使用 xDS 协议与 Istio 进行交互,并将获取到的配置写入到 APISIX 的 xDS 配置中心的方式,来配合 APISIX 生成具体的路由规则,完成对应请求的路由。</p><p>这种方案不仅可以让整个服务网格更加轻量,同时借助于 APISIX 的高拓展性,也可以进行更方便地二次开发与迁移。</p><h3>11. 集成更多生态</h3><p>除了上文提到的 OpenAPI 标准之外,3.0 版本中也会新增非常多的生态插件,比如 OpenFunction、ClickHouse、Elasticsearch、SAML 和 CAS 等,去集成更对关于认证鉴权、安全或者可观测性等。</p><p>其中一个有趣的插件 workflow 是关于流量调度的, 通过该插件就可以在流量控制层面进行一些更细粒度的处理。</p><p><img src="/img/bVc3IcW" alt="" title=""></p><p>比如当条件 A 成立时执行某个行为,条件 B 成立时执行另一个行为等。通过这种更加清晰的方式,让用户更加方便地调度各种业务流量。</p><h2>总结</h2><p>不管是 APISIX 从零开始发展到现在,还是已经推出的 3.0 正式版本,你会发现 APISIX 其实并没有在架构层面进行太多的调整与改动,更多的是进行生态、兼容性和产品应用层面的改变。</p><p><strong>一个开源项目的评判标准,或许并不只有性能和功能,而是需要更多站在用户、开发者和企业的角度,去考虑他们使用这个产品是否可以快速有效地解决当下的痛点。</strong></p><p>而本文中提到的亮点或者新特性,其实都是通过开源社区的大环境,接收了来自不同开发者或者企业用户的反馈而打造出来的,是他们让开源产品更加实用和充满活力。</p><p>最后,也欢迎大家对 APISIX 3.0.0 正式版进行体验和反馈,期待技术与社区所迸发的技术魅力。</p>
腾讯蓝鲸 API 网关如何借助 APISIX 实现产品升级与业务完善
https://segmentfault.com/a/1190000042786982
2022-11-10T17:49:24+08:00
2022-11-10T17:49:24+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<blockquote>分享嘉宾朱雷,腾讯 IEG 运维 PaaS 平台技术负责人。</blockquote><p>蓝鲸(全名“蓝鲸智云”)是一套孵化于腾讯 IEG(互动娱乐事业群)内部,服务于多业务与各内部平台的研运一体化 PaaS。 其作用是在 CI、CD 和 CO 三个阶段,对公司业务提供全生命周期的服务。</p><p><img src="/img/bVc3GZh" alt="" title=""></p><h2>蓝鲸 API 网关</h2><p>既然是为内部业务服务,那么腾讯游戏的业务都有哪些特点呢?</p><p>大家在日常生活中肯定也接触过很多腾讯出品的游戏。在腾讯内部,可能有上千款的游戏业务,除部分自研游戏外,大部分都属于代理类。代理业务的特点在于,它们是由不同公司所开发,因此各产品使用的语言、依赖的存储或者整个架构风格可能都是千差万别的。</p><p>面对这种包含大量异构架构的复杂业务场景,蓝鲸作为一个服务于内部的平台产品,在建设时就需要做到以下几点:</p><ul><li>采用原子化设计,将平台能力抽象、打散,进行微服务化改造,形成一套 PaaS 架构;</li><li>使用低代码技术高效开发 SaaS,来使用 PaaS 平台的原子能力;</li><li>通过各类 SaaS 来灵活应对不同的服务场景。</li></ul><h3>诞生背景</h3><p>考虑到现实中的业务环境与内部业务需求,最终蓝鲸平台的整体架构如下图所示。</p><p><img src="/img/bVc3GZn" alt="" title=""></p><p>中间的蓝色部分是 PaaS 层,其中最大的一块是前面提到的各类原子能力。包括用户管理、统一权限中心、统一配置平台等。</p><p>上层的橙色部分是 SaaS 层,主要由很多不同角色针对特定需求场景开发的 SaaS 构成。这些 SaaS 在开发过程中,或多或少地都需要通过 API 使用 PaaS 层最核心的各平台原子能力。在这种情况下,就势必需要一个统一的 API 网关。</p><p>以上就是最初想要打造蓝鲸 API 网关的原因。将前面的架构图做一些抽象,就会得到一个如下简单的网关画像。</p><p><img src="/img/bVc3GZo" alt="" title=""></p><p>蓝鲸是一个比较复杂的平台,它对于统一网关的需求也会比较复杂。除了最基础的作为代理去调用原子平台的 API 之外,还需要提供一些额外的网关能力。比如服务发现、统一用户认证和鉴权、限流限速,等等。</p><p>另一方面,随着云原生技术的发展,如今内部很多 SaaS 和原子平台也开始部署在 K8s 集群中。这类场景又向网关提出了新的要求,比如需要通过统一流量网关或 API 网关来将外部的调用请求流量统一管控。同时,内部还存在一些业务系统,本身使用了蓝鲸平台的一些基础架构能力,如容器管理或监控等,它们也需要一个统一的服务网关来管理所有调用流量。</p><p>面对外部技术的趋势与内部业务的发展要求,蓝鲸的 API 网关需要支持的场景开始变得越来越多样化。</p><h3>往期迭代</h3><p>蓝鲸 API 网关到目前为止经历了三个阶段的迭代。</p><p>蓝鲸 API 网关的 1.0 版本,主要是让原子平台的调用方(含各 SaaS 和流程引擎)不再直接对接各个原子平台,而是可以直接调用 API 网关,通过网关完成协议转换、权限校验等相关功能。</p><p><img src="/img/bVc3GZw" alt="" title=""></p><p>那时的架构也比较简单,如下图所示整体分为两大块:服务端和管理端。原子平台需要把自身服务注册到 API 网关上,首先得访问管理端,配置好平台的 API 资源地址和各自对应的权限等等。</p><p>当原子平台通过管理端提供了网关所需的配置细节后,数据会被写入到 MySQL 中,供服务端读取。之后,当 SaaS 请求某原子平台的 API 时,首先经过前端的 NGINX 负载均衡器,然后透到网关服务端,服务端读取了相关配置后,再由将请求通过路由转发功能传给后方的各原子平台,完成整套请求流程。为了提升性能,这套架构也引入了 Redis 处理了一些缓存加速等场景。</p><p><img src="/img/bVc3GZx" alt="" title=""></p><p>1.0 版本的架构在内部运行了几年后,随着请求量的增长和场景的复杂化,缺点开始逐渐显现出来。比如:</p><ul><li>框架性能不佳。当时实现时选择了 Django 框架,它在高并发场景下表现一般,处理海量请求时性能捉襟见肘。</li><li>路由实现性能一般。API 路由采用的算法性能较低,影响到路由的匹配和转发速度。</li><li>DB 压力大。路由策略全部存储在 MySQL 中,规则多时需承载大量检索请求,查询压力大。</li><li>网络开销大。Redis 在多种场景下被高强度使用,导致网络开销太大。</li></ul><p>为了解决上述问题,我们在 1.0 版本的基础上进行了迭代,设计实现了 2.0 版本。2.0 版本相比前代,最大的改动就是使用 Go 语言重新实现了网关的框架和转发层。因为 Go 相比 Python,在处理大并发请求的场景下会更有优势。</p><p><img src="/img/bVc3GZA" alt="" title=""></p><p>同时还进行了其他优化变动。比如在内存中维护了一个更高效的路由实现;在中间层引入了基于内存的缓存,以减少对 Redis 的依赖。新架构也增加了对网关多版本和多环境的生命周期管理,引入扩展插件机制,方便开发者通过插件对网关能力进行扩展。</p><p>总体来说,2.0 版本解决了 1.0 版本中遇到的性能问题和大部分痛点。但随着时间的推移,新的问题也开始慢慢浮出水面。</p><h3>技术选型</h3><p>进入云原生时代后,我们发现网关 2.0 版本在一些方面渐渐无法满足业务需求,主要的问题包括以下几点:</p><ul><li><strong>隔离性不足:</strong>无法实现真正的物理隔离;发布过程会导致长连接闪断。</li><li><strong>协议支持单一:</strong>仅支持 HTTP,而实际场景中对非 HTTP 协议的需求在增多。</li><li><strong>不支持动态路由规则:</strong>不支持按条件匹配的动态路由规则;对灰度发布场景不够友好;缺乏场景化组合封装的能力。</li><li><strong>缺乏服务发现能力:</strong>缺乏自动服务发现能力,对微服务架构不友好。</li></ul><p>结合实际业务场景来说,公司内部有很多业务系统都需要使用这个 API 网关,它们对网关的需求虽然大部分相似,但细微处其实又各有差异。假如把对网关的所有多样化需求都整合在同一套网关中,难度很大。因此,我们有了设计分布式网关的想法。即把一个大网关拆分成许多个微网关,利用这些微网关去平衡不同业务系统对网关的需求差异。</p><p><img src="/img/bVc3GZO" alt="" title=""></p><p>分布式网关架构的组件主要分为两类:管理端和微网关实例。管理端统一管控着各个微网关,由各网关的管理员对网关进行配置和管理。微网关实例是独立部署的各个网关服务,各自独立承担特定的某一组服务的访问流量,根据管理端的设定进行相关访问控制。所有微网关实例由同一套管理端管控。</p><p>在微网关的技术选型方面,我们当时参考了市面上比较流行的各网关开源产品,从流行度、技术栈、协议支持等各个层面对比后,最终选择了使用 APISIX 作为微网关最重要的后端技术。</p><p><img src="/img/bVc3GZS" alt="" title=""></p><p>之所以选择 APISIX,是因为它是基于 NGINX+Lua 实现的,所以整体性能相比 Go 而言是有优势的。同时 APISIX 的扩展性非常好,还支持通过多语言插件去扩展能力,在当时也有非常多的成熟的用户案例。</p><p>不过,APISIX 在当时对于我们的内部使用场景来说,还是存在一些缺不足的。比如它的控制面能力有些缺乏,控制面板比较简陋。因此在 APISIX 的基础上,我们按照内部需求去实现定制了 APISIX 的控制面。</p><h2>基于 APISIX 的蓝鲸网关 3.0 迭代</h2><p>云原生环境下,K8s 就是我们需要关注的最重要的基础组件。因为整个微网关都是面向云原生设计,所以 3.0 版本的网关就基于 K8s 去做了新的架构设计。其中最核心的部分,就是使用 K8s 提供的 CRD 自定义资源,实现了对网关的整套操作和扩展。</p><p><img src="/img/bVc3G0a" alt="" title=""></p><p>如上图所示,网关引入了一整套 K8s 的 CRD 资源定义,包括:BkGatewayStage(网关环境)、BkGatewayService(后端服务)等。通过这些 CRD,我们得以控制每个微网关实例的具体行为。</p><p>图中的几个“Operator”是这套架构中最核心的部分。上方是 Plugin Operators 服务,里面包含一系列的插件 Operator。比如负责服务发现的 Operator,会将后端服务注册在服务发现中心的地址写入到 K8s 集群中。</p><p>中间的核心 Operator 监听着所有和网关相关的 CRD 资源。其中的资源调和器(reconciler),负责将读取到的网关配置转换成 APISIX 微网关实例能理解的格式,从而实现微网关的全套生命周期管理。</p><p>目前,这套微网关主要分为两种部署类型:</p><ul><li>共享网关:默认类型,平台统一部署,由平台统一生成与管理 API 访问地址;</li><li>专享网关:使用方自行部署“微网关”实例,接入平台后变为“专享网关”,需手动管理 API 访问地址,流量直接从“专享网关”流入后端服务。</li></ul><p><img src="/img/bVc3G0g" alt="" title=""></p><p>从上图可以看到,管理端统一只有一套,因此它的能力并不提供任何差异化,像多环境管理、权限管理等功能都是所有网关共有。但在它所管理的不同类型的微网关实例中,所支持的特性集就会互有差异。</p><p>拿共享网关实例来说,它所支持的特性集是比较基础的。主要包含统一的登录鉴权、限流熔断和多协议支持等。而到了每个业务独立的专享网关实例,就可以拥有一些不一样的个性化能力。因为专享网关和业务同属一个集群,所以它可轻松实现动态路由、自定义服务发现等能力,也利用 APISIX 的强拓展性去自定义更多能力。</p><p>基于这套架构和模式,蓝鲸 API 网关 3.0 版本在 APISIX 能力的加持下,提供了<a href="https://link.segmentfault.com/?enc=jAVHuSnvsSUvDuVngukJ0A%3D%3D.J4WsdQTrrGpbNFGc6Il%2FoOvLNXGMmbCyloFKRlmsjMP9L2boQ1zWkYGIq%2BhtwUQr" rel="nofollow">更加丰富的功能</a>。</p><h2>基于 APISIX 的蓝鲸网关 3.0 实践场景</h2><h3>服务发现</h3><p>服务发现是微服务架构所需的一个基本能力,在内部,我们主要通过自定义资源 CRD 去实现。一份有效的服务发现 YAML 定义如下图右侧代码所示。</p><p><img src="/img/bVc3G0w" alt="" title=""></p><p>将上述 CRD 资源写入 K8s 集群后,就会触发服务发现相关的控制器的相关动作。之后调和器(Reconciler)会捕获到对应的服务发现配置,创建服务发现相关的程序对象。</p><p>然后它会通过内置的服务发现接口(包含 Watcher、Lister)读取服务发现中心的相关地址信息,将获取到的地址通过 BkGatewayEndpoints 这个 CRD 资源重新写回到 K8s 集群内。再经由右侧的核心 Operator 进行一些复杂处理后,这些 endpoints 最终被同步到 APISIX 对应的上游中,一次完整的服务发现流程就此完成。</p><p>为了方便开发,我们实现了一个通用的服务发现框架。它提供了统一的开发接口和规范,使用它可以低成本地支持其他类型的服务发现场景。</p><h3>统一认证授权</h3><p>统一认证部分比较简单。在业务实践中,我们需要支持来自三种不同来源的请求,分别是浏览器、平台和个人用户。我们基于 APISIX 实现了一个认证插件,来实现统一认证。</p><p><img src="/img/bVc3G0x" alt="" title=""></p><p>具体实现流程如上图所示。请求进来后,插件会从 Header 中读取相关的凭据信息,然后统一调用 BK-Auth 认证服务去校验该凭据,并读取对应的 SaaS 信息。之后再用和后端约定好的私钥,签发一个 JWT token 并将其写入请求头中,最后写入 APISIX 变量。</p><p>除统一认证外,内部业务中也存在一些复杂的鉴权场景。其主要解决的问题是,当一个 SaaS 调用某原子平台的某个资源时,判断该 SaaS 有没有这个权限。目前统一的资源鉴权,也是通过 APISIX 插件实现,这里我们使用了 Go 语言实现,如下图所示。</p><p><img src="/img/bVc3G0C" alt="" title=""></p><p>当客户端的请求过来后,会首先经过认证环节拿到 SaaS 应用信息。然后经过 ext-plugin 处,基于 RPC 与鉴权插件进行交互。此时鉴权插件会直接去查询缓存中的鉴权相关数据(通过全量和增量机制由管理端同步),之后完成鉴权。</p><h3>动态路由</h3><p><img src="/img/bVc3G0D" alt="" title=""></p><p>比较典型的动态路由应用场景来自蓝鲸的容器管理平台。蓝鲸容器平台管理着非常多的 K8s 集群,有一些是业务的服务集群,有一些是业务的数据集群。</p><p>作为用户,常常需要请求这些集群的 apiserver 去完成一些事情。当用户请求进入到微网关时,网关需要根据请求路径,判断该将其转发到哪个集群的 apiserver 中。具体实现流程如下图所示。</p><p><img src="/img/bVc3G0E" alt="" title=""></p><p>请求进入后,动态路由插件首先提取出集群的 ID 信息,然后对路由进行重写,之后判断该集群是否为直连集群。</p><ul><li>对于非直连集群,首先会生成一个 BCS 集群管理器上游,然后借其和 BCS Agent 进行交互,最后将请求传递给集群的 APIServer。</li><li>对于直连的集群,流程则类似于前文的统一鉴权插件,插件会定期同步一些与集群相关的基本信息。找到集群信息后生成相关上游,再通过 APISIX 插件去重定义连接逻辑,最后将请求发送到集群 APIServer。</li></ul><h3>客户端证书管理</h3><p>在我们的业务场景中,有一类原子系统在将资源注册到网关时,使用的是比较复杂的客户端证书验证模式。因此,如果某个用户要请求它的资源,就必须提供一份有效的客户端证书。</p><p><img src="/img/bVc3G0L" alt="" title=""></p><p>具体实现如上图所示。对于网关管理者,首先要在管理端配置好网关在不同环境中使用的客户端证书。创建完成后,证书会被发布到对应的微网关所在的 K8s 集群中。这个过程会使用一些 CRD 资源和 K8s 官方的 Secret 资源,并由核心的 Operator 服务进行持续地调和处理,比如根据域名寻找对应证书等。有效的客户端证书配置,最终会体现在 APISIX Service 的相关配置中(如上图右上方代码红框处)。</p><h2>总结</h2><p>本文从蓝鲸 API 网关的诞生背景与迭代过程入手,为大家呈现了一个 API 网关产品的从 0 到 1 的探索过程。也带来了选择 APISIX 进行网关产品迭代的过程中,一些业务实践场景的细节分享。希望对于想要使用 APISIX 进行网关迭代的企业有一些建设性的启发。</p>
从 Traefik 到 APISIX,汽车智能计算平台公司「地平线」在 Ingress Controller 的探索和实践
https://segmentfault.com/a/1190000042685260
2022-10-25T16:22:53+08:00
2022-10-25T16:22:53+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<blockquote>作者:地平线云原生开发工程师--张昕</blockquote><p>在当前的汽车行业,大多数公司都在向自动驾驶和新能源方向转型,而对于自动驾驶方面,每家企业都投入了大量的资源来完成自动驾驶模型的开发与训练,其中出现了很多明星企业,比如汽车智能计算平台引领者地平线。地平线主要从事汽车智能计算平台的研发,具有领先的深度学习算法和芯片设计能力,致力于通过底层技术赋能,推动汽车产业的创新发展。</p><p>智能汽车是机器人时代第一个大终端,地平线同时也通过软硬件的结合,广泛赋能泛机器人行业的应用落地。在硬件层面,地平线基于自主研发的专用计算架构BPU(Brain Processing Unit) ,推出面向智能驾驶的计算平台征程系列和面向泛机器人的旭日系列。在软件层面,面向智能汽车AI软件产品开发及迭代需求,地平线打造地平线艾迪®AI开发平台,能够为智能汽车AI开发者提供数据标注、训练、优化、部署、管理与性能分析等能力。整套基础设施,开箱即用,用户无需从零搭建一套复杂的自动驾驶跨平台系统,只需聚焦于核心价值积累。</p><p><img src="/img/bVc3gwW" alt="" title=""></p><p>对于一家快速发展的科技公司而言,如何保证业务稳定运行与轻松管理是非常重要的,而网关则是保证业务稳定的第一道关卡。由于之前网关存在了一些无法解决的问题,因此地平线对网关重新进行选型,最终选择了 <a href="https://link.segmentfault.com/?enc=m7gZ94D6BkUh7%2FhFWF4R5A%3D%3D.OY%2F3KLXFW5%2BWsQVGGmC9svyXy8QUtZqNyjzc4Cb9L7s0RA9WRwPG9FdahOo5fkxCfjJqycnAt3Y%2FOAvfkczZ242BUcUlSiNVcaLJSFSRHoM%3D" rel="nofollow">Apache APISIX Ingress Controller</a> 作为公司的流量网关,统一提供服务。</p><h2>网关选型之路</h2><h3>Traefik 的不足</h3><p>在使用 APISIX Ingress 之前,业务系统使用的 Ingress Controller 是 Traefik 1.x 版本,但是存在以下几个问题:</p><ul><li>Traefik 1.x 是通过 Ingress 来配置路由规则的,部分插件需要通过添加 <code>annotation</code> 的方式进行配置。这种方式,只能针对当前 Ingress下的所有规则添加插件,无法实现更细粒度的配置。</li><li>Traefik 1.x 不支持具体规则可视化配置,无法根据 Request URL 通过页面直接定位到具体服务。</li><li>Traefik 的默认配置文件(<code>configmap</code>)内容较少,许多默认配置需要翻阅官方文档,并且有些参数和 NGINX 默认配置不一致,导致维护起来比较麻烦。</li></ul><p>针对以上问题,地平线的技术团队决定更换 Ingress Controller,在选型初期也有考虑将 Traefik 升级到 Traefik 2.0 解决上述问题,但是因为也需要采用新的 CRD 来进行升级,迁移成本也不低,不如尝试下其他 Ingress 方案。</p><h3>APISIX Ingress 的优势</h3><p>在选型初期,我们主要对比了 APISIX Ingress、Kong Ingress 和 Envoy,但是除了 APISIX Ingress 其他网关或多或少在功能或性能上无法满足现有场景的需求,因此最终选择了 APISIX Ingress,除了一些通用的功能外,我们更看重以下几点:</p><ul><li>插件丰富:插件生态好,APISIX 支持的插件,均可以使用 <code>apisix-ingress-controller</code> 做声明式配置,并且可以针对 <code>ApisixRoute</code> 下的单条 <code>backend</code> 定制插件。</li><li>可视化配置:搭配 APISIX Dashboard 可以看到每条<code>apisix</code> <code>route</code>。如果同一域名配置在多个 <code>namespace</code> 或者是多个 <code>yaml</code> 文件中,发生冲突时可以结合 APISIX Dashboard 搜索 <code>path</code> 前缀即可快速定位。</li><li>细粒度校验:APISIX Ingress Controller 会对其管理的 CRD 声明的资源进行校验,假如在 CRD 中声明了不存在的 Service,则会将报错信息存储在 <code>ApisixRoute</code> 的 <code>event</code> 中,此次变更也不会生效,在一定程度上减少了一些因误操作造成的问题。</li><li>功能丰富:APISIX 支持热更新和热插件、代理请求重写、多种身份认证、多语言插件开发等诸多特性,更多功能请参考 <a href="https://link.segmentfault.com/?enc=dthu2abZrd3XrGxOAKQALQ%3D%3D.wpxu4lCremmGBh5J9lpxl6ZWfRq5R4cPRbU9OtMeIEAHp71ZHbtuf5Xd2r2hGBMf" rel="nofollow">APISIX 的功能</a>。</li><li>社区活跃: Issue 的响应速度很快,相对其他社区, APISIX 活跃开发者数量多。</li><li>高性能:从下图中,可以看到在和 Envoy 进行压测对比时,APISIX 性能是 Envoy 的 120% 左右,核心数越多 QPS 差距越大。</li></ul><p><img src="/img/bVc3gwZ" alt="" title=""></p><h2>整体架构</h2><p>从下面的架构图中可以看出,APISIX Ingress 作为一个全流量的入口,无论是命令行工具、Web、SaaS 平台或者 OpenAPI,所有访问的流量均通过 APISIX Ingress 进入上游(业务服务)。而在认证鉴权上,因为公司本身有一个专门的认证服务,所以直接使用了 APISIX 的 <code>forward-auth</code> 插件,实现外部鉴权认证。</p><p><img src="/img/bVc3gw1" alt="" title=""></p><p>在网关层,所有的流量均是通过访问域名进入,此时流量会先经过 LVS,由 LVS 分别转发到后端的 APISIX 节点中,最后再由 APISIX 根据路由规则对流量进行分发,转发至相对应的 Pod 中。而在 LVS 上,为了使 LVS 可以直接指向 APISIX Ingress, 他们也将 APISIX Ingress 默认端口由 <code>9180</code> 更换为 <code>80</code> ,可以更方便的对流量进行转发处理。</p><p><img src="/img/bVc3gxq" alt="" title=""></p><h2>使用场景</h2><p>了解完整体架构后,接下来将分享几个公司目前利用 APISIX Ingress 实现的场景。</p><h3>超大文件上传</h3><p>首先是大文件上传场景,该场景在一般公司可能会比较少,但是如果是做 AI 模型训练的公司,这个场景就比较常见了。该场景主要是在地平线模型训练系统中,研发车采集到的数据会通过网络上传到该系统中,数据大小一般都是在几百 GB 以上,在不调整 APISIX 任何参数的情况下,上传数据量过大时就会发生 OOM。</p><p><img src="/img/bVc3gxr" alt="" title=""></p><p>因为默认 <code>client_body_buffer_size</code> 是 <code>1M</code>`B`,缓冲区满了就会把临时文件写入磁盘,因此造成磁盘 IO 过高。</p><p><img src="/img/bVc3gxK" alt="" title=""></p><p>如果将写入临时文件的目录指向 <code>/dev/shm</code> 共享内存,则又会导致 APISIX(cache)过高。</p><p><img src="/img/bVc3gxL" alt="" title=""></p><p>经过不断的调试,发现是因为 APISIX 没有开启流式上传,针对这个场景,我们将 APISIX 版本由 2.11 升级至 2.13,并且对 APISIX 的参数进行了调整,首先更改 <code>apisix configmap</code>启用流式上传的参数 <code>proxy_request_bufferin</code>`g off<code>,其次再通过 APISIX Ingress Controller 提供的 CRD </code>ApisixPluginConfig<code> 将可复用的配置抽离出来,作为 </code>namespace<code> 级别的配置为需要此场景的路由动态设置 </code>client_max_body_size` 的大小。</p><p><img src="/img/bVc3gxN" alt="" title=""></p><h3>多云环境下的服务调用</h3><p>在多云环境下的服务调用中,部分业务流量首先会到达本地 IDC,之后会经过 APISIX Ingress 到达 Pod,而在 Pod 中,有些服务则会通过域名访问Acloud的服务,部分场景也会存在服务与服务之间进行调用。</p><p>主要是涉及到多云训练,用户会以 IDC 为入口,选择集群后即可将任务提交到对应的云端集群。</p><p><img src="/img/bVc3gxW" alt="" title=""></p><h3>使用 <code>forward-auth</code> 实现外部认证</h3><p>在我们刚开始使用 APISIX Ingress 时,APISIX 并没有支持 <code>forward-auth</code> 插件,因此我们基于 <code>apisix-go-plugin-runner </code>自定义了一个插件,但是这样做就多了一层 gRPC 地调用,调试比较困难,很多日志都无法看到。而在今年年初 APISIX 支持了 <code>forward-auth</code> 插件,我们就将自定义插件更换为官方插件,这样就减少一层 gRPC 地调用,也更加方便的进行监控。</p><p><img src="/img/bVc3gx3" alt="" title=""></p><h3>应用监控</h3><p>在应用监控中,我们在全局启用了 APISIX 的 <code>prometheus</code> 插件,并针对自身业务进行了一些调试和优化,比如增加了实时并发数、QPS、APISIX 实时接口成功率以及 APISIX 的实时带宽,对 APISIX 进行更细粒度的监控。<br><img src="/img/bVc3gx4" alt="" title=""></p><h2>总结</h2><p>当前我们仅在部分业务线使用了 Apache APISIX Ingress Controller 作为流量网关,后续也将上线其他业务,为社区带来更加丰富的应用场景。如果你也正在对 Ingress Controller 进行选型,希望通过阅读本文,能够给你一些帮助。越来越多的用户在生产环境中使用 Apache APISIX Ingress,如果你也正在使用 APISIX Ingress,欢迎在社区中<a href="https://link.segmentfault.com/?enc=ta1Lh901T%2B%2Be6Hon%2F%2Fyd4A%3D%3D.eLi4K1%2FgI3ulUw7vtfpH6WXh%2B%2F4sbgs5bviYSyoKYkdCu9vS22%2BAa86NBRu61ChxuOC7vF4GKWKiwJKeO0KflQ%3D%3D" rel="nofollow">分享你的使用案例</a>。</p>
直播预告|Apache APISIX x KubeSphere 在线 Meetup 来袭
https://segmentfault.com/a/1190000042624615
2022-10-14T14:55:11+08:00
2022-10-14T14:55:11+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<p>10 月 22 日(周六)14:00,Apache APISIX 社区将联合 KubeSphere 社区举办一场线上 Meetup,这也是继去年 6 月成都站 Meetup 后,我们与 KubeSphere 社区联合举办的第二次 Meetup!</p><p>本次活动我们特别邀请到了来自 vivo、驭势科技、希沃、青云的四位技术大咖分享关于两个开源项目的开发与实践案例等话题,将为大家呈现一场技术内容丰富、实践参考性强的直播盛宴。</p><p>欢迎各位社区小伙伴,及广大开源爱好者、开发者预约观看本次直播!</p><p><strong>时间:</strong>2022 年 10 月 22 日(周六)14:00-16:45</p><p><strong>报名方式:</strong>扫描下方海报二维码或点击视频号预约卡片即可报名观看本次直播!</p><p><img src="/img/bVc20LV" alt="img_v2_511792c5-b4f7-4154-a073-768cfa0d060g.jpg" title="img_v2_511792c5-b4f7-4154-a073-768cfa0d060g.jpg"></p>
开源浪潮下,Apache APISIX 如何成为全球最活跃 API 网关
https://segmentfault.com/a/1190000042614763
2022-10-13T11:53:48+08:00
2022-10-13T11:53:48+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<blockquote>白泽平,Apache APISIX PMC 成员,目前主要在 APISIX 和周边项目 APISIX Dashboard 上进行相关贡献。本文整理自阿里云「中间件开发者 Meetup」中的议题分享。</blockquote><p>Apache APISIX 是一个高性能的、动态的、实时的 API 网关,它是基于 NGINX 和 OpenResty 进行实现的。</p><p>作为一个脱胎于 NGINX 和 OpenResty 的软件,APISIX 天然继承了 NGINX 的性能和 OpenResty 的灵活性,因此,APISIX 的性能在一众 API 网关中都是数一数二的。</p><h2>细数 Apache APISIX 优势</h2><h3>架构取长补短</h3><p>具体来说,像 NGINX + Linux epoll 提供了高性能的网络 IO 基础设施,这些是 C 语言实现的,是静态的。而 OpenResty 则集成了 LuaJIT,它基于 NGINX 提供的生命周期钩子进行扩展,允许用户通过 Lua 代码对 NGINX 进行编程。而 LuaJIT 本身,得益于优秀的 JIT 实现,它可以在运行时对代码进行 JIT 编译,当热路径上的内容被编译为机器码后,性能将可以与原生 C 语言相比。</p><p>当然,除了 NGINX 与 OpenResty 的天然特性优势外,APISIX 本身也为性能进行了多处优化。比如没有复用 NGINX 的 location 来处理路由匹配,而是使用了基数树的方式。目前其他很多 API 网关还在使用遍历的方式处理路由,而 APISIX 则不会出现遍历方式的严重性能衰退,在路由很多时(这里指达到数千量级),它也可以提供基本平稳的匹配速度。</p><p>同时,APISIX 也在动态性能层面进行了一些操作。</p><p>相信使用过 NGINX 做服务部署的朋友一定记得,如果你修改了 NGINX 的配置文件,即使只是添加了一个 location,也必须要通过 NGINX reload 指令来应用配置,甚至有时还需要重启。这种情况对于内部应用场景来说还可以接受,但是对线上应用来进行这种操作时,可能会造成客户端连接的中断,这就影响很大。</p><p>而在 APISIX 中,上述配置操作过程都是完全动态的,location 可以动态配置,upstream 和 SSL 证书这些全部都可以动态配置。这主要得益于 APISIX 使用了 etcd 作为配置中心,通过 etcd watch 机制实现了动态的更新其配置,从而不需要依赖 reload 和重启。</p><p>除此之外,像是以往需要 NGINX 修改配置才可以设置的 gzip 和缓存等,也可以动态地在单一路由的维度中进行启用。</p><p><img src="/img/bVc2Ycf" alt="" title=""></p><p>如上是 APISIX 体系的架构图,底层就是刚刚提到的 NGINX + Lua 环境,再向上是 APISIX 的核心模块,它包括路由、上游等相关能力,还有一些常用功能的封装。这个核心模块也是插件框架的入口,用户在类似路由等组件中配置路由时,将被合并在此调用执行。</p><p>APISIX 在核心模块中提供了很多开箱即用的功能,比如负载均衡、动态上游、灰度发布、服务熔断、身份认证、可观测性、服务发现、限流限速和日志收集等功能。</p><p>APISIX 中的很多功能都是通过插件方式进行实现的,目前 APISIX 的插件已接近 80 个,未来还在持续扩展增加中。提到插件,不得不说 APISIX 提供的插件运行时是一个非常易于开发的插件框架,用户可以轻而易举地使用 Lua 编写自己的插件,来实现特定业务功能。如果实在不具备 Lua 的开发维护能力也可以使用外部 Plugin Runner(APISIX 多语言插件) 或者 WASM 开发插件。</p><h3>开源活跃开放</h3><p>APISIX 是 Apache 软件基金会旗下的顶级开源项目,它使用了 Apache License v2 开源协议,对商业使用与二次开发都比较友好。</p><p>同时 APISIX 社区也一直保持着活跃,包括但不限于产品技术本身的活跃(比如近一个月内有 70+ commit,处理了 70+ issue 等),还有社区活动和一些内容分享上的全球布道等。</p><p><img src="/img/bVc2Ycj" alt="" title=""></p><p>Apache Way 的理念,指导着每一个 Apache 基金会开源项目,即「社区>代码」。社区的建设至关重要,重要性甚至超过项目代码本身。回过头来我们看,为什么 APISIX 社区可以如此活跃?</p><ul><li><strong>社区开放,欢迎任何有意义的讨论(比如问题报告、新功能等)。</strong>除此之外,无论项目维护者身份如何、来自什么公司,大家都采用同样的方式参与社区。所有讨论都在邮件列表内进行,未经邮件列表讨论的事项,就是不存在的;全部开发工作也都是公开的,任何事项都有痕迹。</li><li><strong>经常与其他社区进行项目合作。</strong>比如 APISIX 的很多插件都是与其他项目或者服务的集成,像 Casbin 社区参与到 APISIX 项目中,贡献了 <code>casbin</code> 权限管理和 <code>casdoor</code> 身份认证插件。</li><li><strong>通过各种渠道吸引新的贡献者参与社区。</strong>比如 APISIX 项目已连续多年参与 Google GSoC 计划和国内开源供应链点亮计划等活动,吸引国内外对开源项目感兴趣的学生来参与其中,增加学生们对开源社区的经验。</li></ul><h3>周边项目齐开花</h3><p>上文我们提到的都是 APISIX 项目本身的一些内容。但其实,基于 APISIX 这个云原生 API 网关,也衍生出了很多周边项目。</p><p>通过使用 <strong>APISIX Ingress Controller</strong>,用户可以将 Kubernetes Ingress 资源和 APISIX 自定义的 CRD 资源提取出来,进而为 APISIX 配置路由、上游或插件等。它将自动根据 K8s Service 生成上游,并与路由进行关联,提供了一种在 K8s 中的声明式 API 的实现路径。</p><p>除此之外,APISIX 还提供自己的开源控制台实现——<strong>APISIX Dashboard</strong>,它提供了 APISIX 中最常用功能的可视化配置能力,可直接在界面上修改即可完成 API 的配置上下线。这与 APISIX Ingress Controller 提供的代码即配置的思路有所不同,前者注重配置的灵活性,而后者注重规则定义的确定性。</p><p>同时在当前最热门的服务网格领域,APISIX 也在尝试交出自己的答卷。<strong>APISIX 的服务网格项目 Amesh</strong> 现已经开源,目前的方案使用 Istio 作为控制面实现,与 Envoy 一样通过 xDS 协议与 Istio 通信,并将通信规则转换为 APISIX 中的路由与上游配置。从而替换了 Istio + Envoy 组合中 Envoy 的流量网关角色。</p><h2>APISIX 如何应对应用发展</h2><p><img src="/img/bVc2Ycl" alt="" title=""><br>现代应用系统架构已经经过多次发展,从单体应用、多单体应用+企业服务总线的 SOA 到现在的多微服务架构演化。</p><p>当单体应用迭代到微服务后,微服务本身具有一定规模后,单纯的微服务并不足以解决生产上面临的问题。其伴随的一个显著特征就是,服务的数量在大幅增长,各个服务间的调用关系也变得更加复杂。</p><p>因此,在不同的阶段其实大家都面临着很多不同的技术选型。比如服务层面,我们可以用到像是 Apache、NGINX、HAProxy 这样的反向代理工具。进入到微服务时代我们又会用到像 NGINX 或是一些更加现代化的动态 API 管理工具,这里又会因为技术栈的不同扩展出更多的产品。比如在 Java 语言侧选择 Zuul 或者 Spring Cloud Gateway 这样的组件,在 Kubernetes 部署又会有 Ingress 等选择。更进一步到服务网格架构中,又会产生 Istio + Envoy/MOSN 这种选择。</p><p><strong>各种技术手段浩如烟海,如何根据当前企业、团队的状况选择最合适的架构对架构师的能力带来很大的挑战。</strong></p><p>除了整体架构上的选择,随着团队的扩大和业务场景的复杂,对于开发者而言,需要了解和考虑的东西越来越多。这种情况下,各种各样的流量比如 HTTP/TLS、gRPC、四层的 TCP/UDP、中间件的调用(比如 Redis 缓存)等等,对它们进行统一的管理正是当下应用发展阶段所面临的困境。</p><p>APISIX 作为云原生 API 网关,为许多企业提供了一个更优的选择。在应对各种不同场景时,APISIX 有着一些流量代理的用法。比如我们可以将 APISIX 作为一个 LB 来处理对外服务的负载均衡问题,同时也可以将其用作 API 网关来解决微服务的暴露与调用,利用 APISIX Ingress Controller 还可以解决容器中的 API 管理难题。</p><p>可以看到,使用 APISIX 可以为你在不同阶段提供一个统一的、全流量的处理方式。那这种方式能为用户带来哪些收益呢?</p><ul><li>对于研发人员来说,如果一个人掌握了 APISIX 这套体系的技术,他就可以很轻松地完成企业团队中各种不同领域的工作,比如在 API 网关或者 Ingress Controller 这种流量管理的落地场景。</li><li>对于运维人员来说,使用 APISIX 后无论是后续维护现有环境还是切换到新的技术栈上,你都可以直接使用同一种工具和方法进行直接管理,而无需再投入学习和时间成本。同时作为开源产品,学习之后还可以作为自身的技术积累复用到其他工作中,可谓是一举多得。</li><li>对于公司来说,选用 APISIX 的架构不仅仅可以满足现有需求,还为将来的技术演进预留下空间。比如当下是为了满足作为 LB 的需求,如果之后公司技术发展,将来也可以无缝过渡到 API 网关或者容器平台乃至服务网格等领域。</li></ul><h2>展望:与 OpenSergo 的未来计划</h2><p>我们在上文中也提到了与其他社区的积极合作。在之前,OpenSergo 在 APISIX 社区项目仓库的 issue 中曾发起讨论,希望可以通过社区合作的方式,协商建立一个用于流量控制的统一标准,这引起了一部分社区成员的兴趣。之后在一次 APISIX 社区的中文 Weekly Meeting 中,OpenSergo 社区的伙伴也为大家介绍了这一项目的愿景、思路和价值。</p><p>作为一个开放且包容的开源社区,APISIX 是非常欢迎这种社区间合作的,通过项目协同可以发挥出各自项目更大的价值。</p><p>具体来说,由于 OpenSergo 项目标准的表现形式主要为 Kubernetes CRD, 其用于云原生的容器环境中,因此可以优先考虑将它与 APISIX Ingress Controller 项目进行集成,为其增加 OpenSergo 的配置解析和处理模块,将原始配置转换为 APISIX 的内置速率限制插件等。</p><p>这种做法有助于使用 Kubernetes 的用户来增强其流量控制能力,并与其他各种上下游生态进行集成,比如 RPC 框架。对于不使用 Kubernetes 的用户,在目前产品现状下,还缺少一个桥梁去完成 CRD 到 APISIX 插件的配置转换。</p><p>因此,我们也有参考彼此产品的一些未来规划。比如 OpenSergo 的文档中提到,它们将发布专门的 SDK 供数据面组件调用以集成至 OpenSergo。如果有了 SDK 或是其他形式的接口,在接下来的日子里,有望看到 OpenSergo 与 APISIX 的集成项目。</p><p>最后,作为开源活跃项目,APISIX 社区也非常欢迎有兴趣的伙伴来提交这些功能的实现,帮助项目的集成成为现实。相信通过开源间的合作,彼此项目都可以取得更大的成就。</p>
API 网关 Apache APISIX 集成 CNCF OpenFunction
https://segmentfault.com/a/1190000042607206
2022-10-12T09:37:38+08:00
2022-10-12T09:37:38+08:00
API7_技术团队
https://segmentfault.com/u/api7
1
<p>在无服务时代,API 网关依旧是管理和充分利用无服务器平台的关键。因此,Apache APISIX 社区也紧跟趋势,在过去的一年多时间内集成了无服务器提供商,如 <a href="https://link.segmentfault.com/?enc=8AOcrXiPpSg0Q4xhM2RSBg%3D%3D.YHn1KtykGlPZ3Pxx2p3gpQeM%2BXV9kWSxb519XXAPmyA%3D" rel="nofollow">AWS Lambda</a> 和 <a href="https://link.segmentfault.com/?enc=odQRiHMQhf%2FvZLil2J%2BX1g%3D%3D.O0s5rCj3IUIASxleCN1ytac6tToJaIhFfrXO464mMDJ3GXsSD7L9VDyCph1kQVtGH03pSmNV5mrBY4R2rLQTzRpwr5LbwgGc6I0S4kFFu7Y%3D" rel="nofollow">Azure Functions</a> ,以及开源的无服务平台 <a href="https://link.segmentfault.com/?enc=%2Bsmwne04y0yOIHrO%2F01JiQ%3D%3D.%2BSwGBFnBXPNE3gjXDyHqfSlNkvMDUDIa%2FEeXyuLvmqI%3D" rel="nofollow">Apache OpenWhisk</a> 等。</p><p>本月,APISIX 又新增了不少生态插件,其中就包括与 <a href="https://link.segmentfault.com/?enc=YdpMhmT0LkwNNvHAKxwMVg%3D%3D.4bRccxfvtnw6vnfmVlIB0qrNe0vP6oRYh6PZuzYUW88%3D" rel="nofollow">OpenFunction</a> 集成的无服务插件 <code>openfunction</code>。本文将介绍 Apache APISIX 新的无服务插件 <code>openfunction</code>,并带来更多集成细节。</p><h2>Apache APISIX</h2><p><a href="https://link.segmentfault.com/?enc=%2F6HDVewmxRc%2BNrb%2BoSWfZg%3D%3D.0oaPggecdEPgIKM60HF6rPcmnHjxCgVePskRvjz0ipQ%3D" rel="nofollow">Apache APISIX</a> 是一个动态、实时、高性能的 API 网关,提供负载均衡、动态上游、灰度发布、服务熔断、身份认证、可观测性等丰富的流量管理功能。Apache APISIX 不仅支持插件动态变更和热插拔,而且拥有众多实用的插件。</p><h2>OpenFunction</h2><p>OpenFunction 是一个云原生的开源 FaaS 平台,旨在让用户专注于自己的业务逻辑,而不必维护底层运行时环境和基础设施。</p><p>核心功能包括:</p><ul><li>与云提供商的 BaaS 解耦</li><li>可插入的架构,允许多个函数运行时</li><li>同时支持同步和异步函数</li><li>独特的异步函数支持,可以直接使用来自事件源的事件</li><li>支持直接从函数源代码生成兼容 OCI 的容器镜像。</li><li>灵活的从 0 到 n 的自动缩放</li><li>支持基于事件源的特定指标的高级异步功能自动缩放</li><li>通过引入 <a href="https://link.segmentfault.com/?enc=wHSwmTSXwohJjanklz5URw%3D%3D.jgEFeKBZAxqcgx7njyk0L09DOL8teIXItCh7bF0OTio%3D" rel="nofollow">Dapr</a> 简化同步和异步功能的 BaaS 集成</li><li><a href="https://link.segmentfault.com/?enc=%2BYLTG5NuejijAe7xRcO7kw%3D%3D.uBZl%2BrhJwExS2NXKHu4Bm7LHfGixS11uOF1GZ3b%2BuuMjwXQkcocks4qHNeaf7lvo" rel="nofollow">K8s Gateway API</a> 提供的高级函数入口和流量管理(进行中)</li><li>灵活易用的事件管理框架</li></ul><h2>集成原理</h2><p>该插件会将正在进行的请求转发到用户定义的特定 URI,并提供相关的授权细节、请求头、请求正文和查询字符串,然后将响应返回给原始的客户端。</p><p>无服务具有高度可扩展和成本低等优势,使用这种方式部署业务服务能够极大降低资源使用和投入成本。如果你正在使用 OpenFunction 作为无服务平台,你就可以使用 Apache APISIX 去代理这些函数的请求,为函数请求追加服务治理的能力。</p><p>正如前文提到的 OpenFunction 核心功能,<a href="https://link.segmentfault.com/?enc=eAZ1rtFB9Tg0vbcVEo0Hzg%3D%3D.gmHpL4NiJw4v08CbLJEsMsvd3irXu0X8e3ObXYlAo%2B2ECd92jl1mNoiuu0SMivrT" rel="nofollow">K8s Gateway API</a> 提供了函数的入口。OpenFunction 的认证方式取决于 K8s 网关,且随网关的选择而变化。因此这个插件只支持标准的 Basic Auth 认证方式。</p><h2>如何使用插件</h2><h3>步骤一:安装与运行 APISIX</h3><p>在这里,我们推荐使用 Docker 快速安装 APISIX,因此你需要预先安装 <a href="https://link.segmentfault.com/?enc=jYOUjumERG%2FSjxSv7q%2B8tw%3D%3D.OVzdqDaz6GGoscVZLnAnyvwPfQ1zKwyTJOVzmVpn8zI%3D" rel="nofollow">Docker</a> 和 <a href="https://link.segmentfault.com/?enc=MgPRdeSAPcWXy22PBqyATg%3D%3D.AyOzS9V7vGQdOsTJUDa9SAzfXA55sM9OQrr7yqeBZDsJYdECCFlxZiYFqdptyKdB" rel="nofollow">Docker Compose</a>。</p><p>安装详情以及更多安装方式请参考<a href="https://link.segmentfault.com/?enc=akQc4dALpcJrbtXPUyPOwA%3D%3D.v0zBsCqvj%2BLHr3XPfdl2o1C7n8f2%2BJuknnwVjW1mpnA9nt2%2FsOyd0f8KK8HQ8905Vmc%2Fm0ZKbLqYSv%2BmxRA6Ww%3D%3D" rel="nofollow">APISIX 安装指南</a>。</p><p>首先下载 <a href="https://link.segmentfault.com/?enc=ljGBv8QbxG9mpY2vM4%2FDsg%3D%3D.LHN4D0XLE%2FRhx%2BDzsmvV9pEzvPVXhELubJDLaxUAo92GRoxQcCQXlKnDX8%2FBmjvB" rel="nofollow">apisix-docker</a> 仓库。</p><pre><code class="shell">
git clone https://github.com/apache/apisix-docker.git
cd apisix-docker/example
</code></pre><p>然后使用 <code>docker-compose</code> 启用 APISIX。</p><p>对于 <strong>x86</strong> 系统:</p><pre><code class="shell">
docker-compose -p docker-apisix up -d
</code></pre><p>对于 <strong>ARM/M1</strong> 系统:</p><pre><code>
docker-compose -p docker-apisix -f docker-compose-arm64.yml up -d
</code></pre><p>后续的使用请参考 <a href="https://link.segmentfault.com/?enc=8OEbEaQ2UIxgYKg8L1juLg%3D%3D.3cDTxok2tKmqiwE%2F4Ag8q8D1n9pK6YeYJm%2BYhGib9SlLYdUJbpJ2vMSEpHCzhv6tdwH4eeWoToYYBXobvf5JQw%3D%3D" rel="nofollow">快速入门指南</a>。</p><h3>步骤二: 通过 Helm Chart 安装 OpenFunction</h3><p>请确保当前环境中已经安装对应版本的 Kubernetes 集群。详情可参考 <a href="https://link.segmentfault.com/?enc=cN1eHeNPnvldBbP5%2B7KLxQ%3D%3D.Huo5c0PXPbotywos%2BeaiZiUUl0BSu12V6pb6nMnaGiD92Sm6ev31ZVwwCJAGm2q9POA1MnsoQWABZHX43BBEog%3D%3D" rel="nofollow">OpenFunction 安装指南</a> 。</p><pre><code class="shell">
# 添加 OpenFunction 到 Chart 存储库
helm repo add openfunction https://openfunction.github.io/charts/
helm repo update
</code></pre><pre><code class="shell">
# 安装 OpenFunction chart
kubectl create namespace openfunction
helm install openfunction openfunction/openfunction -n openfunction
</code></pre><p>你可以通过以下命令来验证 OpenFunction 是否已经安装成功:</p><pre><code class="shell">
kubectl get pods --namespace openfunction
</code></pre><h3>步骤三:创建并推送函数</h3><p>你可以参考 <a href="https://link.segmentfault.com/?enc=HCAz68DPzWIiP8fyg1JFEQ%3D%3D.AKZLzQ3Gk7RrA7DFHgjcbFlQkTPCwwcAz%2FQzvJ%2FCeT3Jd1W%2BPAMS28OYrmvFm%2FAG" rel="nofollow">OpenFunction 官方示例</a> 创建函数。构建函数时,你需要使用以下命令为容器仓库生成一个密钥,才可以将函数容器镜像推送到容器仓库 ( 例如 Docker Hub 或 Quay.io)。</p><pre><code class="shell">
REGISTRY_SERVER=https://index.docker.io/v1/ REGISTRY_USER=<your_registry_user> REGISTRY_PASSWORD=<your_registry_password>
kubectl create secret docker-registry push-secret \
--docker-server=$REGISTRY_SERVER \
--docker-username=$REGISTRY_USER \
--docker-password=$REGISTRY_PASSWORD
</code></pre><h3>步骤四:启用插件</h3><p>你可以通过以下命令在指定路由中启用该插件:</p><pre><code class="shell">
curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"uri": "/hello",
"plugins": {
"openfunction": {
"function_uri": "http://localhost:30583/default/function-sample/test",
"authorization": {
"service_token": "test:test"
}
}
}
}'
</code></pre><p>其中 <code>9180</code>是 APISIX admin API 暴露的端口 ,而 <code>localhost:30583</code> 是假设本次通过 NodePort 方式映射的网关入口。</p><p>更多关于 OpenFunction 函数入口以及如何设置请参考<a href="https://link.segmentfault.com/?enc=0dXQzEFNE0RmVt7ufWrY9Q%3D%3D.6Vot180HCGeAaMjbLiB5mWskn6aBLvUYuRK702l%2FgmrBzApL2t9xmkQ%2BiBpgs%2F7d0R3JAOLlwtc6oQQtEYAWBs09CV8XhTrnMHBXZTqCvcs%3D" rel="nofollow">Function Entrypoints | OpenFunction</a>。</p><h3>步骤五:发送请求</h3><p>插件配置完成后,你可以向路由发送一个请求,它会调用配置好的函数。官方的示例函数 hello-world 会输出 “Hello, {函数 uri 中的后缀部分}!\n”。</p><pre><code class="shell">
# 9080 是 APISIX 除 admin API 以外暴露的端口
curl -i http://127.0.0.1:9080/hello
</code></pre><p>函数返回的响应如下:</p><pre><code>
hello, test!
</code></pre><h2>关闭插件</h2><p>当你不需要再使用该插件时,可以通过从路由配置中去除 <code>openfunction</code> 插件来禁用它的功能(注意,按照 Apache APISIX 路由 Schema 要求,在去除该插件后,如果该路由没有其他插件,你必须为该路由配置一个上游对象)。</p><pre><code class="shell">
curl http://127.0.0.1:9180/apisix/admin/routes/1 \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"uri": "/hello",
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980": 1
}
}
}'
</code></pre><h2>总结</h2><p>本文为大家介绍了 Apache APISIX <code>openfunction</code> 插件的功能与使用步骤,更多关于 <code>openfunction</code> 插件说明和完整配置列表,可以参考<a href="https://link.segmentfault.com/?enc=L4wIXS7k%2FjTF9FGQhlrDcA%3D%3D.E8RhD7B%2Fxyyo99r6O1NJ6CNrUR%2FrAI0buHi9fFWQvHBHz1jABXa4WIm69UzsnGCc5Mp5QfdEYLjhq3%2FqxMiCaQ%3D%3D" rel="nofollow">官方文档</a>。</p><p>目前,APISIX 社区也在开发其他 Serverless 插件以便与更多云服务进行集成。如果你对此类集成项目感兴趣,也欢迎随时在 <a href="https://link.segmentfault.com/?enc=taR%2FwmPJFDsGhpksJQ935w%3D%3D.%2B3aga8o3l%2FvNXajmm%2BbgX2o5ZYzEq7fo6aPFadRkaMXezlSPoDxJFNZTADejujv1" rel="nofollow">GitHub Discussions</a> 中发起讨论,或通过<a href="https://link.segmentfault.com/?enc=Tol5GNCRmII6ZrFnY6cIcg%3D%3D.MZIyDW2OTI1f%2BN1Fv2GKUhMKEhOr%2BqEStzkTlkpP1RYeDELd0SG1A2kPcK1VP%2Big" rel="nofollow">邮件列表</a>进行交流。</p><h2>关于 API7.ai 与 APISIX</h2><p>API7.ai(<a href="https://link.segmentfault.com/?enc=8nnIM98nNxC2BQxyzkr7qw%3D%3D.gqsZjBrFWpg32IjsS3WJBHxIGw0pCre7uWZdwC1UUzs%3D" rel="nofollow">支流科技</a>)是一家提供 API 处理和分析的开源基础软件公司,于 2019 年开源了新一代云原生 API 网关 -- APISIX 并捐赠给 Apache 软件基金会。此后,API7.ai 一直积极投入支持 Apache APISIX 的开发、维护和社区运营。与千万贡献者、使用者、支持者一起做出世界级的开源项目,是 API7.ai 努力的目标。</p>
放弃 SpringCloud Gateway!Apache APISIX 在「还呗」业务中的技术实践
https://segmentfault.com/a/1190000042594100
2022-10-10T15:09:04+08:00
2022-10-10T15:09:04+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<p>不同行业之间,都会存在一些业务属性上的差距。对于金融领域的应用软件来说,因其涉及到钱等因素,所以在业务上会有以下独特属性:</p><ul><li><strong>稳定性。</strong>金融领域跟钱强相关,这对于业务稳定性就有着非常严格的要求,稳定性一旦出现问题,它将影响整个交易系统的成败。</li><li><strong>强监管。</strong>强监管一般是针对生物医药领域、医疗领域和金融领域,因为它们所呈现的内容都与人的生命相关。所以,更高层面的强监管要求势必会影响一些业务层面的选型和架构呈现。</li><li><strong>准确性和有效性。</strong>由于跟钱强相关,所以在数字层面的呈现更是要求零偏差。就像股票价格一样,它的数字呈现都是精确到了每分每秒和固定数位的。</li></ul><p>基于以上这些特点,金融行业软件系统在进行系统设计、机房拓扑以及中间件选型时,就会出现一些与其他通用行业不太一样的地方。</p><h2>对 Java 的那些爱与恨</h2><h3>金融系统为何独爱 Java</h3><p>Java 自诞生以来就深受开发者的喜爱。在中国有将近 50% 的开发者在使用 Java 作为开发语言。这不单单是因为其语言的优势,也因为 Java 相关的生态非常庞大,尤其是国内的金融系统很多都是基于 Java 的,这导致有段时间大家都误以为所有的系统都是用 Java 做的。</p><p>近 15~20 年以来,大部分金融系统基本都选择了 Java 技术栈,深究其原因,我们认为主要是因为 Java 技术栈有以下几点优势。</p><p><img src="/img/bVc2SPL" alt="" title=""></p><p>正是如此,Java 逐渐得到了金融类软件系统的青睐。</p><h3>云原生时代下的 Java 现状</h3><p>随着技术行业的快速发展,单体架构逐渐被淘汰,微服务和云原生时代正在风靡四海。然而在近几年的技术大环境下,作为面向对象的高级语言,Java 也在一些业务场景中开始略显疲惫:</p><p><img src="/img/bVc2SPJ" alt="" title=""></p><p>首先,Java 性能较低,这点对比一下 C 语言相关技术栈就会明白。Java 是基于虚拟机,它的内存管理是交给虚拟机来解决的,所以当面对一些高性能或动态变化的业务场景时,Java 语言在处理上没有那么强势。</p><p>其次,Java 语言需要更多的资源。一个架构的打造如果不考虑成本,很多问题都很好解决,但在云原生时代下,所有的资源计算变得越来越细、越来越颗粒化。Java 在运作时需要消耗大量的资源,由于 Java 分量重和需要重启的基础特性,因此在高 QPS 或者业务连续性要求较高的场景下,该语言会更容易出现问题。</p><p>最后就是指针变量的问题。习惯于写 C/C++ 语言的同学都知道,指针是一个非常好的资源。但 Java 是基于虚拟机,它把内存管理交给了 GC(Garbage Collection),而不是由手动程序进行管理,所以对于一些特定情况或者高并发、高访问量和高性能的场景下,Java 的实际性能可能就略显不足了。</p><h2>还呗为何选择 APISIX?</h2><p>数禾科技是一家提供智能化金融的服务平台,旗下主要产品有还呗、还享花等。还呗 APP 是一款基于消费多场景的分期服务平台,通过与持牌金融机构合作,为大众提供个人消费信贷服务,并为小微企业主提供贷款资金支持。在业务架构层面,还呗的产品实现一直是依赖 Java 技术栈的。</p><p>Spring Cloud Gateway 是 Spring Cloud 生态下为更好管理微服务而诞生的网关项目,对于公司业务以 Java 为主要开发语言的情况下,Spring Cloud Gateway 通常是个不错的 API 网关选择。但在近期的 API 网关迭代过程中,还呗放弃了使用已久的 Spring Cloud Gateway,而是选择了 Apache APISIX。</p><h3>架构的前后变化</h3><p>在架构层面,还呗在使用 APISIX 前后呈现了如下图所示的变化。</p><p><img src="/img/bVc2SPH" alt="" title=""></p><p>在左侧的使用前架构中,还呗一共使用了三套网关系统,并把网关分为入口网关和出口网关两大类。其中在运营系统网关和出口系统网关中,都使用了 Spring Cloud Gateway 作为网关,而在业务系统网关中则使用了 OpenRestry 作为业务系统网关。</p><p>对于一开始使用 Spring Cloud Gateway 作为运营和出口系统网关,主要是看中了 Spring Cloud 庞大的生态系统,以及简单易部署和易维护的分布式系统开发框架,所以在早期进行业务架构部署时,为了更快搭建起业务而选择使用 Spring Cloud 全家桶。</p><p>但随着业务慢慢发展,原先架构中的网关开始出现一些稳定性的问题,比如内存溢出、CPU 使用率过高等情况。为了升级网关性能及统一多个网关,还呗将架构中的网关全部统一替换为了 Apache APISIX。</p><p>在新网关架构中,业务系统网关会优先把请求流量通过服务发现的方式直接转发到业务系统。如果后端应用在 Consul 中没有健康 Pod 或者后端应用不支持服务发现等,就会把流量转发到以前的内网 K8s Ingress,作为兜底的上游来使用。</p><p>新架构同时也统一了出口网关的两个应用,新出口网关部署在 K8s 集群外的外联区。同时也在出口网关集群前新增一个 SLB,可以统一出口网关的入口 ,方便没有服务发现能力的应用或者其他 VPC 内的系统调用。</p><h3>基于 APISIX 的应用实践</h3><p>实际业务情况下,由于内部已存在多种网关架构,没办法直接使用 Apache APISIX,于是还呗基于 APISIX 进行了一些改造和构建。</p><h4>APISIX 构建部署</h4><p>在内部进行开发时,将 APISIX 网关的代码和定制代码存放在不同路径下,两者协同工作,各自可独立迭代。在部署时则采用 Docker 镜像方式部署,构建一个 APISIX 指定版本的基础镜像,然后再把自定义代码打包形成新镜像。</p><p>自定义代码打包时没有使用 <code>lua_package_path</code> 来指定代码目录,而是直接覆盖基础镜像 <code>apisix</code> 源码目录,如果有同名文件则覆盖源码文件。Dockerfile 如下所示:</p><pre><code class="Dockerfile">FROM registry.xxx.net:5001/apisix-shuhe:v1.5
ENV APP_NAME={{APP_NAME}}
COPY {{PRODUCT_FILE}} /tmp/deploy2/artifact.tar.gz
RUN mkdir /tmp/deploy/ && tar -xf /tmp/deploy2/artifact.tar.gz -C /tmp/deploy/ && \
cp -R /tmp/deploy/apisix/ /usr/local/apisix/ && \
cp /tmp/deploy/bin/apisix /usr/bin/apisix && \
cp /tmp/deploy/conf/apisix-$APP_NAME.yaml /usr/local/apisix/conf/apisix.yaml && \
cp /tmp/deploy/conf/config-$APP_NAME.yaml /usr/local/apisix/conf/config.yaml && \
set -x && \
bin='#! /usr/local/openresty/luajit/bin/luajit\npackage.path = "/usr/local/apisix/?.lua;" .. package.path' && \
sed -i "1s@.*@$bin@" /usr/bin/apisix && \
rm -rf /tmp/*</code></pre><p>APISIX 的日志默认存储在本地(也可以通过 Syslog 等插件收集),通过调整 <code>nginx</code> 配置模板和判断启用的 Profile 来决定日志存储在本地还是通过 Syslog 存储到 FLUENTD 中。同时在构建镜像时替换模板中 <code>FLUENTD_HOST</code> 变量。如下所示:</p><pre><code class="Nginx">{% if gw_profile and string.find( gw_profile,'local') then %}
access_log logs/access.log main;error_log logs/
error.log warn;
{%else%}
access_log syslog:server=${FLUENTD_HOST}:5141 json_format;
error_log syslog:server=${FLUENTD_HOST}:5142 warn;
{%end%}</code></pre><p>在 <code>nginx</code> 配置模板中,还呗不光修改了日志存储,还调整了循环添加 ENV 环境变量、循环添加 lua_shared_dicts 配置及写死一些 NGINX 其他调优参数。</p><p>因为公司是按照业务流量划分为多种网关,这些网关的基本功能都差不多,因此还呗内部采取了「一套代码部署多个网关应用」方案。通过 Profile 功能配置各个网关的 config-xxx.yaml 文件,然后通过公司 DEVOPS 平台构建镜像时,根据应用名构建不同网关的 Docker 镜像即可。</p><h4>公司级定制插件</h4><p>在内部访问运营系统页面时,会调用很多后端的 API 获取数据,这些 API 都需要在 API 网关中配置对应的白名单。在页面中根据登录运营系统用户的角色不同,能够访问的 API 范围也不一样,因此权限系统也需要维护相关 API 列表。每当在页面上新增后端 API 调用时 ,都需要开发人员在网关页面及权限系统页面配置两次,工作冗余且重复。</p><p>为此,还呗把网关配置与权限系统配置打通,只保留权限配置系统的配置入口,网关配置管理系统则定时拉取权限 API,之后转换成网关 API 白名单配置。这样做不仅能减少用户一次配置操作,同时也协助权限系统进行了权限管控。可以保证在运营页面调用的后端 API,一定是在权限系统配置了相关权限。</p><p><img src="/img/bVc2SPE" alt="" title=""></p><p>在公司的实际业务中,经常会遇到原生插件不能满足实际需求的情况,就需要定制开发。好在 APISIX 提供了很多工具类,参照原生插件就可以轻松实现,开发过程也非常简单。以下列举了还呗内部基于 APISIX 进行的其他定制插件:</p><p><img src="/img/bVc2SPA" alt="" title=""></p><h4>网关流量灰度</h4><p>之前还呗使用的 K8s 容器是 OpenShift(现已升级为 ACK 集群),其中 Ingress 是 Haproxy 搭建。由于公网 K8s Ingress 的 Haproxy 不能把一个域名的流量转发到两个 Namespace 的路由中,因此考虑把新网关部署在和老网关相同的 Namespace 下。即域名的路由下挂载多个服务,之后便可以通过路由调整流量比例,控制流量走新网关还是老网关。</p><p>具体实施流程如下图所示,在老网关的 Namespace 下新增 c、d 组用于部署新网关,通过路由控制新老网关的流量比例。</p><p><img src="/img/bVc2SPx" alt="" title=""></p><h2>Java 层面网关的考虑因素</h2><p>很多 Java 工程师在微服务架构中都会选择 Spring Cloud,主要是语言绑定,并用类库的方式放在代码里。但是在实践过程中可能会出现升级困难的情况,如果团队是多语言就需要维护多个类库,假设有 10 个版本与 10 种语言,就需要维护 100 个类库。</p><p><img src="/img/bVc2SPp" alt="" title=""></p><p>此时就可以通过代理的方式(即 API 网关)把多版本和多语言的问题轻松解决。那 Java 技术栈公司选择 APISIX 作为 API 网关后都有哪些收益?我们根据还呗的实践经历,从以下两个角度进行了总结。</p><h3>公司角度</h3><ol><li><strong>功能与性能兼具</strong></li></ol><p>还呗在内部使用 4 核虚拟机无插件空跑压测 APISIX 的 QPS 可以达到 80K,很好地解决了 Spring Cloud Gateway 在承接 C 端流量时出现的性能问题,<strong>而且在生产环境中发现 APISIX 相较于之前网关性能提升了 30% 以上。</strong></p><p><img src="/img/bVc2SPw" alt="" title=""></p><p>其次,得益于云原生属性,APISIX 在实际的测试中完全可以满足公司的需求,比如认证鉴权、可观测性、服务发现、限流限速以及四层和七层流量转发。而在功能扩展方面,APISIX 也支持了 70 余款插件,大部分的业务可以使用其原生插件,很大程度上减少了开发工作。</p><ol start="2"><li><strong>业务支出成本下降</strong></li></ol><p>在使用 APISIX 之前,如果性能出现了瓶颈,公司只能通过不断的增加服务器来解决这个问题,因此相应的硬件成本也会非常的高。</p><p>还呗在进行成本计算时发现,使用 APISIX 后,服务器的数量大概<strong>减少了 60% 左右</strong>。统一技术栈后,业务上也可以很轻松地基于 APISIX 原生框架实现功能的扩展,节省了开发成本,加快了项目上线时间速度。</p><h3>开发者角度</h3><ol><li><strong>满足业务需求</strong></li></ol><p>业务中所使用的软件或技术都应该是为需求而服务。从实际测试结果及调研数据来看,APISIX 的稳定性、可观测性、可扩展性会更好。</p><p>软件最终服务于业务。如果业务需要,可以为公司节约资源,那么无论公司的技术栈是什么,都会使用最符合公司业务的组件。</p><ol start="2"><li><strong>降低维护成本</strong></li></ol><p>相比之前使用的 OpenResty,APISIX 的学习成本相对较低,维护起来也比较省心。同时,APISIX 丰富的插件简化了一些通用功能的实现与部署,大大节约了项目上线的时间。</p><p>同时利用 APISIX 强大的日志和动态调试功能,业务可以很轻松地排查出故障点,从而快速定位、节约时间。</p><h2>总结:金融业务发展趋势</h2><p>在过去的十年里,互联网金融从「野蛮生长」开始逐渐向「精耕细作」模式转变,这个转变主要涉及到的就是系统的变革。</p><p>在野蛮生长阶段,业务讲究的是效率。为了业务更快速地建设,在基础架构选择的时候,负责人更多是选择自己熟悉的语言架构进行搭建。不同的负责人便会选择使用不同的技术栈,因此留下了很多技术债务。从 2017 年开始,依旧活跃的金融企业或服务公司大多都会面临同样的技术现状,那就是存在多套技术组件。这时就需要进行基础设施的统一。</p><p>来到精耕细作阶段,企业就需要对系统进行垂直拆分,由以前的烟囱式拆分成前台、中台及后台等模式。系统到达一个稳定阶段时,就需要把一些东西夯实下来。</p><p>而系统建设的根本目的其实就是为了共用。重复性使用越强,系统的运维成本就越低。所以很多公司到了精耕细作阶段,要么是进行系统的垂直拆分,要么就是进行基础组件的下沉,进而控制运维成本。</p><p>作为企业来说,成本优先依旧是需要考虑的原则。野蛮生长阶段可能只需要尽快实现业务,而在目前大环境下,预算范围之内肯定是成本优先。这样的话,效率和成本永远只能保住一项。因此在成本有限的情况下,企业就会少谈技术的先进性。技术人员在选型的过程中,就不会考虑当下选择的这个技术对团队有多大冲击、对现有的运维和架构带来多少收益等等,更多是从成本角度考虑。</p>
Apache APISIX 集成 Elasticsearch 实现实时日志监控
https://segmentfault.com/a/1190000042562150
2022-09-29T17:10:38+08:00
2022-09-29T17:10:38+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<blockquote>本文将为你介绍 Apache APISIX 的 elasticsearch-logger 插件的相关信息,并通过此插件获取 APISIX 的实时日志。</blockquote><h2><strong>背景信息</strong></h2><p><strong>Apache APISIX</strong> 是一个动态、实时、高性能的 <strong>API</strong> 网关,提供了负载均衡、动态上游、灰度发布、服务熔断、身份认证、可观测性等丰富的流量管理功能。作为 <strong>API</strong> 网关,<strong>Apache APISIX</strong> 不仅拥有丰富的插件,而且支持插件的热加载。</p><p><strong>Elasticsearch</strong> 是一个基于 <a href="https://link.segmentfault.com/?enc=pbdtY%2BikbkDj79PNmOZrUg%3D%3D.NgGQd8B%2BJbwyJXRs7VMX%2FP2FB5vfyhxXMpmNS0RxtmmYLdRUd2o8EQb1SGhInFOi" rel="nofollow">Lucene</a> 库的搜索引擎。它提供了分布式、RESTful 风格的搜索和数据分析引擎,具有可扩展性、可分布式部署和可进行相关度搜索等特点,能够解决不断涌现出的各种用例。同时还可以集中存储用户数据,帮助用户发现意料之中以及意料之外的情况。</p><h2><strong>插件介绍</strong></h2><p><strong>APISIX</strong> 以 <strong>HTTP</strong> 请求的方式向 <strong>Elasticsearch</strong> 发送 <strong>APISIX</strong> 的 <strong>Runtime</strong> 日志。插件 <code>elasticsearch-logger</code> 采用 <a href="https://link.segmentfault.com/?enc=6UQ48RhM6ZlNRYB6Hixzkw%3D%3D.gOFFuT6N%2B4bmZDmDP3eoox%2FuGDzWcxTaOHPwJUGSzWMJiWgiHtN0Ro3QW0oQZNrKks3zWRV9fpSKRbpnW3XtDKbxX%2FcJj4tKj2LJeowlHfiKRr9dBYPS6Oo95CRHfpFM" rel="nofollow">bulk</a> 的格式进行日志上报,这允许 <strong>APISIX</strong> 可以将多条日志合并后再进行上报,这使得 <strong>APISIX</strong> 在对 <strong>Elasticsearch</strong> 进行日志上报方面更加灵活并且具有较好的性能。你可以参考文档 <a href="https://link.segmentfault.com/?enc=ViOtgywTHNrahsaYtWIudg%3D%3D.UM4S1bT%2BKlGodRLwN5rjHXCYS4d0MfEDGiJ45EB0po4q%2FemwAwjQ1S5R3nLiLg7HIP4xSzPOGtqzHsJaqSOmkw%3D%3D" rel="nofollow">APISIX 批处理器</a> 对日志合进行更加细致的配置。</p><h2><strong>配置步骤</strong></h2><p>首先,你需要安装完成 <strong>APISIX</strong>,本文所有步骤基于 <strong>Centos 7.5</strong> 系统进行。详细的安装步骤参考 <a href="https://link.segmentfault.com/?enc=ou7JF%2BzjFVE6%2FXbopMzNBA%3D%3D.YQba0hWIZs%2BqOSdBdIbhmjTZo%2F0EsoPmZN0kHd7%2FFYjeN9zqw2n6T8kn7Jdp8EDjRSM0YiPgtJl%2B38SeXZq6HQ%3D%3D" rel="nofollow">APISIX 安装指南</a>。</p><h3><strong>步骤1:启动 Elasticsearch</strong></h3><p>本示例只演示了通过 <code>docker-compose</code> 启动 <strong>Elasticsearch</strong> 单节点的方式,其它启动方式可参考 <a href="https://link.segmentfault.com/?enc=XvJwzQHbtIv5ZFxY2Q%2BgiQ%3D%3D.mtXqEDLBrwcGpSjGpvoWO8xkGgznJ0GmfwBCxtlJGuyeR6JdSwWZwMw8P5D5R4DnPRl01uR7U%2B8hJCjxJFySdg%3D%3D" rel="nofollow">Elasticsearch 官方文档</a>。</p><pre><code class="Dockerfile"># 使用 docker-compose 启动 1 个 Elasticsearch 节点, 1 个 kibana
version: '3.8'
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:7.17.1
container_name: elasticsearch
environment:
ES_JAVA_OPTS: -Xms512m -Xmx512m
discovery.type: single-node
xpack.security.enabled: 'false'
networks:
- es-net
ports:
- "9200:9200"
- "9300:9300"
kibana:
image: docker.elastic.co/kibana/kibana:7.17.1
container_name: kibana
environment:
ELASTICSEARCH_HOSTS: http://elasticsearch:9200
I18N_LOCALE: zh-CN
networks:
- es-net
depends_on:
- elasticsearch
ports:
- "5601:5601"
networks:
es-net:
driver: bridge</code></pre><h3><strong>步骤2:创建路由并<strong><em><em>配置</em></strong></em>插件</strong></h3><p>APISIX 默认配置文件中已启用 <code>elasticsearch-logger</code> 插件,所以你只需要通过下方命令创建路由并配置 <code>elasticsearch-logger</code> 插件就可以在 APISIX 中正常使用了。</p><pre><code class="Shell">curl http://127.0.0.1:9180/apisix/admin/routes/1 \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"plugins":{
"elasticsearch-logger":{
"endpoint_addr":"http://127.0.0.1:9200",
"field":{
"index":"services",
"type":"collector"
},
"ssl_verify":false,
"retry_delay":1,
"buffer_duration":60,
"max_retry_count":0,
"batch_max_size":1000,
"inactive_timeout":5,
"name":"elasticsearch-logger"
}
},
"upstream":{
"type":"roundrobin",
"nodes":{
"127.0.0.1:1980":1
}
},
"uri":"/elasticsearch.do"
}'</code></pre><p>上述代码中配置了 <strong>Elasticsearch</strong> 地址、目标 <code>field</code>,用户名与密码。</p><p>通过上述设置,就可以实现将 <code>/elasticsearch.do</code> 路径的 <strong>API</strong> 请求日志发送至 <strong>Elasticsearch</strong> 的功能。</p><h3><strong>步骤3:发送请求</strong></h3><p>接下来我们通过 <strong>API</strong> 发送一些请求。</p><pre><code class="Bash">curl -i http://127.0.0.1:9080/elasticsearch.do\?q\=hello
HTTP/1.1 200 OK
...
hello, world</code></pre><p>此时你可以登录 <strong>Kibana</strong> 控制台检索查看相关日志:</p><p><img src="/img/bVc2Kwm" alt="" title=""></p><h2><strong>自定义日志结构</strong></h2><p>当然,在使用过程中我们也可以通过 <code>elasticsearch-logger</code> 插件提供的元数据配置,来设置发送至 <strong>Elasticsearch</strong> 的日志数据结构。通过设置 <code>log_format</code> 数据,可以控制发送的数据类型。</p><p>比如以下数据中的 <code>$host</code>、<code>$time_iso8601</code> 等,都是来自于 <strong>N</strong><strong>GINX</strong> 提供的内置变量;也支持如 <code>$route_id</code> 和 <code>$service_id</code> 等 <strong>Apache APISIX</strong> 提供的变量配置。</p><pre><code class="Bash">curl http://127.0.0.1:9180/apisix/admin/plugin_metadata/elasticsearch-logger \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"log_format": {
"host": "$host",
"@timestamp": "$time_iso8601",
"client_ip": "$remote_addr"
}
}'</code></pre><p>通过发送请求进行简单测试,可以看到上述日志结构设置已生效。目前 <strong>Apache APISIX</strong> 提供多种日志格式模板,在配置上具有极大的灵活性,更多日志格式细节可参考 <a href="https://link.segmentfault.com/?enc=e8QITIgDT1PFVMopWKCwug%3D%3D.MY3w9ybj%2BlPXAajPqT9zoYRJsFDlL4vMOBth17lCNTUCN3fkequesmySPkoP3NTgKz%2BPB1B7uqd3LG77RLD3LLve%2BpitPA3PWOWG%2B8O6mcM%3D" rel="nofollow">Apache APISIX 官方文档</a>。</p><p>此时你可以登录 <strong>Kibana</strong> 控制台检索查看相关自定义日志:</p><p><img src="/img/bVc2Kwq" alt="" title=""></p><p>如需关闭自定义日志结构,可参考下方操作。</p><pre><code class="Bash">curl http://127.0.0.1:9180/apisix/admin/plugin_metadata/elasticsearch-logger \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X DELETE</code></pre><p>此时,插件 <code>elasticsearch-logger</code> 将使用默认格式上报日志。</p><h2><strong>关闭插件</strong></h2><p>如使用完毕,只需移除路由配置中 <code>elasticsearch-logger</code> 插件相关的配置并保存,即可关闭路由上的插件。得益于 <strong>Apache APISIX</strong> 的动态化优势,开启和关闭插件的过程都不需要重启 <strong>Apache APISIX</strong>。</p><pre><code class="Bash">curl http://127.0.0.1:9080/apisix/admin/routes/1 \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"methods": ["GET"],
"uri": "/hello",
"plugins": {},
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980": 1
}
}
}'</code></pre><h2><strong>总结</strong></h2><p>本文为大家介绍了关于 <strong>elasticsearch-logger</strong> 插件的功能与使用步骤,更多关于 <strong>elasticsearch-logger</strong> 插件说明和完整配置列表,可以参考官方文档。</p><p>也欢迎随时在 <a href="https://link.segmentfault.com/?enc=RHrYLp%2FuY9XUIonHu9e12A%3D%3D.ctXBhzPB4AWXPutIeIIzJa1wfCthVPO0vdICf2Hb4Kcua52NwD%2FOnH%2Fm5kIQoWUg" rel="nofollow">GitHub Discussions</a> 中发起讨论,或通过<a href="https://link.segmentfault.com/?enc=KfFatlpBI7Nc0a7FJvMZHw%3D%3D.oCmS%2BNMEsjzWzTnqYomXhvoF0UvbR7QMmenp5GTQfPMwIfzrkhfPibTSZQC6RT2p" rel="nofollow">邮件列表</a>进行交流。</p>
A coredump story about NGINX ctx and error_page
https://segmentfault.com/a/1190000042559152
2022-09-29T09:14:27+08:00
2022-09-29T09:14:27+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<p>线上业务 coredump 是一件让工程师很紧张的事情,特别是每次崩溃都是出现在凌晨。我自己十几年前亲身经历过一次,当时为了定位这个只有凌晨才出现的“幽灵”bug,整个团队是下午回家睡觉,晚上 12 点到公司一起等待崩溃的现场,然后逐个服务来排查。想象一下,漆黑的夜晚和办公楼,只有几个工位亮着灯,守着屏幕上不断跳动的监控数据和日志,既紧张又刺激。我们守了三个晚上,才定位到bug并解决掉,这是难忘的一段经历。</p><p>没想到,这种经历又来了一次。只不过这次不是我们自己的服务,而是客户的线上服务出现了 coredump。这比我们自己完全可控的环境更有挑战:</p><ul><li>只有线上业务发生错误,在预生产和测试环境都无法复现;</li><li>在开源的 Apache APISIX 基础上有自定代码,这部分代码在单独签署 NDA 之前看不到;</li><li>不能增加调试日志;</li><li>只在凌晨 3 点出现,需要调动客户更多资源才能即时处理。</li></ul><p>也就是:<strong>没有复现方法,没有完整源代码,没有可以折腾的环境,但我们要定位原因,给出复现办法,并给出最终修复方案</strong>。期间有挑战有收获,在这里记录下解决问题过程中碰到的一些技术点,希望对大家排查 NGINX 和 APISIX 问题有借鉴。</p><h2><strong>问题描述</strong></h2><p>用户原来使用 APISIX 2.4.1 版本,没有出现问题,升级到 2.13.1 版本后,开始周期性出现 coredump,信息如下:</p><p><img src="/img/bVc2JJj" alt="" title=""></p><p>从 coredump 信息中能看出来 segmentation fault 发生在 <a href="https://link.segmentfault.com/?enc=sRpLVhA5QbiehK6eS4D6qg%3D%3D.Fvn9HSQBeLvqUDmXxdKk8SM6APETmWZoWTXp%2FvbtM%2Bt4TkqeXTJ2ggrlBNEPuLlS7FlwIswBAFBQJIzLMPOFSQFtUPe4GyW3fg8eA6IGsdX4F14K7EdkSYBsg%2Fpkladg" rel="nofollow">lua-var-nginx-module</a> 中。对应的内存数据(只粘贴了部分数据)如下:</p><pre><code class="Plain">#0 0x00000000005714d4 in ngx_http_lua_var_ffi_scheme (r=0x570d700, scheme=0x7fd2c241a760)
at /tmp/vua-openresty/openresty-1.19.3.1-build/openresty-1.19.3.1/../lua-var-nginx-module/src/ngx_http_lua_var_module.c:152
152 /tmp/vua-openresty/openresty-1.19.3.1-build/openresty-1.19.3.1/../lua-var-nginx-module/src/ngx_http_lua_var_module.c: No such file or directory.
(gdb) print *r
$1 = {signature = 0, connection = 0x0, ctx = 0x15, main_conf = 0x5adf690, srv_conf = 0x0, loc_conf = 0x0, read_event_handler = 0xe, write_event_handler = 0x5adf697,
cache = 0x2, upstream = 0x589c15, upstream_states = 0x0, pool = 0x0, header_in = 0xffffffffffffffff}</code></pre><p>可以发现,此时内存数据是存在问题的。</p><h2><strong>分析前的想法</strong></h2><p>对于 segmentation fault 的错误,一般有两种情况:</p><ol><li>问题代码产生了一个非法地址的读/写,例如数组越界,这种情况下会立即出现 segmentation fault。</li><li>问题代码产生了一个错误的写,但是修改的是合法地址,并没有马上产生 segmentation fault。在后续程序的运行过程中,因为访问了该地址的数据,产生了 segmentation fault, 例如错误修改了指针的值,后续在访问这个指针的时候,就可能触发 segmentation fault。</li></ol><p>对于情况 1,如果能拿到调用栈,直接查看调用栈,可以很快定位到问题。</p><p>对于情况 2,<strong>由于不是问题发生的第一现场,产生错误的代码和触发 segmentation fault 的代码可能不在同一个位置,甚至毫不相干,排查起来就相当麻烦</strong>,这时我们只能尽可能多的采集崩溃位置的上下文信息,比如:</p><ul><li>当前的 APISIX 配置具体细节</li><li>当前的请求处理阶段</li><li>当前的请求细节</li><li>当前有多少并发连接数</li><li>当前的错误日志等</li></ul><p>通过这些信息,尝试找到问题的复现场景和通过 review 代码来发现问题。</p><h2>分析过程</h2><ol><li><h4>排查现场</h4></li></ol><p>经过仔细的排查,发现问题都集中出现在晚上 3 点和 4 点,并且 coredump 前可能会有如下错误日志:</p><p><img src="/img/bVc2JJk" alt="" title=""></p><p>最后发现错误日志跟用户的操作有关,因为一些原因这个时间点<strong>所有的<strong><em><em>上游</em></strong></em>信息都会被清空</strong>。所以初步怀疑问题跟<strong>清空<strong><em><em>上游</em></strong></em>的操作</strong>有关,可能是因为错误返回后进入了某个异常分支引发了 coredump 。</p><ol start="2"><li><h4>拿到 Lua 调用栈</h4></li></ol><p>因为 <a href="https://link.segmentfault.com/?enc=MUxSge7qvO2QQ0FmG%2BTm4Q%3D%3D.bQe5DOMDDq8szvkDGvgoukSil9lZ7DAS2T9VwHN%2B%2FUY%3D" rel="nofollow">GDB</a> 不能追踪 Lua 调用栈,不能确定是在哪个位置的调用出现了问题。因此首先要做的肯定是拿到完整的调用栈,我们可以通过以下两种方式获取到调用栈:</p><ol><li>在 lua-var-nginx-module 库中相应的地方加上打印调用栈的操作,例如 <code>print(</code>`debug.traceback<code>(</code>...<code>`))</code>,缺点就是会产生非常多的错误日志,影响到线上环境。</li><li>使用 <a href="https://link.segmentfault.com/?enc=WsWJVZw9kG3mM9zfrJYvXg%3D%3D.K2w7F%2B8vXrNihpLyK%2FOWbw%3D%3D" rel="nofollow">API7.ai</a> 维护的 <a href="https://link.segmentfault.com/?enc=02s7JGT96TEUFEpUtwJS1w%3D%3D.VmmRtEgywidJtmyDnmoIjJhYfSrtqgokJlFMGawEnPXeCep1MbN2zS5dhOXDz1yR" rel="nofollow">openresty-gdb-utils</a> ,这个库通过 GDB 的 DSL 和 python 接口扩展了分析 Lua 调用栈的能力,但是注意编译 Luajit 时需要开启调试符号,这个在 APISIX 中是默认开启的。</li></ol><p>忽略中间过程,最后拿到了如下调用栈。</p><p><img src="/img/bVc2JJl" alt="" title=""></p><p><img src="/img/bVc2JJm" alt="" title=""></p><p>结合 Lua 和 c 的调用栈,可以查到 coredump 是因为在用户的 prometheus 插件中调用了 <code>ngx.ctx.api_ctx.var.scheme</code>,但是为什么会崩溃,我们还需要进一步分析。</p><ol start="3"><li><h4>确认是缓存引起的问题</h4></li></ol><p>出错发生在从<code>ngx.ctx.api_ctx.var</code>中获取变量的场景,调用了上文提到的<code>lua-var-nginx-module</code> 模块,它为了提升效率缓存了当前请求,联想到出问题时请求体值有异常,这个提前缓存的请求体是否正确值得怀疑。为了验证这一点,我们不再使用缓存, 改为每次都重新获取。</p><pre><code class="SQL">else
--val = get_var(key, t._request)
val = get_var(key, nil) <============ t._request change to nil
end</code></pre><p>用户使用新版本挂测了一晚上,问题不再出现,<strong>证明了缓存的请求体确实<strong><em><em>存在</em></strong></em>问题</strong>。</p><ol start="4"><li><h4>找到出问题的 ngx.ctx</h4></li></ol><p>缓存的请求体保存在 <code>ngx.ctx</code> 中,而可能修改<code>ngx.ctx</code>的只有如下位置 <a href="https://link.segmentfault.com/?enc=C48Yt2%2BV7UrE5idSKfMUvw%3D%3D.Qgpz6D9h%2B5hNB73OzRRv%2B7LeLJPmvCgpyW39%2BR84C4Rzy8Nb98extDVADtE6WNUzVwxXcOwH0xh%2FmF21tL1BhjtPzvfGPeEAlNedh3o2PLa8VXZS2nTddx6vVfeEoyOE19t9rhD%2B4dClaoNtJFJVqw%3D%3D" rel="nofollow">apisix/init.lua</a>。</p><pre><code class="SQL">function _M.http_header_filter_phase()
if ngx_var.ctx_ref ~= '' then
-- prevent for the table leak
local stash_ctx = fetch_ctx()
-- internal redirect, so we should apply the ctx
if ngx_var.from_error_page == "true" then
ngx.ctx = stash_ctx <================= HERE
end
end
core.response.set_header("Server", ver_header)
local up_status = get_var("upstream_status")
if up_status then
set_resp_upstream_status(up_status)
end</code></pre><p>这里为什么需要恢复 ngx.ctx?因为 ngx.ctx 保存在 Lua 注册表中,通过注册表的索引可以找到对应的 ngx.ctx。而索引保存在 nginx 请求结构体的 ctx 成员里,每次内部跳转,nginx 都会清空 ctx,导致找不到索引。</p><p>APISIX 为了解决这个问题,创建了一个 table,在跳转前先在 table 中保存当前的 ngx.ctx,然后用 ngx.var 记录 ngx.ctx 在这个 table 中的位置,等到需要恢复的时候,就可以直接从 table 中拿到 ngx.ctx。更多细节可以参考文章:<a href="https://segmentfault.com/a/1190000009485897">对 ngx.ctx 的一次 hack</a> 。</p><p>上图中的代码需要用户配置 <a href="https://link.segmentfault.com/?enc=YCx6%2FMI3%2FvErKVkHYC%2FO4Q%3D%3D.c%2Bltgr%2BntGiK7ARTSDBQRwQaevP8jLIhrfjfSOoK%2BKJDgLU1NCexneHwN31fzzDSmKprRgPutI6lkTFxaQlCpCrMucjWEesL%2FavQAgofoCZzgfHJYrjqnLjfupo8LTBQWqxxNCPa%2B4fMl1A0OPP1Ug%3D%3D" rel="nofollow">error_page</a> 指令, 发生错误后内部跳转才能触发。</p><p>而用户刚好在升级版本后打开了 error_page 指令,再加上前面排查到的因上游变更产生的错误,似乎串联起来了,难道是因为错误处理的过程中恢复 ngx.ctx 出错了?</p><ol start="5"><li><h4>ngx.ctx 为什么会有问题</h4></li></ol><p>带着这个疑问,我们继续排查了 ngx.ctx 备份恢复相关的代码,随后发现了更奇怪的问题。</p><p>在 set_upstream 失败后,压根就不会走恢复 ngx.ctx 的流程,因为这个时候提前退出了,不会备份ngx.ctx,也就不会走到恢复的流程。</p><p><img src="/img/bVc2JJn" alt="" title=""></p><p>这显然是个 bug!因为<strong>跳转之后应该是需要恢复 ngx.ctx</strong> 的。那到底是怎么进到这个流程里恢复了 ngx.ctx?ngx.ctx 又是如何出现的问题?现在的疑问太多了,我们需要收集更多的信息。</p><p>经过协商,我们在线上第二次增加了日志,最后发现:<strong>请求在发生错误跳转后没有经过恢复 ngx.ctx 的流程,直接 coredump 了!</strong></p><p>这是一个令人匪夷所思的现象,要知道内部跳转后,如果不恢复的话,ngx.ctx 为空。代码里有<a href="https://link.segmentfault.com/?enc=YuaKZgkpPuc69TApBIZgHQ%3D%3D.gu9spTNfiYOFInr%2FMwHKR4n%2Bv8d%2B94XQPf904IvB7m3%2FelQ3IX3%2Bawq33AInLzqG47iX1PFGS69Uf04AEuRNT9lkF6vSY0i6214J4TWBpDQmR7dABsYrtCf%2B6cfogliOzkql%2BIezYaJEdmU0BXA5dA%3D%3D" rel="nofollow">对空值进行判断</a>,是不会走到插件里触发 coredump 的代码的。</p><pre><code class="SQL">local function common_phase(phase_name)
local api_ctx = ngx.ctx.api_ctx
if not api_ctx then <============ HERE
return
end
plugin.run_global_rules(api_ctx, api_ctx.global_rules, phase_name)</code></pre><p>所以是触发 error_page 后的内部跳转这个动作导致 ngx.ctx 出现了问题?</p><ol start="6"><li><h4>得到初步结论</h4></li></ol><p>其实定位到现在,出现问题的流程已经基本都清楚了:</p><p>set_upstream 失败后跳转到 error_page 的错误处理阶段,原来应该为空的 ngx.ctx 出现了不符合预期的值。而由于 APISIX 的 bug<strong>, 没有恢复跳转前的 ngx.ctx</strong>,导致接下来访问了 ngx.ctx 内的脏数据,发生了 coredump。所以只要跳转后能恢复 ngx.ctx,就能解决这个问题,具体修复的 commit 细节已放在文末,可直接参考。</p><p>虽然截止到这里,已经能够给用户提供解决方案,但是我们仍然还没有找到问题复现的完整逻辑。因为用户并没有修改 ngx.ctx 的逻辑,开源版本应该可以复现,为了不继续影响用户的线上环境和正常发布流程,我们决定继续刨根问底。</p><ol start="7"><li><h4>线下成功复现</h4></li></ol><p>从已知的条件还不能复现出问题,经过分析,我们打算从以下两个方面进行排查:</p><ul><li>内部跳转是否有特殊的处理流程。</li><li>ngx.ctx 是否有特殊的处理流程。</li></ul><p>对于第一点,在经过 nginx 各个 filter 模块的处理过程中,可能存在某些异常的分支,影响了请求结构体中的 ctx 成员,从而影响了 ngx.ctx。我们排查了用户编译进 nginx 的模块,查看是否有相关的异常分支,但没有发现类似的问题。</p><p>对于第二点,经过排查发现 <strong>ssl 握手过程中存在 ngx.ctx 复用的问题。</strong>HTTP 阶段如果获取不到 ngx.ctx,会尝试从 SSL 阶段获取,SSL 阶段有自己单独的 ngx.ctx,有可能导致 ngx.ctx 出现问题。</p><p>基于此,我们设计了如下几个条件,最终复现了问题:</p><ol><li>特定的 APISIX 版本(跟用户版本相同)</li><li>启用 SSL 证书</li><li>内部错误,触发 error_page(比如指定上游时,无上游节点)</li><li>长连接</li><li>Prometheus 插件(用户在插件中引发 coredump)</li></ol><p>虽然“无米”,但我们最后也做成了这顿“饭”,通过猜想+验证,终于摸索出了这个 bug 的完整复现流程。一旦有完整复现流程,那么解决问题自然不再是难点,最终定位过程也不再详述,相信大家对能准确复现的问题都有着丰富的经验。</p><h2>梳理与总结</h2><p>本次问题的根本原因:由于进入 error_page 后没有恢复 ngx.ctx, 导致长连接上的所有请求都复用了 SSL 阶段的同一个 ngx.ctx。最终导致 ngx.ctx 出现了脏数据,产生了 coredump。</p><p>后续针对该问题我们也提出了相应的解决方案:确保内部跳转后,能够正常恢复 ngx.ctx。具体修复 PR 可参考:</p><p><a href="https://link.segmentfault.com/?enc=jgEwW9mAWq5kjyYJshXo5w%3D%3D.IBF9DUv4zGBB0VP%2F8VBgWrP0%2BxqJWBH2nYVLV5ZIp7xqoDIVmoiK11mX00RnRm812iZ9FS9oOdJ%2F5PFx5%2F52WTN7%2F9k0GqLQe10QJ2qEBxwpG1zZvny%2BGwuoSDcmbpFc" rel="nofollow">https://github.com/apache/api...</a></p><p>如果你的 APISIX 有使用到 error_page 等涉及到内部跳转的功能,推荐升级到最新的版本。</p><p>编写程序和 debug,都是科学严谨的工作,容得不半点含糊。收集信息、不放过蛛丝马迹、分析调用栈,再搞清楚上下文之后做到有的放矢,通过复现才能最终解决问题。</p>
为什么说 Apache APISIX 是最好的 API 网关?
https://segmentfault.com/a/1190000042500351
2022-09-16T15:27:32+08:00
2022-09-16T15:27:32+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<p>今天,我们可以通过手机和各种 APP 完成各种各样的事情,比如社交,网购等。这些行为的背后,API 起到了关键的作用。作为 API 的使用者,我们并不关心 API 的稳定、安全和高效,但是通过 API 提供数据服务的企业则需要选择一个合适的 API 网关,用来保证数千乃至数万的 API 为提供快速和安全的服务。</p><p>在 <a href="https://link.segmentfault.com/?enc=6Co%2BUsqYaGeN%2Fu%2BcidCcWw%3D%3D.oGytNcogfTydKvHmpizU0Y4tFMPH7MmA58Ntf9dfaPPneQ6YnUWXp2qV8VxB0VUvTyGMEkd%2B7soNGsoFOAioEiY%2BBKy88hsk1X8awaOuGzwTxTZtv%2B0VJHmpdtLAdAXD" rel="nofollow">CNCF 的 API Gateway landscape </a>中有接近 20 个 API 网关的选型(不包括公有云厂商的产品),包括 Apache APISIX、Kong、Tyk 等等。很多网关都称自己是下一代 API 网关,是最受欢迎的开源 API 网关项目,那么事实如何呢?本文将通过开发者、知识产权和品牌、技术、生态等多个维度来看看 Apache APISIX 为什么是下一代 API 网关?</p><p><img src="/img/bVc2urr" alt="" title=""></p><h2>工程师的眼睛是雪亮的</h2><p>工程师是 API 和 API 网关的使用者和开发者,更多工程师关注和参与的 API 网关项目,代表的就是技术的趋势。我们从 GitHub 代码贡献者的维度,选取了 4 个开源 API 网关进行对比:Apache APISIX、Kong、Tyk 和 Gloo。</p><p><img src="/img/bVc2urs" alt="" title=""><br>Kong 和 Tyk 都是 2015 年之前开始研发的,Apache APISIX 和基于 Envoy 的 Gloo 是在 2019 年左右开始研发的。从上图中的 <a href="https://link.segmentfault.com/?enc=DrmwsuFcaplv3K5KaZDImg%3D%3D.9QvPI65kohLNU7z%2Fps6ID0g%2FAzQn%2BV6rB9m1FRjDAgdscoIHvbk65i8a%2BU4VH6Kxs%2BHak8%2BxiXj%2FpFkPcx3AHxWb2Eov4Pm7hG3Vx%2FD1KO99tqhwThKza2ZpZljlpj6oTxf7nSc0MPdKwyws9Kx7O7k0l4MrdwqF5iUAHKGlEOM%3D" rel="nofollow">Github Contributor Over Time</a> 可以看出,Kong 经过近 7 年的发展已经有超过 250 个贡献者参与,远远超过了 Tyk 和 Gloo;而最年轻的 Apache APISIX,已经超过 320 个贡献者,数量和增速都远超 Kong,成为最多开发者参与贡献的 API 网关项目。</p><p>除了贡献者总数外,还有一个指标可以看到更深层次的数据:贡献者的月度活跃。下图展示了以上四个开源 API 网关的<a href="https://link.segmentfault.com/?enc=AbR9Y93dpy55Kq2tH2S21g%3D%3D.yyv6WG9b1DA7hs0wV3E1xIe0vZ%2BuV7eN9eTe%2BYYprGPEjWara0fPFOtkF4fFVlpUD%2B9bjMR8Irm71V%2FC%2FIZO2wHxMucWXt7B1m%2FTCFW4Q0ckwUyTq0nFZMuIM%2FC0iUUlmZ75F%2FG45IEOi0w4A0N3Uhf%2F%2F1hFyGYTTk5Z2FeiJmc%3D" rel="nofollow">月度活跃的开发者数量</a>:</p><p><img src="/img/bVc2uru" alt="" title=""></p><p>Tyk 的贡献者月活跃人数,长期都是在 5 个人左右,很少超过 10 个人;而 Kong 和 Gloo 的贡献者月活跃人数均在 10 到 20 之间浮动;而 Apache APISIX 基本保持在 20 人以上,最高接近 40 人,是开发者最活跃的 API 网关。</p><p>这四个开源 API 网关项目背后,都是有对应的商业化公司的,所以还有第三个指标数据,那就是开源项目贡献者和商业公司员工数的对比:</p><table><thead><tr><th>API Gateway</th><th>APISIX</th><th>Kong</th><th>Tyk</th><th>Gloo</th></tr></thead><tbody><tr><td>monthly active contributors</td><td>38</td><td>20</td><td>8</td><td>24</td></tr><tr><td>employees(from LinkedIn)</td><td>40+</td><td>500+</td><td>200+</td><td>100+</td></tr></tbody></table><p>当前(2022年第三季度)Kong 和 Tyk 的开源投入比例(月度活跃贡献者/员工总数)是 4%,Gloo 是 24%,APISIX 则是接近 100%。即使回到 2017 年,Kong 的月度活跃贡献者也在 5 个人左右。</p><p>显而易见,开源项目 Apache APISIX 和它背后的商业化公司 API7.ai 从创建伊始,就保持了对于开源项目的持续投入,赢得了众多开发者的喜爱。</p><h2>开源协议:企业用户选择开源项目的首要考虑因素</h2><p>自从 MongoDB 修改了开源项目的 License 之后,在做基础软件的选型时,开源项目的 License 风险就是企业用户首要考虑的因素。</p><p>从表面上看,Apache APISIX、Kong 和 Gloo 使用的都是商业友好的 Apache License Version 2.0,Tyk 使用的是带有传染性的 Mozilla Public License Version 2.0。</p><p>更深入的看,Kong、Gloo 和 Tyk 这三个都是完全由商业公司控制的开源项目,它们可以像 MongoDB 一样随时在新版本中修改 License,限制公有云或者其他公司免费使用,进而要求你从开源用户转为付费客户来继续使用最新版本。</p><p>没有人知道这样的事情是否会发生,以及发生的几率有多大。这种风险,就像达摩克利斯之剑一样,悬挂在企业用户的头上。</p><p>在这种情况下,使用 Apache 软件基金会或者 CNCF 的开源项目是最明智的。而 Apache APISIX 是全球最大的软件基金会 -- Apache 软件基金会的顶级项目,所有的代码和品牌都归属于 Apache 软件基金会,任何商业公司都不能控制 Apache APISIX 项目,也不可能修改开源项目的 license。企业用户可以一直放心的使用,而不用担心收到律师和合规部门的问询邮件。</p><h2>性能测试对比</h2><p>在社区中经常会有用户提问:基于 Envoy 的 Gloo,和基于 NGINX 的 APISIX,谁的性能更胜一筹?</p><p>虽然性能并不是选型中最重要的指标,但它确实是最直接的指标。下表的表格是 Apache APISIX 和 Gloo 的 Benchmark 结果。从表格中可以看到,Apache APISIX 的 QPS 是 Gloo 的 4.6 倍,同时 Apache APISIX 的延迟还不到 Gloo 的 7%。</p><p><img src="/img/bVc2ury" alt="image.png" title="image.png"></p><p>这并不仅仅是 NGINX 和 Envoy 之间的差异造成的,而是因为 APISIX 在底层做了大量的优化,所以同样基于 NGINX 的 APSIX 相对于 Kong 也有巨大的性能优势。</p><p>为什么 APISIX 的性能优势如何之大?在代码面前,没有任何秘密。让我们从技术的角度来详细分析下。</p><h2>APISIX 的技术优势</h2><p>以下内容是 Apache APISIX 和 Kong、Gloo 相比在技术方面的主要优势,大部分都是在底层模块上的优化和创新。在简单的 PoC 上并不一定能够体现出这些技术的优势,但在复杂的生产环境中,Apache APISIX 的这些优势将会造成巨大的差距。</p><h3>无数据库依赖</h3><p>在 APISIX 项目出现之前,也有非常多的商业 API 网关或开源 API 网关产品,但这些产品大多数都把 API 数据、路由、证书和配置等信息存放在一个关系型数据库中。</p><p>将这些数据存储在关系型数据库的优势非常明显,用户可以更加方便地使用 SQL 语句进行灵活查询,也方便用户进行备份及后续维护。</p><p>但是网关作为一个基础中间件,它处理了所有来自客户端的流量,这种情况下对于可用性的要求便会非常高。如果你的 API 网关依赖了一个关系型数据库,也就意味着关系型数据库一旦出现了故障(比如宕机、丢失数据),API 网关也会因此受到影响,整个业务系统的可用性也会大打折扣。</p><p>而 APISIX 在设计之初,就从底层架构上避免了宕机、丢失数据等情况的发生。因为在控制面上,APISIX 使用了 etcd 存储配置信息,而不是使用关系型数据库,这样做的好处主要有以下几点:</p><ol><li>与产品架构的云原生技术体系更统一;</li><li>更贴合 API 网关存放的数据类型;</li><li>能更好地体现高可用特性;</li><li>拥有低于毫秒级别的变化通知。</li></ol><p>使用 etcd 存储配置信息后,对于数据面而言只需监听 etcd 的变化即可。如果采用轮询数据库的方式,可能需要 5-10 秒才能获取到最新的配置信息;如果监听 etcd 的配置信息变更,APISIX 就可以将获取最新配置的时间控制在毫秒级别之内,达到实时生效。</p><p>因此使用 etcd 作为存储,不仅让 APISIX 在底层上更加贴合云原生,也让它在系统高可用的体现上带来了更多优势。</p><h3><strong>高性能路由匹配算法</strong></h3><p>API 网关需要从每个请求的 Host、URI、HTTP 方法等特征中匹配到目标规则,以决定如何对该请求进行处理,因此一个优秀的匹配算法是必不可少的。Hash 算法性能不错,但无法实现模糊匹配;正则可以模糊匹配,但性能不好,因此 Apache APISIX 选择使用树这样一种高效且支持模糊匹配的搜索数据结构。准确一些,Apache APISIX 使用的是 RadixTree,它提供了 KV 存储查找的数据结构并对只有一个子节点的中间节点进行了压缩,因此它又被称为压缩前缀树。此外,在已知 API 网关产品中 Apache APISIX 首次将 RadixTree 应用到了路由匹配中,支持一个前缀下有多个不同路由的场景,具体实现见 <code>lua-</code>`resty-radixtree`。</p><p>当对某个请求进行匹配时,RadixTree 将采用层层递进的方式进行匹配,其复杂度为 O(K)(K 是路由中 URI 的长度,与 API 数量多少无关),该算法非常适合公有云、CDN以及路由数量比较多的场景,可以很好地满足路由数量快速增长的需求。</p><h3><strong>高性能 IP 匹配算法</strong></h3><p>IP 地址有 2 种记法:标准 IP 表示方法与 CIDR 表示方法,以 32 位的 IPv4 为例:</p><ul><li>标准 IP 记法:192.168.1.1</li><li>CIDR 记法:192.168.1.1/8</li></ul><p>Apache APISIX 的 IP 匹配算法与路由匹配算法所使用的原理以及原始数据是不一样的。以 192.168.1.1 这个 IP 为例,由于每个 IP 段的范围是 0 到 255,因此在对 IP 进行匹配时我们可以认为 IP 地址是由 4 个16 位整数型的数构成的,IP 长度是固定的。那么我们可以采用更高效的算法完成匹配。</p><p>假设现在有一个包含 500 条 IPv4 记录的 IP 库,APISIX 会将 500 条 IPv4 的记录缓存在 Hash 表中,当进行 IP 匹配时使用 Hash 的方式进行查找,时间复杂度为 O(1)。而其他 API 网关则是通过遍历的方式完成 IP 匹配,发送到网关每个请求将逐个遍历最多 500 次是否相等后才能知道计算结果。所以 APISIX 的 高精度 IP 匹配算法大大提高了需要进行海量 IP 黑白名单匹配场景(如 WAF)的效率。</p><h3>精细化路由</h3><p>API 网关通过请求中的流量特征完成预设规则的匹配,常见特征包含了请求中的 Host、URI 路径、URI 查询参数、URI 路径参数、HTTP 请求方法、请求头等,这些特征是大部分 API 网关产品所支持的。相较于其它产品,Apache APISIX 支持了更多特征以解决复杂多变的使用场景。</p><p>首先,Apache APISIX 支持 NGINX 内置变量,意味着我们可以将诸如 <code>uri</code>、<code>server_name</code> 、<code>server_addr</code>、<code>request_uri</code>、<code>remote_port</code>、<code>remote_addr</code>、<code>query_string</code>、<code>host</code>、<code>hostname</code>、<code>arg_name</code>等数十种 Nginx 内置变量作为匹配参数,以支持更复杂多变的匹配场景。NGINX 内置变量列表请参考<a href="https://link.segmentfault.com/?enc=FTrAwSKembzVAiq%2FQ1W9WQ%3D%3D.uYWHcDeF9pN%2BozWmDr91lksZzuQB4FuhK%2FEbsnuQhOhHsiP7L%2FCkrahq0wFdR%2FEd" rel="nofollow"> NGINX 变量</a>。</p><p>其次,Apache APISIX 支持将条件表达式作为匹配规则,其结构是 <code>[var, operator, val], ...]]</code>,其中:</p><ul><li><code>var</code> 值可使用 Nginx 内置变量;</li><li><code>operator</code> 支持相等、不等、大于、小于、正则、包含等操作符。</li></ul><p>假设表达式为 <code>["arg_name", "==", "json"]</code>,它意味着当前请求的 URI 查询参数中,是否有一个为 <code>name</code> 的参数值等于 <code>json</code>。Apache APISIX 是通过自研的库 <code>lua-resty-expr</code> 实现该能力的,具体请参考 <a href="https://link.segmentfault.com/?enc=WMJJ8BvgEobimcim3oh%2Bbw%3D%3D.k8ApTCxBjSThQuey4BA5ZAlvO%2FkrBNw8Y4aBBJxnJtlYKo9dL5Tg5gfrltn%2BtILp" rel="nofollow">lua-resty-expr</a>。该特性将选择权交给了用户,可扩展性强。</p><p>此外,Apache APISIX 支持设置路由 <code>ttl</code> 存活时间:</p><pre><code class="Shell">$ curl http://127.0.0.1:9080/apisix/admin/routes/2?ttl=60 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d '
{
"uri": "/aa/index.html",
"upstream": {
"type": "roundrobin",
"nodes": {
"39.97.63.215:80": 1
}
}
}'</code></pre><p>以上配置表示,在 60s 后 APISIX 会自动删除该路由配置,非常适合一些临时验证的场景,比如金丝雀发布、监控输出等。对于线上的流量分流非常方便,是其它网关产品所不具备的能力。</p><p>最后一点是 Apache APISIX 支持自定义过滤函数,你可以通过在 <code>filter_func</code> 参数中编写自定义 Lua 函数,例如:</p><pre><code class="Lua">$ curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d '
{
"uri": "/index.html",
"hosts": ["foo.com", "*.bar.com"],
"filter_func": "function(vars)
return vars['host'] == 'api7.ai'
end",
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980": 1
}
}
}'</code></pre><p>其中 <code>filter_func</code> 入参是 <code>vars</code>,可从 <code>vars</code> 获取 Nginx 变量,然后实现自定义过滤逻辑。</p><h3><strong>支持多语言插件</strong></h3><p>虽然 API 网关、数据库或其他中间件都属于基础组件,但是更多时候用户是根据使用场景对 API 网关进行一些定制化地开发和系统集成。</p><p>APISIX 目前已经支持了 80 多种插件,但仍然难以涵盖用户的所有使用场景。在实际使用场景中,很多企业都会针对具体业务进行定制化的插件开发,通过网关去集成更多的协议或者系统,最终在网关层实现统一管理。</p><p>在 APISIX 早期版本中,开发者仅能使用 Lua 语言开发插件。虽然通过原生计算语言开发的插件具备非常高的性能,但是学习 Lua 这门新的开发语言是需要时间和理解成本的。</p><p>针对这种情况,APISIX 提供了两种方式来解决:</p><p>第一种方式是通过 Plugin Runner 来支持更多的主流编程语言(比如 Java、Python、Go 等等)。通过这样的方式,可以让后端工程师通过本地 RPC 通信,使用熟悉的编程语言开发 APISIX 的插件。</p><p>这样做的好处是减少了开发成本,提高了开发效率,但是在性能上会有一些损失。那么,有没有一种既能达到 Lua 原生性能,同时又兼顾高级编程语言的开发效率方案呢?</p><p><img src="/img/bVc2urB" alt="" title=""></p><p>第二种方式是使用 Wasm 开发插件,也就是上图左侧部分。Wasm(WebAssembly) 最早是用在前端和浏览器上的一个技术,但是现在在服务端它也逐渐展示出来它的优势。我们将 Wasm 嵌入到了 APISIX 中,用户就可以使用 Wasm 去编译成 Wasm 的字节码在 APISIX 中运行。最终达到的效果就是利用高效率,开发出了一个既有高性能又使用高级计算语言编写的 APISIX 插件。</p><p>因此,在当前 APISIX 版本中,用户可以使用 Lua、Go、Python 和 Wasm 等多种方式,基于 APISIX 编写自定义插件。通过这样的方式,降低了开发者的使用门槛,也为 APISIX 的功能提供了更多的可能性。</p><h2>总结</h2><p>本文从工程师、开源协议、性能评测、技术、生态系统等多个角度分析和比较了一些 API 网关产品,可以看出 Apache APISIX 是其中的佼佼者,引领了 API 网关领域的创新。</p><p>Apache APISIX 不仅是一个可以处理南北向流量的 API 网关,同时也有 APISIX Ingress Controller、Service Mesh 等开源产品。预计在本月底,APISIX 也将发布 3.0 预览版,带来全新功能与全方位产品提升,敬请期待!</p>
三大云厂商 ARM 架构服务器性能对比
https://segmentfault.com/a/1190000042340531
2022-08-18T16:25:47+08:00
2022-08-18T16:25:47+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<h2>背景</h2><p>ARM 架构属于 <a href="https://link.segmentfault.com/?enc=kZYUGBP4eSJTQl1VYv%2FL4Q%3D%3D.6MWSF5r8Czb7DUvi9QdTvjstFISwVNnucgmAI7mu8LxAi4yL0qXCwayK0zFA%2F7ZOQf%2Bk8u5cE2Ju1DkoKdITDQ%3D%3D" rel="nofollow">RISC 设计家族</a>。RISC 微处理器架构设计使用一组高度优化的指令,使小型处理器能够有效地处理复杂的任务。</p><p>ARM 已成为全球最大的计算机生态系统和移动设备的基石,以其低功耗、灵活的许可和低成本等特点,被许多专家认为是云计算的未来。因此,以 AWS、Google Cloud Platform(GCP) 和 Azure 为首的主流云厂商都陆续推出了 ARM 架构的服务器。其中 AWS 更是早在 2018 年就推出了第一款基于 ARM 架构的服务器处理器 AWS Graviton。</p><h3>AWS Graviton</h3><p>AWS Graviton 是 AWS 于 2018 年发布的基于 ARM 架构的一系列服务器处理器,第一代 AWS Graviton 处理器采用定制芯片和 64 位 Neoverse 内核。AWS Graviton2 于 2020 年发布,与第一代 AWS Graviton 处理器相比,AWS Graviton2 处理器在性能和功能方面实现了重大飞跃。性能提升 7 倍、计算核心数量达到 4 倍、缓存达到 2 倍、内存速度提升 5 倍等等<em>。</em>最新的 AWS Graviton3 处理器在 2022 年 5 月底发布,基于更加先进的 <a href="https://link.segmentfault.com/?enc=4o%2FFn%2BgucARJFcVasUkvFw%3D%3D.uqGYIk%2FpVpI5fYLXmwRWrVqvT5QbX5zXHltxe%2BzsXiRJT%2F0IxLx%2B1lNA4u%2BZpPgJC0VxhauH6RqB8KsNPS1zC52LpPP3XhHC1%2FKLHc0ue7o%3D" rel="nofollow">Neoverse V1</a> 设计,与前代处理器相比,更是提升了高达两倍的浮点性能、两倍的加密性能和三倍的 ML 性能,包括对 bfloat16 的支持。下图展示了搭载 AWS Graviton3 处理器的主要机型:</p><p><img src="/img/remote/1460000042340533" alt="img" title="img"></p><h3>Google Cloud Platform T2A</h3><p>Google Cloud Platform(以下简称 GCP)T2A VM 是 Google 在 2022 年 7 月推出的首款 ARM 架构的虚拟机预览版,由基于 Neoverse N1 设计的 Ampere® Altra® Arm 处理器提供支持。Tau T2A VM 有多种预定义的 VM 形状,每个 VM 最多 48 个 vCPU,每个 vCPU 4GB 内存。它们提供高达 32 Gbps 的网络带宽和广泛的网络附加存储选项,使 Tau T2A VM 适用于横向扩展工作负载,包括 Web 服务器、容器化微服务、数据记录处理、媒体转码和 Java 应用程序。主要机型如下图所示:</p><p><img src="/img/remote/1460000042340534" alt="img" title="img"></p><h3>Azure ARM-based Virtual Machines</h3><p>2022 年 4 月,微软宣布推出基于 Ampere® Altra® Arm 处理器的 Azure 虚拟机系列预览版。新的 VM 旨在高效运行横向扩展工作负载、Web 服务器、应用程序服务器、开源数据库、云原生以及丰富的 .NET 应用程序、Java 应用程序、游戏服务器和媒体服务器等。新的 VM 系列包括通用 Dpsv5 和内存优化的 Epsv5 VM,主要机型如下图所示:</p><p><img src="/img/remote/1460000042340535" alt="img" title="img"></p><h2>三大云厂商 ARM 服务器性能测试</h2><p>在本文中,我们将通过测试单核心性能来反映各服务器的整体性能。这里选取网络 IO 密集型的 API 网关 Apache APISIX,分别在 AWS c7g.large、GCP t2a-standard-2 和 Azure D2ps v5(属于 Dpsv5 系列,双核 CPU)三款机型上绑定单个 CPU 核心进行压力测试,并通过 QPS 和响应延迟两个指标来分析服务器的性能。</p><p><a href="https://link.segmentfault.com/?enc=7MdS53uXVgrf%2BZORcdUDGg%3D%3D.kd8kpoIsdSobehMX8wNBycCfJG8Y5tvZwBYOcdBS5xNX%2BvVOiT%2FOxRja7iqbsS7P" rel="nofollow">Apache APISIX</a> 是一个云原生、高性能、可扩展的 API 网关。基于 NGNIX + LuaJIT 和 etcd,APISIX 与传统 API 网关相比,具有动态路由和插件热加载特性,特别适合云原生架构下的 API 管理。</p><p><img src="/img/remote/1460000042340536" alt="img" title="img"></p><p>接下来我们将使用 APISIX 官方开源的<a href="https://link.segmentfault.com/?enc=%2FNRcYs%2Fwt1Zo1nj8pAu9mg%3D%3D.5G0RLQ4LuXu29%2BuOC8N%2Bz7TU7mqAex7hr%2FTnHqqdfLgEUTp5kMrPE2pRlhrKUeWlKTSEubbGlNnxKEx9hu29zQ%3D%3D" rel="nofollow">性能测试脚本</a>进行测试。</p><h3>测试用例</h3><p>本文我们将测试 Apache APISIX 在两个典型场景下的表现,以便获取更加真实、丰富的测试数据。</p><ul><li>场景一:单个上游。该场景下使用单个上游(不包含任何插件),主要测试 APISIX 在纯代理回源模式下的性能表现。</li><li>场景二:单上游+多插件。该场景使用单上游与多插件配合,在这里使用了两个插件。主要测试 APISIX 在开启 <code>limit-count</code> 和 <code>prometheus</code> 两个核心消耗性能插件时的性能表现。</li></ul><h3>测试结果</h3><p>下图是 QPS (每秒查询数)测试结果, 数字越大代表其性能越好。</p><p><img src="/img/remote/1460000042340537" alt="img" title="img"></p><p>下图是响应延迟测试结果,单位为毫秒。数字越小代表其性能越好。</p><p><img src="/img/remote/1460000042340538" alt="img" title="img"></p><p>从 QPS 和响应延迟来看,在类似 Apache APISIX 这种网络 IO 密集型的 API 网关下,AWS C7g 相比 GCP T2A 有 100% 的性能提升,Azure Dpsv5 相比 GCP T2A 则有 15% 左右的性能领先。</p><h2>机器性价比比较</h2><p>由于本文仅专注于测试不同云厂商 ARM 机器的性能,所以在结果呈现中我们将忽略 “CPU 核心数相同时内存不同” 这一变化,只从 CPU 核心数的角度来分析 AWS Graviton3 和 GCP T2A 的性价比。</p><blockquote>在当前的测试场景下,性价比可以理解为:QPS / 成本。</blockquote><p>下表是 AWS C7g (US East Ohio) 、GCP T2A (us-central1) 和 Azure Dpsv5 (East US) 不同核心的服务器每小时价格对比:</p><table><thead><tr><th>VM series / vCPU</th><th>1</th><th>2</th><th>4</th><th>8</th><th>16</th><th>32</th><th>64</th></tr></thead><tbody><tr><td>AWS C7g</td><td>$0.0361</td><td>$0.0723</td><td>$0.1445</td><td>$0.289</td><td>$0.5781</td><td>$1.1562</td><td>$1.7342</td></tr><tr><td>GCP T2A</td><td>$0.0385</td><td>$0.077</td><td>$0.154</td><td>$0.308</td><td>$0.616</td><td>$1.232</td><td>$1.848</td></tr><tr><td>Azure Dpsv5</td><td>*</td><td>$0.077</td><td>$0.154</td><td>$0.308</td><td>$0.616</td><td>$1.232</td><td>$1.848</td></tr></tbody></table><p>参考前文中对 Apache APISIX 性能测试中的单个上游时的 QPS 数据,下表则汇总了 AWS c7g.large、GCP t2a-standard-2 和 Azure Dpsv5 运行一年的成本和性价比。其中只有微软官方公布了对应虚拟机的折扣。其中数字越大,则表明在单位价格能获取到更高的 QPS。</p><table><thead><tr><th> </th><th>一年成本</th><th>性价比(QPS / 成本)</th></tr></thead><tbody><tr><td>AWS c7g.large</td><td>$633.3</td><td>36.3</td></tr><tr><td>GCP t2a-standard-2</td><td>$674.5</td><td>16.8</td></tr><tr><td>Azure D2ps v5</td><td>$398.0(41%折扣)</td><td>33.6</td></tr></tbody></table><p>从测试结果来看,AWS C7g 相比 GCP T2A 和 Azure Dpsv5 拥有更高的性价比。虽然 Azure Dpsv5 相比 GCP T2A 只有 15% 的性能提升, 但是性价比却高了接近一倍。</p><h2>总结</h2><p>AWS 在 2018 年就推出了首款 ARM 架构的处理器 AWS Graviton,比 GCP 提前了大约 4 年进行了基于 ARM 架构的服务器领域的布局,如今 AWS Graviton 处理器已经发展到了第三代。</p><p>通过 Apache APISIX 的性能测试结果和性价比分析,我们可以看出 AWS Graviton3 拥有比 GCP T2A 和 Azure Dpsv5 更高的性能和性价比。这于 AWS 在 ARM 服务器领域深耕多年是分不开的。</p><p>此外,我们在测试过程中仅使用了 Apache APISIX 绑定单核心测试。如果使用多核,AWS Graviton 3 所呈现的性价比可能会进一步提高。</p><h2>参考链接</h2><p><a href="https://link.segmentfault.com/?enc=Kg3qGe3fjBugKqLXPdqwEw%3D%3D.iXR9cuRUh9ys%2FSzsKG%2BrjH7%2Fnt5htN4VI%2B9wb%2Ba%2BAGOzvw3NGc1YoRr%2FK1E291Hpc8luqpPu6riDQe87I0hjKnAHi4pGGjOzbVplok%2BYYC7Gr06YMKzrrAtJgy9VSGKuZ5ke76j8o2imydan%2FRoLiA%3D%3D" rel="nofollow">https://aws.amazon.com/cn/blo...</a></p><p><a href="https://link.segmentfault.com/?enc=mnPXdXGcj96kaNK46i3biw%3D%3D.2gUWnfAVW1us2bCuSWXEVhmqcEIFYtjcmu0jPqqi%2BeQ%2BtoFISl1rkZc4lp8X%2BRua0YyzZya3xJh79XBI%2BX8kDq1aFP07Owd2B7ZFmVK%2Fntw%3D" rel="nofollow">https://cloud.google.com/comp...</a></p><p><a href="https://link.segmentfault.com/?enc=acMdr%2FQMuooOYDZuhr1LUw%3D%3D.0btz5iahPdmtbi0icxYl4nqYnOdZ6pf8CbtXQAOMBT%2BaRD9vREdaCIxs8dL%2F29muIHU9RUYAbedI5IbsKOmoUShY3ACQ%2FETkpXCVQHr%2BVRDM54Fx1GJWEbY25a7BDqTwTo%2FpW0GKf5VRHvgDm%2BmUPsW06RMNPXUfC7lsNDMsfNc%3D" rel="nofollow">https://azure.microsoft.com/e...</a></p>
Apache APISIX在微软云 ARM 和 x86 服务器上的性能测试对比
https://segmentfault.com/a/1190000042303893
2022-08-11T15:25:00+08:00
2022-08-11T15:25:00+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<h2>背景</h2><p>今年 4 月,微软宣布推出基于 Ampere® Altra® Arm 处理器的 Azure 虚拟机系列预览版。新的 VM 系列包括通用 Dpsv5 和内存优化的 Epsv5,具体信息参考下图:</p><p><img src="/img/remote/1460000042303895" alt="img" title="img"></p><p>值得注意的是,Ampere® Altra® Arm 是一款云原生处理器,基于 Ampere® Altra® Arm 处理器的 Azure 虚拟机也因此能以高效的方式运行横向扩展的云原生应用程序。</p><p>那么实际体验和性能如何呢?接下来我们将以一个云原生的 API 网关为例,带大家一起看看基于 ARM 架构的 Azure 虚拟机的表现。这里,我们选择了 Apache APISIX 在通用型 Dpdsv5 系列虚拟机上进行安装测试。</p><p><a href="https://link.segmentfault.com/?enc=H0P%2FYF%2BWBvFLRuMb%2Bsn5Kw%3D%3D.Ajqrrr7eqiaqamjKx%2FFUqe6A6FuXbSGKZIQRek0y05FstytvjH4e1%2FsiQ2JcoDFG" rel="nofollow">Apache APISIX</a> 是一个云原生、高性能、可扩展的 API 网关。基于 NGNIX + LuaJIT 和 etcd,APISIX 与传统 API 网关相比,具有动态路由和插件热加载特性,特别适合云原生架构下的 API 管理。</p><p><img src="/img/remote/1460000042303896" alt="img" title="img"></p><h2>前期准备</h2><p>首先,我们在 Azure 上启动一个 Dpdsv5 系列的实例,操作系统选择 Ubuntu 20.04。</p><p><img src="/img/remote/1460000042303897" alt="img" title="img"></p><p>然后安装 Docker,方便后续使用容器化的方式来部署 Apache APISIX。</p><pre><code class="Bash">sudo apt-get update && sudo apt-get install docker.io</code></pre><h2>部署 Apache APISIX</h2><ol><li>Apache APISIX 使用 etcd 作为配置中心,因此需要先启动一个 etcd 实例。</li></ol><pre><code class="Bash">sudo docker run -d --name etcd \
-p 2379:2379 \
-e ETCD_UNSUPPORTED_ARCH=arm64 \
-e ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379 \
-e ETCD_ADVERTISE_CLIENT_URLS=http://0.0.0.0:2379 \
rancher/coreos-etcd:v3.4.16-arm64</code></pre><ol start="2"><li>启动 Apache APISIX 实例。</li></ol><pre><code class="Bash">sudo docker run --net=host -d apache/apisix:2.14.1-alpine</code></pre><ol start="3"><li>创建路由。</li></ol><pre><code class="Bash">curl "http://127.0.0.1:9080/apisix/admin/routes/1" \
-H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" -X PUT -d '
{
"uri": "/anything/*",
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'</code></pre><ol start="4"><li>访问测试。</li></ol><pre><code class="Bash">curl -i http://127.0.0.1:9080/anything/das</code></pre><p>返回以下结果则表示安装成功:</p><pre><code class="Bash">HTTP/1.1 200 OK
.....</code></pre><h2>Azure Ddsv5 vs Azure Dpdsv5</h2><p>从上述操作来看,Apache APISIX 在 Azure Dpdsv5 上的安装和兼容性测试都能顺利完成。那么 Azure Dpdsv5 的实际性能到底如何呢?接下来我们将使用 Apache APISIX 分别在 Azure Dpdsv5 和 Azure Ddsv5 上做性能测试对比,看看实际表现。</p><p>Azure Ddsv5 是 Azure D 系列的另一款机型,是基于 Intel® x86 架构的, 所以上述 etcd 安装步骤略有不同:</p><pre><code class="Bash">sudo docker run -d --name etcd \
-p 2379:2379 \
-e ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379 \
-e ALLOW_NONE_AUTHENTICATION=yes \
-e ETCD_ADVERTISE_CLIENT_URLS=http://0.0.0.0:2379 \
bitnami/etcd:3.4.16</code></pre><p>为简单起见,本次测试 APISIX 中只启用了一个 Worker,以下性能测试数据均在单核 CPU 上运行。</p><h4>场景一:单上游</h4><p>该场景下将使用单个上游(不包含任何插件),主要测试 APISIX 在纯代理回源模式下的性能表现。在本地环境中进行测试:</p><pre><code class="Plain"># apisix: 1 worker + 1 upstream + no plugin
# 注册路由
curl http://127.0.0.1:9080/apisix/admin/routes/1 \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"uri": "/hello",
"plugins": {
},
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980":1
}
}
}'</code></pre><h4>场景 2:单个上游 + 两个插件</h4><p>该场景下将使用单个上游与两个插件进行,主要测试 APISIX 在开启 <code>limit-count</code> 和 <code>prometheus</code> 两个核心性能消耗插件时的表现。</p><pre><code class="Plain"># apisix: 1 worker + 1 upstream + 2 plugins (limit-count + prometheus)
# 注册路由
curl http://127.0.0.1:9080/apisix/admin/routes/1 \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"uri": "/hello",
"plugins": {
"limit-count": {
"count": 2000000000000,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
},
"prometheus": {}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980":1
}
}
}'</code></pre><h4>数据比较</h4><p>上述两个场景中,分别从<strong>请求 QPS(每秒查询数)</strong> 和<strong>延迟时间</strong>两个层面进行了相关的测试对比。结果如下:</p><ol><li>QPS 比较</li></ol><p><img src="/img/remote/1460000042303898" alt="img" title="img"></p><ol start="2"><li>请求延迟比较</li></ol><p><img src="/img/remote/1460000042303899" alt="img" title="img"></p><table><thead><tr><th> </th><th>单个上游</th><th> </th><th>单个上游+两个插件</th><th> </th></tr></thead><tbody><tr><td> </td><td>Azure Ddsv5</td><td>Azure Dpdsv5</td><td>Azure Ddsv5</td><td>Azure Dpdsv5</td></tr><tr><td>QPS(request/sec)</td><td>14900</td><td>13400</td><td>13100</td><td>11000</td></tr><tr><td>Latency(ms)</td><td>1.07</td><td>1.21</td><td>1.21</td><td>1.43</td></tr></tbody></table><p>从以上数据也可以看出,在 API 网关等网络 IO 密集计算场景下,Dpdsv5 相比同系列 Ddsv5 在性能上仍然有差距。不过好消息是,在同等配置的情况下,Dpdsv5 的价格要比 Ddsv5 便宜 20% 左右。在实际机器选型时,用户可以根据自己的业务体量来灵活决策。</p><h2>总结</h2><p>本文主要使用 Apache APISIX 来对比 Azure Ddsv5 和 Azure Dpdsv5 的性能。从结果可以看出 Azure Dpdsv5 相比 Ddsv5,表现虽然不是那么亮眼,但是由于该系列的机型尚处于预览版,微软正在进行持续的改进和优化,我们也非常期待它的后续表现。</p>
Apache APISIX Ingress v1.5-rc1 发布
https://segmentfault.com/a/1190000042273824
2022-08-05T11:25:41+08:00
2022-08-05T11:25:41+08:00
API7_技术团队
https://segmentfault.com/u/api7
0
<p>Apache APISIX Ingress Controller v1.5-rc1 版本正式发布。此版本历时 7 个月左右的时间,由 36 位贡献者进行了 144 次提交。其中有 22 位新晋贡献者,感谢大家对本项目的贡献与支持!</p><p><img src="/img/remote/1460000042273826" alt="img" title="img"></p><p>接下来就让我们一起看下,在 APISIX Ingress v1.5 版本中有哪些重要更新。</p><h2>所有 CRD API Version 升级至 v2</h2><p>在 APISIX Ingress 项目初始,只有少数的几个 CRD ,并且每个资源都是各自进行 API Version 的维护。这就导致了后续有新资源引入或功能迭代的过程中,会出现每个自定义资源使用的 API Version 都不一样的状况,从而增加用户学习成本。</p><p>所以从 v1.3 版本开始,我们提出了统一所有资源 API Version 的 <a href="https://link.segmentfault.com/?enc=T3nz5vmIuUjpd1e4OSfN%2FA%3D%3D.e4Yb%2BL6Dhvk2J5NZ50abQ1RaHnzNFh1OGkrif2LGAcT19kfH2hFvbOQAwB7xPw8Q2K1%2FCI5%2FxqGbmfcSX4dwdg%3D%3D" rel="nofollow">Proposal</a>。经过两个版本的迭代,现已正式引入了 v2 API Version,同时将 v2beta3 标记为废弃,直到 v1.7 版本时会完全移除 v2beta3。</p><h2>基本支持 Gateway API</h2><p>Gateway API 可以说是下一代的 Ingress 定义,具有更加丰富的表现能力。我们已经在 APISIX Ingress 中完成其中大多数资源的支持(注意,此特性目前还在实验性质,默认不启用)。</p><p>如果想要在 APISIX Ingress 中使用 Gateway API,可以在 controller 的配置文件中传递 <code>enable_gateway_api: true</code> 配置项来开启此功能。</p><p>在安装完 Gateway API 的 CRD 后,即可通过创建 <code>HTTPRoute</code> 资源来完成 7 层代理的配置。如下所示:</p><pre><code class="YAML">apiVersion: gateway.networking.k8s.io/v1alpha2
kind: HTTPRoute
metadata:
name: httpbin-route
spec:
hostnames: ["httpbin.local"]
rules:
- matches:
- path:
type: PathPrefix
value: /ip
backendRefs:
- name: httpbin
port: 80</code></pre><p>该配置生效后,客户端将可以通过 <code>httpbin.local/ip</code> 访问到后端 httpbin 的 80 端口。</p><p>此外,我们计划在 v1.6 版本中实现 Gateway API 剩余 <code>TCPRoute</code> 和 <code>UDPRoute</code> 这两种资源的支持,并计划引入 Gateway API 的一致性测试,以确保我们的实现与 Gateway API 的预期保持一致。</p><h2>允许 Ingress 资源绑定任意插件</h2><p>在 Apache APISIX Ingress Controller 项目中,支持使用 Kubernetes 原生的 Ingress 资源进行代理配置,但如果想要在 Ingress 资源中使用 APISIX 丰富的插件能力,就必须通过增加 Annotation 来完成,并且需要实现对应 Annotation 的逻辑。</p><p>这种方式限制了 Ingress 资源的插件能力,而且为每个插件单独开发 Annotaion 是一个相对高成本的事情。</p><p>在 v1.5 版本中,我们通过为 Ingress 资源增加了一个新的 Annotaion <code>k8s.apisix.apache.org/plugin-config-name</code>,这允许引用任意的 <code>ApisixPluginConfig</code> 资源,从而实现了让 Ingress 资源可以随意使用任意 APISIX 插件的能力。这将大大增加 APISIX Ingress Controller 的易用性,也将降低用户从其他 Ingress controller 向 APISIX Ingress Controller 迁移时的成本。</p><h2>更多细节</h2><p>除以上这些特性外,该版本中还加入了很多其他特性。比如:</p><ul><li>定时 Kubernetes 集群往 APISIX 集群中同步数据的机制,保证 APISIX 集群中数据与在 Kubernetes 集群中配置的一致性;</li><li>为 ApisixConsumer 资源增加了更多的认证方式;</li><li>与 APISIX v2.15 版的兼容;</li><li>将所有依赖更新到最新版本,以便减少安全隐患;</li></ul><p>更多具体发版细节,请参考 1.5-rc1 的 <a href="https://link.segmentfault.com/?enc=8OpIsYuaMC1by18DVkjVdw%3D%3D.%2FpMn5ilCt0hDwpf0KL4rasjAk6V%2FpFV8OEtXsWHhqNGi7wehq57vkcf5I12ZOsba103ejlUYjnPrRq%2FQo50Pgkc5glDfD%2BbNGMKff3Zg9sQ0dECTqEQS%2BEvSd55gAl4i" rel="nofollow">Changelog</a></p>