From e8382bf8de8c5672b370695a2f97853895388875 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Fri, 23 Feb 2024 19:15:20 +0100 Subject: [PATCH] routesrv: discover redis shards without fetching full cluster state Routesrv supports dynamic discovery of Redis instances using Kubernetes Service, see https://github.com/zalando/skipper/blob/master/docs/tutorials/ratelimit.md#redis-based-cluster-ratelimits Dynamic discovery relies on Kubernetes Dataclient that also fetches Ingresses and RouteGroups and creates routes from them. This change enables dynamic discovery of Redis instances using Kubernetes without fetching Ingresses and RouteGroups. When -kubernetes flag is not set but -kubernetes-redis-service-namespace, -kubernetes-redis-service-name and -kubernetes-redis-service-port are provided then `routesrv` creates Kubernetes dataclient to discover Redis instances but does not use this dataclient to load routes. See previous #2934 Signed-off-by: Alexander Yastrebov --- cmd/routesrv/main.go | 4 ---- routesrv/redishandler.go | 19 +++++++++++++++---- routesrv/routesrv.go | 39 ++++++++++++++++++++++++++++----------- 3 files changed, 43 insertions(+), 19 deletions(-) diff --git a/cmd/routesrv/main.go b/cmd/routesrv/main.go index 9b3e2206fd..88e800b382 100644 --- a/cmd/routesrv/main.go +++ b/cmd/routesrv/main.go @@ -12,10 +12,6 @@ func main() { log.Fatalf("Error processing config: %s", err) } - if !cfg.KubernetesIngress { - log.Fatalf("-kubernetes flag required") - } - log.SetLevel(cfg.ApplicationLogLevel) if cfg.ApplicationLogJSONEnabled { log.SetFormatter(&log.JSONFormatter{}) diff --git a/routesrv/redishandler.go b/routesrv/redishandler.go index 8caec16a6e..acf265a65b 100644 --- a/routesrv/redishandler.go +++ b/routesrv/redishandler.go @@ -39,11 +39,22 @@ func (rh *RedisHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } w.Write(address) } - -func getRedisAddresses(opts *skipper.Options, kdc *kubernetes.Client, m metrics.Metrics) func() ([]byte, error) { +func getRedisAddresses(opts *skipper.Options, kdc *kubernetes.Client, loaded bool, m metrics.Metrics) func() ([]byte, error) { return func() ([]byte, error) { - a := kdc.GetEndpointAddresses(opts.KubernetesRedisServiceNamespace, opts.KubernetesRedisServiceName) - log.Debugf("Redis updater called and found %d redis endpoints: %v", len(a), a) + var ( + a []string + err error + ) + if loaded { + a = kdc.GetEndpointAddresses(opts.KubernetesRedisServiceNamespace, opts.KubernetesRedisServiceName) + log.Debugf("GetEndpointAddresses found %d redis endpoints", len(a)) + } else { + a, err = kdc.LoadEndpointAddresses(opts.KubernetesRedisServiceNamespace, opts.KubernetesRedisServiceName) + log.Debugf("LoadEndpointAddresses found %d redis endpoints, err: %v", len(a), err) + if err != nil { + return nil, fmt.Errorf("failed to load redis endpoints: %w", err) + } + } m.UpdateGauge("redis_endpoints", float64(len(a))) result := RedisEndpoints{ diff --git a/routesrv/routesrv.go b/routesrv/routesrv.go index 4c4d249a5c..40b5949c90 100644 --- a/routesrv/routesrv.go +++ b/routesrv/routesrv.go @@ -2,7 +2,6 @@ package routesrv import ( "context" - "fmt" "net/http" "os" "os/signal" @@ -15,8 +14,10 @@ import ( "github.com/zalando/skipper" "github.com/zalando/skipper/dataclients/kubernetes" + "github.com/zalando/skipper/dataclients/routestring" "github.com/zalando/skipper/filters/auth" "github.com/zalando/skipper/metrics" + "github.com/zalando/skipper/routing" "github.com/zalando/skipper/tracing" ) @@ -86,14 +87,20 @@ func New(opts skipper.Options) (*RouteServer, error) { supportHandler.Handle("/debug/pprof/", metricsHandler) } - if !opts.Kubernetes { - return nil, fmt.Errorf(`option "Kubernetes" is required`) + var ( + kdc *kubernetes.Client + dataclient routing.DataClient + ) + if opts.Kubernetes { + kdc, err = kubernetes.New(opts.KubernetesDataClientOptions()) + if err != nil { + return nil, err + } + dataclient = kdc + } else { + dataclient, _ = routestring.New("") } - dataclient, err := kubernetes.New(opts.KubernetesDataClientOptions()) - if err != nil { - return nil, err - } var oauthConfig *auth.OAuthConfig if opts.EnableOAuth2GrantFlow /* explicitly enable grant flow */ { oauthConfig = &auth.OAuthConfig{} @@ -104,12 +111,22 @@ func New(opts skipper.Options) (*RouteServer, error) { // in case we have kubernetes dataclient and we can detect redis instances, we patch redisOptions if opts.KubernetesRedisServiceNamespace != "" && opts.KubernetesRedisServiceName != "" { log.Infof("Use endpoints %s/%s to fetch updated redis shards", opts.KubernetesRedisServiceNamespace, opts.KubernetesRedisServiceName) + rh = &RedisHandler{} - _, err := dataclient.LoadAll() - if err != nil { - return nil, err + if kdc != nil { + if _, err := kdc.LoadAll(); err != nil { + return nil, err + } + rh.AddrUpdater = getRedisAddresses(&opts, kdc, true, m) + } else { + kdc, err := kubernetes.New(opts.KubernetesDataClientOptions()) + if err != nil { + return nil, err + } + // defer kdc.Close() + + rh.AddrUpdater = getRedisAddresses(&opts, kdc, false, m) } - rh.AddrUpdater = getRedisAddresses(&opts, dataclient, m) mux.Handle("/swarm/redis/shards", rh) }