Комментарии 23
Подписываюсь под каждым словом Владимира.
Когда мы в Озоне запиливали поддержку эксклюзивных ядер для виртуалок с Postgresql также столкнулись с проблемой того, что активность от вирт шарила ядра с сетевой активностью и были проблемы. Обнаружили это на одном из сервисов, где транзакции в базке были всего 0.2-0.3 мс, а общая нагрузка на кучу шардов - порядка 2.5 млн tps при сетевом трафике 12 ГБ/c в пике - тут уж совсем не забалуешь с тем на каких ядрах и как запускать виртуалки.
В приведённом примере машинка с двумя процессорами, по 24 физических / 48 гипертредовых ядер на сокет — итого 96 логических ядер, нумерованных от 0 до 95. Две гипертредовых пары мы вынесли под сетевые прерывания — ядра 1 и 49, 2 и 50. 0,3-48,51-95
— это просто свёрнутое представление всех остальных ядер на этой машинке. На каком-нибудь твоём сервере или виртуалки нужные числа будут другие — нужно смотреть топологию конкретной железки
Огромное спасибо за статью.
Правильно ли я понял, что вы взяли первую попавшуюся пару HT ядер относящихся к одному физ ядру в кол-ве 2 шт (1, 49 и 2, 50), после cpu0? Т.е. это с тем же успехом могла бы быть и пара 3, 51 и 23,71, например. Или это какие-то "специальные" ядра под прерывания? Иными словами, не очень понял, как определился маппинг
ядра должны принадлежать процессору, на PCI-линиях которого сидит сетевая карта
расчехляем tcpdump на обоих концах, оставляем работать до появления в логах проблемных trace_id
А как отлавливали проблемные трейсы? Получается, нужно каждый трейс автоматически проверять на "гепы"? Понятно, что как-то это решается, но интересно вдруг есть элегантное решение, чем просто просто перебирать спаны?
Приложение должно требовать процессорное время не реже раза в 10 миллисекунд
А с чем связано ограничение в 10 мс?
А как отлавливали проблемные трейсы? Получается, нужно каждый трейс автоматически проверять на "гепы"? Понятно, что как-то это решается, но интересно вдруг есть элегантное решение, чем просто просто перебирать спаны?
Все трейсы, разумеется, проверять не нужно — тестовое приложение-клиент писало в логи trace_id только аномально длинных запросов. Эти trace_id искались в wireshark с помощью простого совпадения по строке (find packet -> string), а вся сессия находилась через follow -> tcp stream
А с чем связано ограничение в 10 мс?
Только с тем, что приложение было написано так, чтобы отправлять запрос раз в десять миллисекунд. Цифра в 10мс была выбрана достаточно большой, чтобы отделить от штатно отработавших запросов-ответов (1мс), но в то же время не сильно большой, чтобы отловить "микрозатыки".
Отдельно перебирать не нужно. У нас нет семплирования поэтому АБСОЛЮТНО ВСЕ спаны прилетают в коллекторы. В коллекторе они в любом случае обрабатываются тк их нужно приклеить в один трейс. Внутри просто строятся две гистограммы по длительности спанов от сервера и по спанам от клиента. Потом вычитается одно из другого и получается результат.
Спасибо за пост.
Можно было ли добиться такого же результата используя утилиту https://github.com/strizhechenko/netutils-linux ?
Пробежался глазами по ридми — да, утилита может раскидать прерывания лесенкой. Но всё равно нужно будет прокинуть флажки --banirq
— номера прерываний для которых до SCM ещё нужно как-то доставить. procfs и статистика с сетевой карты в диагностике не сильно помогут, к сожалению.
Q: I see that workload is distributed fine, but there is a lot of workload. How to go deeper, how to understand what my system doing right now?
A: Tryperf top
С этим спорить невозможно — в знании того, когда именно следует воспользоваться одним из этих инструментов, и состоит немалая часть работы SRE / сисадмина
Я ставил на дисковые задержки. Но, возможно, это суть следующей статьи :)
Это, разумеется, была одна из гипотез — но проблема наблюдалась даже у stateless-сервисов, которые не пишут логов.
Не только из-за логов возникает iowait. Там от trim операций до свопа куча вариантов.
Спасибо за статью. Было очень интересно, даже когда она была ещё тикетом в внутренем таск-трекере. Для наших многослойных сервисов срезало 100-200 мс в q0.99.
Хайлоад пробивает все абстракции, и приходится учитывать даже такие детали.
Очень интересный материал, про привязку к ядрам всяких JVM видел, а вот про особенности K8S нет. Спасибо.
Попробую усложнить задачку. Было бы очень интересно услышать Ваше мнение.
В большинстве случаев , которые я видел и не могу на них влиять, ваша K8S нода работает внутри VM от VMware или т.п. с share физическими ядрами из пула.
Доступа к гипервизору нет, и увидеть те же настоящие "off-cpu от Грегга" не получится, на мой взгляд. Вернее когда гипервизор не дал самой VM времени поработать CPU. Посадить служебные процессы гостевого kernel можно по такой же методике внутри VM, но они же приземлятся на vCPU и в любом случае будет мешанина из потоков всех виртуалок и самого гипервизора на уровне физ. CPU.
И как замерить шумность облачной среды не понятно имея доступ только во внутрь гостевой ОС (VM)
В большинстве случаев, которые я видел и не могу на них влиять, ваша K8S нода работает внутри VM от VMware или т.п. с share физическими ядрами из пула
Я правильно понимаю, что речь про условные 4*50%vCPU-виртуалки? Т.е. четыре виртуальных ядра, но гарантируется только 0.5с процессорного времени на ядро в секунду? Такие виртуалки да, для критичного к задержкам трафика использовать не рекомендуется — и предпочтительнее взять машинку 2*100%vCPU с гарантированными ядрами. Серьёзные cloud-провайдеры не будут запускать на этих ядрах ничего, что не относится к вашей виртуалке. Далее путь уже зависит от сетевого стека конкретного cloud-провайдера.
Яндекс.Облако, например, может вынести сетевой оверхед на отдельные ядра, которые не видны из гостевой системы: https://cloud.yandex.ru/docs/compute/concepts/software-accelerated-network
AWS может прокинуть виртуальную функцию физической сетевой карты: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/enhanced-networking.html
Словом, облачные провайдеры максимально прозрачны, и в случае необходимости могут сделать вам быструю сеть.
И как замерить шумность облачной среды не понятно имея доступ только во внутрь гостевой ОС (VM)
В случае VMWare — начать с обновления ядра гостя. В 2020 году Алексей Махалов и Томер Зельцер добавили поддержку steal time:
https://lore.kernel.org/all/202003211432.57Hu5M7W%25lkp@intel.com/T/
Да, все верно понимаете. Про возможность получать steal time от VMware по аналогии с KVM не слышал, спасибо за наводку. Поизучаю.
Шумность облака по CPU я пробовал измерять "сажая" потоки на ядра, периодически включая и выключая их с замером разности (есть похожие проекты), но вот вроде бы в kernel 5.14 появился OSNOISE Tracer с "HW noise" счетчиком, но все руки не доходят поизучать, вернее доступов нет из-за вездесущих облаков :) Интересно как это коррелируется со steal time полученным от VMware... По идее, этот счетчик должен быть легко доступен без всяких дебагов ядра или т.п.
Смотрели ли в сторону TCP Nagle's algorithm? в случае с синтетическими тестами и мелкими сообщениями бывает делает погоду
Отличный момент!
Нет, не смотрели в силу используемого стека. Да, в Linux алгоритм Нейгла используется по умолчанию и для отключения нужно нужно явно вызывать setsockopt с TCP_NODELAY. В Go, тем не менее, TCP-сокеты по умолчанию создаются с флагом TCP_NODELAY:
https://github.com/golang/go/blob/release-branch.go1.18/src/net/tcpsock.go#L206-L224
https://github.com/golang/go/blob/release-branch.go1.18/src/net/tcpsockopt_posix.go#L15
Каноническая имплементация gRPC на C++ также проставляет TCP_NODELAY:
https://github.com/grpc/grpc/blob/master/src/core/lib/iomgr/socket_utils_common_posix.cc#L234-L249
https://github.com/grpc/grpc/blob/136055b043dcf2b15f69f535f659e4090cf25b9f/src/core/lib/iomgr/tcp_server_utils_posix_common.cc#L177
https://github.com/grpc/grpc/blob/136055b043dcf2b15f69f535f659e4090cf25b9f/src/core/lib/iomgr/tcp_client_posix.cc#L79
Наверняка пошли бы копать в эту сторону, если бы tcpdump не показал, что каждая из сторон считала, что отсылает свою часть работы вовремя. Но это была бы уже другая статья :)
Куда уходит время? Боремся за миллисекунды в Kubernetes