Sharding per node

This commit is contained in:
Catherine Fang 2022-10-14 08:15:33 -04:00
parent 8a390513f8
commit 2c0c8d2e51
6 changed files with 165 additions and 4 deletions

View File

@ -46,6 +46,7 @@ Usage of ./kube-state-metrics:
--metric-opt-in-list string Comma-separated list of metrics which are opt-in and not enabled by default. This is in addition to the metric allow- and denylists --metric-opt-in-list string Comma-separated list of metrics which are opt-in and not enabled by default. This is in addition to the metric allow- and denylists
--namespaces string Comma-separated list of namespaces to be enabled. Defaults to "" --namespaces string Comma-separated list of namespaces to be enabled. Defaults to ""
--namespaces-denylist string Comma-separated list of namespaces not to be enabled. If namespaces and namespaces-denylist are both set, only namespaces that are excluded in namespaces-denylist will be used. --namespaces-denylist string Comma-separated list of namespaces not to be enabled. If namespaces and namespaces-denylist are both set, only namespaces that are excluded in namespaces-denylist will be used.
--nodename string Set spec.nodeName=nodeName when watching resources. Only available for resources which support nodeName filter.
--one_output If true, only write logs to their native severity level (vs also writing to each lower severity level; no effect when -logtostderr=true) --one_output If true, only write logs to their native severity level (vs also writing to each lower severity level; no effect when -logtostderr=true)
--pod string Name of the pod that contains the kube-state-metrics container. When set, it is expected that --pod and --pod-namespace are both set. Most likely this should be passed via the downward API. This is used for auto-detecting sharding. If set, this has preference over statically configured sharding. This is experimental, it may be removed without notice. --pod string Name of the pod that contains the kube-state-metrics container. When set, it is expected that --pod and --pod-namespace are both set. Most likely this should be passed via the downward API. This is used for auto-detecting sharding. If set, this has preference over statically configured sharding. This is experimental, it may be removed without notice.
--pod-namespace string Name of the namespace of the pod specified by --pod. When set, it is expected that --pod and --pod-namespace are both set. Most likely this should be passed via the downward API. This is used for auto-detecting sharding. If set, this has preference over statically configured sharding. This is experimental, it may be removed without notice. --pod-namespace string Name of the namespace of the pod specified by --pod. When set, it is expected that --pod and --pod-namespace are both set. Most likely this should be passed via the downward API. This is used for auto-detecting sharding. If set, this has preference over statically configured sharding. This is experimental, it may be removed without notice.

View File

@ -63,6 +63,7 @@ type Builder struct {
vpaClient vpaclientset.Interface vpaClient vpaclientset.Interface
namespaces options.NamespaceList namespaces options.NamespaceList
namespaceFilter string namespaceFilter string
nodenameFilter string
ctx context.Context ctx context.Context
enabledResources []string enabledResources []string
familyGeneratorFilter generator.FamilyGeneratorFilter familyGeneratorFilter generator.FamilyGeneratorFilter
@ -112,6 +113,16 @@ func (b *Builder) WithNamespaces(n options.NamespaceList, nsFilter string) {
b.namespaceFilter = nsFilter b.namespaceFilter = nsFilter
} }
// WithNodename sets the nodename property of a Builder.
func (b *Builder) WithNodename(nodenameFilter string) {
b.nodenameFilter = nodenameFilter
}
// MergeFieldSelector merges two fieldSelectors using AND operator.
func (b *Builder) MergeFieldSelector(s1 string, s2 string) (string, error) {
return options.MergeFieldSelector(s1, s2)
}
// WithSharding sets the shard and totalShards property of a Builder. // WithSharding sets the shard and totalShards property of a Builder.
func (b *Builder) WithSharding(shard int32, totalShards int) { func (b *Builder) WithSharding(shard int32, totalShards int) {
b.shard = shard b.shard = shard
@ -467,7 +478,13 @@ func (b *Builder) buildStores(
familyHeaders, familyHeaders,
composedMetricGenFuncs, composedMetricGenFuncs,
) )
listWatcher := listWatchFunc(b.kubeClient, v1.NamespaceAll, b.namespaceFilter) merged, err := b.MergeFieldSelector(b.namespaceFilter, b.nodenameFilter)
if err != nil {
panic(fmt.Sprintf("Failed to merge fieldSelector %s and %s", b.namespaceFilter, b.nodenameFilter))
}
klog.Infof("FieldSelector is used ", merged)
listWatcher := listWatchFunc(b.kubeClient, v1.NamespaceAll, merged)
b.startReflector(expectedType, store, listWatcher, useAPIServerCache)
b.startReflector(expectedType, store, listWatcher, useAPIServerCache) b.startReflector(expectedType, store, listWatcher, useAPIServerCache)
return []cache.Store{store} return []cache.Store{store}
} }
@ -478,7 +495,12 @@ func (b *Builder) buildStores(
familyHeaders, familyHeaders,
composedMetricGenFuncs, composedMetricGenFuncs,
) )
listWatcher := listWatchFunc(b.kubeClient, ns, b.namespaceFilter) merged, err := b.MergeFieldSelector(b.namespaceFilter, b.nodenameFilter)
if err != nil {
panic(fmt.Sprintf("Failed to merge fieldSelector %s and %s", b.namespaceFilter, b.nodenameFilter))
}
klog.Infof("FieldSelector is used ", merged)
listWatcher := listWatchFunc(b.kubeClient, ns, merged)
b.startReflector(expectedType, store, listWatcher, useAPIServerCache) b.startReflector(expectedType, store, listWatcher, useAPIServerCache)
stores = append(stores, store) stores = append(stores, store)
} }
@ -508,7 +530,12 @@ func (b *Builder) buildCustomResourceStores(resourceName string,
familyHeaders, familyHeaders,
composedMetricGenFuncs, composedMetricGenFuncs,
) )
listWatcher := listWatchFunc(customResourceClient, v1.NamespaceAll, b.namespaceFilter) merged, err := b.MergeFieldSelector(b.namespaceFilter, b.nodenameFilter)
if err != nil {
panic(fmt.Sprintf("Failed to merge fieldSelector %s and %s", b.namespaceFilter, b.nodenameFilter))
}
klog.Infof("FieldSelector is used ", merged)
listWatcher := listWatchFunc(customResourceClient, v1.NamespaceAll, merged)
b.startReflector(expectedType, store, listWatcher, useAPIServerCache) b.startReflector(expectedType, store, listWatcher, useAPIServerCache)
return []cache.Store{store} return []cache.Store{store}
} }
@ -519,7 +546,12 @@ func (b *Builder) buildCustomResourceStores(resourceName string,
familyHeaders, familyHeaders,
composedMetricGenFuncs, composedMetricGenFuncs,
) )
listWatcher := listWatchFunc(customResourceClient, ns, b.namespaceFilter) merged, err := b.MergeFieldSelector(b.namespaceFilter, b.nodenameFilter)
if err != nil {
panic(fmt.Sprintf("Failed to merge fieldSelector %s and %s", b.namespaceFilter, b.nodenameFilter))
}
klog.Infof("FieldSelector is used ", merged)
listWatcher := listWatchFunc(customResourceClient, ns, merged)
b.startReflector(expectedType, store, listWatcher, useAPIServerCache) b.startReflector(expectedType, store, listWatcher, useAPIServerCache)
stores = append(stores, store) stores = append(stores, store)
} }

View File

@ -108,6 +108,8 @@ func RunKubeStateMetrics(ctx context.Context, opts *options.Options, factories .
nsFieldSelector := namespaces.GetExcludeNSFieldSelector(opts.NamespacesDenylist) nsFieldSelector := namespaces.GetExcludeNSFieldSelector(opts.NamespacesDenylist)
storeBuilder.WithNamespaces(namespaces, nsFieldSelector) storeBuilder.WithNamespaces(namespaces, nsFieldSelector)
storeBuilder.WithNodename(opts.Nodename.GetNodenameFieldSelector())
allowDenyList, err := allowdenylist.New(opts.MetricAllowlist, opts.MetricDenylist) allowDenyList, err := allowdenylist.New(opts.MetricAllowlist, opts.MetricDenylist)
if err != nil { if err != nil {
return err return err

View File

@ -39,6 +39,7 @@ type Options struct {
Resources ResourceSet Resources ResourceSet
Namespaces NamespaceList Namespaces NamespaceList
NamespacesDenylist NamespaceList NamespacesDenylist NamespaceList
Nodename NodenameType
Shard int32 Shard int32
TotalShards int TotalShards int
Pod string Pod string
@ -103,6 +104,7 @@ func (o *Options) AddFlags() {
o.flags.Var(&o.Resources, "resources", fmt.Sprintf("Comma-separated list of Resources to be enabled. Defaults to %q", &DefaultResources)) o.flags.Var(&o.Resources, "resources", fmt.Sprintf("Comma-separated list of Resources to be enabled. Defaults to %q", &DefaultResources))
o.flags.Var(&o.Namespaces, "namespaces", fmt.Sprintf("Comma-separated list of namespaces to be enabled. Defaults to %q", &DefaultNamespaces)) o.flags.Var(&o.Namespaces, "namespaces", fmt.Sprintf("Comma-separated list of namespaces to be enabled. Defaults to %q", &DefaultNamespaces))
o.flags.Var(&o.NamespacesDenylist, "namespaces-denylist", "Comma-separated list of namespaces not to be enabled. If namespaces and namespaces-denylist are both set, only namespaces that are excluded in namespaces-denylist will be used.") o.flags.Var(&o.NamespacesDenylist, "namespaces-denylist", "Comma-separated list of namespaces not to be enabled. If namespaces and namespaces-denylist are both set, only namespaces that are excluded in namespaces-denylist will be used.")
o.flags.StringVar((*string)(&o.Nodename), "nodename", "", "Set spec.nodeName=nodeName when watching resources. Only available for resources which support nodeName filter.")
o.flags.Var(&o.MetricAllowlist, "metric-allowlist", "Comma-separated list of metrics to be exposed. This list comprises of exact metric names and/or regex patterns. The allowlist and denylist are mutually exclusive.") o.flags.Var(&o.MetricAllowlist, "metric-allowlist", "Comma-separated list of metrics to be exposed. This list comprises of exact metric names and/or regex patterns. The allowlist and denylist are mutually exclusive.")
o.flags.Var(&o.MetricDenylist, "metric-denylist", "Comma-separated list of metrics not to be enabled. This list comprises of exact metric names and/or regex patterns. The allowlist and denylist are mutually exclusive.") o.flags.Var(&o.MetricDenylist, "metric-denylist", "Comma-separated list of metrics not to be enabled. This list comprises of exact metric names and/or regex patterns. The allowlist and denylist are mutually exclusive.")
o.flags.Var(&o.MetricOptInList, "metric-opt-in-list", "Comma-separated list of metrics which are opt-in and not enabled by default. This is in addition to the metric allow- and denylists") o.flags.Var(&o.MetricOptInList, "metric-opt-in-list", "Comma-separated list of metrics which are opt-in and not enabled by default. This is in addition to the metric allow- and denylists")

View File

@ -104,6 +104,36 @@ func (r *ResourceSet) Type() string {
return "string" return "string"
} }
// NodenameType represents a nodeName to query from.
type NodenameType string
// GetNodenameFieldSelector returns a nodename field selector.
func (n *NodenameType) GetNodenameFieldSelector() string {
if string(*n) != "" {
return fields.OneTermEqualSelector("spec.nodeName", string(*n)).String()
}
return fields.Nothing().String()
}
// MergeFieldSelector returns AND of two field selectors.
func MergeFieldSelector(s1 string, s2 string) (string, error) {
selector1, err := fields.ParseSelector(s1)
if err != nil {
return fields.Nothing().String(), err
}
selector2, err := fields.ParseSelector(s2)
if err != nil {
return fields.Nothing().String(), err
}
if selector1.Empty() {
return selector2.String(), nil
}
if selector2.Empty() {
return selector1.String(), nil
}
return fields.AndSelectors(selector1, selector2).String(), nil
}
// NamespaceList represents a list of namespaces to query from. // NamespaceList represents a list of namespaces to query from.
type NamespaceList []string type NamespaceList []string

View File

@ -155,6 +155,100 @@ func TestNamespaceList_ExcludeNamespacesFieldSelector(t *testing.T) {
} }
} }
func TestNodenameFieldSelector(t *testing.T) {
tests := []struct {
Desc string
Nodename NodenameType
Wanted string
}{
{
Desc: "empty node name",
Nodename: "",
Wanted: "",
},
{
Desc: "with node name",
Nodename: "k8s-node-1",
Wanted: "spec.nodeName=k8s-node-1",
},
}
for _, test := range tests {
node := test.Nodename
actual := node.GetNodenameFieldSelector()
if !reflect.DeepEqual(actual, test.Wanted) {
t.Errorf("Test error for Desc: %s. Want: %+v. Got: %+v.", test.Desc, test.Wanted, actual)
}
}
}
func TestMergeFieldSelector(t *testing.T) {
tests := []struct {
Desc string
Namespaces NamespaceList
DeniedNamespaces NamespaceList
Nodename NodenameType
Wanted string
}{
{
Desc: "empty DeniedNamespaces",
Namespaces: NamespaceList{"default", "kube-system"},
DeniedNamespaces: NamespaceList{},
Nodename: "",
Wanted: "",
},
{
Desc: "all DeniedNamespaces",
Namespaces: DefaultNamespaces,
DeniedNamespaces: NamespaceList{"some-system"},
Nodename: "",
Wanted: "metadata.namespace!=some-system",
},
{
Desc: "general case",
Namespaces: DefaultNamespaces,
DeniedNamespaces: NamespaceList{"case1-system", "case2-system"},
Nodename: "",
Wanted: "metadata.namespace!=case1-system,metadata.namespace!=case2-system",
},
{
Desc: "empty DeniedNamespaces",
Namespaces: NamespaceList{"default", "kube-system"},
DeniedNamespaces: NamespaceList{},
Nodename: "k8s-node-1",
Wanted: "spec.nodeName=k8s-node-1",
},
{
Desc: "all DeniedNamespaces",
Namespaces: DefaultNamespaces,
DeniedNamespaces: NamespaceList{"some-system"},
Nodename: "k8s-node-1",
Wanted: "metadata.namespace!=some-system,spec.nodeName=k8s-node-1",
},
{
Desc: "general case",
Namespaces: DefaultNamespaces,
DeniedNamespaces: NamespaceList{"case1-system", "case2-system"},
Nodename: "k8s-node-1",
Wanted: "metadata.namespace!=case1-system,metadata.namespace!=case2-system,spec.nodeName=k8s-node-1",
},
}
for _, test := range tests {
ns := test.Namespaces
deniedNS := test.DeniedNamespaces
selector1 := ns.GetExcludeNSFieldSelector(deniedNS)
selector2 := test.Nodename.GetNodenameFieldSelector()
actual, err := MergeFieldSelector(selector1, selector2)
if err != nil {
t.Errorf("Test error for Desc: %s. Can't merge field selector %v.", test.Desc, err)
}
if !reflect.DeepEqual(actual, test.Wanted) {
t.Errorf("Test error for Desc: %s. Want: %+v. Got: %+v.", test.Desc, test.Wanted, actual)
}
}
}
func TestMetricSetSet(t *testing.T) { func TestMetricSetSet(t *testing.T) {
tests := []struct { tests := []struct {
Desc string Desc string