diff --git a/docs/cspell.json b/docs/cspell.json index 8cbd6e21ffaae..35fa5b916d939 100644 --- a/docs/cspell.json +++ b/docs/cspell.json @@ -92,6 +92,7 @@ "GOAWAY", "GODEBUG", "GOMAXPROCS", + "GOMEMLIMIT", "GSLB", "Gbps", "Ghostunnel", diff --git a/docs/pages/includes/helm-reference/zz_generated.teleport-kube-agent.mdx b/docs/pages/includes/helm-reference/zz_generated.teleport-kube-agent.mdx index 9790bf5f34a75..b413a29e00a7d 100644 --- a/docs/pages/includes/helm-reference/zz_generated.teleport-kube-agent.mdx +++ b/docs/pages/includes/helm-reference/zz_generated.teleport-kube-agent.mdx @@ -1820,6 +1820,23 @@ initContainers: See [the Kubernetes documentation](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for more details. +## `goMemLimitRatio` + +| Type | Default | +|------|---------| +| `float` | `0.9` | + +`goMemLimitRatio` configures the GOMEMLIMIT env var set by the chart. +GOMEMLIMIT instructs the go garbage collector to try to keep allocated memory +below a given threshold. This is a best-effort attempt, but this helps +to prevent OOMs in case of bursts. + +When the memory limits are set and goMemLimitRatio is non-zero, +the chart sets the GOMEMLIMIT to `resources.memory.limits * goMemLimitRatio`. +The value must be between 0 and 1. +Set to 0 to unset GOMEMLIMIT. +This has no effect if GOMEMLIMIT is already set through `extraEnv`. + ## `initSecurityContext` | Type | Default | diff --git a/docs/pages/includes/helm-reference/zz_generated.teleport-relay.mdx b/docs/pages/includes/helm-reference/zz_generated.teleport-relay.mdx index fb7b1178950d8..672223c686639 100644 --- a/docs/pages/includes/helm-reference/zz_generated.teleport-relay.mdx +++ b/docs/pages/includes/helm-reference/zz_generated.teleport-relay.mdx @@ -321,6 +321,23 @@ the chart. See [the Kubernetes documentation](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for more details. +## `goMemLimitRatio` + +| Type | Default | +|------|---------| +| `float` | `0.9` | + +`goMemLimitRatio` configures the GOMEMLIMIT env var set by the chart. +GOMEMLIMIT instructs the go garbage collector to try to keep allocated memory +below a given threshold. This is a best-effort attempt, but this helps +to prevent OOMs in case of bursts. + +When the memory limits are set and goMemLimitRatio is non-zero, +the chart sets the GOMEMLIMIT to `resources.memory.limits * goMemLimitRatio`. +The value must be between 0 and 1. +Set to 0 to unset GOMEMLIMIT. +This has no effect if GOMEMLIMIT is already set through `extraEnv`. + ## `service` `service` options for the Service that points to the Teleport Relay diff --git a/docs/pages/reference/helm-reference/teleport-cluster.mdx b/docs/pages/reference/helm-reference/teleport-cluster.mdx index 7c1f4a976b07b..96c7ef96bbe20 100644 --- a/docs/pages/reference/helm-reference/teleport-cluster.mdx +++ b/docs/pages/reference/helm-reference/teleport-cluster.mdx @@ -2311,6 +2311,22 @@ See [the GitHub PR](https://github.com/gravitational/teleport/pull/36251) for te cpu: 1 memory: 2Gi ``` +## `goMemLimitRatio` + +| Type | Default | +|---------|---------| +| `float` | `0.9` | + +`goMemLimitRatio` configures the GOMEMLIMIT env var set by the chart. +GOMEMLIMIT instructs the go garbage collector to try to keep allocated memory +below a given threshold. This is a best-effort attempt, but this helps +to prevent OOMs in case of bursts. + +When the memory limits are set and goMemLimitRatio is non-zero, +the chart sets the GOMEMLIMIT to `resources.memory.limits * goMemLimitRatio`. +The value must be between 0 and 1. +Set to 0 to unset GOMEMLIMIT. +This has no effect if GOMEMLIMIT is already set through `extraEnv`. ## `podSecurityContext` diff --git a/examples/chart/teleport-cluster/templates/_quantity.tpl b/examples/chart/teleport-cluster/templates/_quantity.tpl new file mode 100644 index 0000000000000..527f719a8d2b8 --- /dev/null +++ b/examples/chart/teleport-cluster/templates/_quantity.tpl @@ -0,0 +1,39 @@ +{{/* This template tries to parse a resource quantity like Kubernetes does. +Helm sadly doesn't offer this critical primitive: https://github.com/helm/helm/issues/11376 +The quantity serialization format is described here: https://github.com/kubernetes/apimachinery/blob/master/pkg/api/resource/quantity.go#L33 + +This template support IEC, SI and decimal notation syntaxes, but has poor error handling.*/}} +{{- define "teleport-cluster.resource-quantity" -}} + {{- $value := . -}} + {{- $unit := 1.0 -}} + {{- if typeIs "string" . -}} + {{- $base2 := dict "Ki" 0x1p10 "Mi" 0x1p20 "Gi" 0x1p30 "Ti" 0x1p40 "Pi" 0x1p50 "Ei" 0x1p60 -}} + {{- $base10 := dict "m" 1e-3 "k" 1e3 "M" 1e6 "G" 1e9 "T" 1e12 "P" 1e15 "E" 1e18 -}} + {{- range $k, $v := merge $base2 $base10 -}} + {{- if hasSuffix $k $ -}} + {{- $value = trimSuffix $k $ -}} + {{- $unit = $v -}} + {{- end -}} + {{- end -}} + {{- end -}} + {{- mulf (float64 $value) $unit -}} +{{- end -}} + +{{/* This renders the GOMEMLIMIT env var unless the user already specified it +in extraEnv, goMemLimitRatio is set to 0, or requests.memory.limit is unset. + +Important: unlike other templates, this should be called on $proxy or $auth instead of .*/}} +{{- define "teleport-cluster.gomemlimit" -}} + {{- $alreadySet := false -}} + {{- range $_, $var := .extraEnv -}} + {{- if eq $var.name "GOMEMLIMIT" -}} + {{- $alreadySet = true -}} + {{- end -}} + {{- end -}} + {{- if and (not $alreadySet) .goMemLimitRatio -}} + {{- $ratio := .goMemLimitRatio -}} + {{- with .resources }}{{ with .limits }}{{ with .memory -}} + {{- include "teleport-cluster.resource-quantity" . | float64 | mulf $ratio | ceil | int -}} + {{- end }}{{ end }}{{ end -}} + {{- end -}} +{{- end -}} diff --git a/examples/chart/teleport-cluster/templates/auth/deployment.yaml b/examples/chart/teleport-cluster/templates/auth/deployment.yaml index 5f03f38e7448b..e5108b39cd6f6 100644 --- a/examples/chart/teleport-cluster/templates/auth/deployment.yaml +++ b/examples/chart/teleport-cluster/templates/auth/deployment.yaml @@ -157,8 +157,13 @@ spec: - name: "teleport" image: '{{ if $auth.enterprise }}{{ $auth.enterpriseImage }}{{ else }}{{ $auth.image }}{{ end }}:{{ include "teleport-cluster.version" . }}' imagePullPolicy: {{ $auth.imagePullPolicy }} - {{- if or $auth.extraEnv $auth.tls.existingCASecretName }} + {{- $gomemlimit := include "teleport-cluster.gomemlimit" $auth }} + {{- if or $auth.extraEnv $auth.tls.existingCASecretName $gomemlimit }} env: + {{- if $gomemlimit }} + - name: GOMEMLIMIT + value: {{ $gomemlimit | quote }} + {{- end }} {{- if (gt (len $auth.extraEnv) 0) }} {{- toYaml $auth.extraEnv | nindent 8 }} {{- end }} diff --git a/examples/chart/teleport-cluster/templates/proxy/deployment.yaml b/examples/chart/teleport-cluster/templates/proxy/deployment.yaml index 0fd3e031750b7..f2daf4e30b73a 100644 --- a/examples/chart/teleport-cluster/templates/proxy/deployment.yaml +++ b/examples/chart/teleport-cluster/templates/proxy/deployment.yaml @@ -176,8 +176,13 @@ spec: - name: "teleport" image: '{{ if $proxy.enterprise }}{{ $proxy.enterpriseImage }}{{ else }}{{ $proxy.image }}{{ end }}:{{ include "teleport-cluster.version" . }}' imagePullPolicy: {{ $proxy.imagePullPolicy }} - {{- if or $proxy.extraEnv $proxy.tls.existingCASecretName }} + {{- $gomemlimit := include "teleport-cluster.gomemlimit" $proxy }} + {{- if or $proxy.extraEnv $proxy.tls.existingCASecretName $gomemlimit }} env: + {{- if $gomemlimit }} + - name: GOMEMLIMIT + value: {{ $gomemlimit | quote }} + {{- end }} {{- if (gt (len $proxy.extraEnv) 0) }} {{- toYaml $proxy.extraEnv | nindent 8 }} {{- end }} diff --git a/examples/chart/teleport-cluster/tests/__snapshot__/auth_deployment_test.yaml.snap b/examples/chart/teleport-cluster/tests/__snapshot__/auth_deployment_test.yaml.snap index 39b1b19654124..03fa2c2e89dcf 100644 --- a/examples/chart/teleport-cluster/tests/__snapshot__/auth_deployment_test.yaml.snap +++ b/examples/chart/teleport-cluster/tests/__snapshot__/auth_deployment_test.yaml.snap @@ -274,6 +274,9 @@ should set resources when set in values: - args: - --diag-addr=0.0.0.0:3000 - --apply-on-startup=/etc/teleport/apply-on-startup.yaml + env: + - name: GOMEMLIMIT + value: "3865470567" image: public.ecr.aws/gravitational/teleport-distroless:18.4.2 imagePullPolicy: IfNotPresent lifecycle: diff --git a/examples/chart/teleport-cluster/tests/__snapshot__/proxy_deployment_test.yaml.snap b/examples/chart/teleport-cluster/tests/__snapshot__/proxy_deployment_test.yaml.snap index a7399b14971bd..64c6ad93d41e6 100644 --- a/examples/chart/teleport-cluster/tests/__snapshot__/proxy_deployment_test.yaml.snap +++ b/examples/chart/teleport-cluster/tests/__snapshot__/proxy_deployment_test.yaml.snap @@ -349,6 +349,9 @@ should set resources for wait-auth-update initContainer when set in values: containers: - args: - --diag-addr=0.0.0.0:3000 + env: + - name: GOMEMLIMIT + value: "3865470567" image: public.ecr.aws/gravitational/teleport-distroless:18.4.2 imagePullPolicy: IfNotPresent lifecycle: @@ -475,6 +478,9 @@ should set resources when set in values: containers: - args: - --diag-addr=0.0.0.0:3000 + env: + - name: GOMEMLIMIT + value: "3865470567" image: public.ecr.aws/gravitational/teleport-distroless:18.4.2 imagePullPolicy: IfNotPresent lifecycle: diff --git a/examples/chart/teleport-cluster/tests/auth_deployment_test.yaml b/examples/chart/teleport-cluster/tests/auth_deployment_test.yaml index 0b36bd5987b35..b13778d0b19c9 100644 --- a/examples/chart/teleport-cluster/tests/auth_deployment_test.yaml +++ b/examples/chart/teleport-cluster/tests/auth_deployment_test.yaml @@ -1021,3 +1021,102 @@ tests: labelSelector: matchLabels: app: baz + + - it: sets GOMEMLIMIT by default (SI unit) + template: auth/deployment.yaml + set: + clusterName: helm-lint + resources: + limits: + memory: "10.5G" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: "GOMEMLIMIT" + value: "9450000000" + + - it: sets GOMEMLIMIT by default (IEC unit) + template: auth/deployment.yaml + set: + clusterName: helm-lint + resources: + limits: + memory: "10.5Gi" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: "GOMEMLIMIT" + value: "10146860237" + + - it: sets GOMEMLIMIT by default (scientific notation) + template: auth/deployment.yaml + set: + clusterName: helm-lint + resources: + limits: + memory: "10.5e9" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: "GOMEMLIMIT" + value: "9450000000" + + - it: honours existing GOMEMLIMIT + template: auth/deployment.yaml + set: + clusterName: helm-lint + resources: + limits: + memory: "10.5G" + extraEnv: + - name: FOO + value: bar + - name: GOMEMLIMIT + value: "5GB" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: "GOMEMLIMIT" + value: "5GB" + + - it: does not set GOMEMLIMIT if ratio is 0 + template: auth/deployment.yaml + set: + clusterName: helm-lint + # we set an extra env so contrainers[0].env always exists + # this makes testing easier + extraEnv: + - name: FOO + value: bar + resources: + limits: + memory: "10.5G" + # we nest under auth to check if merge works properly + auth: + goMemLimitRatio: 0 + asserts: + - notContains: + path: spec.template.spec.containers[0].env + content: + name: "GOMEMLIMIT" + value: "9450000000" + + - it: does not set GOMEMLIMIT if resources are not set + template: auth/deployment.yaml + set: + clusterName: helm-lint + # we set an extra env so contrainers[0].env always exists + # this makes testing easier + extraEnv: + - name: FOO + value: bar + asserts: + - notContains: + path: spec.template.spec.containers[0].env + content: + name: "GOMEMLIMIT" + any: true diff --git a/examples/chart/teleport-cluster/tests/proxy_deployment_test.yaml b/examples/chart/teleport-cluster/tests/proxy_deployment_test.yaml index 3be38c79de584..c36e7f1d513fc 100644 --- a/examples/chart/teleport-cluster/tests/proxy_deployment_test.yaml +++ b/examples/chart/teleport-cluster/tests/proxy_deployment_test.yaml @@ -1140,3 +1140,102 @@ tests: labelSelector: matchLabels: app: baz + + - it: sets GOMEMLIMIT by default (SI unit) + template: proxy/deployment.yaml + set: + clusterName: helm-lint + resources: + limits: + memory: "10.5G" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: "GOMEMLIMIT" + value: "9450000000" + + - it: sets GOMEMLIMIT by default (IEC unit) + template: proxy/deployment.yaml + set: + clusterName: helm-lint + resources: + limits: + memory: "10.5Gi" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: "GOMEMLIMIT" + value: "10146860237" + + - it: sets GOMEMLIMIT by default (scientific notation) + template: proxy/deployment.yaml + set: + clusterName: helm-lint + resources: + limits: + memory: "10.5e9" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: "GOMEMLIMIT" + value: "9450000000" + + - it: honours existing GOMEMLIMIT + template: proxy/deployment.yaml + set: + clusterName: helm-lint + resources: + limits: + memory: "10.5G" + extraEnv: + - name: FOO + value: bar + - name: GOMEMLIMIT + value: "5GB" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: "GOMEMLIMIT" + value: "5GB" + + - it: does not set GOMEMLIMIT if ratio is 0 + template: proxy/deployment.yaml + set: + clusterName: helm-lint + # we set an extra env so contrainers[0].env always exists + # this makes testing easier + extraEnv: + - name: FOO + value: bar + resources: + limits: + memory: "10.5G" + # we nest under proxy to check if merge works properly + proxy: + goMemLimitRatio: 0 + asserts: + - notContains: + path: spec.template.spec.containers[0].env + content: + name: "GOMEMLIMIT" + value: "9450000000" + + - it: does not set GOMEMLIMIT if resources are not set + template: proxy/deployment.yaml + set: + clusterName: helm-lint + # we set an extra env so contrainers[0].env always exists + # this makes testing easier + extraEnv: + - name: FOO + value: bar + asserts: + - notContains: + path: spec.template.spec.containers[0].env + content: + name: "GOMEMLIMIT" + any: true diff --git a/examples/chart/teleport-cluster/values.yaml b/examples/chart/teleport-cluster/values.yaml index 4c4d6029d4210..4d66fd5853825 100644 --- a/examples/chart/teleport-cluster/values.yaml +++ b/examples/chart/teleport-cluster/values.yaml @@ -825,6 +825,18 @@ resources: {} # limits: # memory: "2Gi" +# goMemLimitRatio configures the GOMEMLIMIT env var set by the chart. +# GOMEMLIMIT instructs the go garbage collector to try to keep allocated memory +# below a given threshold. This is a best-effort attempt, but this helps +# to prevent OOMs in case of bursts. +# +# When the memory limits are set and goMemLimitRatio is non-zero, +# the chart sets the GOMEMLIMIT to `resources.memory.limits * goMemLimitRatio`. +# The value must be between 0 and 1. +# Set to 0 to unset GOMEMLIMIT. +# This has no effect if GOMEMLIMIT is already set through `extraEnv`. +goMemLimitRatio: 0.9 + # Pod security context for any pods created by the chart podSecurityContext: {} # fsGroup: 65532 diff --git a/examples/chart/teleport-kube-agent/templates/_quantity.tpl b/examples/chart/teleport-kube-agent/templates/_quantity.tpl new file mode 100644 index 0000000000000..451426d5cc614 --- /dev/null +++ b/examples/chart/teleport-kube-agent/templates/_quantity.tpl @@ -0,0 +1,37 @@ +{{/* This template tries to parse a resource quantity like Kubernetes does. +Helm sadly doesn't offer this critical primitive: https://github.com/helm/helm/issues/11376 +The quantity serialization format is described here: https://github.com/kubernetes/apimachinery/blob/master/pkg/api/resource/quantity.go#L33 + +This template support IEC, SI and decimal notation syntaxes, but has poor error handling.*/}} +{{- define "teleport-kube-agent.resource-quantity" -}} + {{- $value := . -}} + {{- $unit := 1.0 -}} + {{- if typeIs "string" . -}} + {{- $base2 := dict "Ki" 0x1p10 "Mi" 0x1p20 "Gi" 0x1p30 "Ti" 0x1p40 "Pi" 0x1p50 "Ei" 0x1p60 -}} + {{- $base10 := dict "m" 1e-3 "k" 1e3 "M" 1e6 "G" 1e9 "T" 1e12 "P" 1e15 "E" 1e18 -}} + {{- range $k, $v := merge $base2 $base10 -}} + {{- if hasSuffix $k $ -}} + {{- $value = trimSuffix $k $ -}} + {{- $unit = $v -}} + {{- end -}} + {{- end -}} + {{- end -}} + {{- mulf (float64 $value) $unit -}} +{{- end -}} + +{{/* This renders the GOMEMLIMIT env var unless the user already specified it +in extraEnv, goMemLimitRatio is set to 0, or requests.memory.limit is unset. */}} +{{- define "teleport-kube-agent.gomemlimit" -}} + {{- $alreadySet := false -}} + {{- range $_, $var := .Values.extraEnv -}} + {{- if eq $var.name "GOMEMLIMIT" -}} + {{- $alreadySet = true -}} + {{- end -}} + {{- end -}} + {{- if and (not $alreadySet) .Values.goMemLimitRatio -}} + {{- $ratio := .Values.goMemLimitRatio -}} + {{- with .Values.resources }}{{ with .limits }}{{ with .memory -}} + {{- include "teleport-kube-agent.resource-quantity" . | float64 | mulf $ratio | ceil | int -}} + {{- end }}{{ end }}{{ end -}} + {{- end -}} +{{- end -}} diff --git a/examples/chart/teleport-kube-agent/templates/statefulset.yaml b/examples/chart/teleport-kube-agent/templates/statefulset.yaml index 98a549fa3447f..0cd69265e5413 100644 --- a/examples/chart/teleport-kube-agent/templates/statefulset.yaml +++ b/examples/chart/teleport-kube-agent/templates/statefulset.yaml @@ -158,6 +158,11 @@ spec: imagePullPolicy: {{ toYaml .Values.imagePullPolicy }} {{- end }} env: + {{- $gomemlimit := include "teleport-kube-agent.gomemlimit" . }} + {{- if $gomemlimit }} + - name: GOMEMLIMIT + value: {{ $gomemlimit | quote }} + {{- end }} # This variable is set for telemetry purposes. # Telemetry is opt-in and controlled at the auth level. - name: TELEPORT_INSTALL_METHOD_HELM_KUBE_AGENT diff --git a/examples/chart/teleport-kube-agent/tests/__snapshot__/statefulset_test.yaml.snap b/examples/chart/teleport-kube-agent/tests/__snapshot__/statefulset_test.yaml.snap index f3ed50f2bf69b..c5f849e613af6 100644 --- a/examples/chart/teleport-kube-agent/tests/__snapshot__/statefulset_test.yaml.snap +++ b/examples/chart/teleport-kube-agent/tests/__snapshot__/statefulset_test.yaml.snap @@ -1795,6 +1795,8 @@ should provision initContainer correctly when set in values: - args: - --diag-addr=0.0.0.0:3000 env: + - name: GOMEMLIMIT + value: "3865470567" - name: TELEPORT_INSTALL_METHOD_HELM_KUBE_AGENT value: "true" - name: TELEPORT_REPLICA_NAME @@ -2750,6 +2752,8 @@ should set resources when set in values: - args: - --diag-addr=0.0.0.0:3000 env: + - name: GOMEMLIMIT + value: "3865470567" - name: TELEPORT_INSTALL_METHOD_HELM_KUBE_AGENT value: "true" - name: TELEPORT_REPLICA_NAME diff --git a/examples/chart/teleport-kube-agent/tests/statefulset_test.yaml b/examples/chart/teleport-kube-agent/tests/statefulset_test.yaml index e43074c6f3ec1..c3e1d23134d5f 100644 --- a/examples/chart/teleport-kube-agent/tests/statefulset_test.yaml +++ b/examples/chart/teleport-kube-agent/tests/statefulset_test.yaml @@ -951,3 +951,102 @@ tests: content: name: KUBERNETES_TOKEN_PATH value: /var/run/secrets/tokens/join-sa-token + + - it: sets GOMEMLIMIT by default (SI unit) + template: statefulset.yaml + values: + - ../.lint/stateful.yaml + set: + teleportClusterName: teleport.example.com + resources: + limits: + memory: "10.5G" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: "GOMEMLIMIT" + value: "9450000000" + + - it: sets GOMEMLIMIT by default (IEC unit) + template: statefulset.yaml + values: + - ../.lint/stateful.yaml + set: + teleportClusterName: teleport.example.com + resources: + limits: + memory: "10.5Gi" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: "GOMEMLIMIT" + value: "10146860237" + + - it: sets GOMEMLIMIT by default (scientific notation) + template: statefulset.yaml + values: + - ../.lint/stateful.yaml + set: + teleportClusterName: teleport.example.com + resources: + limits: + memory: "10.5e9" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: "GOMEMLIMIT" + value: "9450000000" + + - it: honours existing GOMEMLIMIT + template: statefulset.yaml + values: + - ../.lint/stateful.yaml + set: + teleportClusterName: teleport.example.com + resources: + limits: + memory: "10.5G" + extraEnv: + - name: FOO + value: bar + - name: GOMEMLIMIT + value: "5GB" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: "GOMEMLIMIT" + value: "5GB" + + - it: does not set GOMEMLIMIT if ratio is 0 + template: statefulset.yaml + values: + - ../.lint/stateful.yaml + set: + teleportClusterName: teleport.example.com + resources: + limits: + memory: "10.5G" + goMemLimitRatio: 0 + asserts: + - notContains: + path: spec.template.spec.containers[0].env + content: + name: "GOMEMLIMIT" + value: "9450000000" + + - it: does not set GOMEMLIMIT if resources are not set + template: statefulset.yaml + values: + - ../.lint/stateful.yaml + set: + teleportClusterName: teleport.example.com + asserts: + - notContains: + path: spec.template.spec.containers[0].env + content: + name: "GOMEMLIMIT" + any: true diff --git a/examples/chart/teleport-kube-agent/values.yaml b/examples/chart/teleport-kube-agent/values.yaml index bd7343267583d..8b346f001cda1 100644 --- a/examples/chart/teleport-kube-agent/values.yaml +++ b/examples/chart/teleport-kube-agent/values.yaml @@ -1316,6 +1316,18 @@ initContainers: [] # for more details. resources: {} +# goMemLimitRatio(float) -- configures the GOMEMLIMIT env var set by the chart. +# GOMEMLIMIT instructs the go garbage collector to try to keep allocated memory +# below a given threshold. This is a best-effort attempt, but this helps +# to prevent OOMs in case of bursts. +# +# When the memory limits are set and goMemLimitRatio is non-zero, +# the chart sets the GOMEMLIMIT to `resources.memory.limits * goMemLimitRatio`. +# The value must be between 0 and 1. +# Set to 0 to unset GOMEMLIMIT. +# This has no effect if GOMEMLIMIT is already set through `extraEnv`. +goMemLimitRatio: 0.9 + # initSecurityContext(object) -- sets the init container security context for any # pods created by the chart. # See [the Kubernetes documentation](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) diff --git a/examples/chart/teleport-relay/templates/_quantity.tpl b/examples/chart/teleport-relay/templates/_quantity.tpl new file mode 100644 index 0000000000000..f1ea7f5371df1 --- /dev/null +++ b/examples/chart/teleport-relay/templates/_quantity.tpl @@ -0,0 +1,37 @@ +{{/* This template tries to parse a resource quantity like Kubernetes does. +Helm sadly doesn't offer this critical primitive: https://github.com/helm/helm/issues/11376 +The quantity serialization format is described here: https://github.com/kubernetes/apimachinery/blob/master/pkg/api/resource/quantity.go#L33 + +This template support IEC, SI and decimal notation syntaxes, but has poor error handling.*/}} +{{- define "teleport-relay.resource-quantity" -}} + {{- $value := . -}} + {{- $unit := 1.0 -}} + {{- if typeIs "string" . -}} + {{- $base2 := dict "Ki" 0x1p10 "Mi" 0x1p20 "Gi" 0x1p30 "Ti" 0x1p40 "Pi" 0x1p50 "Ei" 0x1p60 -}} + {{- $base10 := dict "m" 1e-3 "k" 1e3 "M" 1e6 "G" 1e9 "T" 1e12 "P" 1e15 "E" 1e18 -}} + {{- range $k, $v := merge $base2 $base10 -}} + {{- if hasSuffix $k $ -}} + {{- $value = trimSuffix $k $ -}} + {{- $unit = $v -}} + {{- end -}} + {{- end -}} + {{- end -}} + {{- mulf (float64 $value) $unit -}} +{{- end -}} + +{{/* This renders the GOMEMLIMIT env var unless the user already specified it +in extraEnv, goMemLimitRatio is set to 0, or requests.memory.limit is unset. */}} +{{- define "teleport-relay.gomemlimit" -}} + {{- $alreadySet := false -}} + {{- range $_, $var := .Values.extraEnv -}} + {{- if eq $var.name "GOMEMLIMIT" -}} + {{- $alreadySet = true -}} + {{- end -}} + {{- end -}} + {{- if and (not $alreadySet) .Values.goMemLimitRatio -}} + {{- $ratio := .Values.goMemLimitRatio -}} + {{- with .Values.resources }}{{ with .limits }}{{ with .memory -}} + {{- include "teleport-relay.resource-quantity" . | float64 | mulf $ratio | ceil | int -}} + {{- end }}{{ end }}{{ end -}} + {{- end -}} +{{- end -}} diff --git a/examples/chart/teleport-relay/templates/deployment.yaml b/examples/chart/teleport-relay/templates/deployment.yaml index e21d108ade096..e335c1603f55d 100644 --- a/examples/chart/teleport-relay/templates/deployment.yaml +++ b/examples/chart/teleport-relay/templates/deployment.yaml @@ -146,6 +146,11 @@ spec: {{- toYaml . | nindent 12 }} {{- end }} env: + {{- $gomemlimit := include "teleport-relay.gomemlimit" . }} + {{- if $gomemlimit }} + - name: GOMEMLIMIT + value: {{ $gomemlimit | quote }} + {{- end }} {{- /* This variable is set for telemetry purposes. Telemetry is opt-in and controlled at the auth level. */}} - name: TELEPORT_INSTALL_METHOD_HELM_KUBE_AGENT diff --git a/examples/chart/teleport-relay/tests/deployment_test.yaml b/examples/chart/teleport-relay/tests/deployment_test.yaml index 22c77d168027c..430b9a92c1730 100644 --- a/examples/chart/teleport-relay/tests/deployment_test.yaml +++ b/examples/chart/teleport-relay/tests/deployment_test.yaml @@ -316,3 +316,95 @@ tests: content: name: SSL_CERT_FILE value: /etc/teleport-tls-ca.pem + + - it: sets GOMEMLIMIT by default (SI unit) + template: deployment.yaml + values: + - ../.lint/simple.yaml + set: + resources: + limits: + memory: "10.5G" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: "GOMEMLIMIT" + value: "9450000000" + + - it: sets GOMEMLIMIT by default (IEC unit) + template: deployment.yaml + values: + - ../.lint/simple.yaml + set: + resources: + limits: + memory: "10.5Gi" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: "GOMEMLIMIT" + value: "10146860237" + + - it: sets GOMEMLIMIT by default (scientific notation) + template: deployment.yaml + values: + - ../.lint/simple.yaml + set: + resources: + limits: + memory: "10.5e9" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: "GOMEMLIMIT" + value: "9450000000" + + - it: honours existing GOMEMLIMIT + template: deployment.yaml + values: + - ../.lint/simple.yaml + set: + resources: + limits: + memory: "10.5G" + extraEnv: + - name: FOO + value: bar + - name: GOMEMLIMIT + value: "5GB" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: "GOMEMLIMIT" + value: "5GB" + + - it: does not set GOMEMLIMIT if ratio is 0 + template: deployment.yaml + values: + - ../.lint/simple.yaml + set: + resources: + limits: + memory: "10.5G" + goMemLimitRatio: 0 + asserts: + - notContains: + path: spec.template.spec.containers[0].env + content: + name: "GOMEMLIMIT" + value: "9450000000" + + - it: does not set GOMEMLIMIT if resources are not set + template: deployment.yaml + values: + - ../.lint/simple.yaml + asserts: + - notContains: + path: spec.template.spec.containers[0].env + content: + name: "GOMEMLIMIT" + any: true diff --git a/examples/chart/teleport-relay/values.yaml b/examples/chart/teleport-relay/values.yaml index 29077b267868e..ca34d2580ce2b 100644 --- a/examples/chart/teleport-relay/values.yaml +++ b/examples/chart/teleport-relay/values.yaml @@ -206,6 +206,18 @@ highAvailability: # for more details. resources: {} +# goMemLimitRatio(float) -- configures the GOMEMLIMIT env var set by the chart. +# GOMEMLIMIT instructs the go garbage collector to try to keep allocated memory +# below a given threshold. This is a best-effort attempt, but this helps +# to prevent OOMs in case of bursts. +# +# When the memory limits are set and goMemLimitRatio is non-zero, +# the chart sets the GOMEMLIMIT to `resources.memory.limits * goMemLimitRatio`. +# The value must be between 0 and 1. +# Set to 0 to unset GOMEMLIMIT. +# This has no effect if GOMEMLIMIT is already set through `extraEnv`. +goMemLimitRatio: 0.9 + # service -- options for the Service that points to the Teleport Relay # instances. service: