跳到主要内容

· 阅读需 5 分钟

背景说明

前面 SJC 在这篇博客中介绍了如何在本地开发和调试 Higress Controller。而 Higress 控制面除了 Controller 之外还有一个组件,那就是 Pilot。本文就将介绍如何在本地开发和调试 Higress Pilot。

环境准备

步骤一:克隆代码仓库

Pilot 的代码目前是以 Istio 上游仓库 submodule 加补丁文件的形式存在于 Higress 的主仓库中的,所以直接克隆 Higress 的代码主仓库 https://github.com/alibaba/higress 即可。

步骤二:准备项目代码

Higress Pilot 是在 Istio Pilot 的基础上,基于 Higress 自身的功能需求进行了二次开发而构建而成的,所以我们这里需要下载上游代码仓库并应用现有的补丁。我们只需要执行下面这条命令:

make prebuild

Windows 用户注意了!

如果你是在 Windows 上进行开发,并且执行 make 命令有困难的话,不要怕,我们也有办法。因为整个 prebuild 其实也只做了两件事情。

第一件事情是初始化所有的上游子模块,只需要执行这样一条命令:

git submodule update --init

第二件事情是初始化开发目录。这一步骤执行的就是 tools/hack/prebuild.sh。大家可以直接在 Cygwin 或者 Git Bash 等类 Linux 终端下执行这个脚本文件。

如果在应用补丁的时候出现了和换行符有关的报错,那么可以编辑 prebuild.sh,给两处 patch 命令增加 --binary 参数即可。

img.png

步骤三:安装 Higress

既然只是调试 pilot,那么其他组件还是要复用现有 Higress 集群里的。所以我们需要在本地配置一个 Higress 集群。大家可以参考这篇文档:链接

开发与调试

本地开发

本地开发推荐使用 IDE JetBrains GoLand。我们直接在 GoLand 中打开 external/istio 目录,正常进行开发即可。

img.png

调试运行

环境准备

第一步:提取配置文件

在本地任意一个位置创建一个目录,然后在该目录下执行以下命令,生成配置文件:

kubectl get configmap higress-config -n higress-system -o=jsonpath='{.data.mesh}' > ./mesh
kubectl get configmap higress-config -n higress-system -o=jsonpath='{.data.meshNetworks}' > ./meshNetworks

第二步:转发 controller 端口

执行以下命令,将 controller 的 xDS 服务端口转发至本地:

kubectl port-forward deployment/higress-controller -n higress-system 15051

运行配置

配置一:环境变量

CUSTOM_CA_CERT_NAME=higress-ca-root-cert;
JWT_POLICY=none;
PILOT_ENABLE_CROSS_CLUSTER_WORKLOAD_ENTRY=false;
PILOT_ENABLE_GATEWAY_API=true;
PILOT_ENABLE_GATEWAY_API_DEPLOYMENT_CONTROLLER=false;
PILOT_ENABLE_GATEWAY_API_STATUS=false;
PILOT_ENABLE_METADATA_EXCHANGE=false;
PILOT_SCOPE_GATEWAY_TO_NAMESPACE=true;
POD_NAME=higress-controller;
POD_NAMESPACE=higress-system;
REVISION=default;
VALIDATION_ENABLED=false

配置二:命令行参数

discovery --monitoringAddr=:15014 --log_output_level=default:info --domain cluster.local --keepaliveMaxServerConnectionAge 30m --meshConfig ${configDir}/mesh --networksConfig ${configDir}/meshNetworks

注意:其中的 ${configDir} 为在环境准备一步创建的配置目录。

启动调试

在完成以上工作之后,我们就可以直接启动 Pilot 了。它的的 main 函数定义在 pilot/cmd/pilot-discovery/main.go 文件中。

img.png

网关对接

如果想要验证 pilot 下发配置到 gateway 的功能,我们需要修改 gateway 的配置,使之连接到处于开发状态的 pilot 实例。

第一步:修改 higress-config ConfigMap

kubectl edit configmap higress-config -n higress-system

修改两个地方:

  1. discoveryAddress 修改为本机IP:15010
    注意:此处的本机 IP 不可以使用 127.0.0.1 等 loopback IP。需要使用本机有线或无线网卡的 IP。
  2. discoveryAddress 下面添加一个新属性:controlPlaneAuthPolicy: NONE

修改后配置示例:

img.png

第二步:重启 Higress Gateway

kubectl rollout restart deployment higress-gateway -n higress-system

重启后我们可以在 pilot 的控制台输出中看到 gateway 连接上来的并获取配置的日志。

img.png

总结

本地调试和测试是开发过程中必不可少的环节。通过本文的介绍,希望大家可以更加方便的对 pilot 进行本地调试和e2e测试,提高开发效率。同时也希望能够有越来越多的开发者加入到 Higress 研发队伍中,为产品的升级迭代贡献一份力量。

欢迎参与阿里开源贡献👏

· 阅读需 8 分钟

概述

本文旨在介绍如何在本地使用 IDE 来进行 Higress 控制台(以下简称控制台)的开发和调试工作。

代码结构

控制台的代码仓库地址为 https://github.com/higress-group/higress-console

控制台项目使用了前后端分离的架构。在将代码下载到本地后,我们可以看到整个项目主要由以下三个目录组成:backend、frontend 和 helm。它们也分别对应了项目的三个部分:后端、前端和部署。

Backend:后端部分

后端部分是一个使用 Maven 构建配置的 Java 项目,其中共有两个模块:sdk 和 console。

SDK

sdk 中包含了定义了 Higress 治理平面的核心数据模型和业务逻辑,如路由模型、Wasm 插件模型、配置模型转换和读写逻辑等。它也同样作为一个独立产品发布到了 Maven 中央仓库。开发者可以世界使用这个 SDK 来进行面向 Higress 的治理功能开发(参考文档)。

项目中的核心包如下:

  • config:SDK 初始化配置模型
  • constant:各类常量
  • model:各类配置模型
  • service:核心业务逻辑服务
    • kubernetes:与 K8s 模型和 API 相关的服务

Console

console 中定义了控制台所使用的 Restful API 和一些界面相关的非核心功能,例如监控看板管理、用户配置管理等。

项目中的核心包如下:

  • client:封装访问外部 API 的客户端
  • constant:各类常量
  • controller:Restful API 控制器
  • service:业务服务逻辑

Frontend:前端部分

前端部分是一个 NodeJS 项目,使用基于 React 的应用研发框架飞冰(ICE)搭建。

整个项目的目录结构如下:

  • public:项目中使用到的静态资源
  • src:核心代码部分
    • components:在项目中被复用的小型组件
    • interfaces:与 API 交互过程中需要使用到的数据模型
    • locales:国际化资源文件
    • models:前端页面上下文中需要使用到的数据模型
    • pages:各个前端页面及其内部组件
    • services:与 API 进行交互逻辑封装
  • ice.config.mts:飞冰的项目配置文件

Helm:部署部分

控制台使用 Helm Chart 进行部署。这一部分就是 Helm Chart 的代码。整体代码结构遵循 Helm 的官方规范,可参考 Helm 官网

本地运行

后端部分

环境准备

控制台的正常运行依赖 Higress 核心组件,所以需要先准备一个安装好的 Higress 集群。大家可以参考这篇文档:链接。考虑到本地调试的便利度,建议大家使用本地 K8s 环境的方法进行安装。

控制台的后端项目要求 Java 版本不低于 17,所以请确认本地安装的 JDK 版本满足要求。

启动项目

控制台的外部服务依赖主要有两个,一个是 K8s API,另一个是 Higress Controller。

访问 K8s API,控制台默认使用的是本地默认的 kubeconfig (即 ~/.kube/config)。如果需要使用其他配置文件,则可以使用 HIGRESS_CONSOLE_KUBE_CONFIG 环境变量来指定对应的文件路径。

控制台在本地运行状态下,来访问 K8s 集群,使用本地的 15014 端口来访问 Higress Controller。可以使用以下命令将前面安装好的 Higress 实例中 controller 的 15014 端口映射到本地:

kubectl port-forward deployment/higress-controller -n higress-system 15014

img.png

然后使用项目的主类 com.alibaba.higress.console.HigressConsoleApplication 进行启动即可。等待启动完成后,我们就可以使用 http://localhost:8080/ 来访问了。

img.png

如果你是第一次启动,那么访问上述地址的时候会发现并没有页面出现。如果只是要调试 API,这样也是可以正常进行的。但如果要结合网页进行调试,那么需要使用以下命令执行一次 Maven 构建,生成前端页面资源:

./mvnw clean package -Dmaven.test.skip=true

img.png

前端部分

依赖安装

控制台的前端项目要求 NodeJS 的版本不低于 16,所以请确认本地安装的 NodeJS 版本满足要求。

然后使用以下命令安装项目所需的各个依赖包:

npm install

项目启动

使用以下命令契合启动前端页面项目:

npm start

img.png

默认情况下,前端页面会访问 Higress 官方提供的演示版控制台 API。如果不希望影响线上演示数据,或需要与后端 API 进行联调的话,可以修改项目根目录下的 ice.config.mts 文件,将其中的 http://demo.higress.io/ 替换为本地的服务地址(例如:http://127.0.0.1:8080/),然后重新启动前端项目。这样页面上访问的就是本地的测试服务了。

img.png

镜像构建

在命令行下进入 backend 目录并执行 build.sh 即可启动镜像构建。构建生成的镜像名称为 higress-console:0.0.1

注意:如果尝试在 Windows 下构建,请务必确认该目录下的 start.sh 使用的是 Linux 的换行符(即 LF)。否则,构建生成的镜像将无法正常运行。

img.png

总结

控制台为用户提供了 Higress 治理侧的重要组成部分,为用户提供了基础的开箱即用体验。社区也会在控制台方面持续发力,为用户提供更丰富的、更便捷的网关治理体验。希望本文可以让更多的开发者加入控制台的研发队伍中,为控制台的升级迭代贡献一份力量。

欢迎参与阿里开源贡献👏

· 阅读需 5 分钟

Higress 一个遵循开源 Ingress/Gateway API 标准,提供流量调度、服务治理、安全防护三合一的高集成、易使用、易扩展、热更新的下一代云原生网关。而配置管理网关的运维工作中扮演者重要的角色。如何让配置管理自动化,尤其是与其他的运维系统进行对接,就成为了一个非常迫切的需求。本文将介绍如何使用 Higress Admin SDK 来管理 Higress 系统内的各类配置。希望能够对存在此类需求的朋友有所帮助。

2. Higress Admin SDK

Higress Admin SDK 脱胎于 Higress Console。起初,它是作为 Higress Console 的一部分,为前端界面提供实际的功能支持。后来考虑到对接外部系统等需求,我们将配置管理的部分剥离出来,形成一个独立的逻辑组件,便于各个系统进行对接。目前支持服务来源管理、服务管理、路由管理、域名管理、证书管理、插件管理等功能。

Higress Admin SDK 现在只提供 Java 版本,且要求 JDK 版本不低于 17。

3. 开发实操

3.1 环境准备

这里我们以本地基于 Kind 搭建的 K8s 集群作为实验环境。所以首先,请大家参考这篇文档在本地完成 K8s 集群的搭建和 Higress 的安装。

然后,我们需要创建一个测试用的 K8s 服务。大家可以将下方的 YAML 保存为 test.yaml,然后执行 kubectl apply -f test.yaml 命令在 K8s 中创建对应的资源。

kind: Pod
apiVersion: v1
metadata:
name: higress-demo-app
namespace: default
labels:
app: higress-demo
spec:
containers:
- name: higress-demo-app
image: mendhak/http-https-echo:29
---
kind: Service
apiVersion: v1
metadata:
name: higress-demo-service
namespace: default
spec:
selector:
app: higress-demo
ports:
- port: 8080

3.2 代码编写

这里的目标是创建一个路由,使 http://www.test.com/ 这个 URL 指向我们刚刚创建的 higress-demo-service

第一步:配置依赖

根据项目所使用的构建工具来添加 Higress Admin SDK 依赖:

<dependency>
<groupId>io.higress.api</groupId>
<artifactId>higress-admin-sdk</artifactId>
<version>0.0.2</version>
</dependency>
implementation 'io.higress.api:higress-admin-sdk:0.0.2'

第二步:创建 Higress SDK 实例

String kubeConfigFile = Paths.get(System.getProperty("user.home"), "/.kube/config").toString();
HigressServiceConfig config = HigressServiceConfig.builder().withKubeConfigPath(kubeConfigFile).build();
HigressServiceProvider provider = HigressServiceProvider.create(config);

这里我们使用的是 K8s 集群外的配置方式,所以需要设置 kubeConfig 文件的路径,以便 SDK 操作 K8s 内的各类资源。

第二步:创建域名

这里我们使用 SDK 中的 DomainService 来创建一个 www.test.com 域名,并将该域名设置为只开放 HTTP 访问。

Domain domain = Domain.builder().name("www.test.com").enableHttps(Domain.EnableHttps.OFF).build();
provider.domainService().add(domain);

第三步:创建路由

这里我们使用 SDK 中的 DomainService 来创建一个名为 higress-demo 的路由。路由绑定 www.test.com 域名,匹配所有以 / 开头的请求,并将请求转发至 higress-demo-service.default.svc.cluster.local 服务的 8080 端口。

Route route = Route.builder()
.name("higress-demo")
.domains(Collections.singletonList("www.test.com"))
.path(RoutePredicate.builder()
.matchType(RoutePredicateTypeEnum.PRE.name())
.matchValue("/")
.build())
.services(Collections.singletonList(
UpstreamService.builder()
.name("higress-demo-service.default.svc.cluster.local:8080")
.build()
)).build();
provider.routeService().add(route);

3.3 测试验证

执行编写好的代码:确认一切正常。然后在 Shell 中执行以下命令,检查请求路由情况。

curl -svk http://localhost/ -H "Host: www.test.com"

能够以 JSON 格式返回请求的详细信息就说明路由配置已经可以正常工作。

{
"path": "/",
"headers": {
"host": "www.test.com",
"user-agent": "curl/8.4.0",
"accept": "*/*",
"x-forwarded-for": "10.42.0.230",
"x-forwarded-proto": "http",
"x-envoy-internal": "true",
"x-request-id": "4a3db96b-c46c-4c8a-a60f-a513f258736d",
"x-envoy-decorator-operation": "higress-demo-service.default.svc.cluster.local:8080/*",
"x-envoy-attempt-count": "1",
"x-b3-traceid": "a426d189c027371957f008c2cb2e9e8f",
"x-b3-spanid": "57f008c2cb2e9e8f",
"x-b3-sampled": "0",
"req-start-time": "1707363093608",
"original-host": "www.test.com"
},
"method": "GET",
"body": "",
"fresh": false,
"hostname": "www.test.com",
"ip": "10.42.0.230",
"ips": [
"10.42.0.230"
],
"protocol": "http",
"query": {},
"subdomains": [
"www"
],
"xhr": false,
"os": {
"hostname": "higress-demo-app"
},
"connection": {}
}

4. 总结

目前 Higress Admin SDK 支持的功能还比较简单。未来社区也会在进一步着力增强 Higress 的治理侧功能,SDK 的能力也会不断完善。大家对 SDK 和 Console 有任何疑问和建议,都欢迎在 GitHub 上提出。感谢大家的支持!

以上实操过程的项目代码可以在这里下载:下载链接

· 阅读需 27 分钟

引言

在开源社区中,源码分析是深入理解项目内部机制的关键步骤。通过仔细研究项目的源代码,我们可以揭示背后的设计原理、算法和工作流程。这不仅有助于提高我们的编程技能,还可以为开发者社区提供宝贵的学习资源。

本文将聚焦于分析Higress的源码,该项目在开源界备受瞩目。通过浏览阅读其代码,我们将初步展现其核心特性、架构设计和实现细节。希望各位用户以及开发者在本文中能够找到有价值的见解。

关于Higress

Higress是基于阿里内部的Envoy Gateway实践沉淀、以开源Istio + Envoy为核心构建的下一代云原生网关,实现了流量网关 + 微服务网关 + 安全网关三合一的高集成能力,深度集成Dubbo、Nacos、Sentinel等微服务技术栈,能够帮助用户极大的降低网关的部署及运维成本且能力不打折;在标准上全面支持Ingress与Gateway API,积极拥抱云原生下的标准API规范;同时,Higress Controller也支持Nginx Ingress平滑迁移,帮助用户零成本快速迁移到Higress。

代码目录说明

  • cmd: 命令行参数解析等处理代码
  • pkg/ingress: Ingress 资源转换为 Istio 资源等相关代码
  • pkg/bootstrap: 包括启动 gRPC/xDS/HTTP server 等的代码
  • registry: 实现对接多种注册中心进行服务发现的代码
  • envoy: 依赖的 envoy 官方仓库 commit,以及对应的补丁代码
  • istio: 依赖的 istio 官方仓库 commit,以及对应的补丁代码
  • plugins: Higress 插件 sdk,以及官方内置插件代码
  • script: 编译相关脚本
  • docker: docker 镜像构建相关脚本

本文主要围绕着higress-core组件的源码,研究higress-core自启动以来做了哪些事情。higress-core的代码主要位于pkg目录下。

higress-core源码整体分析

启动入口:pkg/cmd/server.go

在该文件下,主要进行了命令参数的解析,并调用NewServer来创建一个Server实例,调用该实例的Start方法来启动实例Server,调用waitForMonitorSignal方法来监听进程的退出。

显然这里有两个很重要的方法函数:NewServerStart,接下来针对这两个函数进行具体分析。

创建实例:NewServer

初始化Pilot的环境 APImodel.Environment

model.Environment为Pilot 中的核心环境对象,集成了许多不同的组件,包括服务发现、配置存储、网络观察等,以提供一个完整的环境 API。

e := &model.Environment{
PushContext: model.NewPushContext(),
DomainSuffix: constants.DefaultKubernetesDomain,
MCPMode: true,
}

model.Environment设置Ledger

e.SetLedger(buildLedger(args.RegistryOptions))

Ledger 是一个表示经过修改的 map 的接口。这个 map 具有三个独特的特征:

  1. 每个 map 的唯一状态都有一个唯一的哈希值。
  2. map 的先前状态在固定的时间内被保留。
  3. 给定先前的哈希值,我们可以从 map 中检索先前的状态(如果仍然被保留)。

Ledger提供了个接口来获取之前版本的Value,接口详情如下:

// GetPreviousValue executes a get against a previous version of the ledger, using that version's root hash.
GetPreviousValue(previousRootHash, key string) (result string, err error)

model.Environment设置服务发现的接口ServiceDiscovery,用于列举服务和实例。

ac := aggregate.NewController(aggregate.Options{
MeshHolder: e,
})
e.ServiceDiscovery = ac

初始化Server实例

s := &Server{
ServerArgs: args,
httpMux: http.NewServeMux(),
environment: e,
readinessProbes: make(map[string]readinessProbe),
server: server.New(),
}
s.environment.Watcher = mesh.NewFixedWatcher(&v1alpha1.MeshConfig{})
s.environment.Init()

这里说明一下Server结构体的各个字段的含义:

type Server struct {
*ServerArgs // Server参数配置
environment *model.Environment // Pilot环境配置
kubeClient higresskube.Client // 与 Kubernetes 集成的客户端
configController model.ConfigStoreCache // 配置存储控制器
configStores []model.ConfigStoreCache // 多个配置存储的缓存
httpServer *http.Server // HTTP 请求处理器
httpMux *http.ServeMux // HTTP 请求多路复用器
grpcServer *grpc.Server // grpc服务
xdsServer *xds.DiscoveryServer // xds服务
server server.Instance // Pilot Server实例配置
readinessProbes map[string]readinessProbe // server内部服务记录表,记录服务是否准备好
}

在这里,server.Instance维护了一个chan变量components chan ComponentComponent则定义了一个函数方法模板。当我们调用InstanceRunComponent(t Component)方法,则会将变量t发送给components管道,而我们调用InstanceStart(stop <-chan struct{}) error方法,则会从components管道去接收Component对象,并执行该对象的函数方法。

type Component func(stop <-chan struct{}) error

创建初始启动函数列表

initFuncList := []func() error{
s.initKubeClient,
s.initXdsServer,
s.initHttpServer,
s.initConfigController,
s.initRegistryEventHandlers,
s.initAuthenticators,
}
  • initKubeClient函数

    1. 首先判断kubeClient是否为空,不为空进行下一步;

    2. 调用istiokube.DefaultRestConfig方法,创建一个具有 RESTful 风格的Kubernetes Config

    3. Kubernetes Config为入参,调用istiokube.NewClientConfigForRestConfig方法创建一个具有 RESTful 风格的Kubernetes Client Config

    4. 调用higress定义的NewClient方法,集成 Istio 客户端、Higress 客户端和可能的 Kingress 客户端,并为每个客户端设置了相应的 SharedInformerFactory。

  • initXdsServer函数

    1. 调用pliot xdsNewDiscoveryServer方法,创建一个xdsServer实例,并初始化一些资源。
      s.xdsServer = xds.NewDiscoveryServer(s.environment, nil, PodName, PodNamespace, s.RegistryOptions.KubeOptions.ClusterAliases)
      s.xdsServer.McpGenerators[gvk.WasmPlugin.String()] = &mcp.WasmpluginGenerator{Server: s.xdsServer}
      s.xdsServer.McpGenerators[gvk.DestinationRule.String()] = &mcp.DestinationRuleGenerator{Server: s.xdsServer}
      s.xdsServer.McpGenerators[gvk.EnvoyFilter.String()] = &mcp.EnvoyFilterGenerator{Server: s.xdsServer}
      s.xdsServer.McpGenerators[gvk.Gateway.String()] = &mcp.GatewayGenerator{Server: s.xdsServer}
      s.xdsServer.McpGenerators[gvk.VirtualService.String()] = &mcp.VirtualServiceGenerator{Server: s.xdsServer}
      s.xdsServer.McpGenerators[gvk.ServiceEntry.String()] = &mcp.ServiceEntryGenerator{Server: s.xdsServer}
      s.xdsServer.ProxyNeedsPush = func(proxy *model.Proxy, req *model.PushRequest) bool {
      return true
      }
    2. xdsServerStart方法注册给pilot server instance,并调用higress server的initGrpcServer初始化函数,将xds服务注册在grpc服务上,使得可以通过grpc协议端口进行xds协议的通信。
  • initHttpServer函数,初始化http server,添加8888端口的debug接口的处理器,并添加/ready路由的处理器,用于遍历higress server的readinessProbes,判断内部服务记录表内的服务是否准备完毕。

    s.xdsServer.AddDebugHandlers(s.httpMux, nil, true, nil)
    s.httpMux.HandleFunc("/ready", s.readyHandler)
  • initConfigController函数,这部分内容比较核心,将会在后面详细展开,暂时先跳过。

  • initRegistryEventHandlers函数,这部分内容跟initConfigController函数有关,暂时先跳过。

  • initAuthenticators函数,顾名思义,就是初始化认证器,并将其应用于 xDS 服务器。通过添加不同的认证器,可以支持不同的身份验证方式,这样服务器在处理请求时可以验证客户端的身份信息。

遍历启动函数列表,执行启动函数

for _, f := range initFuncList {
if err := f(); err != nil {
return nil, err
}
}

注册Pilot Server

s.server.RunComponent(func(stop <-chan struct{}) error {
s.kubeClient.RunAndWait(stop)
return nil
})

注意这里的kubeClient所调用的方法是istio的Kubernetes客户端提供的。

在ready服务记录表里注册xds服务

s.readinessProbes["xds"] = func() (bool, error) {
return s.xdsServer.IsServerReady(), nil
}

启动实例:Start

  • 第一部分:遍历pilot server的components,执行chan管道里的函数
if err := s.server.Start(stop); err != nil {
return err
}
  • 第二部分:等待缓存同步完成
if !s.waitForCacheSync(stop) {
return fmt.Errorf("failed to sync cache")
}
  • 第三部分:启动grpc服务
grpcListener, err := net.Listen("tcp", s.GrpcAddress)
if err != nil {
return err
}
go func() {
log.Infof("starting gRPC discovery service at %s", grpcListener.Addr())
if err := s.grpcServer.Serve(grpcListener); err != nil {
log.Errorf("error serving GRPC server: %v", err)
}
}()
  • 第四部分:启动http服务
httpListener, err := net.Listen("tcp", s.HttpAddress)
if err != nil {
return err
}
go func() {
log.Infof("starting HTTP service at %s", httpListener.Addr())
if err := s.httpServer.Serve(httpListener); err != nil {
log.Errorf("error serving http server: %v", err)
}
}()

higress-core核心源码分析

在前面有两个函数initConfigControllerinitRegistryEventHandlers我们没有介绍,这两个函数可以说是higress-core的核心代码,接下来对这两个函数进行具体剖析。在分析之前,我们先看一下pkg其他目录的功能。

.
├── common //公共包
├── config //配置包
├── ingress //higress定义的ingress资源控制器
│   ├── config //配置ingress config和kingress config
│   ├── kube //集成Kubernetes配置
│   │   ├── annotations //处理 Ingress 的注解相关的代码
│   │   ├── common //通用的代码和工具函数
│   │   ├── configmap //处理 ConfigMap 相关的代码
│   │   ├── controller //控制器相关的代码
│   │   ├── http2rpc //处理 HTTP 到 RPC 的转换的代码
│   │   ├── ingress //处理 Ingress 配置的代码
│   │   ├── ingressv1 //Ingress 的 API 版本 v1 相关的代码
│   │   ├── kingress //处理 Kingress 相关的代码
│   │   ├── mcpbridge //MCP(Mesh Configuration Protocol)的桥接相关的代码
│   │   ├── secret //处理 Secret 相关的代码
│   │   ├── util //通用的工具函数
│   │   └── wasmplugin //处理 WebAssembly 插件相关的代码
│   ├── log //日志
│   ├── mcp
│   └── translation //翻译模块
└── kube //higress定义的Kubernetes客户端,包含了istio提供的Kubernetes客户端

通过目录我们可以清晰地看到higress集成了多个Kubernetes资源以及Ingress配置。

initConfigController函数剖析

我们先大致分析一下这个函数的流程,函数的具体逻辑稍后分析。最后会给出一个函数调用的流程图,可以结合流程图进行源码的理解分析。

  1. 设置 common.Options 结构体的一些选项,这些选项可能影响配置控制器的行为。这些选项包括是否启用控制器、集群 ID、Ingress 类等
  2. 创建一个 translation.NewIngressTranslation 对象,该对象用于处理 Ingress 的翻译和配置
  3. ingressConfig 中添加本地集群的配置,并获取对应的 Ingress 控制器和 Kingress 控制器
  4. 使用 configaggregate.MakeCache 创建一个带有缓存的配置控制器
  5. 创建一个 Istio 配置存储,并将其设置到 Server 对象的 environment.IstioConfigStore
  6. ingressConfig 设置到 Server 对象的 environment.IngressStore
  7. 将包含配置和启动 Ingress 控制器、Kingress 控制器以及相关的配置控制器的函数方法注册到pilot server instance的components管道中。

我们从中挑选出几个比较重要的函数出来:NewIngressTranslationAddLocalClusterInitializeCluster以及configControllerRun方法,其它的像MakeCacheMakeIstioStore方法则是有istio提供的函数方法,用于对接higress中的discovery容器中Pilot组件。确定好接下来要分析的内容之后,我们来到pkg/ingress/translattion目录,查看一下translation.go

translation.go剖析

IngressTranslation实现了两个接口:model.ConfigStoreCachemodel.IngressStore,其中model.ConfigStoreCache还包含了model.ConfigStore接口。我们可以大致浏览一下,可以发现基本上是调用IngressTranslation结构体中的ingressConfigkingressConfig的方法,位于pkg/ingress/config目录下。

除了实现接口方法之外,还提供了NewIngressTranslationAddLocalClusterInitializeCluster方法。同样地,我们也可以发现这三个方法本质上也是调用ingressConfigkingressConfig的方法。

既然translation.go本质上是调用pkg/ingress/config目录下的函数,不妨我们进入该目录,查看一下ingress_config.go,对于kingress_config.go,我们暂不做分析。

ingress_config.go剖析

ingress_config.go中有一个名为IngressConfig结构体,内容如下(附带注释):

type IngressConfig struct {
// 远程 Ingress 控制器的映射,键为集群 ID,值为 common.IngressController
remoteIngressControllers map[string]common.IngressController
// 用于保护并发访问 remoteIngressControllers 的互斥锁
mutex sync.RWMutex

// Ingress 路由和域名的缓存
ingressRouteCache model.IngressRouteCollection
ingressDomainCache model.IngressDomainCollection

// 本地 Kubernetes 客户端
localKubeClient kube.Client

// 处理 VirtualService 事件的事件处理程序切片
virtualServiceHandlers []model.EventHandler
// 处理 Gateway 事件的事件处理程序切片
gatewayHandlers []model.EventHandler
// 处理 DestinationRule 事件的事件处理程序切片
destinationRuleHandlers []model.EventHandler
// 处理 EnvoyFilter 事件的事件处理程序切片
envoyFilterHandlers []model.EventHandler
// 处理 ServiceEntry 事件的事件处理程序切片
serviceEntryHandlers []model.EventHandler
// 处理 WasmPlugin 事件的事件处理程序切片
wasmPluginHandlers []model.EventHandler

// 用于处理监视错误的处理程序
watchErrorHandler cache.WatchErrorHandler

// 缓存的 EnvoyFilter 配置
cachedEnvoyFilters []config.Config

// 正在监视的 Secret 集合
watchedSecretSet sets.Set

// 用于协调注册表的调解器
RegistryReconciler *reconcile.Reconciler

// MCP 桥接是否已调解的标志
mcpbridgeReconciled *atomic.Bool

// 管理 MCP 桥接相关的控制器和列表
mcpbridgeController mcpbridge.McpBridgeController
mcpbridgeLister netlisterv1.McpBridgeLister

// 管理 WasmPlugin 相关的控制器和列表
wasmPluginController wasmplugin.WasmPluginController
wasmPluginLister extlisterv1.WasmPluginLister

// 已注册的 WasmPlugin 集合,键为 WasmPlugin 名称
wasmPlugins map[string]*extensions.WasmPlugin

// 管理 HTTP2RPC 相关的控制器和列表
http2rpcController http2rpc.Http2RpcController
http2rpcLister netlisterv1.Http2RpcLister

// 已注册的 HTTP2RPC 集合,键为 HTTP2RPC 名称
http2rpcs map[string]*higressv1.Http2Rpc

// Configmap 的管理器
configmapMgr *configmap.ConfigmapMgr

// 用于更新 XDS
XDSUpdater model.XDSUpdater

// 处理注解的注解处理程序
annotationHandler annotations.AnnotationHandler

// 命名空间
namespace string

// 集群 ID
clusterId string
}

该结构体提供了一些方法,其实这个结构体本质上也是实现了translation.go提到的两个接口,方法列表和translation.go极其相似,我们分析一下一些比较复杂的方法,剩下的可自行定位到源码查看:

  • NewIngressConfig

    1. 初始化IngressConfig,创建多个子控制器(mcpbridge、wasmplugin、http2rpc等)并赋值给对应的字段上。
    2. 为多个子控制器注册事件监听器,用于监听Kubernetes资源的变化,并进行相应的处理。
      config := &IngressConfig{
      remoteIngressControllers: make(map[string]common.IngressController),
      localKubeClient: localKubeClient,
      XDSUpdater: XDSUpdater,
      annotationHandler: annotations.NewAnnotationHandlerManager(),
      ...
      }
      mcpbridgeController := mcpbridge.NewController(localKubeClient, clusterId)
      mcpbridgeController.AddEventHandler(config.AddOrUpdateMcpBridge, config.DeleteMcpBridge)
      config.mcpbridgeController = mcpbridgeController
      config.mcpbridgeLister = mcpbridgeController.Lister()
      ...

在这里,有几个方法需要注意:

1. `annotations.NewAnnotationHandlerManager()`
2. `mcpbridge.NewController(localKubeClient, clusterId)`
3. `wasmplugin.NewController(localKubeClient, clusterId)`
4. `http2rpc.NewController(localKubeClient, clusterId)`
5. `configmap.NewController(localKubeClient, clusterId, namespace)`

很明显这些方法分别对应pkg/ingress/kube目录下一些自定义的资源配置方法,后面会针对该目录进行单独的介绍。

  • RegisterEventHandler

    1. 对kind变量进行判断,判断属于哪种资源,并添加到对应的事件处理程序切片

      switch kind {
      case gvk.VirtualService:
      m.virtualServiceHandlers = append(m.virtualServiceHandlers, f)

      ...
      }
    2. 注册事件监听器

      for _, remoteIngressController := range m.remoteIngressControllers {
      remoteIngressController.RegisterEventHandler(kind, f)
      }

      我们可以定位一下remoteIngressControllerRegisterEventHandler方法:

      func (c *controller) RegisterEventHandler(kind config.GroupVersionKind, f model.EventHandler) {
      switch kind {
      case gvk.VirtualService:
      c.virtualServiceHandlers = append(c.virtualServiceHandlers, f)
      ...
      }
      }
  • AddLocalCluster

    1. 创建ingress总控制器,并注册到remoteIngressControllers
      ingressController = ingress.NewController(m.localKubeClient, m.localKubeClient, options, secretController)
      m.remoteIngressControllers[options.ClusterId] = ingressController
  • InitializeCluster

    1. 设置错误监听处理器
      _ = ingressController.SetWatchErrorHandler(m.watchErrorHandler)
      其中SetWatchErrorHandler方法如下,调用各个informer的SetWatchErrorHandler方法:
      func (c *controller) SetWatchErrorHandler(handler func(r *cache.Reflector, err error)) error {
      var errs error
      if err := c.serviceInformer.SetWatchErrorHandler(handler); err != nil {
      errs = multierror.Append(errs, err)
      }
      if err := c.ingressInformer.SetWatchErrorHandler(handler); err != nil {
      errs = multierror.Append(errs, err)
      }
      ...
      return errs
      }
    2. 通过go协程启动ingressController
      go ingressController.Run(stop)
  • List

    1. 前期检查,预防空指针或逻辑错误

      if typ != gvk.Gateway &&
      typ != gvk.VirtualService &&
      typ != gvk.DestinationRule &&
      typ != gvk.EnvoyFilter &&
      typ != gvk.ServiceEntry &&
      typ != gvk.WasmPlugin {
      return nil, common.ErrUnsupportedOp
      }

      // Currently, only support list all namespaces gateways or virtualservices.
      if namespace != "" {
      IngressLog.Warnf("ingress store only support type %s of all namespace.", typ)
      return nil, common.ErrUnsupportedOp
      }
    2. 如果kind类型是envoyfilter,拿出来处理掉

      if typ == gvk.EnvoyFilter {
      ...
      // Build configmap envoy filters
      configmapEnvoyFilters, err := m.configmapMgr.ConstructEnvoyFilters()
      ...
      }
    3. 如果不是,调用ingress控制器(如果有kingress控制器,也给带上)的List方法,添加到config列表,以ingress控制器的方法为例,会获取informer对象的存储器,并对其进行遍历,将遍历到的对象进行深拷贝,并返回config列表。由于List方法是实现ConfigStore接口的,istio内部会对这个config列表进行下一步处理。

      var configs []config.Config
      m.mutex.RLock()
      for _, ingressController := range m.remoteIngressControllers {
      configs = append(configs, ingressController.List()...)
      }
      m.mutex.RUnlock()
    4. 对config列表根据创建时间进行排序,并创建一个包装好的config列表,wrapperConfigs里除了Config``,还会包含AnnotationsConfig`注解配置

      common.SortIngressByCreationTime(configs)
      wrapperConfigs := m.createWrapperConfigs(configs)
    5. 根据kind类型,将wrapperConfigs转换为对应的资源,代码如下,并为convertGateways附加注释

      switch typ {
      case gvk.Gateway:
      //1.初始化ConvertOptions
      //2.调用ingress控制器的ConvertGateway方法,在这里,ingress控制器首先做一些检查,并遍历rule规则列表
      // 为每个rule创建IngressDomainBuilder构造器,中间过程会构造gateway包装器、获取tls密钥名称等等
      // 在这个过程中会对config包装器进行修改
      //3.apply注解
      //4.组合成istio能够识别的config列表
      return m.convertGateways(wrapperConfigs), nil
      case gvk.VirtualService:
      return m.convertVirtualService(wrapperConfigs), nil
      case gvk.DestinationRule:
      return m.convertDestinationRule(wrapperConfigs), nil
      ...
      }

      这块内容较多,具备一定的难度,需要对各个资源有一定的理解。

以下几个方法的作用是极为相似的

  • AddOrUpdateWasmPlugin
  • DeleteWasmPlugin
  • AddOrUpdateMcpBridge
  • DeleteMcpBridge
  • AddOrUpdateHttp2Rpc
  • DeleteHttp2Rpc

顾名思义,就是在监听对应资源的过程中,发生资源的添加或者变更或者删除进行对应的操作。

AddOrUpdateWasmPluginDeleteWasmPlugin为例。

  • AddOrUpdateWasmPlugin

    1. 获取wasmplugin
      wasmPlugin, err := m.wasmPluginLister.WasmPlugins(clusterNamespacedName.Namespace).Get(clusterNamespacedName.Name)
    2. 将wasmplugin转化为istio能够识别的wasmplugin
      istioWasmPlugin, err := m.convertIstioWasmPlugin(&wasmPlugin.Spec)
    3. 更新IngressConfig
      m.wasmPlugins[clusterNamespacedName.Name] = istioWasmPlugin
  • DeleteWasmPlugin

    1. 更新IngressConfig
      delete(m.wasmPlugins, clusterNamespacedName.Name)
    2. 如果存在该wasmplugin,触发wasmPluginHandlers的事件监听器,执行对应的操作
      for _, f := range m.wasmPluginHandlers {
      IngressLog.Debug("WasmPlugin triggerd update")
      f(config.Config{Meta: metadata}, config.Config{Meta: metadata}, model.EventDelete)
      }

到此为此,ingress_config.go的内容基本介绍完毕,大部分内容并未做过多深入,感兴趣的朋友可以自行下载源码浏览查看。

我们现在回到最开始的initConfigController函数上,可以发现这个函数的核心内容其实就是ingress_config.go,当然还有对应的多个控制器,关于控制器的介绍,将在后面进行介绍。

initRegistryEventHandlers函数剖析

该函数相对于initConfigController函数来说简单很多,其主要做的内容就是配置xds更新处理器,并将其作为事件监听器的事件处理程序,注册给configController,也就是说,当发生资源的变更时,会通过xds协议进行推送更新。

pkg/ingress/kube目录介绍

这部分内容较多,后续可自行针对感兴趣的部分进行精读。

  • annotations

    这里主要处理ingress注解的配置,在NewAnnotationHandlerManager方法里列出了一些已经支持的注解,每个注解的处理程序需要实现AnnotationHandler所定义的接口列表。

  • common

    这里核心的文件为controller.go,主要做了一些资源的包装,以及定义了IngressController接口方法,其实现类有pkg/ingress/kube/ingress/controller.gopkg/ingress/kube/ingressv1/controller.go

  • configmap

    这里主要为ConfigMap添加了higress的KV对,在controller.go中定义了ItemController接口,和前者提到的一样,这里也提供了相似的更新配置的方法AddOrUpdateHigressConfig,同样地,也是先获取当前的value值,并获取旧的value值,进行一个比对,比对结果可能为新增、更新、删除,分别执行对应的事件监听器里所定义的事件处理器,也就是XDSUpdater

  • controller

    在前面的介绍中,提到了很多次事件监听器,可能会有人疑问是怎么做资源监听的,资源监听的代码就位于本目录下了。这里可以着重介绍一下,事件监听的具体实现原理。

    1. 定义了Controller[lister any]接口,其实现类为CommonController。 我们可以看一下这个实现结构体有哪些需要注意的字段。

      lister: 这个字段为any类型,会在NewCommonController方法里传入进来,点击查看该方法调用情况,可以发现传入该字段的都是各个资源的监听器,例如ConfigMap的监听器就是client.KubeInformer().Core().V1().ConfigMaps().Lister().ConfigMaps(namespace),其它也是如此。 updateHandler: 这个字段为func(util.ClusterNamespacedName)类型,主要用于存储对应监听器lister所提供的更新或者新增事件处理器,存储代码如下:

      func (c *CommonController[lister]) AddEventHandler(addOrUpdate func(util.ClusterNamespacedName), delete ...func(util.ClusterNamespacedName)) {
      c.updateHandler = addOrUpdate
      if len(delete) > 0 {
      c.removeHandler = delete[0]
      }
      }

      removeHandler: 同updateHandler

    2. 接口方法除了刚刚介绍的AddEventHandler方法,还有个Run方法需要注意一下

      func (c *CommonController[lister]) Run(stop <-chan struct{}) {
      defer utilruntime.HandleCrash()
      defer c.queue.ShutDown()
      if !cache.WaitForCacheSync(stop, c.HasSynced) {
      IngressLog.Errorf("Failed to sync %s controller cache", c.typeName)
      return
      }
      IngressLog.Debugf("%s cache has synced", c.typeName)
      go wait.Until(c.worker, time.Second, stop)
      <-stop
      }

      可以看有个wait.Until方法,用于每秒钟执行c.worker方法,直到接收到stop信号,方法调用链如下:c.worker->c.processNextWorkItem()->c.onEvent(ingressNamespacedName)->c.updateHandler(obj),可以看出来当我们创建一个控制器,并为其注册了一个事件监听器以及事件处理器(updateHandler),因此每秒钟会执行事件处理器,也就实现了监听的逻辑。

    也就是说,这个目录下主要定义了一个公共的控制器,可以节省大量代码的编写,我们在创建一个新的子控制器的时候,可以调用NewCommonController方法来实现监听逻辑。

  • http2rpc

    代码较为简单,通过NewCommonController来创建Http2RpcController,并提供了GetHttp2Rpc方法。

  • ingress

    Ingress控制器的核心代码逻辑,主要为前文提到的ingress_config.go所调用。部分内容在前文已经简单介绍过。

  • ingressv1

    Ingress的API版本v1相关的控制器代码。

  • kingress

    kingress相关的控制器代码。

  • mcpbridge

    http2rpc目录。

  • secret

    http2rpc目录。

  • util

    工具包。

  • wasmplugin

    http2rpc目录。

higress-core流程图

经过前面的源码分析,相信大家对higress核心逻辑已经有了初步的理解,后续可以针对自己感兴趣的部分,进行深入研究,其中会涉及到Kubernetes资源配置的核心逻辑以及Istio的基本掌握,当然,如果给它研究透了,对这些基本原理的理解将会更为深刻。

流程图如下:

img.png

对于初始化配置控制器部分,可进一步绘画流程图。

img1.png

关于事件监听部分,其流程图如下:

img2.png

总结

以上是对higress源码的初步分析,希望能够给用户以及开发者对higress有个基本的了解。

· 阅读需 15 分钟

背景说明

本地调试

本地调试旨在在开发者的本地开发环境中识别、定位和修复代码中的错误(bug)。这个阶段的主要目标是确保单个模块或组件的正确性,以便更容易理解和解决问题。

开发者通常使用本地集成开发环境(IDE)或调试器来逐步执行代码、查看变量的值、观察程序的流程,并通过打断点来检查代码的执行过程。

本地调试是一个快速、迅速定位问题并进行修复的阶段,能够提供实时的反馈。它有助于确保代码的局部正确性,而不需要考虑整个系统的交互。

端到端测试

e2e测试是一种端到端的测试,是一种全面的测试方法,用于验证整个软件系统在真实环境中的功能和性能。它模拟用户的实际使用情况,确保整个系统的各个部分正确协同工作,以达到用户期望的功能。

端到端测试关注整个系统的集成和交互,旨在发现不同组件之间的问题以及在实际使用场景中可能出现的 bug。这有助于确保系统在生产环境中的稳定性和可靠性。

在higress中,e2e测试的主要流程可用下图来表示:

img.png

弊端

  • 在本地开发的过程中,我们需要频繁的进行e2e测试,但是每次都要重新构建镜像、加载镜像、安装higress、运行e2e测试,这样的过程非常耗时,因此我们希望尽量地减少这些过程,复用测试环境以提高开发效率。

  • 当e2e运行的时候出现问题,我们希望能够快速定位问题,这就需要在e2e测试中实现本地调试的功能。

  • 另外,通常开发wasm插件的时候是不需要重新构建镜像的,只需要复用测试环境即可。然而,当涉及到higress-core组件代码(higress核心代码)修改的时候,就需要重新构建镜像,频繁地修改代码、重新构建镜像以运行e2e测试,这样开发效率就会大大降低。

解决方案

  • 不涉及higress-core组件修改

可分为端到端测试的优化和在端到端测试中实现本地调试的功能,端到端优化可采用实现测试环境复用的方法,通过修改makefile文件来减少环境的创建等开销。

而在端到端测试中实现本地调试的功能,可通过Goland来实现,提前准备好测试环境,修改e2e测试的部分代码来减少e2e环境创建的开销,更加方便的实现debug功能。

  • 涉及higress-core组件修改

可在本地启动higress-core组件,并使其能够与kind集群中的discovery、gateway等组件进行交互。

例如在涉及到higress-config的ConfigMap修改的时候,其流程大致如下:

img.png

可以发现,只需要修改higress-core容器与discovery容器之间的xds协议通信地址,使其能够与本地的higress-core容器进行交互,其是注册在grpc服务上的,因此只需要修改xds地址为本地的grpc服务地址即可。

具体实现

端到端测试优化

通过makefile来完成,包含启动系列环境和执行e2e测试。

以运行插件测试为例,其make命令如下:

.PHONY: higress-wasmplugin-test
higress-wasmplugin-test: $(tools/kind) delete-cluster create-cluster docker-build kube-load-image install-dev-wasmplugin run-higress-e2e-test-wasmplugin delete-cluster

开发者首次运行环境可先删除最后一个delete-cluster的操作,第二次运行e2e测试的时候可以删除前面的操作,只保留run-higress-e2e-test-wasmplugin的操作,这样就可以减少很多时间。

然而make命令不支持添加到goland里面实现debug功能,运行出错的时候也不方便定位问题,更推荐使用下面这个方法。

在e2e中实现本地调试

通过Goland来完成,提前准备好环境,修改Goland启动参数来完成本地调试。

  • e2e测试之前的准备

根据各自的需求来定制环境,如果测试环境中需要用到higress的controller、gateway等组件,需要提前本地安装好环境,安装教程可参考这里,或者参考端到端测试优化步骤来准备好测试环境。

安装好higress后,可以使用如下命令来查看higress的pod是否正常运行:

kubectl get pods --all-namespaces

显示结果如下图所示:

1.png

都显示Running状态,说明higress已经正常运行。

  • e2e的flag修改

test/e2e/conformance/utils/flags目录下,有一个flags.go文件,里面定义了e2e测试的flag:

var (
IngressClassName = flag.String("ingress-class", "higress", "Name of IngressClass to use for tests")
ShowDebug = flag.Bool("debug", false, "Whether to print debug logs")
CleanupBaseResources = flag.Bool("cleanup-base-resources", true, "Whether to cleanup base test resources after the run")
SupportedFeatures = flag.String("supported-features", "", "Supported features included in conformance tests suites")
ExemptFeatures = flag.String("exempt-features", "", "Exempt Features excluded from conformance tests suites")
IsWasmPluginTest = flag.Bool("isWasmPluginTest", false, "Determine if run wasm plugin conformance test")
WasmPluginType = flag.String("wasmPluginType", "GO", "Define wasm plugin type, currently supports GO, CPP")
WasmPluginName = flag.String("wasmPluginName", "", "Define wasm plugin name")
IsEnvoyConfigTest = flag.Bool("isEnvoyConfigTest", false, "Determine if run envoy config conformance test")
)

可临时修改这些flag的初始值,也可以在Goland编辑器中定义启动参数,例如设置IsWasmPluginTest为true可只运行wasm插件的e2e测试。

修改好flag之后,我们就可以在Goland中通过debug的方式运行e2e测试。可先在如下位置添加一个断点,等待e2e环境准备完毕:

2.png

e2e测试在前期环境准备的过程中会创建一些namespace并启动一些pod,可以手动查看一下pod的启动情况。

img.png

在这张图里面,除了我们提前安装好的higress组件之外,还有一些其他的pod,这些pod是e2e测试过程中创建的,如果有些pod在本地e2e测试中用不到,可手动修改代码来减少前期环境的准备时间。

注意:如果设置了cleanup-base-resources为false,那么e2e测试结束之后,这些pod不会被删除,但是重启的时候会报错,例如:

Pod "consul-standlone" is invalid: spec: Forbidden: pod updates may not change fields other than `spec.containers[*].image`, `spec.initContainers[*].image`, `spec.activeDeadlineSeconds`, `spec.tolerations` (only additions to existing tolerations) or `spec.terminationGracePeriodSeconds` (allow it to be set to 1 if it was previously negative)

建议设置为true,每次测试完需要等待pod的销毁,然后重新测试,这些过程一般很快,也可以修改代码来减少一些pod的创建。

  • e2e测试环境优化

例如只需要higress环境,而不需要higress-conformance-infra,higress-conformance-app-backend等namespace环境,可以手动在如下几行代码里添加注释,来跳过这些环境的创建,然后测试中只用到了higress的组件:

img.png

在这里,我只需要运行EnvoyConfigTracing的Test测试,它只跟higress和higress-conformance-infra有关,注释掉了其他namespace的准备环境,可以看到e2e测试不到1s就结束了。

如果需要其他namespace的环境,而不需要nacos/consul等环境,可以在suite的New方法里找到以下代码块,根据需要进行注释:

// apply defaults
if suite.BaseManifests == nil {
suite.BaseManifests = []string{
"base/manifests.yaml",
"base/consul.yaml",
"base/eureka.yaml",
"base/nacos.yaml",
"base/dubbo.yaml",
}
}

解决好e2e测试的环境相关问题,我们就可以在Goland里添加自己想要的断点,来debug测试用例了。

higress-core本地调试+e2e测试

  • 首先在本地启动higress-core组件,需要修改其启动参数,与正常部署的启动参数保持一致,可参考如下配置Goland的启动参数:

img.png

  • 查找自己的本机非回环网卡的ip地址,例如我使用wlo0网卡,也就是192.168.0.189

img.png

  • 修改xds协议地址,运行kubectl edit cm higress-config -n higress-system,将其修改本地的grpc服务地址,如图:

img.png

将其中的127.0.0.1:15051修改为192.168.0.189:15051,保存退出。

  • 重启controller和gateway deployment,使其能够重新加载配置:
kubectl rollout restart deployment higress-gateway -n higress-system
kubectl rollout restart deployment higress-controller -n higress-system

img.png

  • 重启成功后可以先运行一下简单的e2e测试,比如http route等,测试是否能正常连通gateway,关于ConfigMap部分,我这里给本地higress-core添加了一个global-option的配置,此时kind的core组件还没有该配置,按照前面步骤启动本地higress-core并修改xds地址重启负载之后,运行相关的e2e测试,可以看到测试通过,本地higress-core日志以及kind上的discovery日志如下:

img.png

通过本地higress-core日志可以看到ads成功推送出去envoyfilter配置文件给discovery,而discovery日志则显示lds/rds/cds等成功将配置文件推送给gateway。

hgctl code-debug的使用

hgctl已经推出code-debug的功能,会自动修改xds地址,使其能够与本地的higress-core进行交互。这里需要使用hgctl来安装higress,目前code-debug只支持local-k8s的profile。

  • hgctl安装higress
hgctl install --set profile=local-k8s

等待安装完成后,手动查看一下是否启动成功。

  • 启动本地higress-core

修改Goland启动参数为serve --kubeconfig=/root/.kube/config(改为自己的home目录),然后启动higress-core,等待higress-core启动成功。

  • 开启code-debug
hgctl code-debug start

查看controller和gateway的pod是否重启成功,如果重启成功,说明code-debug已经生效。

  • 其他环境准备

在后面的步骤上涉及到Ingress资源,这里需要准备一下Ingress资源,运行如下命令:

kubectl apply -f - <<EOF
apiVersion: v1
kind: Namespace
metadata:
name: higress-conformance-infra
labels:
higress-conformance: infra
---
apiVersion: v1
kind: Service
metadata:
name: infra-backend-v1
namespace: higress-conformance-infra
spec:
selector:
app: infra-backend-v1
ports:
- protocol: TCP
port: 8080
targetPort: 3000
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: infra-backend-v1
namespace: higress-conformance-infra
labels:
app: infra-backend-v1
spec:
replicas: 2
selector:
matchLabels:
app: infra-backend-v1
template:
metadata:
labels:
app: infra-backend-v1
spec:
containers:
- name: infra-backend-v1
# From https://github.com/kubernetes-sigs/ingress-controller-conformance/tree/master/images/echoserver
image: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/echoserver:v20221109-7ee2f3e
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
resources:
requests:
cpu: 10m
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: higress-conformance-infra-configmap-global-test
namespace: higress-conformance-infra
spec:
ingressClassName: higress
rules:
- host: "foo.com"
http:
paths:
- pathType: Prefix
path: "/foo"
backend:
service:
name: infra-backend-v1
port:
number: 8080
EOF
  • 调试higress-core

以ConfigMap的gzip(位于pkg/ingress/kube/configmap/gzip.go)为例,修改NewDefaultGzip方法,将Enable的默认值修改为true,然后重启higress-core。

在重启之前,先运行如下shell命令,启动envoy查看面板:

hgctl dashboard envoy

访问http://localhost:15000/config_dump,使用Ctrl+F搜索compression_level,可以看到查找结果为空,说明gzip配置还没有生效。

接下来重启higress-core,等待higress-core重启成功,再次访问http://localhost:15000/config_dump,使用Ctrl+F搜索compression_level,可以看到查找结果不为空,说明gzip配置已经生效。

  • 关闭code-debug
hgctl code-debug stop

总结

  1. 本地调试和e2e测试是开发过程中必不可少的环节,通过本文的介绍,我们可以更加方便的进行本地调试和e2e测试,提高开发效率。
  2. 涉及到higress-core组件修改的时候,我们可以通过修改xds地址并本地启动higress-core,而不需要频繁地修改代码、重新构建镜像以运行e2e测试,并且还能调试higress-core组件的代码。
  3. higress后续会推出一些新功能,实现e2e测试的拆分,主要会分为准备环境,运行测试以及清除环境等,灵活性更高。

· 阅读需 6 分钟

加入Higress的商业化Team

我们商业化Team有充足的HC,欢迎加入👏,联系方式:微信(nomadao) 邮箱(zty98751@alibaba-inc.com)

团队介绍

Higress团队属于阿里云云原生中间件团队。

阿里云云原生中间件团队负责分布式软件基础设施,为阿里云上万家企业提供如微服务引擎、分布式事务、EDAS等分布式基础服务,加速企业上云的进程和创新速度。同时,云原生中间件团队也服务着阿里集团众多核心业务和场景,是支撑双十一狂欢节的核心团队之一。

在这里,有非常优秀的中间件产品和场景以及企业互联网架构平台,服务上万家阿里云的企业,支持大规模电商交易业务场景。

在这里,你会参与到全球优秀开源项目(如 Apache Dubbo、RocketMQ、Nacos、Sentinel、Seata、Arthas、Tengine、Istio、Envoy、OpenSergo、Higress)和阿里云核心商业化产品(微服务引擎 MSE、企业级分布式应用服务 EDAS、应用实时监控服务ARMS)研发工作中,一同拓展技术的边界,既赋能阿里巴巴集团,更服务广泛的开发者用户。

在这里,你会参与到中间件、微服务、Serverless 等前沿的技术研发与探索中来;你也会与全球优秀开源产品创始人等组成的明星团队一起工作。

负责工作

  1. 参与阿里云商业版Higress(MSE云原生网关)的产品研发
  2. 参与Higress开源的产品研发和社群运营

职位要求

  1. 3年以上产品研发工作经验,熟练使用Go或C++,有云原生相关产品研发经验者优先。
  2. 有Higress/Envoy/Istio/Nginx开源贡献和开发经验者优先。
  3. 具备出色的项目管理协调和业务规划能力,有团队合作精神,并能借助各方力量推动规则、制度等落地。

加入Higress的开源Team

Higress为参与贡献的同学设计了几个可认领的头衔,达到一定贡献要求,即可在Higress官网提PR的方式认领。

头衔本身是一种荣誉象征,每一位参与Higress的贡献者在开源社区的地位都是平等的

Member 头衔

条件:

  • 获得 8 个完成 issue 的积分(easy +1,normal +2,challenge +4)

Higress开源社区为每个可认领(help wanted标签)的issue都标记了难度标签:简单(level/easy),普通(level/normal),有挑战(level/challenge),完成issue后可以获得对应积分

  • 贡献代码(包含文档)DIFF 行数(包含增删)达到 500+

不仅只有贡献代码,在Higress官网仓库贡献文档,也可以满足此要求

  • 在社区周会进行 1 次主题分享

重点不是分享的内容,是通过分享让大家认识你

申请方式:

直接提PR申请

Reviewer 头衔

条件:

  • 满足Member条件
  • 在SIG的核心 issue 中做出贡献

申请方式:

由Approvers/Maintainer提名后,提PR申请

Approver 头衔

条件:

  • 满足Reviewer条件
  • 主导一个SIG,组织并推进核心issue的tasklist完成

申请方式:

由Maintainer提名后,提PR申请

Maintainer 头衔

Maintainer是对Higress项目(包括Higress下的项目)的演进和发展做出显著贡献的个人。在社区中具有有目共睹的影响力,能够代表Higress参加重要的社区会议和活动。

条件:

  • 满足Approver条件
  • 主导多个SIG,组织并推进核心issue的tasklist完成
  • 积极参与开源社区的日常工作:引导新人开发,用户问题解答等

申请方式:

由2名Maintainer提名后,提PR申请

已获得头衔名单

注:排名按 github id 首字母

Higress Maintainers

姓名github组织
董艺荃CH3CHOTrip.com
耿蕾蕾gengleileiAlibaba
张添翼johnlanniAlibaba
范扬SpecialYangAlibaba

Higress Approvers

姓名github组织
吴新军2456868764-
刘训灼XunzhuoTencent

Higress Reviewers

姓名github组织
凌轶群LynskylatePdd Holdings Inc
刘晓瑞rinfxAlibaba
赵炳堃sjtuzbkAlibaba

Higress Members

姓名github组织
李强林Charlie17LiZJU-SEL
封宇腾FfyytXUPT
田亚涛HinstenyAnt
宋鹏远songpengyuanOkki.com
赵伟基vikizhao156SJTU
韦鑫WeixinXNUAA

· 阅读需 19 分钟

历程回顾

image

Higress 开源一年时间,一共发布了 18 个 release 版本,收获了 40 多位社区贡献者和 1800+ star,上图是这一年过来达成的一些关键的里程碑。

前面半年通过集成开源生态,打磨开源版本稳定性,并在发布 1.0 GA 版本后,社区又马不停蹄发布了 1.1 和 1.2 两个重要版本,实现了非 K8s 部署,Knative 适配等核心能力。

Higress 1.3 版本已经正式发布,除了增加的新功能,已有能力也在大量社区用户反馈的过程中不断完善改进,这个版本同时标志着 1.x 进入可以大规模生产使用的状态。

新版本:功能速览

自发布 1.2 版本过去了一个多月时间,1.3 版本正式发布,带来两个全新能力:

  • 新标准:支持最新版本 Gateway API 标准,并且具备从 Ingress 平滑渐进演进,以及对接多种服务发现机制的能力
  • 新工具:正式 release 了 hgctl (Higress Crontroller) 这个 "All in one" 的新工具,不仅可以替代 Helm 实现更简易的安装,还提供了 WASM 插件开发工具包,以及网关配置检查等丰富功能

除了这两个核心功能外,还有一些实用功能:

  1. 提供了 Higress Admin Java SDK,可以统一对接 K8s 和非 K8s 环境,管理域名/路由等配置
  2. 提供了 OIDC 插件,支持对接 Keycloak/Okta 等第三方身份认证系统
  3. Higress Console 的易用性和安全性提升,不再通过路由暴露,支持界面初始化/修改密码

与此同时,Higress 开源社区已经开始准备踏上一段全新的开源征程...

新标准:支持最新版 Gateway API

Gateway API 在 11 月 1 日正式发布了 1.0.0 版本,其中 GatewayClass, Gateway, HTTPRoute 这三个 API正式宣布 GA,发布了 v1 版本:gateway.networking.k8s.io/v1。

目前 Higress 已经可以支持这些最新版本的 API 配置,只需在安装/升级 Higress 时配置开启 Gateway API:

  • 使用 Helm :通过参数 --set global.enableGatewayAPI=true
  • 使用 Hgctl :通过命令行参数或者 install.yaml 中配置 global.enableGatewayAPI=true

首先创建 GatewayClass 资源:

  apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: higress-gateway
spec:
controllerName: "higress.io/gateway-controller"

接着在安装 Higress 的命名空间下,创建 Gateway 资源,通过 gatewayClassName 关联上面创建的 GatewayClass 资源,指定由 Higress 来管理此 Gateway 配置,下面为域名同时配置了 HTTP 和 HTTPS 入口,并为 HTTPS 入口配置了证书:

  apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: higress-gateway
namespace: higress-system
spec:
gatewayClassName: higress-gateway
listeners:
- name: foobar
hostname: "*.foobar.com"
port: 80
protocol: HTTP
allowedRoutes:
namespaces:
from: All
- name: foobar-https
hostname: "*.foobar.com"
port: 443
protocol: HTTPS
allowedRoutes:
namespaces:
from: All
tls:
certificateRefs:
- kind: Secret
name: wildcard-foobar-com
mode: Terminate

因为上面 Gateway 通过 allowedRoutes 配置了允许所有命名空间的路由来关联,所以这里在 default 命名空间下创建 HTTPRoute,关联上面创建的 Gateway,即可定义域名下的具体路由:

  apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: foobar
namespace: default
spec:
parentRefs:
- name: higress-gateway
namespace: higress-system
hostnames: ["www.foobar.com"]
rules:
- matches:
- path:
type: PathPrefix
value: /foo
backendRefs:
- name: foo-service
port: 5678

以上就是 Gateway API 的一个简单用法示例,Gateway API 还有很多功能和玩法,后面 Higress 公众号/博客会出一个系列进行系统分享和介绍。

欢迎结合官方 API 文档,并使用 hgctl (获取方式见下文)在自己电脑上安装一个 local-k8s 模式的 Higress,来开启对这一新标准的探索:

  # 一键安装, 交互式命令选择第一种安装模式 local-k8s,将默认安装 Gateway API CRD
hgctl install

多种服务发现能力

在 Ingress API 标准下,Higress 对接多种服务发现能力是独树一帜的,在 Gateway API 标准下, Higress 仍就保持了这一能力优势:

首先通过 McpBridge 资源,可以定义多种服务发现机制:

  apiVersion: networking.higress.io/v1
kind: McpBridge
metadata:
name: default
namespace: higress-system
spec:
registries:
- domain: 127.0.0.1
nacosGroups:
- DEFAULT_GROUP
name: my-local-nacos
port: 8848
type: nacos2
- domain: 127.0.0.1
name: my-local-zk
port: 2181
type: zookeeper
- domain: 127.0.0.1
name: my-local-eureka
port: 8761
type: eureka
- domain: 127.0.0.1
consulDatacenter: dc1
name: my-local-consul
port: 8500
type: consul

创建一个 HTTPRoute 资源,可以同时对接 K8s 服务,和注册中心的服务,并实现灰度路由能力:

  apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: http
namespace: default
spec:
parentRefs:
- name: higress-gateway
namespace: higress-system
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: service-provider.DEFAULT-GROUP.public.nacos
group: networking.higress.io
port: 8080
weight: 90
- name: foo-service
port: 5678
weight: 10

和 K8s 服务不同的是,这里 group 为 networking.higress.io 的服务并不需要提前创建 CRD 资源,这更符合传统微服务用户的习惯,即服务模型不需要提前创建,是通过服务节点注册自动生成。

Ingress API 和 Gateway API 之间如何选择

有了 Gateway API,是否应该立即抛弃 Ingress API?

只有最合适的,没有最好的。Gateway API 虽然为更多网关能力做了标准化,但 CRD 的种类和复杂度也增加了,相比之下对于大部分简单用例场景,Ingress API 更加简单易用。

对于以下场景,采用 Gateway API 替代 Ingress API 会带来很大帮助:

  • 大型团队划分了 SRE 角色和业务研发角色,由 SRE 通过 Gateway 资源统一管理站点域名和证书,由业务研发通过 HTTPRoute 资源管理业务路由,实现职责划分,权限收敛
  • 创建的路由和 Service 有不在一个命名空间的需求,可以借助 ReferenceGrant 资源实现
  • 有大量证书需要集中式管理,不希望将证书 Secret 同步到 Ingress 所在命名空间,带来安全风险

Gateway API 的另一个好处是对于更多功能的标准化定义,我们建议遇到实际需要再转换到这个新的标准,而不必盲目跟随。

Higress 支持 Gateway API 和 Ingress API 混合使用,Gateway API 下的域名路由将比 Ingress API 优先匹配,和 Ingress 相同资源名称的 HTTPRoute 还会继承 WASM 插件配置,这样用户可以按需采用 Gateway API,平滑地完成从 Ingress API 向 Gateway API 的演进,无需焦虑 API 标准升级过程中产生业务损失。

新工具:All in one 的 hgctl

替代 Helm 用于安装/升级

在 K8s 下用 Helm 安装/升级组件很方便,但在 Higress 的场景下仍然存在一些问题:

  1. 无法支持非 K8s 场景下的安装/升级
  2. Higress 有很多安装参数,进行升级等操作时不好维护,使用reuse-values 有一些副作用,且容易误操作
  3. 无法管理 CRD 跟随版本更新,需要手动更新

Higress 借鉴了 istio 的 istioctl,提供了 hgctl 这个命令行工具解决了上述问题,通过以下命令即可安装 hgctl

  # 下载最新版 Hgctl:
curl -Ls https://raw.githubusercontent.com/alibaba/higress/main/tools/hack/get-hgctl.sh | VERSION=latest bash

hgctl 集成了三种 Higress 安装模式,并统一了升级/运维操作:

  1. 本地 K8s 环境(例如kind/k3s)模式
  2. 正式 K8s 环境模式
  3. 不依赖 K8s 的纯 Docker 环境模式

直接执行 hgctl install 命令即可选择任意模式进行安装

安装配置文件将存至 ~/.hgctl/profiles/install.yaml 目录下,查看该文件内容如下:

  charts:
higress:
name: higress
# 安装文件的 helm repo 地址
url: https://higress.io/helm-charts
# 执行 hgctl upgrade 时将自动更新至最新版本
version: latest
console:
# 开启可观测组件
o11YEnabled: true
# 控制台实例数
replicas: 1
controller:
# 控制面组件实例数
replicas: 1
gateway:
# 数据面组件实例数
replicas: 1
global:
# 开启 Gateway API
enableGatewayAPI: true
# 开启 Istio API
enableIstioAPI: true
# 设置监听的 ingress class
ingressClass: higress
# 安装模式
install: local-k8s
# 安装命名空间
namespace: higress-system
# 配置传递给 helm 的 values 参数
values: {}
profile: local-k8s

修改上面文件的内容后,执行hgctl upgarde即可实现参数变更生效,如果只想修改参数,不想触发版本升级,可以将 version 参数固定为当前版本。

WASM 插件开发工具包

为了标准化并简化 WASM 插件的开发/编译/测试/发布,Higress 提供了 hgctl plugin 这一子命令,使用方式为:

  1. hgctl plugin init: 初始化 Golang WASM 插件项目,包括三个文件;
  2. 用户编写 WASM 插件逻辑;
  3. hgctl plugin build --output-type files: 构建 WASM 插件,在本地输出构建产物;
  4. hgctl plugin test: 使用 docker compose 在本地测试 WASM 插件,如需修改插件逻辑,则返回第 2 步;
  5. hgctl plugin build --output-type image: 构建 WASM 插件作为 OCI 镜像上传至镜像仓库;
  6. hgctl plugin install: 安装 WASM 插件,可以通过本地的 yaml 文件或插件项目进行安装。

另外,若需要查看已安装的插件,则使用 hgctl plugin ls;若需要操作插件配置,则使用 hgctl plugin config

通过这个工具,可以在构建 WASM 插件的同时,根据配置代码自动生成插件的配置说明文档,以及包含插件配置约束的元信息文件,这些内容都将和 WASM 文件一起放入 OCI 镜像制品中,通过镜像方式进行版本管理和分发。这一机制是后续 Higress 建设 WASM 插件市场的基石。

其他功能

另外介绍两个实用的子命令:

  1. hgctl dashboard: 用于呼出 UI 界面,例如 Higress 控制台,直接通过 hgctl dashboard console 即可打开
  $ hgctl dashboard
Access to Higress web UIs

Usage:
hgctl dashboard [flags]
hgctl dashboard [command]

Aliases:
dashboard, dash, d

Available Commands:
console Open Console web UI
controller Open Controller debug web UI
envoy Open Envoy admin web UI
grafana Open Grafana web UI
prometheus Open Prometheus web UI
  1. hgctl gateway-config: 用于查看数据面的 envoy 配置,可以快速验证配置是否正确下发
  $ hgctl gateway-config
Retrieve information about Higress Gateway Configuration.

Usage:
hgctl gateway-config [command]

Aliases:
gateway-config, gc

Available Commands:
all Retrieves all Envoy xDS resources from the specified Higress Gateway
bootstrap Retrieves bootstrap Envoy xDS resources from the specified Higress Gateway
cluster Retrieves cluster Envoy xDS resources from the specified Higress Gateway
endpoint Retrieves endpoint Envoy xDS resources from the specified Higress Gateway
listener Retrieves listener Envoy xDS resources from the specified Higress Gateway
route Retrieves route Envoy xDS resources from the specified Higress Gateway

新征程:API Gateway

上面说了 Gateway API,接着我们聊聊 API Gateway 😄,API Gateway 有两层定义:

  1. 狭义上:满足统一接入,将路由转发到不同服务的运维需求,即可称为 API Gateway;这里 API 的定义是服务的路由
  2. 广义上:在实现服务转发的基础上,需要识别带业务语义的接口,将业务能力 API 化管理,统一对外提供服务;这里 API 的定义是业务功能接口

Higress 已经实现了狭义上的 API Gateway 能力,并且是基于 Gateway/Ingress API 这些通用路由标准来实现的。而与服务路由标准不同,业务功能接口的标准是 Swagger/OAS3/RPC IDL 等,做为 API Gateway 需要提供以下关键能力:

  1. 支持通过上传 Swagger 等接口定义文件的方式导入 API
  2. 对 API 实现精细化策略管理,例如根据出入参定义实现参数映射/转换
  3. 实现以 API 方式开放能力时的认证/鉴权,调用量控制/审计能力

Higress 新的开源征程将向具备业务 API 管理能力的 API Gateway 形态进发。在实现方式上,我们将基于 WASM 插件去扩展这一部分能力,这可以降低我们对上游 Envoy 社区的侵入性改造,同时让对 API 的精细化策略管理具备自定义扩展能力。 目前社区已经有一些 Proposal ,欢迎了解:

https://github.com/alibaba/higress/issues/535

https://github.com/alibaba/higress/issues/601

欢迎有更多小伙伴加入,和我们一起踏上新的征程,有兴趣的小伙伴可以联系我(微信:nomadao),加入 API Gateway 的 SIG(兴趣小组)。

社区致谢

首先要感谢 Envoy 和 Istio 社区,Higress 站在这两个软件的肩膀上演进能力,得以:

  1. 通过 WASM 机制扩展 Envoy 数据面能力,持续不断地扩大网关插件生态
  2. 通过专注在网关领域,在 Istio 优秀的控制面基础上,进一步做抽象和简化,降低上手和运维门槛

还要感谢参与 Higress 开源贡献的开发者们,这里重点感谢下为 1.3 版本做出核心贡献的开发者:

Maintainer:董艺荃(CH3CHO)

人如其名“艺全”,十八般武艺样样精通,不管是控制台 TS 前端,Java 后台,还是 Higress 的 GO 控制面,以及 Standalone 安装 Shell 脚本和各种 CICD 流程,通通手到擒来。

在 1.3 版本中主要负责了 Higress 支持 Gateway API 的能力,以及实现了 Higress Admin Java SDK 可以提供外部集成用于管理 Higress 配置,并改进了 Higress Console 的安全性和易用性。

除了开发贡献之外,他还对社区用户充满善意和热情,无论是在 GitHub 的 Issues/Discussions,或是社区交流微信/钉钉群,随处可见他帮助用户解决问题的身影。

Approver:吴新军(Jun),刘训灼(Xunzhuo)

两位都在多个 Higress 版本为社区做出了贡献,Jun 的主要贡献有:在多注册中心的服务发现支持,全局配置管理架构抽象;Xunzhuo 的主要贡献有:Higress E2E 测试流程的搭建,GitHub CI 流程的建设,hgctl 的整体架构设计。

在 1.3 版本中二位协作完成了 hgctl 的多样化能力建设,帮助 Higress 的易用性又上到了一个新的台阶。

两位同学作为 Approver 积极参与社区贡献 PR 的 Review,目前分别是 Higress Tools SIG 和 Higress GatewayAPI SIG 的领导者。

Member:韦鑫(WeixinX),封宇腾(Fkbqf)

两位都是通过中科院开源之夏(OSPP 2023)项目开始参与 Higress 贡献,WeixinX 是一名研二的学生,Fkbqf 是一名大三的学生。

在 1.3 版本中,WeixinX 实现了 hgctl plugin 子命令的能力,同时贡献了 Go 实现的 Basic Auth 插件,以及对标 Spring Cloud Gateway 请求响应转换能力的 Transformer 插件;Fkbqf 则实现了更为复杂的 OIDC 插件,具备比 Envoy 自带 OAuth2 Filter 更强大的功能,并且具备良好的扩展性。

两位同学除了开发贡献以外,用课余时间积极参与 Higress 社区周会,一起探讨和学习技术,不亦乐乎。能够成为你们人生学业进阶路上的阶梯,Higress 荣幸之至。

Higress 社区后续整体的 Roadmap 规划如下:

image

欢迎更多小伙伴一起加入我们:

higress-comm

· 阅读需 11 分钟

导读: 本文将和大家一同回顾 Spring Cloud Gateway 是如何满足 HTTP 请求/响应转换需求场景的,并为大家介绍在这种场景下使用 Higress 云原生网关的解决方案,同时还对比了两者的性能差异。

1. SCG 修改请求/响应

Spring Cloud Gateway (以下简称为 SCG) 中,当我们需要对 HTTP 请求或响应进行修改时,SCG 提供了许多内置的 GatewayFilter 来满足我们对这种应用场景的需求,例如 AddRequestHeader,AddRequestParameter, DedupeResponseHeader,MapRequestHeader, ModifyRequestBody 等。

考虑以下简单用例:

  • 添加请求头部 X-First,值从请求路径中获取,例如从 /response-headers?testKey=testValue 中获取 "response-headers";
  • 将请求头部 X-First 的值映射给 X-Second;
  • 添加请求查询参数 k1=v1;
  • 剔除重复的响应头部 X-Dedupe。

在 SCG 中使用 GatewayFilter 我们可以这样配置:

# application.yaml:

spring:
cloud:
gateway:
routes:
- id: test_route
uri: lb://httpbin-svc
predicates:
- Path=/{api}/**
filters:
- AddRequestHeader=X-First, {api}
- MapRequestHeader=X-First, X-Second
- AddRequestParameter=k1, v1
- DedupeResponseHeader=X-Dedupe, RETAIN_FIRST

相信拥有 SCG 使用经验的同学对上述配置一定不陌生,那么本文将重点给出另一种能够满足上述需求并且性能更加优越的解决方案——使用 Higress 云原生网关的 Transformer 插件

2. Higress 插件与 SCG 性能比较

我们在同一吞吐量水平(QPS)上,开启/关闭 Higress Transformer 插件 和 SCG 相应 GatewayFilters,统计两者在 CPU 和内存资源上的开销。

经过测试,我们得到的结论是:

  • 在 Higress 未启用 Transformer 插件,SCG 未启用 GatewayFilters 的条件下,SCG 的 CPU, 内存资源开销分别约为 Higress的 3.30, 4.88倍;
  • 在 Higress 启用 Transformer 插件,SCG 启用相应 GatewayFilters 的条件下,SCG 的 CPU,内存资源开销分别约为 Higress 的 2.98, 3.19倍。

相同 QPS 下的 CPU 开销

相同 QPS 下的内存开销

可见 Higress Transformer 相比于 SCG GatewayFilter 有着相当不错的性能表现!

接下来我们将进一步为大家介绍 Higress 云原生网关以及上述提到的 Higress Transformer 插件。

3. Higress 简介

Higress 是基于阿里内部的 Envoy Gateway 实践沉淀、以开源 Istio + Envoy 为核心构建的下一代云原生网关,实现了流量网关+微服务网关+安全网关三合一的高集成能力,深度集成 Dubbo、Nacos、Sentinel 等微服务技术栈,能够帮助用户极大地降低网关的部署及运维成本且能力不打折;在标准上全面支持 Ingress 与 Gateway API,积极拥抱云原生下的标准 API 规范;同时,Higress Controller 也支持 Nginx Ingress 平滑迁移,帮助用户零成本快速迁移到 Higress。 Higress 提供了一套 Wasm (WebAssembly) SDK,使得开发者能够轻松使用 C++,Golang,Rust 开发 Wasm 插件增强网关能力。下面将为大家介绍 Higress Transformer 插件的基本功能,最后简单说明 Transformer 插件的核心代码逻辑。

Higress Architecture

4. Transformer 插件介绍

Higress Transformer 插件可以对请求/响应头部、请求查询参数、请求/响应体参数进行转换,支持的转换操作类型包括删除(remove)、重命名(rename)、更新(replace)、添加(add)、追加(append)、映射(map)和去重(dedupe)。

接下来我们复现最开始提到的 SCG GatewayFilter 简单用例,来演示如何使用该插件(以下使用 Higress 控制台可以很方便地部署插件,当然也可以使用 K8s YAML Manifests 的方式):

  1. 首先根据官方文档 快速安装 Higress,结果如下:
$ kubectl -n higress-system get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
higress-console 1/1 1 1 1d
higress-console-grafana 1/1 1 1 1d
higress-console-prometheus 1/1 1 1 1d
higress-controller 1/1 1 1 1d
higress-gateway 1/1 1 1 1d
  1. 通过 Higress 控制台添加域名(foo.bar.com)、路由配置(foo),将流量转发至后端的 httpbin 服务:

image

image

  1. 为 foo 路由添加 Transformer 插件(当前未推送插件至官方镜像仓库,可以先使用 docker.io/weixinx/transformer:v0.1.0,或到代码仓库自行构建):

image

注:为了能够同时完成请求和响应转换的需求,我们需要为 foo 路由再添加一个 Transformer 插件,命名为 transformer-resp,用于处理响应方向。

  1. 添加 Transformer 配置并开启该插件:
  • 添加请求头部 X-First,值从请求路径中获取,例如从 /response-headers?testKey=testValue 中获取 "response-headers";
  • 将请求头部 X-First 的值映射给 X-Second;
  • 添加请求查询参数 k1=v1;
  • 剔除重复的响应头部 X-Dedupe。
# transformer:
type: request # 指定 Transformer 类型
rules: # 指定转换规则
- operate: add # 指定转换操作类型
headers: # 指定头部转换规则
- key: X-First
value: $1 # 正则表达式捕获组 $1,支持 RE2 语法
path_pattern: ^\/(\w+)[\?]{0,1}.*$
querys: # 指定查询参数转换规则
- key: k1
value: v1
- operate: map
headers:
- key: X-First
value: X-Second
---
# transformer-resp:
type: response
rules:
- operate: dedupe
headers:
- key: X-Dedupe
value: RETAIN_FIRST
  1. 发送请求进行测试:
# 验证请求方向转换
$ curl -v -H "host: foo.bar.com" "127.0.0.1/get"
...
>
< HTTP/1.1 200 OK
...
<
{
"args": {
# 添加了查询参数 k1=v1
"k1": "v1"
},
"headers": {
...
"X-First": "get", # 添加了请求头部 X-First,值 "get" 来自请求路径
"X-Second": "get" # 映射了请求头部 X-Second
},
...
# 添加了查询参数 k1=v1
"url": "http://foo.bar.com/get?k1=v1"
}


# 验证响应方向转换
$ curl -v -H "host: foo.bar.com" \
"127.0.0.1/response-headers?X-Dedupe=1&X-Dedupe=2&X-Dedupe=3"
...
>
< HTTP/1.1 200 OK
< x-dedupe: 1 # 保留了响应头部 X-Dedupe 的第一个值
...
<
{
...
# 通过查询参数传给 httpbin 的自定义响应头部
"X-Dedupe": [
"1",
"2",
"3"
],
...
}

❗️需要注意的是:

  • 与上述例子相同,若有同时处理请求和响应转换的需求,则需要为相应路由添加两个 Transformer 插件,分别处理请求方向和响应方向(正在优化);
  • 请求体支持的 Content-Type 有:application/json, application/x-www-form-urlencoded, multipart/form-data;而响应体仅支持 application/json;
  • 更多说明详见插件文档

5. Transformer 逻辑

本节将简单说明 Higress Transformer 插件的核心代码逻辑,希望可以为有兴趣优化该插件或进行二次开发的同学提供一些帮助。

首先该插件代码位于Higress 仓库的 plugins/wasm-go/extensions/transformer 目录下,使用 Higress 提供的 Wasm SDK 进行开发(关于如何开发 Wasm 插件详见官方文档)。

插件的配置模型 TransformerConfig:

# 模型以插件配置的形式暴露给用户
type TransformerConfig struct {
typ string # Transformer 类型,[request, response]
rules []TransformRule # 转换规则

trans Transformer # Transformer 实例,不对用户暴露配置,用于实际的转换操作
}

type TransformRule struct {
operate string # 转换操作类型
headers []Param # header 参数
querys []Param # query 参数
body []Param # body 参数
}

type Param struct {
key string # 表示字段的 key
value string # 表示字段的 value 或 key (map) 或 strategy (dedupe)
valueType string # 为 application/json body 指定 value 的数据类型
hostPattern string # host 正则匹配模式
pathPattern string # path 正则匹配模式
}

其中 Transformer 作为接口分别有请求和响应两个实现(requestTransformer, responseTransformer),主要实现了 3 个接口方法 TransformHeaders,TransformerQuerys 和 TransformBody:

type Transformer interface {
TransformHeaders(host, path string, hs map[string][]string) error
TransformQuerys(host, path string, qs map[string][]string) error
TransformBody(host, path string, body interface{}) error
...
}

var _ Transformer = (*requestTransformer)(nil)
var _ Transformer = (*responseTransformer)(nil)

由于头部(Headers)和查询参数(Querys)都是以 key-value 的形式存在,因此通过 kvHandler 对两者采用统一的处理逻辑;而 Body 由于请求、响应支持不同的 Content-Type,因此分别通过 requestBodyHandler (kvHandler, jsonHandler 组合)和 responseBodyHandler (jsonHandler) 进行处理。综上,在修改该插件逻辑时,主要对 kvHandler 和 jsonHandler 进行修改即可,其中 jsonHandler 依赖 GJSONSJSON 工具库。

image

目前 handler 中的转换顺序是被硬编码的(remove -> rename -> replace -> add -> append -> map -> dedupe),我们对此有优化的打算,也欢迎感兴趣的同学参与进来 ~

6. 总结

本文带大家了解了 Higress Transformer 插件,并与 Spring Cloud Gateway 进行了性能比较,在文章的最后还说明了该插件的核心代码逻辑,希望能够为大家从 Spring Cloud Gateway 迁移至 Higress 提供帮助!

image

如果您觉得 Higress 对您有帮助,欢迎前往 Github: Higress 为我们 star⭐️ 一下!期待与您在 Higress 社区相遇 ~

· 阅读需 9 分钟

分享简介

在9月26日的2023 KubeCon阿里云云原生开放日,Higress的分享内容分为两部分:

Part 1. 由上海费芮网络科技系统运维副总监戴喜军,带来费芮在选型企业版Higress作为K8s Ingress替代Nginx Ingress的介绍

Part 2. 由Higress开源社区负责人澄潭,带来Higress开源项目的介绍

开源版和企业版是Higress的一体两面,通过本次分享,相信大家会对Higress有更全面的了解。

Part 1. 费芮互动使用Higress作为Kubernetes Ingress实现稳定性和性能提升

费芮通过Higress解决了原本Nginx Ingress网关的诸多痛点,性能提升90%,响应时间下降50%,并大幅提升业务入口的稳定及安全性,高效支撑每日1亿+粉丝交互, 4万+线下门店、每月3000万+笔的移动支付需求。

费芮业务场景

image image

费芮专注于移动营销、O2O、社交媒体、移动电商领域的创新与研发。费芮互动自主研发的自媒体平台运维超过2亿粉丝 ; 有超过4万家线下门店采用费芮O2O解决方案。费芮的主要客户包括优衣库,必胜客,肯德基,星巴克,SPG,欧莱雅,Innisfree,迪卡侬,顶新集团等。

image

费芮内部业务系统的日均发布次数达到100次之多,会涉及到400多条Ingress路由规则的日常更新,且网关每天需要承载的PV流量达到了1个亿。Ingress网关的性能和稳定性至关重要。

Nginx Ingress 痛点

image

配置变更频繁,导致Nginx进程频繁重启,大量连接瞬时断开后并发重连会导致后端服务产生压力,严重时造成大量请求失败。

image

Nginx Ingress的Controller组件和Nginx组件运行在同一个POD中,控制面资源使用还会影响到数据面的性能。

image

Nginx Ingress还缺少面向服务限流的能力,只能实现面向单个来源IP限流,对于后端服务保护来说没有意义。

Higress 收益

image

Higress企业版采用了全托管架构,与业务集群分离,无需自己运维,稳定性有更好保障。

image

配置更新都是动态加载,无需重启数据面,保障业务平稳发布,websocket连接的业务收益也特别明显,长连接可以始终保持不会断开。

image

开启了TLS硬件加速,TLS握手时延直接减半,QPS吞吐提升86%。

image

image

开启WAF对吞吐影响还是比较明显的,下降了30%,但相比Ingress Nginx直接下降了90%,性能提升还是很明显,而且更大的优势是基于阿里云WAF产品,防护规则是实时更新的,而非Modsecurity的静态规则。

image

Higress集成了Sentinel限流的能力,可以直接面向服务的QPS吞吐上限/并发数上线进行限流,并且相比Nginx只能配置单机限制阈值,需要关注网关节点数量,Higress这里的配置是全局阈值,不受网关扩缩容影响。

触发限流后可以自定义响应内容或者重定向到指定地址,都是很实用的能力。

Part 2. Higress开源之路:扎根开源生态,定义云原生网关

开源是云原生生态的基石,Higress也是借助了开源生态的力量,站在Istio/Envoy的肩膀上开发出了更多实用的功能,我们选择将MSE Higress(企业版)中的核心能力全部开源,决心扎根在开源生态中,让Higress变得更普惠,有更多人使用,从而让Higress更加强大。

简介

image

Higress实际上有三次诞生过程:第一次是在阿里集团内部业务需求驱动下诞生;第二次是随着内部使用逐渐成熟沉淀为阿里云上的MSE Higress云原生网关产品;第三次是随着云产品成熟,2022年11月在云栖大会上正式宣布开源。2023年5月Release了第一个GA版本,意味着开源版本也走入成熟阶段。

image

从配置流转的过程来看Higress的架构:

  1. 首先用户可以通过UI控制台/命令行工具多种方式来下发配置
  2. 到了控制面,如果是K8s下,配置持久化基于CRD,如果不是K8s,配置持久化基于Nacos
  3. 除了用户下发的路由配置,实现服务路由还需要的服务IP地址信息,支持从K8s service/Nacos/Eureka/Consul/ZooKeeper/DNS/固定IP等多种方式获取
  4. Higress数据面是基于Envoy,配置通过xDS下发,这里复用了Istio的配置下发机制,因为是内置了这个机制,无需单独部署Istio
  5. 最终配置下发到数据面生效,Higress基于Envoy扩展了很多能力,而且基于Wasm扩展机制,可以很方便开发自定义插件,且具备全局/域名/路由维度的生效粒度

核心能力

image image

高集成:同时集成经典微服务生态和K8s开源生态能力,可以帮助业务从传统架构迁移到云原生架构,基于流量灰度等能力,可以保障这一过程的平滑

image

标准化:兼容Nginx Ingress常用注解,基于统一的Ingress标准可以轻松实现Nginx到Higress这一技术鸿沟的跨越,Higress也已经支持Gateway API,路由标准本身也能借助Higress实现平滑升级

image

image

易扩展:借助Higress的Wasm SDK,很少的业务代码就可以开发一个灵活的插件;并且支持基于OCI镜像标准分发,可以让插件的文档,配置约束等跟随插件本身一起被分发和共享;和传统Nginx类网关最大的差别,在于插件的分发集成阶段,实现了插件版本更新跟网关自身版本更新的解耦。

image

image

热更新:Envoy相比Nginx更合理的配置系统抽象,让Higress具备了Nginx Ingress无法实现的配置热更新能力

回顾与展望

image

Higress开源的前半年,专注于开源生态的打通和易用性的提升,并基于Github Action构建了开源的集成测试体系,来保障项目质量,在今年5月份发布第一个GA稳定版本后,在多个核心社区开发者的努力下,我们又很快发布了1.1和1.2两个大版本,推出了非K8s部署/Knative支持等重量级功能。

image

未来的RoadMap,Higress会聚焦在Gateway API/插件生态/API管理三个方向上,随着社区开发团队的不断壮大,Higress已经建立了多个不同方向的SIG 并行推进核心功能演进,未来将不断有重量级功能推出,尽请期待。

直播回看

https://www.aliyun.com/activity/middleware/CloudNative_Meetup

· 阅读需 4 分钟

前置准备

安装 Docker Compose

请参考 Docker 官方文档来安装 Docker Engine,其中已经内置了 Docker Compose 组件:https://docs.docker.com/engine/install/

环境验证

  1. 启动终端;
  2. 执行 docker compose version 命令,确认可以正常输出 Docker Compose 的版本。
    Docker Compose version v2.20.2

安装 Higrees

确定配置目录

由于这次我们准备使用文件来管理 Higress 的配置数据,所以需要先确定保存配置文件的目录。下面我们将以 ~/higress/conf 目录为例进行介绍。

执行安装

启动终端,并执行以下命令:

curl -fsSL https://higress.io/standalone/get-higress.sh | bash -s -- -c file://~/higress/conf -p <你的密码> -a

请耐心等待安装过程执行完毕。Higress 的执行文件将被安装在当前目录下的 higress 子目录内。配置数据则将被写入 ~/higress/conf 目录内。

在安装完成后,脚本会自动启动 Higress。当终端输出如下信息时,则说明 Higress 已安装完成并成功启动。

Higress is now started. You can check out its status by executing /home/ch3cho/higress/bin/status.sh

Higress Gateway is listening on:
http://0.0.0.0:80
https://0.0.0.0:443
Visit Higress Console: http://localhost:8080/

Higress 路由配置

为了着重说明基于文件的路由配置方式,这里将不再展开介绍使用 Higress 控制台来进行配置的具体步骤。如有需要,大家可以查阅其他文档。

创建服务来源

使用文本编辑器将以下内容写入 ~/higress/conf/mcpbridges/default.yaml 文件中:

apiVersion: networking.higress.io/v1
kind: McpBridge
metadata:
name: default
namespace: higress-system
spec:
registries:
- domain: httpbin.org
name: httpbin
port: 80
type: dns

创建域名配置

使用文本编辑器将以下内容写入 ~/higress/conf/configmaps/domain-foo.bar.com.yaml 文件中:

apiVersion: v1
kind: ConfigMap
metadata:
name: domain-foo.bar.com
namespace: higress-system
data:
domain: foo.bar.com
enableHttps: "off"

创建服务路由

使用文本编辑器将以下内容写入 ~/higress/conf/ingresses/route-foo-bar.yaml 文件中:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
higress.io/destination: httpbin.dns
higress.io/ignore-path-case: "false"
name: route-foo-bar
namespace: higress-system
spec:
ingressClassName: higress
rules:
- host: foo.bar.com
http:
paths:
- backend:
resource:
apiGroup: networking.higress.io
kind: McpBridge
name: default
path: /
pathType: Prefix

请求验证

在终端中执行以下命令:

curl http://localhost/get?foo=bar -H 'Host: foo.bar.com'

请求应返回一段包含请求信息的 JSON 数据:

{
"args": {
"foo": "bar"
},
"headers": {
"Accept": "*/*",
"Host": "foo.bar.com",
"Original-Host": "foo.bar.com",
"Req-Start-Time": "1693049173053",
"User-Agent": "curl/8.1.2",
"X-Amzn-Trace-Id": "Root=1-11111111-111111111111111111111111",
"X-B3-Sampled": "0",
"X-B3-Spanid": "2222222222222222",
"X-B3-Traceid": "33333333333333333333333333333333",
"X-Envoy-Attempt-Count": "1",
"X-Envoy-Decorator-Operation": "httpbin.dns:80/*",
"X-Envoy-Internal": "true"
},
"origin": "192.168.16.1, 123.123.123.123",
"url": "http://foo.bar.com/get?foo=bar"
}

已知问题

在 Windows 操作系统中,直接修改挂载到 Docker 容器中的本地文件后,容器内的进程无法收到通知(详情请查看 fsnotify/fsnotify #292)。如果要使用文件来保存配置数据的话,在直接修改配置文件后,Higress 无法立即加载到新的配置。如果需要在 Windows 上独立部署 Higress 网关,可以考虑通过 Higress Console 来管理配置信息,或使用 Nacos 保存网关配置。

参考文档

更多相关信息与 Higress 的其他部署方式可查阅以下文档: