From 82f567907e51472b37c59887898172dff92dc947 Mon Sep 17 00:00:00 2001 From: mickeyzzc Date: Wed, 11 Oct 2023 13:47:05 +0800 Subject: [PATCH 1/9] pods support no node scraping Signed-off-by: mickeyzzc --- pkg/app/server.go | 2 +- pkg/options/options.go | 2 ++ pkg/options/types.go | 5 ++++- pkg/options/types_test.go | 22 ++++++++++++++++++++-- 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/pkg/app/server.go b/pkg/app/server.go index 09ce7cad82..f00d9198f1 100644 --- a/pkg/app/server.go +++ b/pkg/app/server.go @@ -204,7 +204,7 @@ func RunKubeStateMetrics(ctx context.Context, opts *options.Options) error { namespaces := opts.Namespaces.GetNamespaces() nsFieldSelector := namespaces.GetExcludeNSFieldSelector(opts.NamespacesDenylist) - nodeFieldSelector := opts.Node.GetNodeFieldSelector() + nodeFieldSelector := opts.Node.GetNodeFieldSelector(opts.NoNodeScrape) merged, err := storeBuilder.MergeFieldSelectors([]string{nsFieldSelector, nodeFieldSelector}) if err != nil { return err diff --git a/pkg/options/options.go b/pkg/options/options.go index 2a60f89cad..78a10d64c9 100644 --- a/pkg/options/options.go +++ b/pkg/options/options.go @@ -46,6 +46,7 @@ type Options struct { Namespaces NamespaceList `yaml:"namespaces"` NamespacesDenylist NamespaceList `yaml:"namespaces_denylist"` Node NodeType `yaml:"node"` + NoNodeScrape bool `yaml:"no_node_scrape"` Pod string `yaml:"pod"` Port int `yaml:"port"` Resources ResourceSet `yaml:"resources"` @@ -120,6 +121,7 @@ func (o *Options) AddFlags(cmd *cobra.Command) { o.cmd.Flags().BoolVar(&o.CustomResourcesOnly, "custom-resource-state-only", false, "Only provide Custom Resource State metrics (experimental)") o.cmd.Flags().BoolVar(&o.EnableGZIPEncoding, "enable-gzip-encoding", false, "Gzip responses when requested by clients via 'Accept-Encoding: gzip' header.") + o.cmd.Flags().BoolVar(&o.NoNodeScrape, "enable-no-node-scrape", false, "This configuration is used in conjunction with node configuration. When this configuration is true, node configuration is empty and the metric of no scheduled pods is scraped. This is experimental.") o.cmd.Flags().BoolVarP(&o.Help, "help", "h", false, "Print Help text") o.cmd.Flags().BoolVarP(&o.UseAPIServerCache, "use-apiserver-cache", "", false, "Sets resourceVersion=0 for ListWatch requests, using cached resources from the apiserver instead of an etcd quorum read.") o.cmd.Flags().Int32Var(&o.Shard, "shard", int32(0), "The instances shard nominal (zero indexed) within the total number of shards. (default 0)") diff --git a/pkg/options/types.go b/pkg/options/types.go index ac0c11275e..a5a1c9ce21 100644 --- a/pkg/options/types.go +++ b/pkg/options/types.go @@ -108,10 +108,13 @@ func (r *ResourceSet) Type() string { type NodeType string // GetNodeFieldSelector returns a nodename field selector. -func (n *NodeType) GetNodeFieldSelector() string { +func (n *NodeType) GetNodeFieldSelector(b bool) string { if string(*n) != "" { return fields.OneTermEqualSelector("spec.nodeName", string(*n)).String() } + if b { + return fields.OneTermEqualSelector("spec.nodeName", "").String() + } return EmptyFieldSelector() } diff --git a/pkg/options/types_test.go b/pkg/options/types_test.go index a1b43a2c0b..96ec0675bc 100644 --- a/pkg/options/types_test.go +++ b/pkg/options/types_test.go @@ -175,7 +175,25 @@ func TestNodeFieldSelector(t *testing.T) { for _, test := range tests { node := test.Node - actual := node.GetNodeFieldSelector() + actual := node.GetNodeFieldSelector(false) + if !reflect.DeepEqual(actual, test.Wanted) { + t.Errorf("Test error for Desc: %s. Want: %+v. Got: %+v.", test.Desc, test.Wanted, actual) + } + } + tests1 := []struct { + Desc string + Node NodeType + Wanted string + }{ + { + Desc: "empty node name", + Node: "", + Wanted: "spec.nodeName=", + }, + } + for _, test := range tests1 { + node := test.Node + actual := node.GetNodeFieldSelector(true) if !reflect.DeepEqual(actual, test.Wanted) { t.Errorf("Test error for Desc: %s. Want: %+v. Got: %+v.", test.Desc, test.Wanted, actual) } @@ -238,7 +256,7 @@ func TestMergeFieldSelectors(t *testing.T) { ns := test.Namespaces deniedNS := test.DeniedNamespaces selector1 := ns.GetExcludeNSFieldSelector(deniedNS) - selector2 := test.Node.GetNodeFieldSelector() + selector2 := test.Node.GetNodeFieldSelector(false) actual, err := MergeFieldSelectors([]string{selector1, selector2}) if err != nil { t.Errorf("Test error for Desc: %s. Can't merge field selector %v.", test.Desc, err) From f8b0cb75cac2a7d02f1b34670b945c270bf75e4a Mon Sep 17 00:00:00 2001 From: mickeyzzc Date: Wed, 11 Oct 2023 14:05:31 +0800 Subject: [PATCH 2/9] add examples Signed-off-by: mickeyzzc --- .../deployment-no-node-pods.yaml | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 examples/daemonsetsharding/deployment-no-node-pods.yaml diff --git a/examples/daemonsetsharding/deployment-no-node-pods.yaml b/examples/daemonsetsharding/deployment-no-node-pods.yaml new file mode 100644 index 0000000000..144a76f446 --- /dev/null +++ b/examples/daemonsetsharding/deployment-no-node-pods.yaml @@ -0,0 +1,59 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/component: exporter + app.kubernetes.io/name: kube-state-metrics + app.kubernetes.io/version: 2.10.0 + name: kube-state-metrics-pods + namespace: kube-system +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: kube-state-metrics + template: + metadata: + labels: + app.kubernetes.io/component: exporter + app.kubernetes.io/name: kube-state-metrics + app.kubernetes.io/version: 2.10.0 + spec: + automountServiceAccountToken: true + containers: + - args: + - --resources=pods + - --node="" + - --enable-no-node-scrape + image: registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.10.0 + livenessProbe: + httpGet: + path: /healthz + port: 8080 + initialDelaySeconds: 5 + timeoutSeconds: 5 + name: kube-state-metrics + ports: + - containerPort: 8080 + name: http-metrics + - containerPort: 8081 + name: telemetry + readinessProbe: + httpGet: + path: / + port: 8081 + initialDelaySeconds: 5 + timeoutSeconds: 5 + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 65534 + seccompProfile: + type: RuntimeDefault + nodeSelector: + kubernetes.io/os: linux + serviceAccountName: kube-state-metrics From 539f57d75dd5338cceb97dc5206899abd4b6fd0e Mon Sep 17 00:00:00 2001 From: mickeyzzc Date: Thu, 12 Oct 2023 21:00:10 +0800 Subject: [PATCH 3/9] simplified configuration Signed-off-by: mickeyzzc --- .../deployment-no-node-pods.yaml | 1 - pkg/app/server.go | 2 +- pkg/options/options.go | 6 +- pkg/options/types.go | 52 +++++++++-- pkg/options/types_test.go | 90 +++++++++++-------- 5 files changed, 102 insertions(+), 49 deletions(-) diff --git a/examples/daemonsetsharding/deployment-no-node-pods.yaml b/examples/daemonsetsharding/deployment-no-node-pods.yaml index 144a76f446..b22d105758 100644 --- a/examples/daemonsetsharding/deployment-no-node-pods.yaml +++ b/examples/daemonsetsharding/deployment-no-node-pods.yaml @@ -24,7 +24,6 @@ spec: - args: - --resources=pods - --node="" - - --enable-no-node-scrape image: registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.10.0 livenessProbe: httpGet: diff --git a/pkg/app/server.go b/pkg/app/server.go index f00d9198f1..09ce7cad82 100644 --- a/pkg/app/server.go +++ b/pkg/app/server.go @@ -204,7 +204,7 @@ func RunKubeStateMetrics(ctx context.Context, opts *options.Options) error { namespaces := opts.Namespaces.GetNamespaces() nsFieldSelector := namespaces.GetExcludeNSFieldSelector(opts.NamespacesDenylist) - nodeFieldSelector := opts.Node.GetNodeFieldSelector(opts.NoNodeScrape) + nodeFieldSelector := opts.Node.GetNodeFieldSelector() merged, err := storeBuilder.MergeFieldSelectors([]string{nsFieldSelector, nodeFieldSelector}) if err != nil { return err diff --git a/pkg/options/options.go b/pkg/options/options.go index 78a10d64c9..dc20bf0e79 100644 --- a/pkg/options/options.go +++ b/pkg/options/options.go @@ -74,6 +74,7 @@ func NewOptions() *Options { MetricAllowlist: MetricSet{}, MetricDenylist: MetricSet{}, MetricOptInList: MetricSet{}, + Node: NodeType{}, AnnotationsAllowList: LabelsAllowList{}, LabelsAllowList: LabelsAllowList{}, } @@ -121,7 +122,6 @@ func (o *Options) AddFlags(cmd *cobra.Command) { o.cmd.Flags().BoolVar(&o.CustomResourcesOnly, "custom-resource-state-only", false, "Only provide Custom Resource State metrics (experimental)") o.cmd.Flags().BoolVar(&o.EnableGZIPEncoding, "enable-gzip-encoding", false, "Gzip responses when requested by clients via 'Accept-Encoding: gzip' header.") - o.cmd.Flags().BoolVar(&o.NoNodeScrape, "enable-no-node-scrape", false, "This configuration is used in conjunction with node configuration. When this configuration is true, node configuration is empty and the metric of no scheduled pods is scraped. This is experimental.") o.cmd.Flags().BoolVarP(&o.Help, "help", "h", false, "Print Help text") o.cmd.Flags().BoolVarP(&o.UseAPIServerCache, "use-apiserver-cache", "", false, "Sets resourceVersion=0 for ListWatch requests, using cached resources from the apiserver instead of an etcd quorum read.") o.cmd.Flags().Int32Var(&o.Shard, "shard", int32(0), "The instances shard nominal (zero indexed) within the total number of shards. (default 0)") @@ -138,7 +138,7 @@ func (o *Options) AddFlags(cmd *cobra.Command) { o.cmd.Flags().StringVar(&o.TLSConfig, "tls-config", "", "Path to the TLS configuration file") o.cmd.Flags().StringVar(&o.TelemetryHost, "telemetry-host", "::", `Host to expose kube-state-metrics self metrics on.`) o.cmd.Flags().StringVar(&o.Config, "config", "", "Path to the kube-state-metrics options config file") - o.cmd.Flags().StringVar((*string)(&o.Node), "node", "", "Name of the node that contains the kube-state-metrics pod. Most likely it should be passed via the downward API. This is used for daemonset sharding. Only available for resources (pod metrics) that support spec.nodeName fieldSelector. This is experimental.") + o.cmd.Flags().Var(&o.Node, "node", "Name of the node that contains the kube-state-metrics pod. Most likely it should be passed via the downward API. This is used for daemonset sharding. Only available for resources (pod metrics) that support spec.nodeName fieldSelector. This is experimental.") o.cmd.Flags().Var(&o.AnnotationsAllowList, "metric-annotations-allowlist", "Comma-separated list of Kubernetes annotations keys that will be used in the resource' labels metric. By default the annotations metrics are not exposed. To include them, provide a list of resource names in their plural form and Kubernetes annotation keys you would like to allow for them (Example: '=namespaces=[kubernetes.io/team,...],pods=[kubernetes.io/team],...)'. A single '*' can be provided per resource instead to allow any annotations, but that has severe performance implications (Example: '=pods=[*]').") o.cmd.Flags().Var(&o.LabelsAllowList, "metric-labels-allowlist", "Comma-separated list of additional Kubernetes label keys that will be used in the resource' labels metric. By default the labels metrics are not exposed. To include them, provide a list of resource names in their plural form and Kubernetes label keys you would like to allow for them (Example: '=namespaces=[k8s-label-1,k8s-label-n,...],pods=[app],...)'. A single '*' can be provided per resource instead to allow any labels, but that has severe performance implications (Example: '=pods=[*]'). Additionally, an asterisk (*) can be provided as a key, which will resolve to all resources, i.e., assuming '--resources=deployments,pods', '=*=[*]' will resolve to '=deployments=[*],pods=[*]'.") o.cmd.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.") @@ -163,7 +163,7 @@ func (o *Options) Usage() { // Validate validates arguments func (o *Options) Validate() error { shardableResource := "pods" - if o.Node == "" { + if o.Node.String() == "" { return nil } for _, x := range o.Resources.AsSlice() { diff --git a/pkg/options/types.go b/pkg/options/types.go index a5a1c9ce21..d7a9b5b185 100644 --- a/pkg/options/types.go +++ b/pkg/options/types.go @@ -18,6 +18,7 @@ package options import ( "errors" + "regexp" "sort" "strings" @@ -105,17 +106,52 @@ func (r *ResourceSet) Type() string { } // NodeType represents a nodeName to query from. -type NodeType string +type NodeType map[string]struct{} -// GetNodeFieldSelector returns a nodename field selector. -func (n *NodeType) GetNodeFieldSelector(b bool) string { - if string(*n) != "" { - return fields.OneTermEqualSelector("spec.nodeName", string(*n)).String() +func (n *NodeType) Set(value string) error { + s := *n + cols := strings.Split(value, ",") + for _, col := range cols { + col = strings.TrimSpace(col) + if len(col) != 0 { + s[col] = struct{}{} + } + } + return nil +} + +func (n NodeType) AsSlice() []string { + cols := make([]string, 0, len(n)) + for col := range n { + cols = append(cols, col) } - if b { - return fields.OneTermEqualSelector("spec.nodeName", "").String() + return cols +} + +func (n NodeType) String() string { + return strings.Join(n.AsSlice(), ",") +} + +func (n *NodeType) Type() string { + return "string" +} + +// GetNodeFieldSelector returns a nodename field selector. +func (n *NodeType) GetNodeFieldSelector() string { + if nil == n || len(*n) == 0 { + klog.InfoS("Using node type is nil") + return EmptyFieldSelector() } - return EmptyFieldSelector() + pattern := "[^a-zA-Z0-9_,-]+" + re := regexp.MustCompile(pattern) + result := re.ReplaceAllString(n.String(), "") + klog.InfoS("Using node type", "node", result) + return fields.OneTermEqualSelector("spec.nodeName", result).String() + +} + +type NodeValue interface { + GetNodeFieldSelector() string } // EmptyFieldSelector returns an empty field selector. diff --git a/pkg/options/types_test.go b/pkg/options/types_test.go index 96ec0675bc..9522927c84 100644 --- a/pkg/options/types_test.go +++ b/pkg/options/types_test.go @@ -162,38 +162,32 @@ func TestNodeFieldSelector(t *testing.T) { Wanted string }{ { - Desc: "empty node name", - Node: "", + Desc: "with node name", Wanted: "", }, { - Desc: "with node name", - Node: "k8s-node-1", + Desc: "empty node name", + Node: NodeType( + map[string]struct{}{ + "": {}, + }, + ), + Wanted: "spec.nodeName=", + }, + { + Desc: "with node name", + Node: NodeType( + map[string]struct{}{ + "k8s-node-1": {}, + }, + ), Wanted: "spec.nodeName=k8s-node-1", }, } for _, test := range tests { node := test.Node - actual := node.GetNodeFieldSelector(false) - if !reflect.DeepEqual(actual, test.Wanted) { - t.Errorf("Test error for Desc: %s. Want: %+v. Got: %+v.", test.Desc, test.Wanted, actual) - } - } - tests1 := []struct { - Desc string - Node NodeType - Wanted string - }{ - { - Desc: "empty node name", - Node: "", - Wanted: "spec.nodeName=", - }, - } - for _, test := range tests1 { - node := test.Node - actual := node.GetNodeFieldSelector(true) + actual := node.GetNodeFieldSelector() if !reflect.DeepEqual(actual, test.Wanted) { t.Errorf("Test error for Desc: %s. Want: %+v. Got: %+v.", test.Desc, test.Wanted, actual) } @@ -212,43 +206,67 @@ func TestMergeFieldSelectors(t *testing.T) { Desc: "empty DeniedNamespaces", Namespaces: NamespaceList{"default", "kube-system"}, DeniedNamespaces: NamespaceList{}, - Node: "", - Wanted: "", + Node: NodeType( + map[string]struct{}{ + "": {}, + }, + ), + Wanted: "spec.nodeName=", }, { Desc: "all DeniedNamespaces", Namespaces: DefaultNamespaces, DeniedNamespaces: NamespaceList{"some-system"}, - Node: "", - Wanted: "metadata.namespace!=some-system", + Node: NodeType( + map[string]struct{}{ + "": {}, + }, + ), + Wanted: "metadata.namespace!=some-system,spec.nodeName=", }, { Desc: "general case", Namespaces: DefaultNamespaces, DeniedNamespaces: NamespaceList{"case1-system", "case2-system"}, - Node: "", - Wanted: "metadata.namespace!=case1-system,metadata.namespace!=case2-system", + Node: NodeType( + map[string]struct{}{ + "": {}, + }, + ), + Wanted: "metadata.namespace!=case1-system,metadata.namespace!=case2-system,spec.nodeName=", }, { Desc: "empty DeniedNamespaces", Namespaces: NamespaceList{"default", "kube-system"}, DeniedNamespaces: NamespaceList{}, - Node: "k8s-node-1", - Wanted: "spec.nodeName=k8s-node-1", + Node: NodeType( + map[string]struct{}{ + "k8s-node-1": {}, + }, + ), + Wanted: "spec.nodeName=k8s-node-1", }, { Desc: "all DeniedNamespaces", Namespaces: DefaultNamespaces, DeniedNamespaces: NamespaceList{"some-system"}, - Node: "k8s-node-1", - Wanted: "metadata.namespace!=some-system,spec.nodeName=k8s-node-1", + Node: NodeType( + map[string]struct{}{ + "k8s-node-1": {}, + }, + ), + Wanted: "metadata.namespace!=some-system,spec.nodeName=k8s-node-1", }, { Desc: "general case", Namespaces: DefaultNamespaces, DeniedNamespaces: NamespaceList{"case1-system", "case2-system"}, - Node: "k8s-node-1", - Wanted: "metadata.namespace!=case1-system,metadata.namespace!=case2-system,spec.nodeName=k8s-node-1", + Node: NodeType( + map[string]struct{}{ + "k8s-node-1": {}, + }, + ), + Wanted: "metadata.namespace!=case1-system,metadata.namespace!=case2-system,spec.nodeName=k8s-node-1", }, } @@ -256,7 +274,7 @@ func TestMergeFieldSelectors(t *testing.T) { ns := test.Namespaces deniedNS := test.DeniedNamespaces selector1 := ns.GetExcludeNSFieldSelector(deniedNS) - selector2 := test.Node.GetNodeFieldSelector(false) + selector2 := test.Node.GetNodeFieldSelector() actual, err := MergeFieldSelectors([]string{selector1, selector2}) if err != nil { t.Errorf("Test error for Desc: %s. Can't merge field selector %v.", test.Desc, err) From d33929e543c54260c555157e69177d65100ea847 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=93=9D=E5=AE=9D=E7=9F=B3=E7=9A=84=E5=82=BB=E8=AF=9D?= Date: Thu, 12 Oct 2023 23:20:38 +0800 Subject: [PATCH 4/9] examples generated from libsonnet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 蓝宝石的傻话 --- .../deployment-no-node-pods.yaml | 4 ++-- .../kube-state-metrics.libsonnet | 24 +++++++++++++++++++ pkg/options/options.go | 1 - 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/examples/daemonsetsharding/deployment-no-node-pods.yaml b/examples/daemonsetsharding/deployment-no-node-pods.yaml index b22d105758..5b4c2c0526 100644 --- a/examples/daemonsetsharding/deployment-no-node-pods.yaml +++ b/examples/daemonsetsharding/deployment-no-node-pods.yaml @@ -3,9 +3,9 @@ kind: Deployment metadata: labels: app.kubernetes.io/component: exporter - app.kubernetes.io/name: kube-state-metrics + app.kubernetes.io/name: kube-state-metrics-global app.kubernetes.io/version: 2.10.0 - name: kube-state-metrics-pods + name: kube-state-metrics-global namespace: kube-system spec: replicas: 1 diff --git a/jsonnet/kube-state-metrics/kube-state-metrics.libsonnet b/jsonnet/kube-state-metrics/kube-state-metrics.libsonnet index 4ac16fbbc3..a492ef1a9b 100644 --- a/jsonnet/kube-state-metrics/kube-state-metrics.libsonnet +++ b/jsonnet/kube-state-metrics/kube-state-metrics.libsonnet @@ -373,6 +373,30 @@ }, ), + deploymentNoNodePods: + local c = ksm.deployment.spec.template.spec.containers[0] { + args: [ + '--resources=pods', + '--node=""', + ], + }; + local shardksmname = ksm.name + "-global"; + std.mergePatch(ksm.deployment, + { + metadata: { + name: shardksmname, + labels: {'app.kubernetes.io/name': shardksmname} + }, + spec: { + template: { + spec: { + containers: [c], + }, + }, + }, + }, + ), + daemonset: // extending the default container from above local c0 = ksm.deployment.spec.template.spec.containers[0] { diff --git a/pkg/options/options.go b/pkg/options/options.go index dc20bf0e79..c89ef63f43 100644 --- a/pkg/options/options.go +++ b/pkg/options/options.go @@ -46,7 +46,6 @@ type Options struct { Namespaces NamespaceList `yaml:"namespaces"` NamespacesDenylist NamespaceList `yaml:"namespaces_denylist"` Node NodeType `yaml:"node"` - NoNodeScrape bool `yaml:"no_node_scrape"` Pod string `yaml:"pod"` Port int `yaml:"port"` Resources ResourceSet `yaml:"resources"` From 5fc87fac85fe3cbd47843e009c2513716daeb5f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=93=9D=E5=AE=9D=E7=9F=B3=E7=9A=84=E5=82=BB=E8=AF=9D?= Date: Fri, 13 Oct 2023 00:08:54 +0800 Subject: [PATCH 5/9] add test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 蓝宝石的傻话 --- pkg/options/types_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkg/options/types_test.go b/pkg/options/types_test.go index 9522927c84..4b89f76f4f 100644 --- a/pkg/options/types_test.go +++ b/pkg/options/types_test.go @@ -165,6 +165,11 @@ func TestNodeFieldSelector(t *testing.T) { Desc: "with node name", Wanted: "", }, + { + Desc: "with node name", + Node: nil, + Wanted: "", + }, { Desc: "empty node name", Node: NodeType( From f7ca9909dfe2bc6a297d683f04d1627aeeff5e81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=93=9D=E5=AE=9D=E7=9F=B3=E7=9A=84=E5=82=BB=E8=AF=9D?= Date: Wed, 18 Oct 2023 14:01:18 +0800 Subject: [PATCH 6/9] globals rename pods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 蓝宝石的傻话 --- examples/daemonsetsharding/deployment-no-node-pods.yaml | 4 ++-- jsonnet/kube-state-metrics/kube-state-metrics.libsonnet | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/daemonsetsharding/deployment-no-node-pods.yaml b/examples/daemonsetsharding/deployment-no-node-pods.yaml index 5b4c2c0526..6d8fc97492 100644 --- a/examples/daemonsetsharding/deployment-no-node-pods.yaml +++ b/examples/daemonsetsharding/deployment-no-node-pods.yaml @@ -3,9 +3,9 @@ kind: Deployment metadata: labels: app.kubernetes.io/component: exporter - app.kubernetes.io/name: kube-state-metrics-global + app.kubernetes.io/name: kube-state-metrics-pods app.kubernetes.io/version: 2.10.0 - name: kube-state-metrics-global + name: kube-state-metrics-pods namespace: kube-system spec: replicas: 1 diff --git a/jsonnet/kube-state-metrics/kube-state-metrics.libsonnet b/jsonnet/kube-state-metrics/kube-state-metrics.libsonnet index a492ef1a9b..a7c2904123 100644 --- a/jsonnet/kube-state-metrics/kube-state-metrics.libsonnet +++ b/jsonnet/kube-state-metrics/kube-state-metrics.libsonnet @@ -380,7 +380,7 @@ '--node=""', ], }; - local shardksmname = ksm.name + "-global"; + local shardksmname = ksm.name + "-pods"; std.mergePatch(ksm.deployment, { metadata: { From d1f04c2479c792d15e420255d5c6829fdd95766c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=93=9D=E5=AE=9D=E7=9F=B3=E7=9A=84=E5=82=BB=E8=AF=9D?= Date: Fri, 20 Oct 2023 23:10:16 +0800 Subject: [PATCH 7/9] Add code comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 蓝宝石的傻话 --- pkg/options/types.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/options/types.go b/pkg/options/types.go index d7a9b5b185..9f03ec863b 100644 --- a/pkg/options/types.go +++ b/pkg/options/types.go @@ -108,6 +108,7 @@ func (r *ResourceSet) Type() string { // NodeType represents a nodeName to query from. type NodeType map[string]struct{} +// Set converts a comma-separated string of nodename into a slice and appends it to the NodeList func (n *NodeType) Set(value string) error { s := *n cols := strings.Split(value, ",") @@ -120,6 +121,7 @@ func (n *NodeType) Set(value string) error { return nil } +// AsSlice returns the LabelsAllowList in the form of plain string slice. func (n NodeType) AsSlice() []string { cols := make([]string, 0, len(n)) for col := range n { @@ -132,6 +134,7 @@ func (n NodeType) String() string { return strings.Join(n.AsSlice(), ",") } +// Type returns a descriptive string about the NodeList type. func (n *NodeType) Type() string { return "string" } @@ -150,6 +153,7 @@ func (n *NodeType) GetNodeFieldSelector() string { } +// NodeValue represents a nodeName to query from. type NodeValue interface { GetNodeFieldSelector() string } From ad0e06c453fa07359c1d764039b44abf56d7c39c Mon Sep 17 00:00:00 2001 From: mickeyzzc Date: Thu, 23 Nov 2023 21:42:22 +0800 Subject: [PATCH 8/9] update readme ,track metrics for unassigned pods Signed-off-by: mickeyzzc --- README.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9685543d34..77f2d16300 100644 --- a/README.md +++ b/README.md @@ -250,7 +250,7 @@ The downside of using an auto-sharded setup comes from the rollout strategy supp For pod metrics, they can be sharded per node with the following flag: -* `--node` +* `--node=$(NODE_NAME)` Each kube-state-metrics pod uses FieldSelector (spec.nodeName) to watch/list pod metrics only on the same node. @@ -276,6 +276,21 @@ spec: fieldPath: spec.nodeName ``` +To track metrics for unassigned pods, you need to add an additional deployment and set `--node=""`, as shown in the following example: +``` +apiVersion: apps/v1 +kind: Deployment +spec: + template: + spec: + containers: + - image: registry.k8s.io/kube-state-metrics/kube-state-metrics:IMAGE_TAG + name: kube-state-metrics + args: + - --resources=pods + - --node="" +``` + Other metrics can be sharded via [Horizontal sharding](#horizontal-sharding). ### Setup From 37ceaf4218252c54d82e4f06612248168db8f7a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20R=C3=BCger?= Date: Tue, 19 Dec 2023 19:48:27 +0100 Subject: [PATCH 9/9] fix: Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 77f2d16300..bbfa9f1a17 100644 --- a/README.md +++ b/README.md @@ -277,6 +277,7 @@ spec: ``` To track metrics for unassigned pods, you need to add an additional deployment and set `--node=""`, as shown in the following example: + ``` apiVersion: apps/v1 kind: Deployment