До Go 1.25 GOMAXPROCS по умолчанию равнялся числу логических CPU машины. Контейнер с лимитом в два CPU на хосте с 32 логическими CPU получал 32. Это 32 P, которые гоняют горутины при квоте в два CPU на период, так что процесс съедает квоту в начале периода, а остаток каждого периода простаивает из-за троттлинга.
В Go 1.25 значение по умолчанию на Linux поменялось. Рантайм читает лимит CPU из cgroup и берёт его, если он меньше числа CPU. Дробный лимит округляется вверх. Ниже 2 значение по умолчанию не опускается, кроме случая, когда у самой машины CPU меньше двух. Ещё рантайм периодически перечитывает лимит, так что изменение на ходу тоже подхватится.
Новое поведение отключается тремя способами. Переменная окружения GOMAXPROCS, вызов runtime.GOMAXPROCS со значением или строка go с версией 1.24 и ниже в go.mod, потому что для таких версий по умолчанию действует containermaxprocs=0. И смотрит рантайм только на лимит. Запрос CPU без лимита ничего не меняет. В терминах Kubernetes это limits.cpu, а под, у которого задан только requests.cpu, остаётся со старым значением по умолчанию.
Для проверки у меня программа, которая печатает две строки.
package main
import (
"fmt"
"runtime"
)
func main() {
fmt.Println("GOMAXPROCS", runtime.GOMAXPROCS(0))
fmt.Println("NumCPU", runtime.NumCPU())
}
Собираю с таким go.mod, чтобы действовало новое значение по умолчанию.
module probe
go 1.25
NumCPU - это число, которое раньше бралось по умолчанию. Если в контейнере с лимитом ниже числа CPU обе строки совпадают, мешает что-то из трёх пунктов выше.
Так что число, за которым я слежу, упало с 32 готовых к работе P до двух.