Резервирование CPU в кластере

Резервирование (pinning) CPU для etcd в кластере можно настроить в графическом или программном интерфейсе.

Резервирование CPU приводит к уменьшению доступных (Allocatable) CPU на узле. Это можно увидеть на странице узла или при осуществлении describe узла (разделы "Capacity" и "Allocatable"). Само резервирование никак не лимитирует CPU, а лишь устанавливает "веса" (приоритизацию). Т.е. Pod без лимита всегда сможет потреблять 100% CPU, если в этот момент никто другой не претендует на CPU. Так же Allocatable учитывается на этапе планирования Pod с указанными запросами: если по запросам не хватит ресурсов, то Pod не будет запущен или будут вытесняться менее приоритетные поды.

Важно ‒ Рекомендуется, чтобы в результате резервирования на узле должно остаться не менее 6 свободных CPU.

Резервирование CPU в графическом интерфейсе

В интерфейсе кластера нужно перейти в раздел "Конфигурация узлов", создать новый объект NCI, выбрать раздел "Kubelet", в селекторе узлов выбрать ключ "node-role.kubernetes.io/control-plane" (рисунок 108).

Рисунок 108 ‒ Создание объекта NCI

Далее необходимо заполнить поля:

  • Политика управления CPU (cpuManagerPolicy) ‒ со значением static. Это значение сообщает Kubelet, что можно привязывать поды к зарезервированным CPU.
  • Перечень резервируемых CPU (reservedSystemCPUs) ‒ в виде диапазона или через запятую, например, 0-1.
  • В блоке "Feature Gates" указать параметры в формате ключ-значение:
  • CPUManager: true;
  • CPUManagerPolicyOptions: true;
  • CPUManagerPolicyAlphaOptions: true;
  • CPUManagerPolicyBetaOptions: true;
  1. В блоке "Параметры управления CPU" (cpuManagerPolicyOptions) указать параметры в формате "ключ-значение" (рисунок 109):
  • full-pcpus-only: true;
  • distribute-cpus-across-numa: true;
  • align-by-socket: true;

Рисунок 109 ‒ Параметры в формате "ключ-значение"

  1. сохранить созданный объект.

Kubelet будет перезапущен автоматически.

Резервирование CPU в программном интерфейсе

Для резервирования CPU в программном интерфейсе нужно выполнить следующие шаги:

  1. подключиться к кластеру из консоли;
  2. создать манифест NCI:
apiVersion: node.shturval.tech/v1beta2
kind: NodeConfigItem
metadata:
name: cpu-pinning-test
spec:
kubelet:
cpuManagerPolicy: static
featureGates:
CPUManager: true
CPUManagerPolicyOptions: true
CPUManagerPolicyAlphaOptions: true
CPUManagerPolicyBetaOptions: true
cpuManagerPolicyOptions:
align-by-socket: "true"
distribute-cpus-across-numa: "true"
full-pcpus-only: "true"
reservedSystemCPUs: 0-1
nodeconfigselector:
node-role.kubernetes.io/control-plane: "
priority: 100
  1. применить созданный манифест:
kubectl apply -f cpu-pinning-test.yaml

Проверка состояния резервирования CPU

Для проверки состояния Kubelet CPU Manager нужно выполнить команду:

cat /var/lib/kubelet/cpu_manager_state

Пример результата команды

{"policyName":"static","defaultCpuSet":"0-1,4-7","entries":{"3f6868ef501db41abc9cc55c40ab1020":{"etcd":"2-3"}},"checksum":4106740397}

Здесь можно увидеть, что:

  • используется политика static;
  • по умолчанию все поды размещаются в распределенном пуле процессоров: 0-1,4-7;
  • для пода etcd выделены CPU 2-3.

В случае успешного резервирования CPU в логе Kubelet будет получено сообщение:

policy_static.go:175] "Reserved CPUs not available for exclusive assignment" reservedSize=2 reserved="0-1" reservedPhysicalCPUs="0-1"

Следует обратить внимание, что есть вероятность, что на виртуальных машинах не заработает выравнивание по сокетам, если выставить больше, чем 1 сокет на VM, так как нет возможности повлиять на количество NUMA-node. В таком случае необходимо удалить параметр: align-by-socket: true.

Пример ошибки:

I0613 19:29:30.525539    5408 cpu_manager.go:172] "Detected CPU topology" topology={"NumCPUs":8,"NumCores":8,"NumSockets":2,"NumNUMANodes":1,"CPUDetails":{"0":
{"NUMANodeID":0,"SocketID":0,"CoreID":0}
,"1":{"NUMANodeID":0,"SocketID":0,"CoreID":1},"2":{"NUMANodeID":0,"SocketID":0,"CoreID":2},"3":{"NUMANodeID":0,"SocketID":0,"CoreID":3},"4":{"NUMANodeID":0,"SocketID":1,"CoreID":4},"5":{"NUMANodeID":0,"SocketID":1,"CoreID":5},"6":{"NUMANodeID":0,"SocketID":1,"CoreID":6},"7":{"NUMANodeID":0,"SocketID":1,"CoreID":7}}}
июн 13 19:29:30 shturval-mycluster-cpu-master-qc2rv kubelet[5408]: E0613 19:29:30.525580    5408 container_manager_linux.go:329] "Failed to initialize cpu manager" err="new static policy error: Align by socket is not compatible with hardware where number of sockets are more than number of NUMA"
июн 13 19:29:30 shturval-mycluster-cpu-master-qc2rv kubelet[5408]: E0613 19:29:30.525610    5408 run.go:74] "command failed" err="failed to run Kubelet: new static policy error: Align by socket is not compatible with hardware where number of sockets are more than number of NUMA"

Условия использования зарезервированных ресурсов для подов

Резервирование CPU приводит к тому, что никакие поды не будут размещаться на выделенных под резервирование CPU выбранных узлов. Исключение представляют те поды, у которых выполняются все условия использования гарантированных ресурсов:

  • должны присутствовать как requests, так и limits;
  • requests и limits по CPU должны быть целыми числами;
  • requests и limits должны совпадать.

Пример:

spec:
containers:
‒ name: etcd
resources:
limits:
memory: "500Mi"
cpu: "2"
example.com/device: "1"
requests:
memory: "500Mi"
cpu: "2"
example.com/device: "1"

Если все условия выполнены, то после запуска пода в статусе будет указан тип QoS Class Guaranteed.

Следует обратить внимание, что на резервируемых процессорах в кластере могли быть зарезервированы/запущены поды до резервирования CPU. Убедиться, что узлы перезапустились после применения настроек Kubelet. Резервирование CPU не означает, что на этих ресурсах не будут исполняться процессы ОС. Это гарантирует, что Kubelet не будет размещать на зарезервированных CPU другие поды.