控制面启动与初始化
把 cmd/higress/main.go 一路追到 pkg/bootstrap/server.go 的最后一个 init。读完这一期你能在白板上完整画出 Higress 启动的 6 步主线。
main.go 全文 32 行
package main
import (
"fmt"; "os"
"istio.io/pkg/log"
"github.com/alibaba/higress/v2/pkg/cmd"
)
func main() {
log.EnableKlogWithCobra()
if err := cmd.GetRootCommand().Execute(); err != nil {
fmt.Fprintln(os.Stderr, err); os.Exit(1)
}
}
它只是一个 thin shell:日志接管 + 启动 Cobra root 命令。所有逻辑都被推到 pkg/cmd,让 main 可被替换、可被测试。
EnableKlogWithCobra
该函数把 K8s 的 klog 输出桥接到 Istio 的 istio.io/pkg/log,并把 klog 的 --v、-vmodule 等 flag 注册到 cobra root。这是 Higress 与 client-go 共用日志栈的关键。
它的意义在于:你可以用 Istio 的 --log_output_level 控制所有依赖库日志。
--log_output_level=klog:debug 这类。GetRootCommand
// pkg/cmd/root.go
func GetRootCommand() *cobra.Command {
cmd := &cobra.Command{Use: "higress", Short: "Higress",
Long: "Next-generation Cloud Native Gateway"}
cmd.AddCommand(getServerCommand())
cmd.AddCommand(getVersionCommand())
return cmd
}
整棵命令树只有两枝:serve 与 version。结构简单到极致——所有复杂度都在 getServerCommand 的 RunE 里。
higress debug 子命令,从哪个文件开始?server 子命令
pkg/cmd/server.go 是控制面真正的入口。三件事:
- 把 cobra flag 绑到
serverArgs *bootstrap.ServerArgs。 - RunE 里调
serverProvider(serverArgs)→bootstrap.NewServer得到 Server。 - 调
server.Start(stop),再waitForMonitorSignal阻塞直到收到 SIGTERM。
serverProvider = func(args *bootstrap.ServerArgs) (bootstrap.ServerInterface, error) {
return bootstrap.NewServer(args)
}
这层间接抽出 serverProvider 是为了测试可替换。
serverProvider 弄成包级变量?提示:pkg/cmd/server_test.go。version 子命令
用法:higress version -o json。实现在 pkg/cmd/version/,输出二进制版本、git commit、build date,并尝试连接 xDS 拉取数据面版本。这是排查"我装的是哪个版本"问题的官方途径。
-ldflags "-X version.gitRevision=..."。ServerArgs 参数对象
定义于 pkg/bootstrap/server.go。它聚合了所有命令行参数:
type ServerArgs struct {
XdsOptions XdsOptions // 推送 debounce/EDS 控制
RegistryOptions RegistryOptions // K8s / FileDir 等
KeepaliveOptions *keepalive.Options
HTTPAddr, GRPCAddr, MonitoringAddr string
Namespace string
GatewaySelectorKey, GatewaySelectorValue string
EnableAutomaticHttps bool
// ...
}
这是经典的"Parameter Object"模式,让 server 包能被库式调用。
pkg/cmd/server.go 绑定 + ServerArgs 加字段 + 真正消费它的地方。ServerInterface 抽象
type ServerInterface interface {
Start(stop <-chan struct{}) error
WaitUntilCompletion()
}
把 NewServer 返回类型抽象为接口,让 pkg/cmd/server.go 不直接依赖 *Server,方便测试替换。这是依赖倒置的微缩样本。
NewServer 主线
func NewServer(args *ServerArgs) (*Server, error) {
s := &Server{...}
s.initKubeClient(args)
s.initMeshConfig()
s.initConfigController()
s.initRegistryEventHandlers()
s.initXdsServer()
s.initGrpcServer()
s.initAuthenticators()
s.initAutomaticHttps()
s.initHttpServer()
return s, nil
}
这就是控制面的"启动 6+步"骨架——把每一步当黑盒,就能在 5 分钟内读完 main path。
initKubeClient
位于 server.go 第 411 行附近。基于 in-cluster 或 kubeconfig 创建 istiokube.Client,并包一层 Higress 自己的 higresskube.Client(pkg/kube/client.go)。
关键点:Higress 用的是 Istio 的 multi-client 封装,一份 Client 持有 typed kube + dynamic + istio + 自定义 CRD 多个 clientset。
--kubeconfig ~/.kube/config + --master ...。MeshConfig Watcher
Higress 复用 Istio 的 meshwatcher:监听一个 ConfigMap(默认 higress-config)里的 mesh config YAML,变更时把新配置 hot reload 到 environment.Mesh。
这是 Pilot 推送语义的关键依赖:Mesh 决定了默认 sidecar 行为、扩展协议等。
initConfigController
核心:构造一个聚合 ConfigStore,把 K8s 原生 CRD store 与 Higress 自己的 IngressTranslation 合并:
stores := []model.ConfigStoreController{}
stores = append(stores, kubeCRDStore)
stores = append(stores, ingressTranslation)
configController = configaggregate.MakeWriteableCache(stores, nil)
对 Pilot 来说看到的是一份 ConfigStore;对 Higress 来说,IngressConfig 可以注入任意自定义合成的 VirtualService / DR / EnvoyFilter。这是 Higress 改造 Istio 最巧妙的接缝。
initRegistryEventHandlers
把 ServiceRegistry 的事件(Service/Endpoint 增删改)转发给 xDS:变更触发 push。具体做法是注册 ServiceHandler、WorkloadHandler 到聚合 ServiceRegistry,回调里调用 XDSServer.ConfigUpdate。
initXdsServer
构造 xds.NewDiscoveryServer(environment, generators, ...)。environment 包了 ConfigStore + ServiceRegistry + Mesh + PushContext。generators 是按资源类型分发的 xDS 生成器(LDS/RDS/CDS/EDS/SDS 等)。
Higress 在此阶段可以注入自定义 generator(如对 WasmPlugin 特殊处理),但大多数情况复用 Istio 的实现。
initGrpcServer
初始化一个 gRPC server(默认 :15010 明文 / :15012 双向 TLS),把 xds.DiscoveryServer 注册进去,再加上 grpc_prometheus、reflection。
istiogrpc.NewGrpcServer(...) // 复用 Istio 的辅助函数
prometheus.Register(grpcSrv)
reflection.Register(grpcSrv)grpcurl 调试。initAuthenticators
为 xDS 连接配置认证器列表(kube SAToken / JWT / clientCert)。Higress 默认开启 kubeauth.NewKubeJWTAuthenticator,让 Envoy sidecar 用 ServiceAccount Token 鉴权。
initAutomaticHttps
对接 pkg/cert 的 CertManager:开启自动证书签发(ACME 或自签)。具体实现见第 10 期。这里只看到一个轻量入口:根据 ServerArgs.EnableAutomaticHttps 是否启动。
initHttpServer 调试入口
默认 :8080 暴露调试端点:
/readyreadyHandler — 等所有 cache synced/registry/statusregistryWatcherStatusHandler — 各注册中心健康度- Pilot 自带的
/debug/...路径(如/debug/configz、/debug/syncz)
/debug/syncz。Start 与 waitForCacheSync
func (s *Server) Start(stop <-chan struct{}) error {
s.server.RunComponent(...) // 一组 lifecycle 组件
if !s.waitForCacheSync(stop) { ... } // 所有 informer cache 完成
// 启动 HTTP/gRPC listener,开始接受 ADS 连接
return nil
}
waitForCacheSync 阻塞到所有 Lister 列表完成首次 List & Watch;否则 xDS 推送的内容是空的会引发 Envoy 误判。
优雅退出 waitForShutDown
收到 SIGTERM 后:
- 停接受新的 ADS 连接(关闭 listener);
- 给 Envoy 留一段时间消费已发送的资源;
- 退出 informer / workqueue;
- 退出进程。
这是控制面"不重启 Envoy"零中断升级的基础。
环境变量与 Feature Flag
Higress 复用 Istio 的 env.Register 模式,把 feature flag 集中在 pkg/cmd/server.go 顶部:
keepConfigLabels = env.Register(
"CONTROLLER_KEEP_XDS_CONFIG_LABELS", true,
"If enabled, Higress Controller will keep all the labels ...").Get()
所有环境变量在 /debug/info 类页面可见,便于运维核对。