Начало работы с Cgroups в Linux: cpuset

Docker

Это четвертая статья из серии Cgroup, обзор прошлых выпусков:

пройти черезПредыдущая статья, мы научились просматривать текущую информацию cgroup, как работать/sys/fs/cgroupкаталог для динамической настройки контрольных групп, а также научился устанавливать доли ЦП и квоты ЦП для управленияsliceвнутри и разныеsliceВремя использования ЦП между. В этой статье мы продолжим изучение ограничений на время использования ЦП.

Для некоторых программ, интенсивно использующих ЦП, необходимо не только получить больше времени использования ЦП, но и уменьшить переключение контекста, вызванное рабочей нагрузкой при регулировании. В современных многоядерных системах каждое ядро ​​имеет собственный кеш, и если процесс частого планирования выполняется на разных ядрах, это неизбежно приведет к накладным расходам, таким как аннулирование кеша. Так есть ли способ изолировать ядра процессора? Чтобы быть точным, запущенный процесс привязан к указанному ядру для запуска. Хотя все программы созданы равными операционной системе,Но некоторые программы более равны, чем другие.

Для тех более равных программ нам нужно выделять больше ресурсов процессора, ведь люди очень эксцентричны. Без лишних слов, давайте посмотрим, как использоватьcgroupОграничивает процесс от использования указанных ядер ЦП.

1. Просмотр конфигурации ЦП

Нумерация ядер ЦП обычно начинается с 0, а диапазон нумерации 4 ядер составляет0-3. Мы можем видеть/proc/cpuinfoчтобы определить некоторую информацию о процессоре:

$ cat /proc/cpuinfo
...
processor    : 3
vendor_id    : GenuineIntel
cpu family    : 6
model        : 26
model name    : Intel(R) Xeon(R) CPU           X5650  @ 2.67GHz
stepping    : 4
microcode    : 0x1f
cpu MHz        : 2666.761
cache size    : 12288 KB
physical id    : 6
siblings    : 1
core id        : 0
cpu cores    : 1
apicid        : 6
initial apicid    : 6
fpu        : yes
fpu_exception    : yes
cpuid level    : 11
wp        : yes
flags        : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc eagerfpu pni ssse3 cx16 sse4_1 sse4_2 x2apic popcnt tsc_deadline_timer hypervisor lahf_lm ssbd ibrs ibpb stibp tsc_adjust arat spec_ctrl intel_stibp flush_l1d arch_capabilities
bogomips    : 5333.52
clflush size    : 64
cache_alignment    : 64
address sizes    : 43 bits physical, 48 bits virtual
  • processor: указывает номер ядра, но это не ядро ​​физического ЦП, точнее его можно назвать **номер логического ядра.
  • physical id: Указывает ядро ​​физического ЦП, на котором расположено текущее логическое ядро, также пронумерованное от 0, что означает, что это логическое ядро ​​находится на 7-м физическом ЦП.
  • core id: Если это значение больше 0, обратите внимание, возможно, на вашем сервере включена гиперпоточность. Если гиперпоточность включена, каждое физическое ядро ​​ЦП будет эмулировать 2 потока, также называемых логическими ядрами (которые отличаются от логических ядер выше, но имеют одно и то же имя). Если вы хотите подтвердить, включена ли на сервере гиперпоточность, вы можете проверить это с помощью следующей команды:
$ cat /proc/cpuinfo | grep -e "core id" -e "physical id"

physical id    : 0
core id        : 0
physical id    : 2
core id        : 0
physical id    : 4
core id        : 0
physical id    : 6
core id        : 0

еслиphysical idа такжеcore idвсе так жеprocessorЕсли он появляется дважды, можно сделать вывод, что гиперпоточность включена. Видимо мой сервер не работает.

2. Архитектура NUMA

Есть такое понятиеNUMA (неоднородный доступ к памяти),СейчасНеоднородная архитектура доступа к памяти. Если к материнской плате подключено несколько процессоров,NUMAАрхитектура. Каждый ЦП занимает отдельную область и обычно имеет независимый вентилятор.

ОдинNUMAУзел включает аппаратные устройства, такие как ЦП и память, непосредственно подключенные к области.PCI-E. Это также вводит понятие близости ЦП, то есть ЦП обращается к одному и тому жеNUMAПамять на узле быстрее, чем доступ к другому узлу.

Вы можете просмотреть архитектуру NUMA машины с помощью следующей команды:

$ numactl --hardware

available: 1 nodes (0)
node 0 cpus: 0 1 2 3
node 0 size: 2047 MB
node 0 free: 1335 MB
node distances:
node   0
  0:  10

Видно, что сервер не используетсяNUMAАрхитектура, всего однаNUMAУзел, то есть есть только один ЦП, и все 4 логических ядра находятся на этом ЦП.

3. isolcpus

Одной из самых важных обязанностей Linux является планирование процессов, а процесс — это просто абстракция процесса запуска программы, которая выполняет серию инструкций, и компьютер следует этим инструкциям, чтобы завершить фактическую работу. С аппаратной точки зрения именно центральный процессор или ЦП фактически выполняет эти инструкции. По умолчанию планировщик процессов может запланировать процесс на любое из ядер ЦП, поскольку он уравновешивает распределение вычислительных ресурсов в зависимости от нагрузки.

Чтобы увеличить видимый эффект эксперимента, можно изолировать определенные логические ядра, чтобы система никогда не использовала эти ядра по умолчанию, если я не укажу определенные процессы для их использования. Для этого используйте параметр ядраisolcpusНапример: если вы хотите, чтобы система по умолчанию не использовала логические ядра 2, 3 и 4, вы можете добавить в список параметров ядра следующее:

isolcpus=1,2,3
# 或者
isolcpus=1-3

Для CnetOS 7 его можно изменить напрямую/etc/default/grub:

$ cat /etc/default/grub

GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="crashkernel=auto rd.lvm.lv=rhel/root rd.lvm.lv=rhel/swap rhgb quiet isolcpus=1,2,3"
GRUB_DISABLE_RECOVERY="true"

затем перестроитьgrub.conf:

$ grub2-mkconfig -o /boot/grub2/grub.cfg

После перезагрузки система больше не будет использовать логические ядра 2, 3 и 4, будет использоваться только ядро ​​1. Найдите программу для загрузки процессора (Предыдущая статьяпрограмма), используйте команду top для просмотра загрузки ЦП:

воплощать в жизньtopПосле команды нажмите клавишу с цифрой 1 на странице списка, чтобы увидеть все процессоры.

Вы можете видеть, что система использует только ядро ​​​​1. Давайте посмотрим, как привязать программу к определенному ядру процессора.

4. Создайте контрольную группу

Привязать программу к указанному ядру на самом деле очень просто, достаточно установитьcpusetКонтроллер подойдет.systemctlможет управлять ресурсами, находящимися под его контролемcgroupКонтроллеры, но может управлять только ограниченным количеством контроллеров (ЦП, Память и BlockIO) и не может управлятьcpusetконтроллер. Несмотря на то чтоsystemdОн не поддерживает cpuset, но я думаю, что он будет поддерживаться в будущем.Кроме того, есть немного неуклюжий метод, который может достичь той же цели, который будет представлен позже.

Все операции, связанные с cgroup, основаны на виртуальной файловой системе cgroup в ядре.Использовать cgroup очень просто, просто смонтируйте файловую систему. По умолчанию файловая система монтируется в/sys/fs/cgroupкаталог, взгляните на этот каталог:

$ ll /sys/fs/cgroup

总用量 0
drwxr-xr-x 2 root root  0 3月  28 2020 blkio
lrwxrwxrwx 1 root root 11 3月  28 2020 cpu -> cpu,cpuacct
lrwxrwxrwx 1 root root 11 3月  28 2020 cpuacct -> cpu,cpuacct
drwxr-xr-x 2 root root  0 3月  28 2020 cpu,cpuacct
drwxr-xr-x 2 root root  0 3月  28 2020 cpuset
drwxr-xr-x 4 root root  0 3月  28 2020 devices
drwxr-xr-x 2 root root  0 3月  28 2020 freezer
drwxr-xr-x 2 root root  0 3月  28 2020 hugetlb
drwxr-xr-x 2 root root  0 3月  28 2020 memory
lrwxrwxrwx 1 root root 16 3月  28 2020 net_cls -> net_cls,net_prio
drwxr-xr-x 2 root root  0 3月  28 2020 net_cls,net_prio
lrwxrwxrwx 1 root root 16 3月  28 2020 net_prio -> net_cls,net_prio
drwxr-xr-x 2 root root  0 3月  28 2020 perf_event
drwxr-xr-x 2 root root  0 3月  28 2020 pids
drwxr-xr-x 4 root root  0 3月  28 2020 systemd

можно увидетьcpusetКонтроллеры уже созданы и смонтированы по умолчанию. посмотриcpusetЧто есть в каталоге:

$ ll /sys/fs/cgroup/cpuset

总用量 0
-rw-r--r-- 1 root root 0 3月  28 2020 cgroup.clone_children
--w--w--w- 1 root root 0 3月  28 2020 cgroup.event_control
-rw-r--r-- 1 root root 0 3月  28 2020 cgroup.procs
-r--r--r-- 1 root root 0 3月  28 2020 cgroup.sane_behavior
-rw-r--r-- 1 root root 0 3月  28 2020 cpuset.cpu_exclusive
-rw-r--r-- 1 root root 0 3月  28 2020 cpuset.cpus
-r--r--r-- 1 root root 0 3月  28 2020 cpuset.effective_cpus
-r--r--r-- 1 root root 0 3月  28 2020 cpuset.effective_mems
-rw-r--r-- 1 root root 0 3月  28 2020 cpuset.mem_exclusive
-rw-r--r-- 1 root root 0 3月  28 2020 cpuset.mem_hardwall
-rw-r--r-- 1 root root 0 3月  28 2020 cpuset.memory_migrate
-r--r--r-- 1 root root 0 3月  28 2020 cpuset.memory_pressure
-rw-r--r-- 1 root root 0 3月  28 2020 cpuset.memory_pressure_enabled
-rw-r--r-- 1 root root 0 3月  28 2020 cpuset.memory_spread_page
-rw-r--r-- 1 root root 0 3月  28 2020 cpuset.memory_spread_slab
-rw-r--r-- 1 root root 0 3月  28 2020 cpuset.mems
-rw-r--r-- 1 root root 0 3月  28 2020 cpuset.sched_load_balance
-rw-r--r-- 1 root root 0 3月  28 2020 cpuset.sched_relax_domain_level
-rw-r--r-- 1 root root 0 3月  28 2020 notify_on_release
-rw-r--r-- 1 root root 0 3月  28 2020 release_agent
-rw-r--r-- 1 root root 0 3月  28 2020 tasks

В этом каталоге есть только конфигурация по умолчанию, без какой-либо подсистемы cgroup. Далее мы создаемcpusetподсистемы и установить соответствующие параметры привязки:

$ mkdir -p /sys/fs/cgroup/cpuset/test
$ echo "3" > /sys/fs/cgroup/cpuset/test/cpuset.cpus
$ echo "0" > /sys/fs/cgroup/cpuset/test/cpuset.mems

Сначала создайте подсистему cpuset с именемtest, затем привяжите ядро ​​4 к этой подсистеме, т.е.cpu3. заcpuset.memsПо параметрам каждый узел памяти иNUMAУзлы соответствуют один к одному. Если требования к памяти процесса велики, вы можете поместить всеNUMAВ него настраиваются узлы. используется здесьNUMAКонцепция чего-либо. Из соображений производительности сконфигурированные логические ядра и узлы памяти обычно принадлежат одному и тому же серверу.NUMAузел, доступныйnumactl --hardwareКоманда изучает их отношение отображения. Видимо мой хост не используетNUMAсхемы, просто установите для нее узел 0.

Проверятьtestсодержание:

$ cd /sys/fs/cgroup/cpuset/test
$ ll
总用量 0
-rw-rw-r-- 1 root root 0 3月  28 17:07 cgroup.clone_children
--w--w---- 1 root root 0 3月  28 17:07 cgroup.event_control
-rw-rw-r-- 1 root root 0 3月  28 17:07 cgroup.procs
-rw-rw-r-- 1 root root 0 3月  28 17:07 cpuset.cpu_exclusive
-rw-rw-r-- 1 root root 0 3月  28 17:07 cpuset.cpus
-r--r--r-- 1 root root 0 3月  28 17:07 cpuset.effective_cpus
-r--r--r-- 1 root root 0 3月  28 17:07 cpuset.effective_mems
-rw-rw-r-- 1 root root 0 3月  28 17:07 cpuset.mem_exclusive
-rw-rw-r-- 1 root root 0 3月  28 17:07 cpuset.mem_hardwall
-rw-rw-r-- 1 root root 0 3月  28 17:07 cpuset.memory_migrate
-r--r--r-- 1 root root 0 3月  28 17:07 cpuset.memory_pressure
-rw-rw-r-- 1 root root 0 3月  28 17:07 cpuset.memory_spread_page
-rw-rw-r-- 1 root root 0 3月  28 17:07 cpuset.memory_spread_slab
-rw-rw-r-- 1 root root 0 3月  28 17:07 cpuset.mems
-rw-rw-r-- 1 root root 0 3月  28 17:07 cpuset.sched_load_balance
-rw-rw-r-- 1 root root 0 3月  28 17:07 cpuset.sched_relax_domain_level
-rw-rw-r-- 1 root root 0 3月  28 17:07 notify_on_release
-rw-rw-r-- 1 root root 0 3月  28 17:07 tasks

$ cat cpuset.cpus
3

$ cat cpuset.mems
0

В настоящее время файл задач пуст, то есть в этой подсистеме процессорного набора нет запущенных процессов. Вам нужно найти способ запустить указанный процесс в этой подсистеме, есть два пути:

  1. уже запущенный процессPIDнаписатьtasksв файле;
  2. использоватьsystemdСоздайте демон, который записывает настройки cgroupserviceфайл (по существу такой же, как метод 1).

Давайте сначала рассмотрим метод 1, сначала запустим программу:

$ nohup sha1sum /dev/zero &
[1] 3767

потомPIDзаписывается в тестовую директориюtasksсередина:

$ echo "3767" > /sys/fs/cgroup/cpuset/test/tasks

Проверьте загрузку процессора:

Видно, что ядерное связывание вступает в силу,PIDПроцесс для 3767 запланирован наcpu3начальство.

Давайте снова посмотрим на метод 2, хотя в настоящее времяsystemdНе поддерживается для использованияcpusetЧтобы указать ЦП службы, но у нас все еще есть замаскированный метод, содержимое файла службы выглядит следующим образом:

$ cat /etc/systemd/system/foo.service

[Unit]
Description=foo
After=syslog.target network.target auditd.service

[Service]
ExecStartPre=/usr/bin/mkdir -p /sys/fs/cgroup/cpuset/testset
ExecStartPre=/bin/bash -c '/usr/bin/echo "2" > /sys/fs/cgroup/cpuset/testset/cpuset.cpus'
ExecStartPre=/bin/bash -c '/usr/bin/echo "0" > /sys/fs/cgroup/cpuset/testset/cpuset.mems'
ExecStart=/bin/bash -c "/usr/bin/sha1sum /dev/zero"
ExecStartPost=/bin/bash -c '/usr/bin/echo $MAINPID > /sys/fs/cgroup/cpuset/testset/tasks'
ExecStopPost=/usr/bin/rmdir /sys/fs/cgroup/cpuset/testset
Restart=on-failure

[Install]
WantedBy=multi-user.target

Запустите службу и проверьте загрузку процессора:

Процесс в этой службе действительно запланирован наcpu2начальство.

5. Вернуться к Докеру

Наконец мы возвращаемся кDocker,DockerФактически реализуется нижний слой системыcgroup,namespaceи другие технологии интегрированы в инструмент, который публикуется зеркально, таким образом формируяDocker, я уверен, что все это знают, поэтому я не буду распространяться об этом. Для Docker есть ли способ держать контейнер всегда в одном или несколькихCPUбегать на нем? На самом деле это довольно просто, просто используйте--cpuset-cpusпараметры могут это сделать!

Давайте продемонстрируем, как указать работающий контейнерCPUОсновной номер 1:

🐳 → docker run -d --name stress --cpuset-cpus="1" progrium/stress -c 4

Просмотр нагрузки на ЦП хоста:

ТолькоCpu1Достиг100%, другие процессоры контейнером не используются.

Если вы видели сериалпервая статья, следует знать, что в новом использованииsystemdвыполнитьinitсистема (например,ConetOS 7), система создает 3 верхнего уровняslice:System, Userа такжеMachinemachine.sliceявляется расположением по умолчанию для всех виртуальных машин и контейнеров Linux, в то время как Docker на самом делеmachine.sliceВариант , вы можете думать об этом какmachine.slice.

Если в системе работает Kubernetes,machine.sliceэто становитсяkubepods:

Для облегчения управления cgroups,systemdбудет для каждогоsliceСоздайте подсистему, например подсистему докеров:

Затем в соответствии с настройками контейнера поместите его под соответствующий контроллер, здесь нас интересуетcpusetКонтроллер, посмотрите, что находится в его каталоге:

Проверьте каталог докера:

Вы можете видеть, что Docker создает подкаталог для каждого контейнера.7766..Это соответствует контейнеру, который мы создали ранее:

🐳 → docker ps|grep stress

7766580dd0d7        progrium/stress     "/usr/bin/stress --v…"   36 minutes ago      Up 36 minutes                           stress

Давайте проверим конфигурацию в этом каталоге:

$ cd /sys/fs/cgroup/cpuset/docker/7766580dd0d7d9728f3b603ed470b04d0cac1dd923f7a142fec614b12a4ba3be

$ cat cpuset.cpus
1

$ cat cpuset.mems
0

$ cat tasks
6536
6562
6563
6564
6565

$ ps -ef|grep stress
root      6536  6520  0 10:08 ?        00:00:00 /usr/bin/stress --verbose -c 4
root      6562  6536 24 10:08 ?        00:09:50 /usr/bin/stress --verbose -c 4
root      6563  6536 24 10:08 ?        00:09:50 /usr/bin/stress --verbose -c 4
root      6564  6536 24 10:08 ?        00:09:50 /usr/bin/stress --verbose -c 4
root      6565  6536 24 10:08 ?        00:09:50 /usr/bin/stress --verbose -c 4

Конечно, вы также можете связать контейнеры для запуска на нескольких ядрах ЦП, поэтому я не буду вдаваться в подробности. В следующей статье будет рассказано, как ограничить cgroupBlockIO.

Публичный аккаунт WeChat

Отсканируйте QR-код ниже, чтобы подписаться на официальную учетную запись WeChat, и ответьте на официальную учетную запись ◉Добавить группу◉, чтобы присоединиться к нашей облачной коммуникационной группе и обсудить облачные технологии с Сунь Хунляном, директором Чжаном, Ян Мином и другими важными шишками.