GOMAXPROCS, GOMEMLIMIT and Kubernetes

The Go runtime has a few environment variables that can be configured, but there are two that DevOps folks often use as a starting point: GOMAXPROCS and GOMEMLIMIT. Let’s take a look at what each of them does.
Recap
From the Go runtime documentation:
GOMEMLIMIT (Go 1.19+)
GOMEMLIMIT
variable sets a soft memory limit for the runtime. This memory limit includes the Go heap and all other memory managed by the runtime, and excludes external memory sources such as mappings of the binary itself, memory managed in other languages, and memory held by the operating system on behalf of the Go program. GOMEMLIMIT
is a numeric value in bytes with an optional unit suffix. The supported suffixes include B, KiB, MiB, GiB, and TiB. These suffixes represent quantities of bytes as defined by the IEC 80000-13 standard. That is, they are based on powers of two: KiB means 2^10 bytes, MiB means 2^20 bytes, and so on. The default setting is math.MaxInt64, which effectively disables the memory limit. runtime/debug.SetMemoryLimit allows changing this limit at run time.This is the value that tells the Go runtime how much memory we have available. The default is to not enable the memory limit.
GOMAXPROCS
GOMAXPROCS
variable limits the number of operating system threads that can execute user-level Go code simultaneously. There is no limit to the number of threads that can be blocked in system calls on behalf of Go code; those do not count against the GOMAXPROCS
limit. This package’s GOMAXPROCS function queries and changes the limit.From the above and (GOMAXPROCS, a good friend of DevOps), a key point to note is that if we set a pod’s CPU limit to 1 CPU but it’s running on a machine with 16 cores, our app will default to GOMAXPROCS=16
. This can lead to unintentional performance degradation when the workload exceeds the limit.
Configuration when working with Kubernetes
The previous article introduced the automaxprocs package, which saves us from having to manually adjust GOMAXPROCS
. However, we still need to manually adjust GOMEMLIMIT
. When we deploy in Kubernetes, we can use the value from resourceFieldRef
as shown in the example below.
...
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 1000m
memory: 512Mi
env:
- name: GOMEMLIMIT
valueFrom:
resourceFieldRef:
resource: limits.memory
- name: GOMAXPROCS
valueFrom:
resourceFieldRef:
resource: limits.cpu
In this example, the values will be calculated and put into the GOMAXPROCS
and GOMEMLIMIT
environment variables for our app as follows:
GOMAXPROCS: 1
GOMEMLIMIT: 536870912
These set values will be used by the Go runtime automatically. The Go runtime uses a 0
(zero value) as the default when no value is passed. This means that even if we don’t set resource limits for the Pod, the Go runtime will fall back to its default values, so we don’t have to worry about the service failing to run lol.