NFS подсистема древняя (в смысле богатая историей). Исторически сложилась ситуация, когда она считается базовым функционалом UNIX (а по наследству и Linux, и BSD). Несмотря на то, что NFS, по сути, является отдельной подсистемой вроде HTTP-сервера или иного сервера приложений, конфигурируемого отдельно от несущей операционной системы, ситуация с настройкой NFS несколько иная - для версий "2" и "3" запустить "просто NFS", без корректировки параметров смежных уровней абстракции нереально. С версией NFSv4 ситуация сильно улучшилась (теперь NFS практически автономный сервис), но разработчики излишне, на мой взгляд, озабочены поддержкой обратной совместимости и обеспечением работы подсистемы в условиях эксплуатации оной "дураком", запуская сразу все её составляющие, даже никому не нужные уже лет десять; очень неприятно, знаете ли, знать, что кроме нужного сервиса принимающего подключения по протоколу NFSv4, на сервере запущено ещё с десяток сервисов, обеспечивающих работу с протоколами NFSv1, NFSv2 и NFSv3. Мало того, что ненужные сервисы элементарно занимают неиспользуемыми ими ресурсы, так их наличие ещё и вносит путаницу в процесс первичной настройки и локализации неисправностей в последствии; мало где я читал столько чепухи, сколько в ветках обсуждения процедур настройки NFS разного рода интернет форумов - местами просто феерия непонимания связи параметров конфигурирования с целями такового.
Заинтересовавшимся рекомендую сразу начать читать здесь: http://wiki.linux-nfs.org/
Я буду использовать в работе только NFSv4 и только на транспорте TCP (главное преимущество TCP - более эффективный механизм повторной передачи, что компенсирует возможные сбои в работе, позволяя продолжать её даже на приходящем в неисправность оборудовании). Отключить поддержку NFSv2 и NFSv3 в Debian Linux Lenny/Squeeze/Wheezy исправлением значений пары-тройки переменных нельзя (так, что бы изменения сохранились бы при обновлении конфигурируемых пакетов) и здесь я даже не буду этого делать, оставив конфигурацию сервисов "по умолчанию"; далее мы будем управлять таковой "на лету", путём воздействия на неё из "командной строки". Для энтузиастов позже напишу отдельную заметку о запуске NFS с поддержкой только одной, нужной нам, версии "4".
Устанавливаем необходимые пакеты:
# aptitude install --without-recommends nfs-common nfs-server
APT-пакет "nfs-server" - виртуальный и объединяет собой как минимум "nfs-common", "nfs-kernel-server" и "portmap". Любителям точности можно устанавливать непосредственно целевые пакеты.
Надо заметить, что сборщик пакетов NFS в Debian совсем не заинтересован в детальной проработке их конфигурации; что-то можно настроить, а что-то нельзя, несмотря на то, что препятствий к этому с точки зрения воздействия на целевое программное обеспечение нет никаких. Отключить всё не получится, но на некоторые составляющие мы можем повоздействовать через предусмотренные конфигурационный файлы. Корректируем два файла, применяя значения указанным переменным:
# vi /etc/default/nfs-common
....
# Отключаем выделенный сервис "statd" (он уже включен в сервис nfsd NFSv4 и отдельно нужен только для поддержки NFSv2 и NFSv3)
NEED_STATD=no
# Влючаем выделенный сервис (предусмотренный для поддержки NFSv4) трансляции имён пользователей в идентификаторы (он полезен в системе с полноценной аутентификацией клиентов с помощью протоколов PAM или Kerberos)
NEED_IDMAPD=yes
# Отключаем выделенный сервис, авторизирующий подключающихся пользователей с помощью протокола Kerberos (сейчас нам это не нужно, так как серверы выделенные и подключения защищены на транспортном уровне)
NEED_GSSD=no
....
# Отключаем выделенный сервис "statd" (он уже включен в сервис nfsd NFSv4 и отдельно нужен только для поддержки NFSv2 и NFSv3)
NEED_STATD=no
# Влючаем выделенный сервис (предусмотренный для поддержки NFSv4) трансляции имён пользователей в идентификаторы (он полезен в системе с полноценной аутентификацией клиентов с помощью протоколов PAM или Kerberos)
NEED_IDMAPD=yes
# Отключаем выделенный сервис, авторизирующий подключающихся пользователей с помощью протокола Kerberos (сейчас нам это не нужно, так как серверы выделенные и подключения защищены на транспортном уровне)
NEED_GSSD=no
....
# vi /etc/default/nfs-kernel-server
....
# Зададим количество серверов принимающих запросы. Наш сервер будет обслуживать всего одного клиента, так что укажем запускать пару экземпляров (по умолчанию: 8)
RPCNFSDCOUNT=2
# Отключаем выделенный сервис, нужный только для публикации ресурсов, требующих авторизии подключающихся пользователей с помощью протокола Kerberos
NEED_SVCGSSD=no
....
# Зададим количество серверов принимающих запросы. Наш сервер будет обслуживать всего одного клиента, так что укажем запускать пару экземпляров (по умолчанию: 8)
RPCNFSDCOUNT=2
# Отключаем выделенный сервис, нужный только для публикации ресурсов, требующих авторизии подключающихся пользователей с помощью протокола Kerberos
NEED_SVCGSSD=no
....
В итоге мы чуть уменьшили количество задействуемых компонентов, в целом оставив конфигурацию "по умолчанию".
Перезапустим сервисы NFS с обновлёнными настройками:
# /etc/init.d/nfs-common restart
# /etc/init.d/nfs-kernel-server restart
# /etc/init.d/nfs-kernel-server restart
Возможно попытка запуска ненастроенного NFS-сервера не будет успешной и сопровождаться следующим сообщением об ошибке:
[warn] Not starting NFS kernel daemon: no exports. ... (warning).
В "Linux Debian Lenny/Squeeze" можно было запустить NFS-сервер с пустым перечнем экспортируемых ресурсов - вхолостую, так сказать. Это удобно, когда сразу после старта системы в объявлении ресурсов нет необходимости или возможности. В "Debian Wheezy" эту возможность прикрыли, прерывая запуск NFS-сервера в случае пустых файлов "/etc/exports" и "/etc/exports.d/*exports". Глупо, надо заметить, и придётся с этим побороться.
Переделывать стартовый скрип не вижу смысла - мало ли что ещё поменяется в следующих реализациях, о чём я своевременно не узнаю? Потому просто подставим на публикацию в файле "/etc/exports" корень NFS, доступ к которому разрешим только на чтение:
# vi /etc/exports
....
/mnt/export 10.10.12.192/28(fsid=0,ro,nohide,root_squash,no_subtree_check,sync)
/mnt/export 10.10.12.192/28(fsid=0,ro,nohide,root_squash,no_subtree_check,sync)
# /etc/init.d/nfs-kernel-server restart
[ ok ] Stopping NFS kernel daemon: mountd nfsd.
[ ok ] Unexporting directories for NFS kernel daemon....
[ ok ] Exporting directories for NFS kernel daemon....
[ ok ] Starting NFS kernel daemon: nfsd mountd.
[ ok ] Unexporting directories for NFS kernel daemon....
[ ok ] Exporting directories for NFS kernel daemon....
[ ok ] Starting NFS kernel daemon: nfsd mountd.
Сразу можно будет посмотреть, опубликовался ли наш ресурс:
# showmount --exports
Export list:
/mnt/export 10.10.12.192/28
/mnt/export 10.10.12.192/28
Нашему серверу достаточно иметь запущенными всего пару сервисов, удостоверимся в их наличии:
# ps wax | grep -v grep | grep nfs
....
2831 ... [nfsd4]
2832 ... [nfsd]
....
2831 ... [nfsd4]
2832 ... [nfsd]
....
Опять же, в плане сетевых подключений для поддержания NFSv4 поверх TCP нам требуется прослушивание лишь одного порта "TCP:2049":
# netstat -apn | grep 2049
tcp ... 0 0.0.0.0:2049 0.0.0.0:* LISTEN
udp ... 0 0.0.0.0:2049 0.0.0.0:*
udp ... 0 0.0.0.0:2049 0.0.0.0:*
Как я уже упоминал выше, мы будем использовать только NFSv4 поверх TCP. По умолчанию запускается масса сервисов, обеспечивающих поддержку всей линейки протоколов NFS как на транспорте TCP, так и на UDP. Можете полюбоваться на них, запросив с помощью утилиты "rpcinfo" перечень зарегистрированных на сервере RPC-процессов:
# rpcinfo -p 127.0.0.1
На этом предварительную настройку подсистемы NFS будем считать завершённой.
Следующим, по сути первым, этапом будет оформление публикуемых (экспортируемых) ресурсов в должном виде.
Реализация NFSv4 не поддерживает публикацию произвольных веток файловых систем; теперь требуется предварительно создать некую отправную точку, которая объявляется условным корнем файловой системы, содержимое которой уже доступно для публикации (экспортирования). Получается своего рода "chroot"; надо полагать, это сделано в целях повышения уровня безопасности "от дурака", что бы случайно не раскрыть всем окружающим важные ресурсы.
Заведём себе такую точку сбора:
# mkdir -p /mnt/export
Непосредственно в директории выделенной для публикации можно разместить соответствующие ресурсы, а можно смонтировать их сюда с помощью функционала "--bind" (создавая синоним ресурса, а не ссылку на таковой, как некоторые неверно считают; преимуществом этого способа над символьными ссылками является возможность обходить ограничения доступа к файловой системе, возникающие перед процессами, запущенными в среде "chroot"):
# mkdir -p /mnt/export/storage0
# mount --bind /mnt/storage0 /mnt/export/storage0
# mount --bind /mnt/storage0 /mnt/export/storage0
Удостоверимся, что ресурс успешно смонтирован:
# mount
....
/mnt/storage0 on /mnt/export/storage0 type none (rw,bind)
/mnt/storage0 on /mnt/export/storage0 type none (rw,bind)
Следующим шагом станет определение локальных прав собственности публикуемых (экспортируемых) ресурсов и правил доступа к таковым удалёнными клиентами.
Специфика NFS в том, что (до последнего времени, когда стала возможной аутентификация подключающихся пользователей с помощью Kerberos) сервер доверяет клиенту и принимает "на веру" заявленные им при доступе к ресурсам UID и GID, на основании которых определяется, имеет клиент право доступа к ресурсам или нет. Предполагается, что в пределах схемы существует общее пространство имен пользователей, то есть пользователь "storage" на любом клиентском (с точки зрения сервера NFS) компьютере имеет то же имя и тот же идентификатор, что и пользователь "storage" на сервере NFS; по умолчанию, если GID и UID клиента не обнаруживаются на сервере, их значения транслируются в значения пользователя "nobody:nogroup" сервера. В NFSv4 трансляцией имён и идентификаторов занимается сервис "idmapd".
Соответственно, самое простое, что мы можем сделать для обеспечения возможности операций чтения и записи с ресурсам, так это сделать их собственником пользователя "nobody:nogroup", разрешив ему всё необходимое. Однако, я считаю, что лучше завести специального пользователя как на сервере так и на клиенте, задав им при создании идентичные UID и GID, хотя бы и для того, что бы в последствии наладить полноценное удостоверение подлинности клиента при обращении к серверу, попутно избежав выдачи слишком высокого уровня доступа к ресурсам пользователю "nobody:nogroup", который по идее (в рамках которой он создавался) вообще не должен иметь возможности изменять что либо.
Добавляем пользователя, собственника ресурсов хранилища публикуемых с помощью NFS:
# groupadd --gid 1500 storage
# useradd --shell /bin/false --home-dir /var/lib/nfs --uid 1500 --gid storage storage
# useradd --shell /bin/false --home-dir /var/lib/nfs --uid 1500 --gid storage storage
Естественно, что цифровые значения UID и GID необходимо подобрать несовпадающие с уже имеющимися во всех задействованных в схеме системах.
Обращаю внимание на то, что я лишил пользователя "storage" возможности работать в так называемой "оболочке", иначе говоря, от имени пользователя можно запустить приложение или обратится к ресурсам, но нельзя будет работать в "командной строке".
Делаем пользователя и группу "storage:storage" собственниками содержимого хранилища:
# chown -R storage:storage /mnt/storage0
# chmod -R ug+rw /mnt/storage0
# chmod -R o-rw /mnt/storage0
# chmod -R ug+rw /mnt/storage0
# chmod -R o-rw /mnt/storage0
Пока не заведена система удостоверения подлинности подключающихся клиентов проще всего указать ключами "all_squash, anonuid=1500, anongid=1500" серверу NFS считать всех анонимными пользователями и преобразовывать их UID и GID в значения локального пользователя "storage:storage", собственника ресурсов хранилища. Тогда мы сможем обратится к ресурсам от имени любого пользователя; в данном упрощённом варианте пользователи с идентичными именами, UID и GID на всех серверах и клиентах схемы нужны более для того, что бы корректно отображать параметры доступа на удалённых узлах, явно показывая в выводе утилит вроде "ls" собственника ресурсов и права доступа к таковым.
# exportfs -i -o fsid=0,rw,nohide,all_squash,anonuid=1500,anongid=1500,no_subtree_check,sync 10.10.12.192/28:/mnt/export/storage0
Отменить экспортирование:
# exportfs -u 10.10.12.192/28:/mnt/export/storage0
Где:
"-i" - игнорируем содержимое "/etc/exports" руководствуясь только параметрами данной команды;
"-o fsid=0" - обозначает, что публикуется корневой для всех остальных экспортируемых каталогов (соответственно, все остальные каталоги, расположенные внутри него, публикуются с параметром "-o fsid=1").
"-o fsid=0" - обозначает, что публикуется корневой для всех остальных экспортируемых каталогов (соответственно, все остальные каталоги, расположенные внутри него, публикуются с параметром "-o fsid=1").
Проверяем, успешно ли опубликованы ресурсы:
# showmount --exports
/mnt/export 10.10.12.192/28
/mnt/export/storage0 10.10.12.192/28
/mnt/export/storage0 10.10.12.192/28
На стороне сборки опубликованных NFS-ресурсов потребуется провести несколько иные работы.
Установим утилиты подключения к NFS-ресурсам и монтирования их в локальную файловую систему:
# aptitude install --without-recommends nfs-common nfs-client
Чуть подправим конфигурацию NFS-клиента:
# vi /etc/default/nfs-common
....
# Отключаем выделенный сервис "statd" (он уже включен в сервис nfsd NFSv4 и отдельно нужен только для поддержки NFSv2 и NFSv3)
NEED_STATD=no
# Включаем выделенный сервис (предусмотренный для поддержки NFSv4) трансляции имён пользователей в идентификаторы
NEED_IDMAPD=yes
# Отключаем выделенный сервис, авторизирующий подключающихся пользователей с помощью протокола Kerberos (сейчас нам это не нужно, так как серверы выделенные и подключения защищены на транспортном уровне)
NEED_GSSD=no
....
# Отключаем выделенный сервис "statd" (он уже включен в сервис nfsd NFSv4 и отдельно нужен только для поддержки NFSv2 и NFSv3)
NEED_STATD=no
# Включаем выделенный сервис (предусмотренный для поддержки NFSv4) трансляции имён пользователей в идентификаторы
NEED_IDMAPD=yes
# Отключаем выделенный сервис, авторизирующий подключающихся пользователей с помощью протокола Kerberos (сейчас нам это не нужно, так как серверы выделенные и подключения защищены на транспортном уровне)
NEED_GSSD=no
....
# /etc/init.d/nfs-common restart
Добавляем пользователя, собственника ресурсов хранилища, публикуемых с помощью NFS:
# groupadd --gid 1500 storage
# useradd --shell /bin/false --home-dir /var/lib/nfs --uid 1500 --gid storage storage
# useradd --shell /bin/false --home-dir /var/lib/nfs --uid 1500 --gid storage storage
Создаём директорию, в которую будем монтировать NFS-ресурсы:
# mkdir -p /mnt/import
Запрашиваем перечень доступных для монтирования удалённых NFS-ресурсов:
# showmount --exports 10.10.12.201
Export list for 10.10.12.201:
/mnt/export 127.0.0.1/32
/mnt/export/storage0 10.10.12.192/28
/mnt/export 127.0.0.1/32
/mnt/export/storage0 10.10.12.192/28
Ранее, в настройках NFSv4-сервера мы обозначили директорию "/mnt/export" корневой для клиентов с помощью опции "fsid=0". Теперь при монтировании с NFSv4-клиента следует указывать целевые ресурсы отталкиваясь не от корневой директории NFS-сервера, а от своеобразного "chroot" по адресу "/mnt/export". То есть, если требуется смонтировать ресурс "/mnt/export/resource", нужно монтировать "/resource":
# mount.nfs4 -o proto=tcp,port=2049,hard,intr,tcp,sync,noatime,nodiratime,nodev,noexec,nosuid 10.10.12.195:/storage0 /mnt/import
Иногда удобнее позволить NFS-клиенту уведомлять приложение использующее его ресурсы о проблемах со смонтированной файловой системой. Для этого монтируем файловую систему с опцией "soft", указываю количество попыток перемонтирования до вывода сообщения об ошибке опцией "retrans=5" (пять раз) и указываем таймаут для запроса опцией "timeo=600" (минута, в десятых долях секунды):
# mount.nfs4 -o soft,intr,retrans=5,timeo=600,tcp,sync,noatime,nodiratime,nodev,noexec,nosuid 10.10.12.195:/storage0 /mnt/import
За несколько лет эксплуатации я так и не пришёл к выводу, какой режим выгоднее - "мягкий", с некорректно отрабатывающими ответы NFS приложениями, или "жёсткий" - с зависающими в ожидании ответа приложениями. Пока остаюсь с "hard".
Иногда сбой в работе приложения обращающегося к файловой системе NFS и не получающего ответа приводит к его зависанию намертво (например MHDDFS сваливается до уровня "defunct" и "zombie" после длительного (десятки минут и более) ожидания ответа от файловой системы и множества дублирующих неудовлетворённых запросов к ресурсам) так, что его порой невозможно остановить без форсированной ("echo 1 > /proc/sys/kernel/sysrq; echo b > /proc/sysrq-trigger") перезагрузки системы (если зависшее приложение использует модуль ядра, драйвер файловой системы или что-то низкоуровневое в этом роде, то даже может не сработать программная перезагрузка, так как система будет ждать ответа от зависшего приложения или заблокированного им модуля бесконечно долго).
Важный аспект оптимизации производительности - заранее определяемый размер передаваемого пакета данных. Если пакет меньше оптимально - их будет передаваться избыточно много (затраты ресурсов на формирование пакетов), а если больше оптимально - на каком-то из уровней будут затрачиваться ресурсы на из дробление, инкапсуляцию и последующую сборку. Полагаю отталкиваться при выборе оптимального размера пакетов следует исходя из неизменяемых значений - например размера блока файловой системы хранения (как правило он уже установлен исходя из параметров аппаратуры). Узнать его можно следующим образом:
# blockdev --getbsz /dev/sda
В современных блочных устройствах это будет "4096" байт, скорее всего. Тогда подгоним размер пакета под это значение, установив соответствующие опции "rsize=4096,wsize=4096".
Переход к настройкам автоматизации публикации NFS-ресурсов.