Глава 8. Регулировка ядра KVM для повышения производительности

В данной главе мы собираемся рассмотреть следующие рецепты регулировки производительности:

  • Настройку самого ядра для низкой латентности операций ввода/ вывода

  • Регулировку оперативной памяти для гостевых KVM

  • Опции производительности ЦПУ

  • Регулировка NUMA при помощи libvirt

  • Регулировка самого ядра в отношении сетевой производительности

Введение

В данной главе мы собираемся изучить различные варианты настройки и инструменты, которые могут помочь улучшить имеющуюся производительность ОС определённого хоста и всех работающих на нём экземпляров KVM.

При исполнении виртуальных машин KVM важно понимать, что с точки зрения самого хоста все они являются обычными процессами. Мы можем увидеть, что гости KVM являются процессами Linux опросив его дерево процессов в имеющемся гипервизоре:


root@kvm:~# virsh list
Id Name State
----------------------------------------------------
16 kvm running
root@kvm:~# pgrep -lfa qemu
19913 /usr/bin/qemu-system-x86_64 -name kvm -S -machine pc-i440fx-trusty,accel=kvm,usb=off -m 1024 -realtime mlock=off -smp 1,sockets=1,cores=1,threads=1 -uuid 283c6653-9981-9396-efb4-fb864d87f769 -no-user-config -nodefaults -chardev socket,id=charmonitor,path=/var/lib/libvirt/qemu/domain-kvm/monitor.sock,server,nowait -mon chardev=charmonitor,id=monitor,mode=control -rtc base=utc -no-shutdown -boot strict=on -device piix3-usb-uhci,id=usb,bus=pci.0,addr=0x1.0x2 -drive file=/tmp/debian.img,format=raw,if=none,id=drive-ide0-0-0 -device ide-hd,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0,bootindex=1 -netdev tap,fd=26,id=hostnet0 -device rtl8139,netdev=hostnet0,id=net0,mac=52:54:00:2f:df:93,bus=pci.0,addr=0x3 -chardev pty,id=charserial0 -device isa-serial,chardev=charserial0,id=serial0 -vnc 0.0.0.0:0 -device cirrus-vga,id=video0,bus=pci.0,addr=0x2 -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x4 -msg timestamp=on
root@kvm:~#
 	   

Все виртуальные ЦПУ, выделенные всем гостям KVM, являются потоками (threads), управляемые планировщиком самого хоста:


root@kvm:~# ps -eLf
UID PID PPID LWP C NLWP STIME TTY TIME CMD
...
libvirt+ 19913 1 19913 0 3 14:02 ? 00:00:00 /usr/bin/qemu-system-x86_64 -name kvm -S -machine pc-i440fx-trusty,accel=kvm,usb=off -m 1024 -realtime mlock=off -smp 1,sockets=1,cores=1,threads=1 -uuid 283c6653-9981-9396-efb4-fb864d87f769 -no-user-config -nodefaul
libvirt+ 19913 1 19914 0 3 14:02 ? 00:00:08 /usr/bin/qemu-system-x86_64 -name kvm -S -machine pc-i440fx-trusty,accel=kvm,usb=off -m 1024 -realtime mlock=off -smp 1,sockets=1,cores=1,threads=1 -uuid 283c6653-9981-9396-efb4-fb864d87f769 -no-user-config -nodefaul
libvirt+ 19913 1 19917 0 3 14:02 ? 00:00:00 /usr/bin/qemu-system-x86_64 -name kvm -S -machine pc-i440fx-trusty,accel=kvm,usb=off -m 1024 -realtime mlock=off -smp 1,sockets=1,cores=1,threads=1 -uuid 283c6653-9981-9396-efb4-fb864d87f769 -no-user-config -nodefaul
...
root@kvm:~#
 	   

В зависимости от самого типа планировщика операций ввода/ вывода, сетевого драйвера имеющейся libvirt, а также настроек оперативной памяти общая производительность всех виртуальных машин может очень значительно варьироваться. Прежде чем вносить какие- либо изменения во все упомянутые ранее компоненты, важно понимать тот тип деятельности ОС конкретного гостя, который будет выполняться на нём. Регулировки самого хоста и ОС гостя для нагрузок с интенсивным использованием памяти будут отличаться от нагрузок, связанных с вводом/ выводом или ЦПУ.

Поскольку все экземпляры KVM всего лишь обычные процессы Linux, можно применять драйвер QEMU к любым их следующих контроллеров cgroup (Control Group, Групп управления): cpuset, cpu, memory и blkio, а также к еонтроллерам устройств. Использование контроллеров cgroup предоставляет более гранулированное управление над доступными ЦПУ, оперативной памятью, а также ресурсами ввода, что мы и собираемся более подробно рассмотреть в последующих рецептах.

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

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

Регулировка ядра для снижения латентности ввода/ вывода

В данном рецепте мы собираемся рассмотреть некоторые методики оптимизации производительности имеющихся дисков посредством выбора некоторого расписания операций ввода/ вывода и регулирования блочного ввода/ вывода с применением Групп управления (cgroup) Linux для всех виртуальных гостей и самого хоста.

Существуют три планировщика ввода/ вывода для выбора его в ОС самого хоста или в определённом экземпляре KVM:

  • noop: Это один из самых простых планировщиков ядра; она работает посредством помещения всех поступающих операций ввода/ вывода в простую очередь FIFO (First In, First Out, Первый пришёл, первый исполнен). Данный планировщик полезен когда ОС данного хоста не пытается переупорядочивать запросы операций ввода/ вывода при работе множества виртуальных машин.

  • deadline: Данный планировщик накладывает ограничение времени жизни на все операции ввода/ вывода для предотвращения блокировок запросов на неопределённое время, выставляя приоритеты на запросы чтения, поскольку блокируемыми обычно являются процессы операций чтения.

  • cfq:Основной целью CFQ (Completely Fair Queuing, Полностью справедливой очереди) является максимальное общее использование ЦПУ при разрешении наилучшей производительности взаимодействия.

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

В качестве основного правила, выбирайте планировщик noop для всех гостевых ОС, что позволит гипервизору их хоста лучше оптимизировать все запросы операций ввода/ вывода, так как он осведомлён обо всех запросах, поступающих от своих виртуальных гостей. Однако, если используемое машинами KVM хранилище это тома iSCSI или любое другое удалённое хранилище, такое как GlusterFS, применение планировщика deadline может дать лучшие результаты.

В самых современных ядрах Linux планировщиком по умолчанию является deadline и этого может быть достаточно для хостов, на которых исполняется множество виртуальных машин KVM. Как и с любой другой регулируемой системой, при изменении своих планировщиков на основном хосте и в гостевых ОС необходимы выполнять тестирование.

Приготовление

Для данного рецепта нам понадобится следующее:

  • Некий хост Ubuntu с установленными и настроенными на нём libvirt и QEMU

  • Какая- то работающая виртуальная машина KVM

Как это сделать...

Чтобы изменить свой планировщик операций ввода/ вывода на самом хосте и в определённом экземпляре KVM, а также установить веса операций ввода/ вывода выполните следующие шаги:

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

    
    root@kvm:~# cat /sys/block/sda/queue/scheduler
    noop deadline [cfq]
    root@kvm:~#
     	   
  2. Измените текущий планировщик ввода вывода на требуемый и убедитесь что он применяется и работает:

    
    root@kvm:~# echo deadline > /sys/block/sda/queue/scheduler
    root@kvm:~# cat /sys/block/sda/queue/scheduler
    noop [deadline] cfq
    root@kvm:~#
     	   
  3. Чтобы сделать эти изменения постоянными между перезагрузками сервера добавьте следующие строки в свои настройки GRUB по умолчанию и обновите их:

    
    root@kvm:~# echo 'GRUB_CMDLINE_LINUX="elevator=deadline"' >> /etc/default/grub
    root@kvm:~# tail -1 /etc/default/grub
    GRUB_CMDLINE_LINUX="elevator=deadline"
    root@kvm:~# update-grub2
    Generating grub configuration file ...
    Found linux image: /boot/vmlinuz-3.13.0-107-generic
    Found initrd image: /boot/initrd.img-3.13.0-107-generic
    done
    root@kvm:~# cat /boot/grub/grub.cfg | grep elevator
     linux /boot/vmlinuz-3.13.0-107-generic root=/dev/md126p1 ro elevator=deadline rd.fstab=no acpi=noirq noapic cgroup_enable=memory swapaccount=1 quiet
     linux /boot/vmlinuz-3.13.0-107-generic root=/dev/md126p1 ro elevator=deadline rd.fstab=no acpi=noirq noapic cgroup_enable=memory swapaccount=1 quiet
     linux /boot/vmlinuz-3.13.0-107-generic root=/dev/md126p1 ro recovery nomodeset elevator=deadline
    root@kvm:~#
     	   
  4. Для выбранного экземпляра KVM установите на постоянной основе планировщик операций ввода/ вывода noop:

    
    root@kvm:~# virsh console kvm1
    Connected to domain kvm1
    Escape character is ^]
    root@kvm1:~# echo 'GRUB_CMDLINE_LINUX="elevator=noop"' >>>> /etc/default/grub
    root@kvm1:~# tail -1 /etc/default/grub
    GRUB_CMDLINE_LINUX="elevator=noop"
    root@kvm1:~# update-grub2
    Generating grub configuration file ...
    Found linux image: /boot/vmlinuz-3.13.0-107-generic
    Found initrd image: /boot/initrd.img-3.13.0-107-generic
    done
    root@kvm1:~# cat /boot/grub/grub.cfg | grep elevator
     linux /boot/vmlinuz-3.13.0-107-generic root=/dev/md126p1 ro elevator=noop rd.fstab=no acpi=noirq noapic cgroup_enable=memory swapaccount=1 quiet
     linux /boot/vmlinuz-3.13.0-107-generic root=/dev/md126p1 ro elevator=noop rd.fstab=no acpi=noirq noapic cgroup_enable=memory swapaccount=1 quiet
     linux /boot/vmlinuz-3.13.0-107-generic root=/dev/md126p1 ro recovery nomodeset elevator=noop
    root@kvm1:~#
     	   
  5. Установите некий вес 100 для данного экземпляра KVM, воспользовавшись контроллером cgroup blkio:

    
    root@kvm:~# virsh blkiotune --weight 100 kvm
    
    root@kvm:~# virsh blkiotune kvm
    weight : 100
    device_weight :
    device_read_iops_sec:
    device_write_iops_sec:
    device_read_bytes_sec:
    device_write_bytes_sec:
    
    root@kvm:~#
     	   
  6. Найдите необходимую иерархию каталога cgroup в данном хосте:

    
    root@kvm:~# mount | grep cgroup
    none on /sys/fs/cgroup type tmpfs (rw)
    systemd on /sys/fs/cgroup/systemd type cgroup (rw,noexec,nosuid,nodev,none,name=systemd)
    root@kvm:~#
     	   
  7. Убедитесь что данная cgroup для текущего экземпляра KVM содержит тот вес, который мы установили ранее на своём контроллере blkio:

    
    root@kvm:~# cat /sys/fs/cgroup/blkio/machine/kvm.libvirt-qemu/blkio.weight
    100
    root@kvm:~#
     	   
[Замечание]Замечание

Для более подробного объяснения того, как работают cgroups Linux обратитесь к книге Packt publishing Containerization with LXC {Прим. пер.: или её переводу у нас Контейнеризация при помощи LXC}.

Как это работает...

Мы можем увидеть какой планировщик операций ввода/ вывода нашего ядра в настоящее время применяется, опросив имеющийся файл scheduler в данной виртуальной файловой системе /sys. На шаге 1 мы обнаружили, что это планировщик cfq. Затем, на шаге 2, мы осуществили замену данного планировщика ввода/ вывода в работающей системе. Имейте в виду, пожалуйста, что изменение данного планировщика по запросу как в этом случае, не сохранится после перезагрузки сервера. На шагах 3 и 4 мы изменили свои настройки GRUB путём добавления о новом планировщике для операций загрузчика самого ядра. Перезапуск данного сервера или конкретной виртуальной машины теперь выберет этот новый планировщик ввода/ вывода.

При работе множества виртуальных машин в одном и том же хосте может оказаться полезным предоставление большего приоритета некоторым из них на основе определённого критерия, такого как время дня и загруженность ВМ. На шаге 5 мы воспользовались контроллером cgroup blkio для установки веса данной гостевой KVM. Меньший вес даст лучший приоритет операций ввода/ вывода. На шагах 6 и 7 мы можем обнаружить, что была создана правильная иерархия cgroup, а файл blkio.weight содержит новое значение веса, которое мы установили с помощью команды virsh.

Регулировки памяти для гостевых KVM

Когда дело доходит до регулировок оперативной памяти гостевых KVM имеются доступными несколько вариантов в зависимости от имеющихся на данной виртуальной машине рабочей нагрузки. Одной из таких опций является HugePage.

Большинство хостов Linux по умолчанию для адресации памяти применяют сегменты по 4кБ, имеющие название страниц (pages). Однако, имеющееся ядро в состоянии применять страницы большего размера. использование HugePage (страниц более 4кБ) может улучшить производительность за счёт роста попаданий в имеющийся кэш ЦПУ в сравнении с TLB (Transaction Lookaside Buffer, буфером быстрого преобразования адреса). Такой TLB является кэшированием памяти, в котором сохраняются последние трансляции виртуальной памяти в физические адреса для быстрой выборки.

В данном рецепте мы собираемся разрешить и установить HugePage в своём гипервизоре определённой гостевой KVM, а затем проверим все отрегулированные опции, которые предоставит команда virsh.

Приготовление

Для данного рецепта нам понадобится следующее:

  • Некий хост Ubuntu с установленными и настроенными на нём libvirt и QEMU

  • Какая- то работающая виртуальная машина KVM

Как это сделать...

Чтобы разрешить и установить в своём гипервизоре и определённой гостевой KVM HugePages, а также применить команду virsh для установки различных опций памяти выполните приводимую ниже последовательность:

  1. Проверьте текущие установки HugePage в ОС данного хоста:

    
    root@kvm:~# cat /proc/meminfo | grep -i huge
    AnonHugePages: 509952 kB
    HugePages_Total: 0
    HugePages_Free: 0
    HugePages_Rsvd: 0
    HugePages_Surp: 0
    Hugepagesize: 2048 kB
    root@kvm:~#
     	   
  2. Подключитесь к гостевой KVM и проверьте её текущие установки HugePage:

    
    root@kvm1:~# cat /proc/meminfo | grep -i huge
    HugePages_Total: 0
    HugePages_Free: 0
    HugePages_Rsvd: 0
    HugePages_Surp: 0
    Hugepagesize: 2048 kB
    root@kvm1:~#
     	   
  3. Увеличьте соответствующий размер имеющегося пула HugePage с 0 на 25000 в данном гипервизоре и проверьте следующим образом:

    
    root@kvm:~# sysctl vm.nr_hugepages=25000
    vm.nr_hugepages = 25000
    root@kvm:~# cat /proc/meminfo | grep -i huge
    AnonHugePages: 446464 kB
    HugePages_Total: 25000
    HugePages_Free: 24484
    HugePages_Rsvd: 0
    HugePages_Surp: 0
    Hugepagesize: 2048 kB
    root@kvm:~# cat /proc/sys/vm/nr_hugepages
    25000
    root@kvm:~#
     	   
  4. Проверьте поддерживает ли ЦПУ данного гипервизора размеры HugePage 2МБ и 1ГБ {Прим. пер.: см. Флаги /proc/cpuinfo}:

    
    root@kvm:~# cat /proc/cpuinfo | egrep -i "pse|pdpe1" | tail -1
    flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm arat epb xsaveopt pln pts dtherm tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid
    root@kvm:~#
     	   
  5. Установите размер HugePage 1ГБ изменив настройку GRUB по умолчанию и перезагрузившись:

    
    root@kvm:~# cat /etc/default/grub
    ...
    GRUB_CMDLINE_LINUX_DEFAULT="rd.fstab=no acpi=noirq noapic cgroup_enable=memory swapaccount=1 quiet hugepagesz=1GB hugepages=1"
    ...
    root@kvm:~# update-grub
    Generating grub configuration file ...
    Found linux image: /boot/vmlinuz-3.13.0-107-generic
    Found initrd image: /boot/initrd.img-3.13.0-107-generic
    done
    root@kvm:~# cat /boot/grub/grub.cfg | grep -i huge
     linux /boot/vmlinuz-3.13.0-107-generic root=/dev/md126p1 ro elevator=deadline rd.fstab=no acpi=noirq noapic cgroup_enable=memory swapaccount=1 quiet hugepagesz=1GB hugepages=1
     linux /boot/vmlinuz-3.13.0-107-generic root=/dev/md126p1 ro elevator=deadline rd.fstab=no acpi=noirq noapic cgroup_enable=memory swapaccount=1 quiet hugepagesz=1GB hugepages=1
    root@kvm:~# reboot
     	   
  6. Установите необходимый пакет HugePages:

    
    root@kvm:~# apt-get install hugepages
     	   
  7. Проверьте текущий размер HugePage:

    
    root@kvm:~# hugeadm --pool-list
     Size Minimum Current Maximum Default
     2097152 25000 25000 25000 *
    root@kvm:~#
     	   
  8. Разрешите поддержку HugePage для KVM:

    
    root@kvm:~# sed -i 's/KVM_HUGEPAGES=0/KVM_HUGEPAGES=1/g' /etc/default/qemu-kvm
    root@kvm:~# root@kvm:~# /etc/init.d/libvirt-bin restart
    libvirt-bin stop/waiting
    libvirt-bin start/running, process 16257
    root@kvm:~#
     	   
  9. Смонтируйте виртуальную файловую систему HugeTable в ОС данного хоста:

    
    root@kvm:~# mkdir /hugepages
    root@kvm:~# echo "hugetlbfs /hugepages hugetlbfs mode=1770,gid=2021 0 0" >> /etc/fstab
    root@kvm:~# mount -a
    root@kvm:~# mount | grep hugepages
    hugetlbfs on /hugepages type hugetlbfs (rw,mode=1770,gid=2021)
    root@kvm:~#
     	   
  10. Измените имеющиеся настройки для данной гостевой KVM и разрешите HugePages:

    
    root@kvm:~# virsh destroy kvm1
    Domain kvm1 destroyed
    root@kvm:~# virsh edit kvm1
    ...
    <memoryBacking>
    <hugepages/>
    </memoryBacking>
    ...
    Domain kvm1 XML configuration edited.
    
    root@kvm:~# virsh start kvm1
    Domain kvm1 started
    
    root@kvm:~#
     	   
    [Совет]Совет

    Если при запуске данного экземпляра KVM вы обнаруживаете такую ошибку: error: internal error: hugetlbfs filesystem is not mounted or disabled by administrator config (файловая система hugetlbfs не смонтировалась или запрещена в настройках администратора), проверьте что данная виртуальная файловая система успешно смонтировалась на шаге 9.

    [Совет]Совет

    Если при запуске данного экземпляра KVM вы обнаруживаете такую ошибку: error: internal error: process exited while connecting to monitor: file_ram_alloc: can't mmap RAM pages: Cannot allocate memory (выход процесса при подключении к монитору ... нет возможности выделения памяти), на шаге 3 вам необходимо увеличить рахмер пула HugePages.

  11. Обновите жёсткий предел памяти для данного экземпляра KVM и выполните проверку следующим образом:

    
    root@kvm:~# virsh memtune kvm1
    hard_limit : unlimited
    soft_limit : unlimited
    swap_hard_limit: unlimited
    
    root@kvm:~# virsh memtune kvm1 --hard-limit 2GB
    
    root@kvm:~# virsh memtune kvm1
    hard_limit : 1953125
    soft_limit : unlimited
    swap_hard_limit: unlimited
    
    root@kvm:~#
     	   

Как это работает...

Libvirt и KVM выполняют поддержку и получают преимущество от HugePages. Но вам стоит быть осведомлённым о том, что не все рабочие нагрузки выиграют при наличии страниц с размером, большим чем установлен по умолчанию. Те экземпляры, которые исполняют базы данных и связанные с памятью экземпляры KVM являются хорошими вариантами применения. Как и всегда, прежде чем разрешить данное свойство, замерьте производительность вашего приложения внутри данной виртуальной машины чтобы гарантировать что он выиграет от HugePages.

В данном рецепте мы разрешаем HugePages в своём хосте и определённой гостевой ОС и устанавливаем некий жёсткий предел на используемую этим гостем память. Давайте пройдём по эти шагам более внимательно.

На шагах 1 и 2 мы проверяем текущее состояние HugePages. Из вывода мы можем увидеть, что в настоящее время нет выделенного пула для размещения HugePages, о чём сообщает поле HugePages_Total

На шаге 3 мы увеличиваем размер пула HugePages до 25000. Это изменение осуществляется по запросу и не сохраняется при перезагрузке сервера. Чтобы сделать его неизменным, вы можете добавить его в свой файл /etc/sysctl.conf.

Чтобы применять данное свойство HugePage, нам необходимо удостовериться, что данный ЦПУ нашего сервера хоста имеет аппаратную поддержку для них, что отображается флагами pse и pdpe1, как это показывается на шаге 4.

На шаге 5 мы настраиваем начальный загрузчик GRUB на запуск своего ядра с поддержкой HugePages и устанавливаем для них размер 1ГБ.

Хотя мы можем работать напрямую с файлами, выставляемыми нашей виртуальной файловой системой /proc, на шаге 6 мы устанавливаем пакет HugePages, который предоставляет несколько полезных инструментов пространства пользователя для просмотра и управления различными установками памяти. Мы применяем команду hugeadm на шаге 7 для просмотра размера своего пула HugePages.

Чтобы включить поддержку HugePages для KVM, мы обновляем файл /etc/default/qemu-kvm на шаге 8, монтируем необходимую файловую систему для них на шаге 9 и, наконец, перенастраиваем данную виртуальную машину KVM на применение HugePages добавив строфу <hugepages/> для своего объекта <memoryBacking>.

Libvirt предоставляет удобный способ управления объёмом выделяемой памяти для гостевых KVM. На шаге 11 мы устанавливаем жёсткий предел в 2ГБ для своей виртуальной машины kvm1.

Опции производительности ЦПУ

Существует несколько методов для управления ЦПУ и всеми доступными частотами ЦПУ для машин KVM с применением cgroups, а также для libvirt с предоставлением прикрепления ЦПУ и функций родства (affinity), которые мы собираемся исследовать в данном рецепте. Родство (affinity) ЦПУ является свойством планировщика, которое подключает некий процесс к заданному множеству ЦПУ на данном хосте.

При предоставлении виртуальных машин с помощью libvirt, поведение по умолчанию состоит в предоставлении гостей на все доступные ядра ЦПУ. В некоторых случаях NUMA (Non-Uniform Memory Access , архитектура Неоднородного доступа к памяти) является хорошим примером того, что когда нам необходимо назначить некоторое Неоднородного доступа к памяти) является хорошим примером того, что когда нам необходимо назначить некоторое ядро для экземпляра KVM (как мы это собираемся увидеть в следующем рецепте), тогда будет лучше назначать эту виртуальную машину некоторому определённому ядру ЦПУ. Так как каждая виртуальная машина KVM является неким процессом ядра, (конкретно для наших примеров qemu-system-x86_64), мы можем сделать это при помощи таких инструментов как команда taskset или virsh. Также мы можем воспользоваться подсистемеой ЦПУ cgroups для управления выделением частот ЦПУ, которые производят более гранулированное управление для ресурсов использования на каждую виртуальную машину.

Приготовление

Для данного рецепта нам понадобится следующее:

  • Некий хост Ubuntu с установленными и настроенными на нём libvirt и QEMU

  • Какая- то работающая виртуальная машина KVM

Как это сделать...

Чтобы прикрепить некую виртуальную машину KVM к определённому ЦПУ и изменить все доли ЦПУ (shares) сделайте следующее:

  1. Получите информацию обо всех доступных ядрах ЦПУ в данном гипервизоре:

    
    root@kvm:~# virsh nodeinfo
    CPU model: x86_64
    CPU(s): 40
    CPU frequency: 2593 MHz
    CPU socket(s): 1
    Core(s) per socket: 10
    Thread(s) per core: 2
    NUMA cell(s): 2
    Memory size: 131918328 KiB
    
    root@kvm:~#
     	   
  2. Извлеките информацию о выделении ЦПУ для данной гостевой KVM:

    
    root@kvm:~# virsh vcpuinfo kvm1
    VCPU: 0
    CPU: 2
    State: running
    CPU time: 9.1s
    CPU Affinity: yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
    
    root@kvm:~#
     	   
  3. Прикрепите ЦПУ экземпляра KVM (VCPU: 0) к соответствующему первому ЦПУ гипервизора (CPU: 0) и отобразите результат:

    
    root@kvm:~# virsh vcpupin kvm1 0 0 --live
    
    root@kvm:~# virsh vcpuinfo kvm1
    VCPU: 0
    CPU: 0
    State: running
    CPU time: 9.3s
    CPU Affinity: y---------------------------------------
    
    root@kvm:~#
     	   
  4. Выдайте список всех долей времени исполнения, которые назначены некоторому экземпляру KVM:

    
    root@kvm:~# virsh schedinfo kvm1
    Scheduler : posix
    cpu_shares : 1024
    vcpu_period : 100000
    vcpu_quota : -1
    emulator_period: 100000
    emulator_quota : -1
    
    root@kvm:~#
     	   
  5. Измените вес текущего ЦПУ исполняющейся виртуальной машины:

    
    root@kvm:~# virsh schedinfo kvm cpu_shares=512
    Scheduler : posix
    cpu_shares : 512
    vcpu_period : 100000
    vcpu_quota : -1
    emulator_period: 100000
    emulator_quota : -1
    
    root@kvm:~#
     	   
  6. Проверьте эти доли ЦПУ в соответствующей подсистеме cgroups ЦПУ:

    
    root@kvm:~# cat /sys/fs/cgroup/cpu/machine/kvm1.libvirt-qemu/cpu.shares
    512
    root@kvm:~#
     	   
  7. Проверьте и обновите определение XML экземпляра:

    
    root@kvm:~# virsh dumpxml kvm1
    ...
     <vcpu placement='static'>1</vcpu>
     <cputune>
       <shares>512</shares>
       <vcpupin vcpu='0' cpuset='0'/>
     </cputune>
    ...
    root@kvm:~#
     	   

Как это работает...

Мы начали с получения информации об имеющихся ресурсах ЦПУ, доступных в данном гипервизоре. Из вывода на шаге 1 мы можем видеть, что ОС данного хоста имеет 40 ЦПУ в одном сокете.

На шаге 2 мы собираем информацию о всех ЦПУ виртуальных машин и их сходству с ЦПУ данного хоста. В данном примере наша гостевая KVM имеет один виртуальный ЦПУ, обозначаемый как запись VCPU: 0 и родство со всеми 40 процессорами гипервизора, что отображено полем CPU Affinity: yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy.

На шаг 3 м прикрепляем/ привязываем данный виртуальный ЦПУ к определённому первому физическому процессору в своём гипревизоре. Отметим изменение вывода соответствующего родства: CPU Affinity: y---------------------------------------..

Из вывода команды virsh на 4 шаге мы можем ознакомиться с тем, что все выделенные данному экземпляру KVM доли ЦПУ установлены в значение 1024. Это назначение является соотношением, которое означает что если другой гость имеет 512 долей, он будет иметь вдвое меньше времени исполнения ЦПУ чем экземпляр с 1024 долями. Мы уменьшаем это значение на шаге 5.

На шагах 6 и 7 мы подтверждаем что все доли ЦПУ были верно установлены в соответствующих подсистемах cgroups в их ОС хоста. Как уже упоминалось ранее, доли ЦПУ настроены при помощи cgroups и могут регулироваться напрямую или предоставляемой libvirt функциональностью через её команду virsh.

Регулировки NUMA с применением libvirt

NUMA является некоторой технологией, которая позволяет имеющейся системной памяти подразделяться на зоны, также именуемые узлами. Эти узлы NUMA затем выделяются определённым ЦПУ или сокетам. В противовес обычному монолитному подходу к оперативной памяти, при котором всякий ЦПУ/ всякое ядро могут осуществлять доступ ко всей оперативной памяти вне зависимости от её расположения, что обычно выражается в больших задержках, ограниченные NUMA процессы могут осуществлять доступ к памяти которая является локальной для конкретно того ЦПУ, на котором она будет исполняться. В большинстве случаев это намного быстрее чем для памяти, подключаемой к удалённому ЦПУ в данной системе.

Libvirt применяет библиотеку libnuma чтобы включить функциональность NUMA для виртуальных машин, что мы можем увидеть так:


root@kvm:~# ldd /usr/sbin/libvirtd | grep numa
libnuma.so.1 => /usr/lib/x86_64-linux-gnu/libnuma.so.1 (0x00007fd12d49e000)
root@kvm:~#
 	   

Libvirt NUMA поддерживает следующие политики выделения памяти для размещения виртуальных машин в узлах NUMA:

  • strict: Размещение будет оклонено если данная память не может быть выделена на данном целевом узле

  • interleave: Страницы памяти выделяются карусельным (round- robin) образом

  • preferred: Данная политика позволяет гипервизору предоставлять память из других узлов в случае, когда для определённого узла недостаточно доступной памяти

В данном рецепте мы собираемся разрешить доступ NUMA для некоторого экземпляра KVM и изучить его воздействие на общую производительность системы.

Приготовление

Для данного рецепта нам понадобится следующее:

  • Некий хост Ubuntu с установленными и настроенными на нём libvirt и QEMU

  • Какая- то работающая виртуальная машина KVM

  • Утилита numastat

Как это сделать...

Чтобы разрешить некоторой виртуальной машине KVM исполняться в некотором заданном узле NUMA, а ЦПУ применять строгую политику NUMA выполните следующие шаги:

  1. Установите пакет numactl проверьте свою настройку оборудования данного гипервизора:

    
    root@kvm:~# apt-get install numactl
    ...
    root@kvm:~# numactl --hardware
    available: 2 nodes (0-1)
    node 0 cpus: 0 1 2 3 4 5 6 7 8 9 20 21 22 23 24 25 26 27 28 29
    node 0 size: 64317 MB
    node 0 free: 3173 MB
    node 1 cpus: 10 11 12 13 14 15 16 17 18 19 30 31 32 33 34 35 36 37 38 39
    node 1 size: 64509 MB
    node 1 free: 31401 MB
    node distances:
    node 0 1
     0: 10 21
     1: 21 10
    root@kvm:~#
     	   
  2. Отобразите текущее размещение NUMA для определённой гостевой KVM:

    
    root@kvm:~# numastat -c kvm1
    
    Per-node process memory usage (in MBs) for PID 22395 (qemu-system-x86)
                Node 0    Node 1    Total
                ------    ------    -----
    Huge             0         0        0
    Heap             1         1        2
    Stack            2         2        4
    Private         39        21       59
    -------     ------    ------    -----
    Total           42        23       65
    root@kvm:~#
     	   
  3. Измените определение XML данного экземпляра, установите его режим памяти в strict и выберите конкретно второй узел NUMA (индексация начинается с 0, поэтому наш второй узел NUMA помечен как 1), затем перезапустите своего гостя:

    
    root@kvm:~# virsh edit kvm1
    ...
    <vcpu placement='static' cpuset='10-11'>2</vcpu>
    <numatune>
     <memory mode='strict' nodeset='1'/>
    </numatune>
    ...
    Domain kvm1 XML configuration edited.
    
    root@kvm:~# virsh destroy kvm1
    Domain kvm1 destroyed
    
    root@kvm:~# virsh start kvm1
    Domain kvm1 started
    
    root@kvm:~#
     	   
  4. Получите все параметры NUMA для данного экземпляра KVM:

    
    root@kvm:~# virsh numatune kvm1
    numa_mode : strict
    numa_nodeset : 1
    
    root@kvm:~#
     	   
  5. Выведите все текущее родство виртуальных ЦПУ:

    
    root@kvm:~# virsh vcpuinfo kvm1
    VCPU: 0
    CPU: 11
    State: running
    CPU time: 8.4s
    CPU Affinity: ----------yy----------------------------
    
    VCPU: 1
    CPU: 10
    State: running
    CPU time: 0.3s
    CPU Affinity: ----------yy----------------------------
    
    root@kvm:~#
     	   
  6. Выведите размещение данного узла NUMA для нашего экземпляра KVM:

    
    root@kvm:~# numastat -c kvm1
    
    Per-node process memory usage (in MBs) for PID 22395 (qemu-system-x86)
                Node 0    Node 1    Total
                ------    ------    -----
    Huge             0         0        0
    Heap             0         3        3
    Stack            0         2        2
    Private          0       174      174
    -------     ------    ------    -----
    Total            0       179      179
    root@kvm:~#
     	   

Как это работает...

Мы начинаем с опроса установок NUMA в ОС своего хоста. Из вывода команды numactl на 1 шагу мы можем обнаружить, что наш гипервизор имеет два узла NUMA: узел 0 и узел 1. Каждый узел управляет неким списком ЦПУ. В данном случае узел 1 NUMA содержит ЦПУ с 10 по 19 и с 30 по 30, а также содержит 64ГБ памяти. Это означает, что 64ГБ оперативной памяти предназначено для локалбного использования этими ЦПУ и доступ к этой памяти с этих ЦПУ предполагается намного более быстрым чем с ЦПУ, которые являются частью узла 0. Чтобы улучшить показатели задержек памяти для некоторой гостевой KVM, нам необходимо прикреплять те виртуальные ЦПУ, которые назначены данной виртуальной машине на ЦПУ, которые являются частью одного и того же узла NUMA.

На шаге 2 мы можем увидеть что все экземпляры KVM применяют память из обоих узлов NUMA, что далеко от идеальности.

На шаге 3 мы изменяем определение XML данного гостя и прикрепляем этогогостя к 10му и 11му ЦПУ, которые являются частью единого узла 1 NUMA, применяя параметр cpuset='10-11'. Мы также определяем режим strict узла NUMA и свой второй узел NUMA в общем параметре <memory mode='strict' nodeset='1'/>.

После перезапуска этого экземпляра, на шаге 4, мы убеждаемся что данная гостевая KVM теперь работает с применением в узле 1 режима NUMA strict. Мы также убеждаемся, что прикрепление этого ЦПУ именно такое, как мы на самом деле указали на шаге 5. Отметим, что имеющееся родство ЦПУ отмечено 10м и 11м элементами из всех элементов родства ЦПУ.

Из вывода на шаге 6 мы можем увидеть, что данная гостевая KVM теперь использует память только из узла 1 NUMA, чего мы и добивались.

Если вы исполняете приложения с интенсивно используемой памятью до и после данных регулировок NUMA и выполните тестирование, вы скорее всего обнаружите серьёзный прирост производительности когда осуществите доступ к большим объёмам памяти внутри данной гостевой KVM благодаря локальности самого ЦПУ и оперативной памяти, предоставляемых NUMA.

Также ознакомьтесь...

В данном рецепте мы увидели примеры того как вручную назначать некий процесс KVM какому- то узлу NUMA путём изменения соответствующего XML определения данного гостя. Некоторые дистрибутивы Linux, такие как RHEL/CentOS 7 и Ubuntu 16.04 предоставляют службу numad (демон NUMA), целью которой является автоматическая балансировка процессов между узлами NUMA путём мониторинга текущей топологии памяти:

  1. Чтобы установить эту службу в Ubuntu 16.04 выполните:

    
    root@kvm:~# lsb_release -a
    No LSB modules are available.
    Distributor ID: Ubuntu
    Description: Ubuntu 16.04.2 LTS
    Release: 16.04
    Codename: xenial
    root@kvm2:~# apt install numad
    ...
    root@kvm:~#
     	   
  2. Для запуска этой службы исполните такоой код:

    
    root@kvm:~# service numad start
    root@kvm2:~# pgrep -lfa numad
    12601 /usr/bin/numad -i 15
    root@kvm:~#
     	   
  3. Для управления определённой гостевой KVM при помощи numad передайте идентификатор процесса данного экземпляра KVM:

    
    root@kvm:~# numad -S 0 -p $(pidof qemu-system-x86_64)
    root@kvm:~#
     	   
  4. Данная служба зарегистрирует все попытки ребалансировки NUMA:

    
    root@kvm:~# tail /var/log/numad.log
    Thu May 25 21:06:42 2017: Changing THP scan time in /sys/kernel/mm/transparent_hugepage/khugepaged/scan_sleep_millisecs from 10000 to 1000 ms.
    Thu May 25 21:06:42 2017: Registering numad version 20150602 PID 12601
    Thu May 25 21:09:25 2017: Adding PID 4601 to inclusion PID list
    Thu May 25 21:09:25 2017: Scanning only explicit PID list processes
    root@kvm:~#
     	   

    Данная служба numad может оказаться полезной в узлах OpenStack {Прим. пер.: а также Proxmox и т.п.}, в которых ручная балансировка NUMA может оказаться слишком запутанной.

Регулировки ядра для повышения производительности сети

Большинство современных ядер Linux снабжены достаточными регулировками для различных сетевых рабочих нагрузок. Некоторый дистрибутивы предоставляют предварительно определённые службы регулировок (хорошим примером является tuned для Red Hat/CentOS), которые включают некий набор профилей на основе роли данного сервера.

Давайте пошагово пройдём весь процесс, происходящий при передаче и приёме данных в обычном хосте Linux, прежде чем мы зароемся в то как регулировать сам гипервизор:

  1. Определённое приложение вначале записывает свои данные в некий сокет, который по очереди помещается в имеющиеся буферы передачи.

  2. Само ядро инкапсулирует эти данные в некий PDU (Protocol Data Unit , Протокольный блок данных).

  3. Этот PDU затем перемещается в соответсвующую очередь передачи для каждого устройства.

  4. Имеющийся драйвер NIC (Network Interface Cards, Плат сетевого интерфейса) затем извлекает этот PDU из очереди передачи и копирует его в NIC.

  5. NIC отправляет эти данные и возбуждает аппаратное прерывание.

  6. С другой стороны данного коммуникационного канала соответствующая NIC получает этот кадр, копирует его в свой буфер приёма и возбуждает аппаратное прерывание.

  7. Соответствующее ядро по очереди обрабатывает все прерывания и возбуждает некое программное прерывание для обработки данного пакета.

  8. Наконец, это ядро обрабатывает данное программное прерывание и перемещает данный пакет верх по имющемуся стеку TCP/IP для его деинкапсуляции и помещения результата в буфер прийма для процесса считывания из нго.

В данном рецепте мы собираемся обследовать несколько практических приёмов для регулировок имеющегося ядра Linux, как правло, имеющих результатом лучшую сетевую производительность для хостов KVM со множеством арендаторов.

[Замечание]Замечание

Убедитесь, пожалуйста, что вы определили некий базовый уровень прежде чем приступите к выполнению каких бы то ни было изменений, предварительно измерив производительность данного хоста. Делайте небольшие изменения приращениями, затем замеряйте полученное воздействие вновь. Все примеры в данном рецепте не подразумевают того что вы просто скопирует и вставите их без предварительного осмысления возможных положительных или отрицательных воздействий, которые они могут сделать. Применяйте представленные примеры как некое руководство к тому что может быть настроено - реальные значения необходимо тщательно рассматривать в каждом конкретном случае, основываясь на типе имеющегося у вас сервера и всём окружении.

Приготовление

Для данного рецепта нам понадобится следующее:

  • Некий хост Ubuntu с установленными и настроенными на нём libvirt и QEMU

  • Какая- то работающая виртуальная машина KVM

Как это сделать...

Для регулировки имеющегося ядра с целью улучшения сетевой производительности осуществите следующие этапы (для дополнительной информации о том что регулируется в самом ядре прочтите раздел Как это работает...):

  1. Увеличьте размер буфера сокета приёма и отправки TCP:

    
    root@kvm:~# sysctl net.core.rmem_max
    net.core.rmem_max = 212992
    root@kvm:~# sysctl net.core.wmem_max
    net.core.wmem_max = 212992
    root@kvm:~# sysctl net.core.rmem_max=33554432
    net.core.rmem_max = 33554432
    root@kvm:~# sysctl net.core.wmem_max=33554432
    net.core.wmem_max = 33554432
    root@kvm:~#
     	   
  2. Увеличьте пределы своих буферов TCP: минимальное, значение по умолчанию, а также максимальное число байт. Установите максимум в 16МБ для 1GE NIC и 32МБ или 54МБ для 10GE NIC:

    
    root@kvm:~# sysctl net.ipv4.tcp_rmem
    net.ipv4.tcp_rmem = 4096 87380 6291456
    root@kvm:~# sysctl net.ipv4.tcp_wmem
    net.ipv4.tcp_wmem = 4096 16384 4194304
    root@kvm:~# sysctl net.ipv4.tcp_rmem="4096 87380 33554432"
    net.ipv4.tcp_rmem = 4096 87380 33554432
    root@kvm:~# sysctl net.ipv4.tcp_wmem="4096 65536 33554432"
    net.ipv4.tcp_wmem = 4096 65536 33554432
    root@kvm:~#
     	   
  3. Убедитесь что масштабирование окна TCP разрешено:

    
    root@kvm:~# sysctl net.ipv4.tcp_window_scaling
    net.ipv4.tcp_window_scaling = 1
    root@kvm:~#
     	   
  4. Чтобы помочь увеличению пропускной способности TCP для 1G NIC и выше, увеличьте имеющуюся длину своей очереди передачи этого сетевого интерфейса. Для путей с более чем 50 мс RTT рекомендуется значение 5000 - 10000:

    
    root@kvm:~# ifconfig eth0 txqueuelen 5000
    root@kvm:~#
     	   
  5. Снизьте установленное значение tcp_fin_timeout:

    
    root@kvm:~# sysctl net.ipv4.tcp_fin_timeout
    net.ipv4.tcp_fin_timeout = 60
    root@kvm:~# sysctl net.ipv4.tcp_fin_timeout=30
    net.ipv4.tcp_fin_timeout = 30
    root@kvm:~#
     	   
  6. Уменьшите имеющееся значение tcp_keepalive_intvl:

    
    root@kvm:~# sysctl net.ipv4.tcp_keepalive_intvl
    net.ipv4.tcp_keepalive_intvl = 75
    root@kvm:~# sysctl net.ipv4.tcp_keepalive_intvl=30
    net.ipv4.tcp_keepalive_intvl = 30
    root@kvm:~#
     	   
  7. Разрешите быстрый повтор циклов сокетов TIME_WAIT. Установленным по умолчанию значением является 0 (отключено):

    
    root@kvm:~# sysctl net.ipv4.tcp_tw_recycle
    net.ipv4.tcp_tw_recycle = 0
    root@kvm:~# sysctl net.ipv4.tcp_tw_recycle=1
    net.ipv4.tcp_tw_recycle = 1
    root@kvm:~#
     	   
  8. Разрешите повторное использование сокетов в их состоянии TIME_WAIT для новых соединений. Установленным по умолчанию значением является 0 (отключено):

    
    root@kvm:~# sysctl net.ipv4.tcp_tw_reuse
    net.ipv4.tcp_tw_reuse = 0
    root@kvm:~# sysctl net.ipv4.tcp_tw_reuse=1
    net.ipv4.tcp_tw_reuse = 1
    root@kvm:~#
     	   
  9. Начиная с версии ядра 2.6.13, Linux поддерживает встраиваемые алгоритмя управления перегруженностью. Применяемый определённый алгоритм управления перегруженностью устанавливается с использованием переменной sysctl net.ipv4.tcp_congestion_control, которая по умолчанию в Ubuntu установлена в значения bic/cibic. Для получеия списка доступных в вашем ядре алгоритмов управления перегруженностью (если вы применяете 2.6.20 или выше), выполните следующее:

    
    root@kvm:~# sysctl net.ipv4.tcp_available_congestion_control
    net.ipv4.tcp_available_congestion_control = cubic reno
    root@kvm:~#
     	   
  10. Чтобы сделать доступными дополнительные подключаемые алгоритмы управления перегруженностью загрузите следующие доступные модули ядра:

    
    root@kvm:~# modprobe tcp_htcp
    root@kvm:~# modprobe tcp_bic
    root@kvm:~# modprobe tcp_vegas
    root@kvm:~# modprobe tcp_westwood
    root@kvm:~# sysctl net.ipv4.tcp_available_congestion_control
    net.ipv4.tcp_available_congestion_control = cubic reno htcp bic vegas westwood
    root@kvm:~#
     	   
  11. Для длинных, быстрых путей обычно лучше применять алгоритмы cubic или htcp. Cubic является установленным по умолчанию для целого ряда дистрибутивов Linux, однако если он не является значением по умолчанию в вашей системе, вы можете сделать следующее:

    
    root@kvm:~# sysctl net.ipv4.tcp_congestion_control
    net.ipv4.tcp_congestion_control = cubic
    root@kvm:~#
     	   
  12. Если ваш гипервизор перенасыщен соединениями SYN, следующие опции могут помочь уменьшить воздействие этого:

    
    root@kvm:~# sysctl net.ipv4.tcp_max_syn_backlog
    net.ipv4.tcp_max_syn_backlog = 2048
    root@kvm:~# sysctl net.ipv4.tcp_max_syn_backlog=16384
    net.ipv4.tcp_max_syn_backlog = 16384
    root@kvm:~# sysctl net.ipv4.tcp_synack_retries
    net.ipv4.tcp_synack_retries = 5
    root@kvm:~# sysctl net.ipv4.tcp_synack_retries=1
    net.ipv4.tcp_synack_retries = 1
    root@kvm:~#
     	   
  13. Наличие достаточного числа доступных дескрипторов файлов крайне важно, так как достатосно многое из всего что есть в Linux является неким файлом. Каждое сетевое соединение применяет некий файловый дескриптор/ сокет. Чтобы проверить ваши текущие максимаольное и доступное значения файловых дескрипторов исполните такой код:

    
    root@kvm:~# sysctl fs.file-nr
    fs.file-nr = 1280 0 13110746
    root@kvm:~#
     	   
  14. Чтлбы увеличить значение максимума файловых дескрипторов выполните это:

    
    root@kvm:~# sysctl fs.file-max=10000000
    fs.file-max = 10000000
    root@kvm:~# sysctl fs.file-nr
    fs.file-nr = 1280 0 10000000
    root@kvm:~#
     	   
  15. Если ваш гипервизор применяет правила iptables с учётом состояния соединения, тогда ваш модуль ядра nf_conntrack может исчерпывать память для отслеживания соединений и будет зарегистрирована некая ошибка: nf_conntrack: table full, dropping packet (таблица переполнена, пакет отвергнут). Чтобы поднять этот предел и тем самым выделить болше памяти, вам необходимо вычислить сколько каждое соединение использует оперативной памяти. Вы можете получить эту информацию из файла proc /proc/slabinfo. Значение записи nf_conntrack показывает число активных записей, насколько велик каждый объект и сколько умещается в листе (slab, каждый лист умещается в одной или более страниц ядра, которая обычно 4кБ, если не используется HugePage). Ведя учёт накладных расходов имеющегося размера страниц ядра, вы можете увидеть из slabinfo, что каждый объект nf_conntrack потребляет примерно 316 байт (это значение может быть разным в разных системах). Поэтому для отслеживания 1М соединений вам понадобится выделить премерно 316 МБ памяти:

    
    root@kvm:~# sysctl net.netfilter.nf_conntrack_count
    net.netfilter.nf_conntrack_count = 23
    root@kvm:~# sysctl net.netfilter.nf_conntrack_max
    net.netfilter.nf_conntrack_max = 65536
    root@kvm:~# sysctl -w net.netfilter.nf_conntrack_max=1000000
    net.netfilter.nf_conntrack_max = 1000000
    root@kvm:~# echo 250000 > /sys/module/nf_conntrack/parameters/hashsize # hashsize = nf_conntrack_max / 4
    root@kvm:~#
     	   

Как это работает...

На шаге 1 мы увеличили максимальное значение буферов сокетов отправки и приёма. Это выделит больше памяти для имеющегося стека TCP, однако в серверах с большим объёмом памяти и значительным числом соединений TCP это будет гарантировать, что данные размеры буферов будут достаточными. Хорошей начальной точкой для выбора значения по умолчанию является BDP (Bandwidth Delay Product, Произведение полосы пропускания и задержки), основывающийся на измерении задержки, например, умножьте значение полосы пропускания данного соединения на среднее время пути с возвратом до некоторого хоста.

На шаге 2 мы увеличиваем минимальное, по умолчанию и максимальное значения байт, используемых TCP для регулировки размерами буфера отправки. TCP динамически выравнивает этот размер своего буфера отправки от установленного по умолчанию значения.

На шаге 3 мы убеждаемся что разрешено окно масштабирования. Окно масштабирования TCP автоматически увеличивает соответствующий размер окна приёма.

[Замечание]Замечание

Для получения дополнительной информации по окну масштабирования, пожалуйста, обратитесь к https://en.wikipedia.org/wiki/TCP_window_scale_option {Прим. пер.: см. также Какие параметры влияют на производительность приложений? Часть 1. TCP Window Size.}

На шаге 5 мы понижаем значение tcp_fin_timeout, которое определяет сколько секунд ждать некий окончательный пакет FIN прежде чем данный сокет будет принудительно закрыт. На шагах 6 и 7 мы уменьшаем значение в секундах между пробами TCP keep-alive и быстрым повтором циклов сокетов в соответствующем состоянии TIME_WAIT.

Чтобы освежить в памяти.ю приводимая ниже схема отображает различные состояния соединения TCP, в котором оно может находиться:

 

Рисунок 1


Диаграмма состояний TCP

На шаге 8 мы разрешаем повторное использование сокетов в состоянии TIME_WAIT только для новых соединений. В хостах с большим числом экземпляров KVM это может иметь существенное воздействие на то, насколько быстро может устанавливаться новое соединение.

На шагах 9 и 10 мы разрешаем различные алгоритмы управления перегрузкой (заторами: congestion control algorithms). Сам выбор алгоритмов управления перегруженностью выполняется при построении ядра системы. На шаге 11 мы выбираем алгоритм cubic, при котором данное окно является некоторой кубической зависимостью от времени с последнего события затора, причём точка перегиба устанавливается на то окно, которое предшествовало этому событию.

[Замечание]Замечание

Для получения дополнительной информации по сетевым алгоритмам заторов- предотвращения обратитесь, пожалуйста к https://en.wikipedia.org/wiki/TCP_window_scale_option {Прим. пер.: см. также Какие параметры влияют на производительность приложений? Часть 1. TCP Window Size.}

В системах испытывающих перенасыщение количество имеющихся запросов SYN может помочь регулирование значения максимального число запросов соединений в очереди, которые всё ещё не получили подтверждения от клиента соединения, с применением опций tcp_max_syn_backlog и tcp_synack_retries. Мы делаем это на шаге 12.

На шагах 13 и 14 мы увеличиваем значение максимального числа дескрипторов файлов во всей системе. Это помогает, когда присутствует большое число сетевых соединений, так как каждое соединение требует некоторого файлового дескриптора.

На самом последнем шаге мы имеем дело с опцией nf_conntrack_max. Это полезно если мы отслеживаем соединения в своём гипервизоре при помощи модуля ядра nf_conntrack.