Q: FreeBSD-FreeBSD remote shell speed

Уважаемые FreeBSD-веды!

у меня в подполе происходит Я гоняю zfs snapshots с ящика на ящик таким примерно способом:

zfs send .... | mbuffer -s 64k -m 64m | ssh -c aes128-gcm@openssh.com box2 zfs recv

И между двумя ящиками на i3 все происходит хорошо: ssh с этим шифром бегает на скорости 900+MB/sec, реально zfs recv может принять 450-500 и я всем доволен.

Но на Atom C3758 все не так радужно: одно ядро медленнее раза в 4 и ssh -c aes128-gcm@openssh.com дает мегабайт 300 в секунду, одно ядро выжирается целиком.

Думал, что дело в шифровании. Поставил openssh-portable c NONECIPHER, те же ~300 мегабайт/сек.

Посоветовали rsh. Долго вспоминал как настраивать, лет 15 не касался, настроил, ~300 MB/sec.

Запускаю так (ssh/rsh - одинаково по смыслу):

$ dd if=/dev/zero bs=1G count=5 | mbuffer -s 64k -m 64m | rsh 10.5.0.3 mbuffer -q -s 64k -m 64m -o /dev/null<br>
        in @&nbsp; 282 MiB/s, out @&nbsp; 282 MiB/s, 5046 MiB total, buffer 100% full5+0 records in<br>
        5+0 records out<br>
        5368709120 bytes transferred in 17.227217 secs (311641109 bytes/sec)

Удаление mbuffer с посылающей стороны ничего не меняет.

При этом с mbuffer на mbuffer все хорошо:

$ dd if=/dev/zero bs=1G count=50 | mbuffer -4 -O 10.5.0.3:530 -s 64k -m 64m<br>
        in @ 1180 MiB/s, out @ 1180 MiB/s, 49.8 GiB total, buffer 100% full50+0 records in<br>
        50+0 records out<br>
        53687091200 bytes transferred in 49.435914 secs (1085993701 bytes/sec)

(c принимающей стороны там через inetd:

courier stream  tcp     nowait  root    /usr/local/bin/mbuffer  mbuffer -q -o /dev/null -s 64k -m 64m

На интерфейсах с обоих сторон одинаково:

a # ifconfig ix0
ix0: flags=8843 metric 0 mtu 9000     options=e404bb RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,JUMBO_MTU,VLAN_HWCSUM,LRO,VLAN_HWTSO,RXCSUM_IPV6,TXCSUM_IPV6 

Вопрос: что бы такое потюнить (у ssh, openssh-portable, rsh, FreeBSD) чтобы ну хотя-бы мегабайт 500 в секунду получить?

 

Comments

> что бы такое потюнить (у ssh, openssh-portable, rsh
Лучше исключить ssh из цепочки пересылки данных, даже с выключенным шифрованием.
Замена mbuffer на nc (netcat) на обеих концах может ощутимо поднять скорость. Были прецеденты.

Тут такая история
1) ssh (и с шифрованием и без) работает быстрее rsh
2) mbuffer - mbuffer выжирает нормально почти всю полосу, ну там не 10 гигабит а 8, мне достаточно.

Проблема в том, что мне нужен именно remote shell, чтобы ошибки с удаленного конца ловить.

То есть конечно, если rsh починить не получится, придется mbuffer - mbuffer или netcat, но не хотелось бы

> Проблема в том, что мне нужен именно remote shell, чтобы ошибки с удаленного конца ловить.

Ммм, а в чём проблема с ошибками?

s=/var/run/send.success
rm $f && zfs send | { ssh remotehost zfs recv && touch $s; }
if [ -e $s ]; then
success
else
failure
fi

zfs recv примет данные откуда? C stdin? Ну вот со скоростью подачи через ssh и есть проблема.

Ну то есть наверное вы имели в виду "на удаленном хвосте запустить mbuffer -I | zfs recv && touch

Ну да, так можно, онанизм но можно.

Нет, я имел в виду именно то, что написал. Если удалённый zfs recv вернёт ошибку, то ssh её вернёт тоже и локальный touch не исполнится. И какая разница - проверять код возврата сложной команды или простого test -e localfile ? Суть одна и решение полностью корректное.

Ну подождите: zfs send | { ssh remotehost zfs recv && touch $s; }
Это именно кормление zfs recv данными через медленный (в данном случае) ssh.

Оно у меня так сейчас примерно и работает, но проблема в том, что ssh на новом ящике - медленный.

Это я понимаю.

Я не понял, отчего вот это было сказано? "Проблема в том, что мне нужен именно remote shell, чтобы ошибки с удаленного конца ловить." Под remote shell тут понимается именно ssh? Он не имеет проблем с передачей признака ошибки, как впрочем и rsh.

Я имел в виду вот что:
Если я пускаю zfs send | ssh remote zfs recv
То где бы оно не сломалось, тут, там, посередине - достаточно проверить один код ошибки, если он не 0, то клеим ласты.

А если же мы к примеру запускаем как-то так (синтаксис условный, могу напутать с кавычками)
ssh remote "nс -l port | zfs recv" & # я кстати сходу даже не знаю можно ли так, но почему собственно и нет....
sleep 1;
zfs send | nc remote port

То вся логика обработки ошибок, количества мест их возникновения и проч - сильно усложняется в сравнении с one-liner.

А если я для сохранения 1-liner на своей стороне - на удаленную сторону привешу zfs recv в inetd.conf - то скрипты опять 1-liner, но вот обработка ошибок еще более усложняется.

Теперь понятно.

Я, кстати, сейчас припомнил, что для определенных комбинаций скорости CPU источника и приёмника и скорости сети при передаче бекапов мне неплохо помогала компрессия потока при помощи zstd (порт archivers/zstd):

dump ... | nice zstd | ...

На Xeon E5620 2.40GHz оно мне сжимало со скоростью 48 мегабайт в секунду и экономило 37%, сжимая 417G до 263G и время передачи с 5 до 3 часов (там был медленный приёмник).

То что в ежедневном режиме гоняется через zfs send - оно по большей части сжатое (в смысле - сами файлы внутри сжатые/несжимаемые).

Ну и на 10G, то есть гигабайт в секунду, сжимать могут не только лишь все.

И ещё момент касательно zfs send и компресии: когда я развлекался с этой темой пару лет назад, zfs send --compressed ещё не существовало даже в OpenZFS, хотя уже был фичереквест. Тогда для сжатых файловых систем zfs send безусловно распаковывал данные перед отправкой, так что в обратной компрессии перед выпуском в сеть был смысл для сжимаемых данных и незанятых ядер CPU.

Нынче уже есть zfs send -c и оно сжатые блоки данных посылает в сжатом виде, но вот блоки с метаданными оно AFAIK таки разжимает перед отправкой и если метаданных много... Можно потестировать.

У меня данные, за редчайшими исключениями, плохо сжимаемые. Соответственно, zfs-компрессия не используется.

Я бы и для плохо сжимаемых данных включал lz4. Впрочем для данного процессора лучше проверить.

lz4 в смысле latency вовсе не бесплатный - если данные не жмутся, лучше не надо

Если мне не изменяет память, если блок не жмётся то пишется не жатый и читается соответственно. Если удалось хоть немного сжать, то с диска будет читаться меньше секторов, что может компенсировать время затраченное на декомпрессию, поэтому с latency не всё так однозначно.

А не возникает ли там проблем на пустом месте от небольших сжатий? Ну там блоки стали не известного размера, а переменного?

А с чего им возникнуть? Блоки по дизайну могут быть любого размера. Помнится у тебя в конфигурации raidz2 (4+2) два диска при чтении часто простаивали. А если бы блоки были разные - читалось бы со всех. Плюсов достаточно и они понятны, а из минусов на ум приходит только использование на очень медленных процессорах.

Ну считая что ядро у этого нового атома - в 4 раза медленнее, чем у i3/3.3Ghz, там, прикидывая хрен к носу, ~200MB/s на ядро должно получаться. Или больше гига на всех восьми.

Попробую вот на тестовом томе, который 3x6Tb stripe

Достать линейку и померяться - несложно.
Взял я самые сжимаемые файлы, которые у меня есть - нежатые raw (там в каждом 16-битном слове два верхних бита - нули, т.е. на 12% жмется гарантировано, ну а дальше - как кто умеет, zip где-то 80% от полного объема).

Ну и результаты (других файлов на томе нет):
zfs get all | grep compr
ztest compressratio 1.01x -
ztest compression off default
ztest refcompressratio 1.00x -
ztest/test compressratio 1.01x -
ztest/test compression lz4 local
ztest/test refcompressratio 1.01x -

1% - бессмысленно.
Для других данных (из тех что у меня много) будет еще хуже.

Скорость записи, соответственно, ну тоже не растет. При записи 600Mb/sec - 6 процессорных threads каждая жрет половинку CPU по top.

Не настаиваю :-), но упомяну про скрытые плюсы. Очень хорошо жмутся метаданные (пусть их объём и невелик по отношению к данным). Насколько помню, ты используешь 1MB recordsize. Если файл больше 1MB и нет сжатия, то последний блок аллоцирует на диске 1MB. Если предположить, что размеры файлов имеют случайное распределение, то в среднем на хвосте файла можно ожидать выигрыша в 0,5MB на диске и это не отражается в коэффициенте сжатия. Как оказалось, embedded-data block pointers после включения lz4 имеют ощутимую долю запросов (если где-то ещё остался счётчик могу посмотреть % от общего числа запросов в реальной эксплуатации). Ну и по умолчанию арк теперь жмёт блоки данных (lz4) и хранит их в таком виде в памяти. Экономить на процессорном времени не сильно получится :-).

Похоже я когда-то пропустил и метаданные теперь по умолчанию жмутся на диске независимо от установок датасета.

А я опять вляпался в primarycache=metadata (+, если правильно помню, prefetch и/или большой recordsize).

Было опять смешно - 400Mb/s с дисков и меньше 100 уходит в сеть.

Если правильно помню :-), recordsize не соответствует размеру блока передачи по smb. На всякий случай, как-то недавно обнаружил, что primarycache=metadata (и даже none) при send/recieve не спасает от вымывания арка.

Ну меня primarycache=metadata сильно спасло от вымывания ARC (ARC перестал пухнуть с первым же бэкапом - они у меня на отдельный Zpool), но общий результат получился огорчительным, backup verify стал идти в разы дольше.

Если несколько пулов на общий арк и внеший бэкап по самбе тогда конечно имеет смысл. А чтение (верификация) по самбе с primarycache=metadata думаю для приемлемой скорости нужно хорошо согласовать с чтением из пула.

Я не вижу ручек для согласования без общего ухудшения всего, самба то тоже общая (как и ARC).

Нужно, наверное, играть recordsize на бэкапном пуле. Но я вспоминаю, что года 2 или 3 назад я во все это уже играл - и в результате наплевал на вымывание ARC, так оказалось проще всего.

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

Я на бэкапный пул поставил запиливаться два старых OCZ Vertex. Выкинуть жалко, использовать невозможно, пусть падут смертью храбрых уж, заодно посмотрим, сколько это займет.
Пока вместе с data, посмотрю на эффективность, должна быть хорошая, если нет - то только метадату на них отправлю

Метадату я стараюсь держать всю в арке и что бы она оттуда не уходила вообще. Ради интереса проверь какой объём она занимает в арке для всех твоих данных в пуле (без сжатия и с ним). Думаю не пожалеешь :-).

А что значит "стараюсь"? Там же решают за нас.

vfs.zfs.arc_meta_limit определяет максимальный размер метаданных в арке. К сожалению "за нас решили" какой порядок вытеснения записей в арке и он всё таки умудряется вытеснять метаданные даже если их меньше лимита. Но можно закомментировать "ненужное". Я оставил вытеснение метаданных только в случае превышения лимита и при сигнале системы, что мало памяти.

В смысле - понастраивать ядро через vim?

Не, не могу пойтить на это, svn up или все сломает или сам сломается

Сегодня нужно было подойти к линейке. Попробовал заодно освежить память по поводу primarycache=metadata. Пул из четырёх дисков (страйп из двух зеркал). Взял файл с recordsize=128k.
dd if=/data/test/test_32g_128k of=/dev/zero bs=128k
34359738368 bytes transferred in 180.599959 secs (190253301 bytes/sec)
Префетч не работает, получаем прогнозируемую скорость для единичной очереди.
Чтение этого файла по сети эксплорером 300+MB/s, gstat показывает, что суммарная очередь на чтение иногда доходит до 8.
Создал файл с recordsize=1M.
dd if=/data/test/test_32g_1M of=/dev/zero bs=128k
34359738368 bytes transferred in 1284.648935 secs (26746403 bytes/sec)
Хорошо видно, как резко падает скорость, если читаем данные мимо арка и блоками меньше чем размер recordsize.
dd if=/data/test/test_32g_1M of=/dev/zero bs=1M
34359738368 bytes transferred in 162.679963 secs (211210636 bytes/sec)
Так ближе к норме.
Чтение эксплорером 130+MB/s, gstat показывает суммарное чтение с дисков около 180MB/s.
Эксплорер опять "химичит", но это уже не спасает.

Во-первых, dd if=/dev/zero bs=1G это плохая идея (читать крупными кусками из /dev/zero под FreeBSD). Во-вторых (и убедиться в первых) - надо запускать top -SHPI в процессе на обоих системах и глядеть, где узкое место. Ещё полезно в процессе запускать systat -vm 3 и глядеть в правый столбец на количество прерываний.

(по частям буду отвечать) Да, bs=1G - идея не очень, ну то есть 1M - быстрее раза в два. Тем не менее "скорость - достаточная"

# dd if=/dev/zero bs=1G count=100 of=/dev/null
100+0 records in
100+0 records out
107374182400 bytes transferred in 5.003121 secs (21461438177 bytes/sec)
lexa@home-gw:~# dd if=/dev/zero bs=1M count=100000 of=/dev/null
100000+0 records in
100000+0 records out
104857600000 bytes transferred in 2.521523 secs (41585024741 bytes/sec)

20 и 40 GB/sec в таком раскладе. mbuffer понятно тоже жрет от души и с ним  (| mbuffer -o /dev/null) получается 2.5 и 5, но и в этом случае это все на порядок больше той скорости, что мы имеем с rsh/ssh (а dd bs=1G| mbuffer | network | mbuffer - ну 8Gbit дают, в отличие от 2.5 через *sh).

Пошел пускать топы и systat.

Скорость тут достаточная, потому что не приходится параллельно тратить CPU на обслуживание сетевых прерываний и CPU хватает. А когда сеть тоже хочет CPU - картина будет другая.

Ну вот как бы сказать: dd bs=1G | mbuffer | network | mbuffer | devnull - 8гигабит
а dd bs=1G | rsh | network | mbuffer | devnull - 2.5

Сдается мне, дело тут не (только) в dd.

Конечно, не только в dd. Узкое место наверняка CPU, а dd bs=1G только усугубляет проблему, создавая немалый дополнительный вклад в загрузку CPU драйвером /dev/zero, что никак не помогает вычленить основную неоптимизированную подсистему, чтобы её оптимизировать :-)

У меня узкое место - на принимающей стороне.

Вот типичная картина (скриншоты проще получаются в png, пардон)
top на sender: https://www.dropbox.com/s/3issenlt4x833ah/top-rsh-send.png?dl=0
top на receiver: https://www.dropbox.com/s/1r6fva7876mekxf/top-rsh-recv.png?dl=0

На sender делаю так:
# dd if=/dev/zero bs=1M count=10000 | rsh 10.5.0.3 dd of=/dev/null
10000+0 records in
10000+0 records out
10485760000 bytes transferred in 33.885600 secs (309445904 bytes/sec)
20475234+8465 records in
20480000+0 records out
10485760000 bytes transferred in 33.756500 secs (310629359 bytes/sec)

При этом ssh стабильно быстрее (процентов на 10) вместе со всем шифрованием:
# dd if=/dev/zero bs=1M count=5000 | ssh -c aes128-gcm@openssh.com 10.5.0.3 dd of=/dev/null
5000+0 records in
5000+0 records out
5242880000 bytes transferred in 15.304234 secs (342577094 bytes/sec)
10240000+0 records in
10240000+0 records out
5242880000 bytes transferred in 14.986247 secs (349846106 bytes/sec)

Это на принимающей стороне HPN, на передающей - обычный, сейчас попробую HPN patches применить еще.

> У меня узкое место - на принимающей стороне.

Да, так оно обычно и бывает. На принимающей стороне, судя по https://www.dropbox.com/s/1r6fva7876mekxf/top-rsh-recv.png?dl=0 многовато прерываний и просто безобразное время system time, которое объясняется не заданным bs у принимающего dd, от чего он использует дефолтный bs=512 и насилует ядро безумным количеством сисколлов write() - лучше всё же задать bs=1m.

Что касается прерываний на принимающей стороне - не вижу скриншотов systam и я не уловил, какие там сетевые интерфейсы? Если Intel, то максимизировано ли количество приёмных дескрипторов чипа и настроена ли группировка прерываний (interrupt coalescing) под бОльшую нагрузку? Я не пробовал тюнить десятигигабитные интелевские интерфейсы, а насчет гигабитных можно почитать тут: https://dadv.livejournal.com/139170.html

Я что хочу сказать
1) когда я фигачу ssh (360Mb/sec)/rsh (до 330) - я вижу ~40k прерываний в секунду. Типа, много.
если на принимающей стороне увеличить bs у dd, результат не меняется
2) Дальше беру mbuffer:
Sender:
dd if=/dev/zero bs=1M count=50000 | mbuffer -4 -O 10.5.0.3:3333 -s 64k -m 64m
in @ 1180 MiB/s, out @ 1180 MiB/s, 48.3 GiB total, buffer 100% full50000+0 records in
50000+0 records out
52428800000 bytes transferred in 42.463043 secs (1234692477 bytes/sec)

Receiver:
mbuffer -4 -I 3333 -s 64k -m 64m -o /dev/null
in @ 1180 MiB/s, out @ 1180 MiB/s, 48.3 GiB total, buffer 0% full
summary: 48.8 GiByte in 42.5sec - average of 1176 MiB/s

Прерываний 75-85k в секунду.

Теперь уж netcat, раз его все советуют:
dd if=/dev/zero bs=1M count=50000 | nc 10.5.0.3 3333
50000+0 records in
50000+0 records out
52428800000 bytes transferred in 82.591318 secs (634797954 bytes/sec)

mbuffer быстрее.

Из чего я делаю вывод, что дело не в настройках собственно сетевого интерфейса ix (сетевая карта на sender Intel X540, на принимающей Ethernet Connection X553/X557-AT 10GBASE-T, обе 10G). Возможно interrupt moderation помог бы немного, но для 9k-пакетов там количество пакетов на самом деле не очень большое.

Дело, как я думаю, в настройках tcp. Что-то такое mbuffer умеет, чего ssh/rsh не умеют. Может быть тупо большой tcp buffer и не скейлить его.

вдогонку: судя по 98% interrupt при тестах с mbuffer, этот 1.1 гигабайт/сек - близко к пределу, но тем не менее - могет.

> если на принимающей стороне увеличить bs у dd, результат не меняется

Зато будет виднее, кто жрёт CPU, а кто нет.

> Может быть тупо большой tcp buffer и не скейлить его.

mbuffer, судя по его сорцам, по умолчанию ставит размер приёмного буфера в 1 мегабайт (TCP RWIN), а rsh использует системный дефолт:

$ sysctl net.inet.tcp | fgrep recv
net.inet.tcp.recvspace: 65536
net.inet.tcp.recvbuf_max: 2097152
net.inet.tcp.recvbuf_inc: 16384
net.inet.tcp.recvbuf_auto: 1

Вероятно, автоскейлинг тупо не успевает разогнать. Можно попробовать либо поднять первоначальное значение net.inet.tcp.recvspace сразу до мегабайта, либо резко увеличить инкремент net.inet.tcp.recvbuf_inc

No luck:
# sysctl net.inet.tcp | grep space
net.inet.tcp.sendspace: 1048576
net.inet.tcp.recvspace: 1048576
(на обоих концах, конечно)

# dd if=/dev/zero bs=1M count=10000 | ssh -c aes128-gcm@openssh.com 10.5.0.4 dd of=/dev/null bs=1M
10000+0 records in
10000+0 records out
10485760000 bytes transferred in 33.001974 secs (317731297 bytes/sec)

lexa@home-gw:/home/lexa# dd if=/dev/zero bs=1M count=10000 | rsh 10.5.0.4 dd of=/dev/null bs=1M
10000+0 records in
10000+0 records out
10485760000 bytes transferred in 34.201657 secs (306586318 bytes/sec)

Читать tcpdump (гигабайтный) на предмет что там с tcp options происходит и отчего - уже не хочется.

Я сдался, 350Mb/sec меня, на самом деле, устраивают почти.

Прежде чем начинать тюнить - надо определить узкое место. И раз rsh тоже тормозит (а это голый TCP-сокет), то узкое место наверняка не (только) в ssh.

Там как раз и об узких местах.

У нас на линуксах в какой-то момент выяснилось, что rsh теперь тот же ssh...

На FreeBSD не так