diff --git a/charts/linkerd-control-plane/README.md b/charts/linkerd-control-plane/README.md index 811941f095a08..aded7f7852c2b 100644 --- a/charts/linkerd-control-plane/README.md +++ b/charts/linkerd-control-plane/README.md @@ -175,11 +175,15 @@ Kubernetes: `>=1.21.0-0` | identity.issuer.tls | object | `{"crtPEM":"","keyPEM":""}` | Which scheme is used for the identity issuer secret format | | identity.issuer.tls.crtPEM | string | `""` | Issuer certificate (ECDSA). It must be provided during install. | | identity.issuer.tls.keyPEM | string | `""` | Key for the issuer certificate (ECDSA). It must be provided during install | +| identity.kubeAPI.clientBurst | int | `200` | Burst value over clientQPS | +| identity.kubeAPI.clientQPS | int | `100` | Maximum QPS sent to the kube-apiserver before throttling. See [token bucket rate limiter implementation](https://github.com/kubernetes/client-go/blob/v12.0.0/util/flowcontrol/throttle.go) | | identity.serviceAccountTokenProjection | bool | `true` | Use [Service Account token Volume projection](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#service-account-token-volume-projection) for pod validation instead of the default token | | identityTrustAnchorsPEM | string | `""` | Trust root certificate (ECDSA). It must be provided during install. | | identityTrustDomain | string | clusterDomain | Trust domain used for identity | | imagePullPolicy | string | `"IfNotPresent"` | Docker image pull policy | | imagePullSecrets | list | `[]` | For Private docker registries, authentication is needed. Registry secrets are applied to the respective service accounts | +| kubeAPI.clientBurst | int | `200` | Burst value over clientQPS | +| kubeAPI.clientQPS | int | `100` | Maximum QPS sent to the kube-apiserver before throttling. See [token bucket rate limiter implementation](https://github.com/kubernetes/client-go/blob/v12.0.0/util/flowcontrol/throttle.go) | | linkerdVersion | string | `"linkerdVersionValue"` | control plane version. See Proxy section for proxy version | | networkValidator.connectAddr | string | `"1.1.1.1:20001"` | Address to which the network-validator will attempt to connect. we expect this to be rewritten | | networkValidator.enableSecurityContext | bool | `true` | Include a securityContext in the network-validator pod spec | diff --git a/charts/linkerd-control-plane/templates/identity.yaml b/charts/linkerd-control-plane/templates/identity.yaml index b22357f019592..3964efe74e018 100644 --- a/charts/linkerd-control-plane/templates/identity.yaml +++ b/charts/linkerd-control-plane/templates/identity.yaml @@ -159,6 +159,8 @@ spec: - -identity-clock-skew-allowance={{.Values.identity.issuer.clockSkewAllowance}} - -identity-scheme={{.Values.identity.issuer.scheme}} - -enable-pprof={{.Values.enablePprof | default false}} + - -kube-apiclient-qps={{.Values.identity.kubeAPI.clientQPS}} + - -kube-apiclient-burst={{.Values.identity.kubeAPI.clientBurst}} {{- include "partials.linkerd.trace" . | nindent 8 -}} env: - name: LINKERD_DISABLED diff --git a/charts/linkerd-control-plane/values.yaml b/charts/linkerd-control-plane/values.yaml index 0fb82cc2f3819..f88a34d02f468 100644 --- a/charts/linkerd-control-plane/values.yaml +++ b/charts/linkerd-control-plane/values.yaml @@ -48,6 +48,13 @@ identityTrustAnchorsPEM: | # -- Trust domain used for identity # @default -- clusterDomain identityTrustDomain: "" +kubeAPI: &kubeapi + # -- Maximum QPS sent to the kube-apiserver before throttling. + # See [token bucket rate limiter + # implementation](https://github.com/kubernetes/client-go/blob/v12.0.0/util/flowcontrol/throttle.go) + clientQPS: 100 + # -- Burst value over clientQPS + clientBurst: 200 # -- Additional annotations to add to all pods podAnnotations: {} # -- Additional labels to add to all pods @@ -326,6 +333,7 @@ identity: # -- Use [Service Account token Volume projection](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#service-account-token-volume-projection) for pod validation instead of the default token serviceAccountTokenProjection: true + issuer: scheme: linkerd.io/tls @@ -344,6 +352,8 @@ identity: # install keyPEM: | + kubeAPI: *kubeapi + # -|- CPU, Memory and Ephemeral Storage resources required by the identity controller (see `proxy.resources` for sub-fields) #identityResources: # -|- CPU, Memory and Ephemeral Storage resources required by proxy injected into identity pod (see `proxy.resources` for sub-fields) diff --git a/cli/cmd/testdata/install_controlplane_tracing_output.golden b/cli/cmd/testdata/install_controlplane_tracing_output.golden index ebcd0b21dac44..b86e66368aefd 100644 --- a/cli/cmd/testdata/install_controlplane_tracing_output.golden +++ b/cli/cmd/testdata/install_controlplane_tracing_output.golden @@ -525,6 +525,9 @@ data: AiAtuoI5XuCtrGVRzSmRTl2ra28aV9MyTU7d5qnTAFHKSgIgRKCvluOSgA5O21p5 51tdrmkHEZRr0qlLSJdHYgEfMzk= -----END CERTIFICATE----- + kubeAPI: + clientBurst: 200 + clientQPS: 100 serviceAccountTokenProjection: true identityProxyResources: null identityResources: null @@ -859,6 +862,8 @@ spec: - -identity-clock-skew-allowance=20s - -identity-scheme=linkerd.io/tls - -enable-pprof=false + - -kube-apiclient-qps=100 + - -kube-apiclient-burst=200 - -trace-collector=collector.linkerd-jaeger.svc.cluster.local:55678 env: - name: LINKERD_DISABLED diff --git a/cli/cmd/testdata/install_custom_domain.golden b/cli/cmd/testdata/install_custom_domain.golden index 0306f1a465e7a..e51f9b6c9da27 100644 --- a/cli/cmd/testdata/install_custom_domain.golden +++ b/cli/cmd/testdata/install_custom_domain.golden @@ -525,6 +525,9 @@ data: AiAtuoI5XuCtrGVRzSmRTl2ra28aV9MyTU7d5qnTAFHKSgIgRKCvluOSgA5O21p5 51tdrmkHEZRr0qlLSJdHYgEfMzk= -----END CERTIFICATE----- + kubeAPI: + clientBurst: 200 + clientQPS: 100 serviceAccountTokenProjection: true identityProxyResources: null identityResources: null @@ -859,6 +862,8 @@ spec: - -identity-clock-skew-allowance=20s - -identity-scheme=linkerd.io/tls - -enable-pprof=false + - -kube-apiclient-qps=100 + - -kube-apiclient-burst=200 env: - name: LINKERD_DISABLED value: "linkerd-await cannot block the identity controller" diff --git a/cli/cmd/testdata/install_custom_registry.golden b/cli/cmd/testdata/install_custom_registry.golden index cb07eb109bfa0..efc0d2347dccf 100644 --- a/cli/cmd/testdata/install_custom_registry.golden +++ b/cli/cmd/testdata/install_custom_registry.golden @@ -525,6 +525,9 @@ data: AiAtuoI5XuCtrGVRzSmRTl2ra28aV9MyTU7d5qnTAFHKSgIgRKCvluOSgA5O21p5 51tdrmkHEZRr0qlLSJdHYgEfMzk= -----END CERTIFICATE----- + kubeAPI: + clientBurst: 200 + clientQPS: 100 serviceAccountTokenProjection: true identityProxyResources: null identityResources: null @@ -859,6 +862,8 @@ spec: - -identity-clock-skew-allowance=20s - -identity-scheme=linkerd.io/tls - -enable-pprof=false + - -kube-apiclient-qps=100 + - -kube-apiclient-burst=200 env: - name: LINKERD_DISABLED value: "linkerd-await cannot block the identity controller" diff --git a/cli/cmd/testdata/install_default.golden b/cli/cmd/testdata/install_default.golden index 0306f1a465e7a..e51f9b6c9da27 100644 --- a/cli/cmd/testdata/install_default.golden +++ b/cli/cmd/testdata/install_default.golden @@ -525,6 +525,9 @@ data: AiAtuoI5XuCtrGVRzSmRTl2ra28aV9MyTU7d5qnTAFHKSgIgRKCvluOSgA5O21p5 51tdrmkHEZRr0qlLSJdHYgEfMzk= -----END CERTIFICATE----- + kubeAPI: + clientBurst: 200 + clientQPS: 100 serviceAccountTokenProjection: true identityProxyResources: null identityResources: null @@ -859,6 +862,8 @@ spec: - -identity-clock-skew-allowance=20s - -identity-scheme=linkerd.io/tls - -enable-pprof=false + - -kube-apiclient-qps=100 + - -kube-apiclient-burst=200 env: - name: LINKERD_DISABLED value: "linkerd-await cannot block the identity controller" diff --git a/cli/cmd/testdata/install_default_override_dst_get_nets.golden b/cli/cmd/testdata/install_default_override_dst_get_nets.golden index a355cf87ffa1c..dec2ad863658d 100644 --- a/cli/cmd/testdata/install_default_override_dst_get_nets.golden +++ b/cli/cmd/testdata/install_default_override_dst_get_nets.golden @@ -525,6 +525,9 @@ data: AiAtuoI5XuCtrGVRzSmRTl2ra28aV9MyTU7d5qnTAFHKSgIgRKCvluOSgA5O21p5 51tdrmkHEZRr0qlLSJdHYgEfMzk= -----END CERTIFICATE----- + kubeAPI: + clientBurst: 200 + clientQPS: 100 serviceAccountTokenProjection: true identityProxyResources: null identityResources: null @@ -859,6 +862,8 @@ spec: - -identity-clock-skew-allowance=20s - -identity-scheme=linkerd.io/tls - -enable-pprof=false + - -kube-apiclient-qps=100 + - -kube-apiclient-burst=200 env: - name: LINKERD_DISABLED value: "linkerd-await cannot block the identity controller" diff --git a/cli/cmd/testdata/install_default_token.golden b/cli/cmd/testdata/install_default_token.golden index 332d1a68874f5..da1c42ab9eeab 100644 --- a/cli/cmd/testdata/install_default_token.golden +++ b/cli/cmd/testdata/install_default_token.golden @@ -525,6 +525,9 @@ data: AiAtuoI5XuCtrGVRzSmRTl2ra28aV9MyTU7d5qnTAFHKSgIgRKCvluOSgA5O21p5 51tdrmkHEZRr0qlLSJdHYgEfMzk= -----END CERTIFICATE----- + kubeAPI: + clientBurst: 200 + clientQPS: 100 serviceAccountTokenProjection: false identityProxyResources: null identityResources: null @@ -859,6 +862,8 @@ spec: - -identity-clock-skew-allowance=20s - -identity-scheme=linkerd.io/tls - -enable-pprof=false + - -kube-apiclient-qps=100 + - -kube-apiclient-burst=200 env: - name: LINKERD_DISABLED value: "linkerd-await cannot block the identity controller" diff --git a/cli/cmd/testdata/install_ha_output.golden b/cli/cmd/testdata/install_ha_output.golden index 21162d710c681..ce711f938cadd 100644 --- a/cli/cmd/testdata/install_ha_output.golden +++ b/cli/cmd/testdata/install_ha_output.golden @@ -543,6 +543,9 @@ data: AiAtuoI5XuCtrGVRzSmRTl2ra28aV9MyTU7d5qnTAFHKSgIgRKCvluOSgA5O21p5 51tdrmkHEZRr0qlLSJdHYgEfMzk= -----END CERTIFICATE----- + kubeAPI: + clientBurst: 200 + clientQPS: 100 serviceAccountTokenProjection: true identityProxyResources: null identityResources: @@ -930,6 +933,8 @@ spec: - -identity-clock-skew-allowance=20s - -identity-scheme=linkerd.io/tls - -enable-pprof=false + - -kube-apiclient-qps=100 + - -kube-apiclient-burst=200 env: - name: LINKERD_DISABLED value: "linkerd-await cannot block the identity controller" diff --git a/cli/cmd/testdata/install_ha_with_overrides_output.golden b/cli/cmd/testdata/install_ha_with_overrides_output.golden index f1dcae1f530f1..b653e961755f1 100644 --- a/cli/cmd/testdata/install_ha_with_overrides_output.golden +++ b/cli/cmd/testdata/install_ha_with_overrides_output.golden @@ -543,6 +543,9 @@ data: AiAtuoI5XuCtrGVRzSmRTl2ra28aV9MyTU7d5qnTAFHKSgIgRKCvluOSgA5O21p5 51tdrmkHEZRr0qlLSJdHYgEfMzk= -----END CERTIFICATE----- + kubeAPI: + clientBurst: 200 + clientQPS: 100 serviceAccountTokenProjection: true identityProxyResources: null identityResources: @@ -930,6 +933,8 @@ spec: - -identity-clock-skew-allowance=20s - -identity-scheme=linkerd.io/tls - -enable-pprof=false + - -kube-apiclient-qps=100 + - -kube-apiclient-burst=200 env: - name: LINKERD_DISABLED value: "linkerd-await cannot block the identity controller" diff --git a/cli/cmd/testdata/install_heartbeat_disabled_output.golden b/cli/cmd/testdata/install_heartbeat_disabled_output.golden index d0ced48d3849f..dd3b68b62a0f4 100644 --- a/cli/cmd/testdata/install_heartbeat_disabled_output.golden +++ b/cli/cmd/testdata/install_heartbeat_disabled_output.golden @@ -456,6 +456,9 @@ data: AiAtuoI5XuCtrGVRzSmRTl2ra28aV9MyTU7d5qnTAFHKSgIgRKCvluOSgA5O21p5 51tdrmkHEZRr0qlLSJdHYgEfMzk= -----END CERTIFICATE----- + kubeAPI: + clientBurst: 200 + clientQPS: 100 serviceAccountTokenProjection: true identityProxyResources: null identityResources: null @@ -790,6 +793,8 @@ spec: - -identity-clock-skew-allowance=20s - -identity-scheme=linkerd.io/tls - -enable-pprof=false + - -kube-apiclient-qps=100 + - -kube-apiclient-burst=200 env: - name: LINKERD_DISABLED value: "linkerd-await cannot block the identity controller" diff --git a/cli/cmd/testdata/install_helm_control_plane_output.golden b/cli/cmd/testdata/install_helm_control_plane_output.golden index 9ac6c65276378..aad3e2ad8fbb0 100644 --- a/cli/cmd/testdata/install_helm_control_plane_output.golden +++ b/cli/cmd/testdata/install_helm_control_plane_output.golden @@ -514,6 +514,9 @@ data: scheme: linkerd.io/tls tls: crtPEM: test-crt-pem + kubeAPI: + clientBurst: 200 + clientQPS: 100 serviceAccountTokenProjection: true identityProxyResources: null identityResources: null @@ -832,6 +835,8 @@ spec: - -identity-clock-skew-allowance=20s - -identity-scheme=linkerd.io/tls - -enable-pprof=false + - -kube-apiclient-qps=100 + - -kube-apiclient-burst=200 env: - name: LINKERD_DISABLED value: "linkerd-await cannot block the identity controller" diff --git a/cli/cmd/testdata/install_helm_control_plane_output_ha.golden b/cli/cmd/testdata/install_helm_control_plane_output_ha.golden index 66f3ef2a3962a..5488b7a99dff6 100644 --- a/cli/cmd/testdata/install_helm_control_plane_output_ha.golden +++ b/cli/cmd/testdata/install_helm_control_plane_output_ha.golden @@ -532,6 +532,9 @@ data: scheme: linkerd.io/tls tls: crtPEM: test-crt-pem + kubeAPI: + clientBurst: 200 + clientQPS: 100 serviceAccountTokenProjection: true identityProxyResources: null identityResources: @@ -903,6 +906,8 @@ spec: - -identity-clock-skew-allowance=20s - -identity-scheme=linkerd.io/tls - -enable-pprof=false + - -kube-apiclient-qps=100 + - -kube-apiclient-burst=200 env: - name: LINKERD_DISABLED value: "linkerd-await cannot block the identity controller" diff --git a/cli/cmd/testdata/install_helm_output_ha_labels.golden b/cli/cmd/testdata/install_helm_output_ha_labels.golden index f976a0a4e19b6..a293f637afd0b 100644 --- a/cli/cmd/testdata/install_helm_output_ha_labels.golden +++ b/cli/cmd/testdata/install_helm_output_ha_labels.golden @@ -532,6 +532,9 @@ data: scheme: linkerd.io/tls tls: crtPEM: test-crt-pem + kubeAPI: + clientBurst: 200 + clientQPS: 100 serviceAccountTokenProjection: true identityProxyResources: null identityResources: @@ -911,6 +914,8 @@ spec: - -identity-clock-skew-allowance=20s - -identity-scheme=linkerd.io/tls - -enable-pprof=false + - -kube-apiclient-qps=100 + - -kube-apiclient-burst=200 env: - name: LINKERD_DISABLED value: "linkerd-await cannot block the identity controller" diff --git a/cli/cmd/testdata/install_helm_output_ha_namespace_selector.golden b/cli/cmd/testdata/install_helm_output_ha_namespace_selector.golden index f6d39a2a3d938..1f963c0abe117 100644 --- a/cli/cmd/testdata/install_helm_output_ha_namespace_selector.golden +++ b/cli/cmd/testdata/install_helm_output_ha_namespace_selector.golden @@ -527,6 +527,9 @@ data: scheme: linkerd.io/tls tls: crtPEM: test-crt-pem + kubeAPI: + clientBurst: 200 + clientQPS: 100 serviceAccountTokenProjection: true identityProxyResources: null identityResources: @@ -893,6 +896,8 @@ spec: - -identity-clock-skew-allowance=20s - -identity-scheme=linkerd.io/tls - -enable-pprof=false + - -kube-apiclient-qps=100 + - -kube-apiclient-burst=200 env: - name: LINKERD_DISABLED value: "linkerd-await cannot block the identity controller" diff --git a/cli/cmd/testdata/install_no_init_container.golden b/cli/cmd/testdata/install_no_init_container.golden index fcea6e8714f69..e57eb4c444337 100644 --- a/cli/cmd/testdata/install_no_init_container.golden +++ b/cli/cmd/testdata/install_no_init_container.golden @@ -525,6 +525,9 @@ data: AiAtuoI5XuCtrGVRzSmRTl2ra28aV9MyTU7d5qnTAFHKSgIgRKCvluOSgA5O21p5 51tdrmkHEZRr0qlLSJdHYgEfMzk= -----END CERTIFICATE----- + kubeAPI: + clientBurst: 200 + clientQPS: 100 serviceAccountTokenProjection: true identityProxyResources: null identityResources: null @@ -859,6 +862,8 @@ spec: - -identity-clock-skew-allowance=20s - -identity-scheme=linkerd.io/tls - -enable-pprof=false + - -kube-apiclient-qps=100 + - -kube-apiclient-burst=200 env: - name: LINKERD_DISABLED value: "linkerd-await cannot block the identity controller" diff --git a/cli/cmd/testdata/install_output.golden b/cli/cmd/testdata/install_output.golden index 9d14e1d1a4174..8db837a8ad11c 100644 --- a/cli/cmd/testdata/install_output.golden +++ b/cli/cmd/testdata/install_output.golden @@ -518,6 +518,9 @@ data: AiAtuoI5XuCtrGVRzSmRTl2ra28aV9MyTU7d5qnTAFHKSgIgRKCvluOSgA5O21p5 51tdrmkHEZRr0qlLSJdHYgEfMzk= -----END CERTIFICATE----- + kubeAPI: + clientBurst: 200 + clientQPS: 100 serviceAccountTokenProjection: true identityProxyResources: null identityResources: null @@ -835,6 +838,8 @@ spec: - -identity-clock-skew-allowance=20s - -identity-scheme=linkerd.io/tls - -enable-pprof=false + - -kube-apiclient-qps=100 + - -kube-apiclient-burst=200 env: - name: LINKERD_DISABLED value: "linkerd-await cannot block the identity controller" diff --git a/cli/cmd/testdata/install_proxy_ignores.golden b/cli/cmd/testdata/install_proxy_ignores.golden index 81dee954ee422..7e71dfcfe84da 100644 --- a/cli/cmd/testdata/install_proxy_ignores.golden +++ b/cli/cmd/testdata/install_proxy_ignores.golden @@ -525,6 +525,9 @@ data: AiAtuoI5XuCtrGVRzSmRTl2ra28aV9MyTU7d5qnTAFHKSgIgRKCvluOSgA5O21p5 51tdrmkHEZRr0qlLSJdHYgEfMzk= -----END CERTIFICATE----- + kubeAPI: + clientBurst: 200 + clientQPS: 100 serviceAccountTokenProjection: true identityProxyResources: null identityResources: null @@ -859,6 +862,8 @@ spec: - -identity-clock-skew-allowance=20s - -identity-scheme=linkerd.io/tls - -enable-pprof=false + - -kube-apiclient-qps=100 + - -kube-apiclient-burst=200 env: - name: LINKERD_DISABLED value: "linkerd-await cannot block the identity controller" diff --git a/cli/cmd/testdata/install_values_file.golden b/cli/cmd/testdata/install_values_file.golden index 6451a20bfd251..6fb94c410a7a5 100644 --- a/cli/cmd/testdata/install_values_file.golden +++ b/cli/cmd/testdata/install_values_file.golden @@ -525,6 +525,9 @@ data: AiAtuoI5XuCtrGVRzSmRTl2ra28aV9MyTU7d5qnTAFHKSgIgRKCvluOSgA5O21p5 51tdrmkHEZRr0qlLSJdHYgEfMzk= -----END CERTIFICATE----- + kubeAPI: + clientBurst: 200 + clientQPS: 100 serviceAccountTokenProjection: true identityProxyResources: null identityResources: null @@ -859,6 +862,8 @@ spec: - -identity-clock-skew-allowance=20s - -identity-scheme=linkerd.io/tls - -enable-pprof=false + - -kube-apiclient-qps=100 + - -kube-apiclient-burst=200 env: - name: LINKERD_DISABLED value: "linkerd-await cannot block the identity controller" diff --git a/cli/cmd/upgrade_test.go b/cli/cmd/upgrade_test.go index ba71966e52247..ed324837af877 100644 --- a/cli/cmd/upgrade_test.go +++ b/cli/cmd/upgrade_test.go @@ -424,6 +424,7 @@ func testUpgradeOptions() (flagOptions, error) { } func testOptions(t *testing.T) (*charts.Values, flagOptions) { + t.Helper() installValues, err := testInstallOptions() if err != nil { t.Fatalf("failed to create install options: %s", err) @@ -444,10 +445,12 @@ func replaceVersions(manifest string) string { } func generateIssuerCerts(t *testing.T, b64encode bool) issuerCerts { + t.Helper() return generateCerts(t, "identity.linkerd.cluster.local", b64encode) } func generateCerts(t *testing.T, name string, b64encode bool) issuerCerts { + t.Helper() ca, err := tls.GenerateRootCAWithDefaults("test") if err != nil { t.Fatal(err) @@ -572,6 +575,7 @@ func pathMatch(path []string, template []string) bool { } func renderInstall(t *testing.T, values *charts.Values) *bytes.Buffer { + t.Helper() var buf bytes.Buffer if err := renderCRDs(&buf, valuespkg.Options{}); err != nil { t.Fatalf("could not render install manifests: %s", err) @@ -599,6 +603,7 @@ func renderUpgrade(installManifest string, upgradeOpts []flag.Flag, templateOpts } func renderInstallAndUpgrade(t *testing.T, installOpts *charts.Values, upgradeOpts []flag.Flag, templateOpts valuespkg.Options) (*bytes.Buffer, *bytes.Buffer, error) { + t.Helper() err := validateValues(context.Background(), nil, installOpts) if err != nil { return nil, nil, fmt.Errorf("failed to validate values: %w", err) diff --git a/controller/cmd/identity/main.go b/controller/cmd/identity/main.go index 465dfe941de1b..8c12421158e87 100644 --- a/controller/cmd/identity/main.go +++ b/controller/cmd/identity/main.go @@ -40,6 +40,8 @@ func Main(args []string) { identityIssuanceLifeTime := cmd.String("identity-issuance-lifetime", "", "the amount of time for which the Identity issuer should certify identity") identityClockSkewAllowance := cmd.String("identity-clock-skew-allowance", "", "the amount of time to allow for clock skew within a Linkerd cluster") enablePprof := cmd.Bool("enable-pprof", false, "Enable pprof endpoints on the admin server") + qps := cmd.Float64("kube-apiclient-qps", 100, "Maximum QPS sent to the kube-apiserver before throttling") + burst := cmd.Int("kube-apiclient-burst", 200, "Burst value over kube-apiclient-qps") issuerPath := cmd.String("issuer", "/var/run/linkerd/identity/issuer", @@ -137,10 +139,16 @@ func Main(args []string) { // // Create k8s API // - k8sAPI, err := k8s.NewAPI(*kubeConfigPath, "", "", []string{}, 0) + config, err := k8s.GetConfig(*kubeConfigPath, "") + if err != nil { + log.Fatalf("Error configuring Kubernetes API client: %s", err) + } + k8sAPI, err := k8s.NewAPIForConfig(config, "", []string{}, 0, float32(*qps), *burst) if err != nil { log.Fatalf("Failed to load kubeconfig: %s: %s", *kubeConfigPath, err) } + log.Infof("Using k8s client with QPS=%.2f Burst=%d", config.QPS, config.Burst) + v, err := idctl.NewK8sTokenValidator(ctx, k8sAPI, dom) if err != nil { log.Fatalf("Failed to initialize identity service: %s", err) diff --git a/controller/k8s/api.go b/controller/k8s/api.go index 64902e31aeff8..e3f800359c419 100644 --- a/controller/k8s/api.go +++ b/controller/k8s/api.go @@ -79,7 +79,7 @@ func InitializeAPI(ctx context.Context, kubeConfig string, ensureClusterWideAcce return nil, err } - k8sClient, err := k8s.NewAPIForConfig(config, "", []string{}, 0) + k8sClient, err := k8s.NewAPIForConfig(config, "", []string{}, 0, 0, 0) if err != nil { return nil, err } @@ -89,7 +89,7 @@ func InitializeAPI(ctx context.Context, kubeConfig string, ensureClusterWideAcce // InitializeAPIForConfig creates Kubernetes clients and returns an initialized API wrapper. func InitializeAPIForConfig(ctx context.Context, kubeConfig *rest.Config, ensureClusterWideAccess bool, cluster string, resources ...APIResource) (*API, error) { - k8sClient, err := k8s.NewAPIForConfig(kubeConfig, "", []string{}, 0) + k8sClient, err := k8s.NewAPIForConfig(kubeConfig, "", []string{}, 0, 0, 0) if err != nil { return nil, err } diff --git a/controller/webhook/launcher.go b/controller/webhook/launcher.go index c653cb62b7122..2dade0cd21d4d 100644 --- a/controller/webhook/launcher.go +++ b/controller/webhook/launcher.go @@ -44,7 +44,7 @@ func Launch( log.Fatalf("error building Kubernetes API config: %s", err) } - k8sAPI, err := pkgk8s.NewAPIForConfig(config, "", []string{}, 0) + k8sAPI, err := pkgk8s.NewAPIForConfig(config, "", []string{}, 0, 0, 0) if err != nil { //nolint:gocritic log.Fatalf("error configuring Kubernetes API client: %s", err) diff --git a/multicluster/cmd/check.go b/multicluster/cmd/check.go index 46624569a2d9d..622ef9cbe738c 100644 --- a/multicluster/cmd/check.go +++ b/multicluster/cmd/check.go @@ -352,7 +352,7 @@ func (hc *healthChecker) checkRemoteClusterConnectivity(ctx context.Context) err errors = append(errors, fmt.Errorf("* secret: [%s/%s] cluster: [%s]: unable to parse api config: %w", secret.Namespace, secret.Name, link.TargetClusterName, err)) continue } - remoteAPI, err := k8s.NewAPIForConfig(clientConfig, "", []string{}, healthcheck.RequestTimeout) + remoteAPI, err := k8s.NewAPIForConfig(clientConfig, "", []string{}, healthcheck.RequestTimeout, 0, 0) if err != nil { errors = append(errors, fmt.Errorf("* secret: [%s/%s] cluster: [%s]: could not instantiate api for target cluster: %w", secret.Namespace, secret.Name, link.TargetClusterName, err)) continue @@ -400,7 +400,7 @@ func (hc *healthChecker) checkRemoteClusterAnchors(ctx context.Context, localAnc errors = append(errors, fmt.Sprintf("* secret: [%s/%s] cluster: [%s]: unable to parse api config: %s", secret.Namespace, secret.Name, link.TargetClusterName, err)) continue } - remoteAPI, err := k8s.NewAPIForConfig(clientConfig, "", []string{}, healthcheck.RequestTimeout) + remoteAPI, err := k8s.NewAPIForConfig(clientConfig, "", []string{}, healthcheck.RequestTimeout, 0, 0) if err != nil { errors = append(errors, fmt.Sprintf("* secret: [%s/%s] cluster: [%s]: could not instantiate api for target cluster: %s", secret.Namespace, secret.Name, link.TargetClusterName, err)) continue diff --git a/pkg/charts/linkerd2/values.go b/pkg/charts/linkerd2/values.go index 791318a45a61b..ae2d8f4f163fe 100644 --- a/pkg/charts/linkerd2/values.go +++ b/pkg/charts/linkerd2/values.go @@ -229,9 +229,10 @@ type ( // Identity contains the fields to set the identity variables in the proxy // sidecar container Identity struct { - ExternalCA bool `json:"externalCA"` - ServiceAccountTokenProjection bool `json:"serviceAccountTokenProjection"` - Issuer *Issuer `json:"issuer"` + ExternalCA bool `json:"externalCA"` + ServiceAccountTokenProjection bool `json:"serviceAccountTokenProjection"` + Issuer *Issuer `json:"issuer"` + KubeAPI *KubeAPI `json:"kubeAPI"` } // Issuer has the Helm variables of the identity issuer @@ -242,6 +243,12 @@ type ( TLS *IssuerTLS `json:"tls"` } + // KubeAPI contains the kube-apiserver client config + KubeAPI struct { + ClientQPS float32 `json:"clientQPS"` + ClientBurst int `json:"clientBurst"` + } + // Webhook Helm variables for a webhook Webhook struct { *TLS diff --git a/pkg/charts/linkerd2/values_test.go b/pkg/charts/linkerd2/values_test.go index 91c6b3c6fb020..ebd3413d3495c 100644 --- a/pkg/charts/linkerd2/values_test.go +++ b/pkg/charts/linkerd2/values_test.go @@ -179,6 +179,10 @@ func TestNewValues(t *testing.T) { TLS: &IssuerTLS{}, Scheme: "linkerd.io/tls", }, + KubeAPI: &KubeAPI{ + ClientQPS: 100, + ClientBurst: 200, + }, }, NodeSelector: map[string]string{ "kubernetes.io/os": "linux", diff --git a/pkg/k8s/api.go b/pkg/k8s/api.go index c555155dc1764..85a4ac22920a1 100644 --- a/pkg/k8s/api.go +++ b/pkg/k8s/api.go @@ -50,17 +50,33 @@ func NewAPI(configPath, kubeContext string, impersonate string, impersonateGroup if err != nil { return nil, fmt.Errorf("error configuring Kubernetes API client: %w", err) } - return NewAPIForConfig(config, impersonate, impersonateGroup, timeout) + return NewAPIForConfig(config, impersonate, impersonateGroup, timeout, 0, 0) } // NewAPIForConfig uses a Kubernetes config to construct a client for accessing // the configured cluster -func NewAPIForConfig(config *rest.Config, impersonate string, impersonateGroup []string, timeout time.Duration) (*KubernetesAPI, error) { +func NewAPIForConfig( + config *rest.Config, + impersonate string, + impersonateGroup []string, + timeout time.Duration, + qps float32, + burst int, +) (*KubernetesAPI, error) { // k8s' client-go doesn't support injecting context // https://github.com/kubernetes/kubernetes/issues/46503 // but we can set the timeout manually config.Timeout = timeout + if qps > 0 && burst > 0 { + config.QPS = qps + config.Burst = burst + prometheus.SetClientQPS("k8s", config.QPS) + prometheus.SetClientBurst("k8s", config.Burst) + } else { + prometheus.SetClientQPS("k8s", rest.DefaultQPS) + prometheus.SetClientBurst("k8s", rest.DefaultBurst) + } wt := config.WrapTransport config.WrapTransport = prometheus.ClientWithTelemetry("k8s", wt) diff --git a/pkg/prometheus/prometheus.go b/pkg/prometheus/prometheus.go index d883cbe71d373..27c6fe4dbc401 100644 --- a/pkg/prometheus/prometheus.go +++ b/pkg/prometheus/prometheus.go @@ -78,12 +78,26 @@ var ( }, []string{"client"}, ) + clientQPS = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "http_client_qps", + Help: "Max QPS used for the client config.", + }, + []string{"client"}, + ) + clientBurst = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "http_client_burst", + Help: "Burst used for the client config.", + }, + []string{"client"}, + ) ) func init() { prometheus.MustRegister( - serverCounter, serverLatency, serverResponseSize, - clientCounter, clientLatency, clientInFlight, + serverCounter, serverLatency, serverResponseSize, clientCounter, + clientLatency, clientInFlight, clientQPS, clientBurst, ) } @@ -127,3 +141,11 @@ func ClientWithTelemetry(name string, wt func(http.RoundTripper) http.RoundTripp ) } } + +func SetClientQPS(name string, qps float32) { + clientQPS.With(prometheus.Labels{"client": name}).Set(float64(qps)) +} + +func SetClientBurst(name string, burst int) { + clientBurst.With(prometheus.Labels{"client": name}).Set(float64(burst)) +}