Оптимизация NFS

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

Конфигурация сервера:

Улучшать будем в три этапа:

  1. Общее для клиента и сервера
  2. Серверная часть
  3. Клиентская часть

1 Общее для клиента и сервера

1.1 Параметры ядра

Первым делом подправим настройки ядра для улучшения производительности как с клиентской, так и с серверной стороны. Есть такая штука как буфер сокета. Такой буфер есть на входящие и исходящие запроси. Так как RPC запросов на запись и считывание может быть много, и если CPU или диск не успевает все обработать, то мы храним все запросы в маленькой очереди сокета, чтобы не потерялись. Из документации по ядру:

Размер буфера будем выставлять в соотношении 1 MiB на каждый 1 GB оперативной памяти. Нужные параметры выставляются следующим образом.

root@nfs:~# sysctl -w net.core.rmem_default=67108864
или
root@nfs:~# echo 67108864 > /proc/sys/net/core/rmem_default

root@nfs:~# sysctl -w net.core.rmem_max=67108864
или
root@nfs:~# echo 67108864 > /proc/sys/net/core/rmem_max

root@nfs:~# sysctl -w net.core.wmem_default=67108864
или
root@nfs:~# echo 67108864 > /proc/sys/net/core/wmem_default

root@nfs:~# sysctl -w net.core.wmem_max=67108864
или
root@nfs:~# echo 67108864 > /proc/sys/net/core/wmem_max 

Таким образом размер буфера сокетов обновиться в реальном времени (на лету). Чтобы параметры сохранились после ребута, нужно добавить их в автозагрузку.

root@nfs:~# cat /etc/sysctl.conf
…
net.core.wmem_max = 67108864
net.core.rmem_max = 67108864
net.core.wmem_default = 67108864
net.core.rmem_default = 67108864
…

Также нужно увеличить общую очередь всех входящих пакетов, UDP и TCP буфер для всех сокетов. Из документации ядра:

Vector of 3 INTEGERs: min, pressure, max (in pages). Number of pages allowed for queueing by all UDP sockets.
min: Below this number of pages UDP is not bothered about its memory appetite. When amount of memory allocated by UDP exceeds this number, UDP starts to moderate memory usage.
pressure: This value was introduced to follow format of tcp_mem.
max: Number of pages allowed for queueing by all UDP sockets.
Defaults are calculated at boot time from amount of available memory.

Vector of 3 INTEGERs: min, pressure, max (in pages).
min: below this number of pages TCP is not bothered about its memory appetite.
pressure: when amount of memory allocated by TCP exceeds this number of pages, TCP moderates its memory consumption and enters memory pressure mode, which is exited when memory consumption falls under «min».
max: number of pages allowed for queueing by all TCP sockets.
Defaults are calculated at boot time from amount of available memory.

Maximum number of packets, queued on the INPUT side, when the interface receives packets faster than kernel can process them.

tcp_mem и udp_mem измеряется в страницах памяти. Размер одной страницы – 4096 байт. По умолчанию, он большой, но можно увеличить на 10-20%, а netdev_max_backlog можно увеличить в 3 раза.

root@nfs:~# sysctl -w net.ipv4.udp_mem="1549836 2066449 3099672"
или
root@nfs:~# echo “1549836 2066449 3099672” > /proc/sys/net/ipv4/udp_mem

root@nfs:~# sysctl -w net.ipv4.tcp_mem="1549836 2066449 3099672"
или
root@nfs:~# echo “1549836 2066449 3099672” > /proc/sys/net/ipv4/tcp_mem

root@nfs:~# sysctl -w net.core.netdev_max_backlog=3000
или
root@nfs:~# echo 3000 > /proc/sys/net/core/netdev_max_backlog

Т.е. min = 1549836 pages = 1549836 x 4096 byte = 6348128256 byte = ~5.9 GB.
После, нужно добавить все в автозагрузку. После чего, мы должны получить следующею картину.

root@nfs:~# cat /etc/sysctl.conf
…
net.core.wmem_max = 67108864
net.core.rmem_max = 67108864
net.core.wmem_default = 67108864
net.core.rmem_default = 67108864
net.ipv4.udp_mem = "1549836 2066449 3099672"
net.ipv4.tcp_mem = "1549836 2066449 3099672"
net.core.netdev_max_backlog = 3000
…

1.2 Сетевой стек

1.2.1 Ring buffer (Driver queue)

Рассмотрим схему сетевого стека, которую мы будем улучшать (рис. 1), чтобы понять, как вообще происходить передача данных через сетевую карту. Между IP стеком (IP stack) и контроллером сетевой карты располагается “очередь драйвера” (Driver queue). Что также называют FIFO ring buffer. В этой очереди хранятся указатели на упомянутые выше буферы сокетов ядра (SKB), которые в свою очередь уже хранят сами пакетные данные. В IP стеке в свою очередь храниться очередь IP пакетов. Аппаратный драйвер опустошает очередь IP стека и по шине данных отправляет информацию через NIC дальше. Driver queue – это простая first-in, first-out очередь где все запросы идут и обрабатываются последовательно (в принципе, что и должна делать сетевая карта быстро и просто). Но между IP стеком и этой очередью есть еще специальный слой для управления трафиком (Queueing discipline – Qdisc), а именно:

Здесь как раз выставляются биты TOS. Тюнить его мы не будем, но это тоже возможно.

NFS_tune

Рисунок 1 — Схема сетевого стека

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

root@nfs:~# ethtool -g eth0
Ring parameters for eth0:
Pre-set maximums:
RX:             4096
RX Mini:        0
RX Jumbo:       0
TX:             4096
Current hardware settings:
RX:             256
RX Mini:        0
RX Jumbo:       0
TX:             256

Это означает, что драйвер этой сетевой карты может держать в очереди не больше 4096 SKB-дескрипторов. Этот параметр нужно улучшать, если NIC драйвер не поддерживает BQL(Byte Queue Limits). BQL появился начиная с ядра 3.3 для автоматического вычисления размера очереди драйвера. BQL тюнить не надо, его максимальный лимит можно узнать следующим образом.

root@nfs:~# find /sys/ -name 'byte_queue_limits'
root@nfs:~# cat cat /sys/devices/pci0000:00/0000:00:1c.0/0000:06:00.0/net/eth0/queues/tx-0/byte_queue_limits/limit_max
1879048192

Как видно, это 1.8GB (этот параметр измеряется уже в байтах).
Для случаев, когда нет поддержки BQL, очередь драйвера улучшается следующим способом.

root@nfs:~# ethtool -G eth0 tx 4096
root@nfs:~# ethtool -G eth1 tx 4096
root@nfs:~# ethtool -G eth2 tx 4096
root@nfs:~# ethtool -G eth4 tx 4096 
root@nfs:~# ethtool -G eth0 rx 4096
root@nfs:~# ethtool -G eth1 rx 4096
root@nfs:~# ethtool -G eth2 rx 4096
root@nfs:~# ethtool -G eth4 rx 4096

Тут было улучшено очередь для всех сетевых интерфейсов по входящему и исходящему трафику.

1.2.2 MTU

Как мы знаем, у сетевых карт есть максимальный размер передаваемого блока данных (MTU). По умолчанию он становит 1500 байт. Если на уровни приложений какой-то апликейшн отправляет пакет размером 5КB в буфер сокета, то ІР стеку этот пакет нужно будет разбить на 4 IP пакета и передать на сетевую карту через очередь драйвера (т.е. 1500 х 4 = 6КВ и 1КВ лишних данных). Чтобы избежать данного overhead-a в линуксе имплементировано:

Посмотреть значение этих параметров можно так:

root@nfs:~# ethtool -k eth0 | grep -E 'tcp-segmentation-offload|udp-fragmentation-offload|generic-segmentation-offload'
tcp-segmentation-offload: on
udp-fragmentation-offload: off [fixed]
generic-segmentation-offload: on

Все это позволяет ІР стеку создавать пакеты размер которых превышает MTU (IPv4 maximum = 65536 байт) и добавить их в очередь драйвера, где уже самой NIC карте предоставляется роль разбиения пакетов на нужный размер. И в этом случаи мы можем ей помочь, увеличив размер MTU используя Jumbo Frames до 9КВ.

root@nfs:~# ifconfig eth0 mtu 9000
root@nfs:~# ifconfig eth1 mtu 9000
root@nfs:~# ifconfig eth2 mtu 9000
root@nfs:~# ifconfig eth3 mtu 9000

Данные команды увеличат размер MTU до 9000. Теперь проверяем новый размер MTU и также добавляем изменения в настройку интерфейсов.

root@nfs:~# netstat -i | awk '{print $1,$2}'
Kernel Interface
Iface MTU
eth0 9000
eth1 9000
eth2 9000
eth4 9000

root@nfs:~# vim /etc/network/interfaces
...
iface xxx inet static
   ...
   mtu 9000
   ...
...

Но хочу заметить, что если сервер соединен с клиентом через сетевой свитч/роутер, то Jambo frames должен также быть включен на сетевом оборудовании, иначе возникнут проблемы с сетью.

2 Серверная часть

Чисто серверная часть заключается в улучшении NFSd плис предыдущие все настройки ядра и сети. Исходя из Solaris NFS Server Performance and Tuning Guide for Sun Hardware количество nfsd демонов (потоков) должно быть равным:

Добавить nfsd демонов можно следующими способами (20 nfsd/CPU).

root@sto:~# rpc.nfsd 640     
root@sto:~# echo 640 > /proc/fs/nfsd/threads
root@sto:~# vim /etc/default/nfs-kernel-server
…
RPCNFSDCOUNT=640
…

Первые две команды добавят nfsd процессов на лету (в реальном времени) без перезагрузки nfsd демона. Последняя команда добавить конфигурацию для автостарта nfsd, чтобы применить эту настройку, нужно перезагрузить nfsd демон.

3 Клиентская часть

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

root@client:~# mount -o rw,soft,nfsvers=3,intr,bg,timeo=100,retrans=8,rsize=1048576,wsize=1048576 sto:/data01 /mnt/sto

Все эти флаги описаны в предыдущей статье. Если коротко, здесь мы монтируем NFS v3 шару в режиме soft с интервалом таймаута запросов в 10 секунд с 8-ю попытками подключения до сервера. Максимальный размер для NFS запросов на считывания и записи становит 1048576 (1MiB).
Если используется Jambo Frames, то rsize и wsize можно поставить 9000 байт, и тогда каждый клиентский NFS запрос будет передаваться по сети без фрагментации.
Хочу заметить, что даже если вы указали в опциях монтирования самый большой размер для запросов, то при монтировании, ядро само обрежет до максимального, который оно может поддерживать.

root@client:~# cat /proc/mounts | grep sto
sto:/data01 /mnt/sto nfs rw,relatime,vers=3,rsize=65536,wsize=65536,namlen=255,soft,proto=tcp,timeo=100,retrans=8,sec=sys,mountaddr=sto,mountvers=3,mountport=47879,mountproto=udp,local_lock=none,addr=x.x.x.x 0 0
или
root@client:~# nfsstat -m

Манипуляции с timeo и retrains (увеличивать значения) нужно проводить если вы получаете ошибки вида:

kernel: nfs: server < system name > not responding, timed out 

Или видите, что retrans счетчик начал стремительно расти:

root@client:~# nfsstat -rc
Client rpc stats:
calls      retrans    authrefrsh
281447039   12930      281455555

На этом все. В следующей статье пойдет речь о мониторинги и логировании в NFS.

Автор: admin, 21 ноября 2016
Рубрики: Linux
Метки: ,

Написать комментарий

Последние статьи

Яндекс.Метрика
?>