При загрузке centos зависает на started nfs status monitor for nfsv2 3 locking

Обновлено: 05.07.2024

How to Install and Configure an NFS Server on CentOS 8

В этой статье вы найдете руководство по настройке сервера NFSv4 в CentOS 8, а также узнаете, как смонтировать файловую систему NFS на клиенте.

Сетевая файловая система (NFS) - это протокол распределенной файловой системы, который позволяет вам совместно использовать удаленные каталоги по сети. С помощью NFS вы можете монтировать удаленные каталоги в вашей системе и работать с файлами на удаленном компьютере, как если бы они были локальными файлами.

Протокол NFS по умолчанию не шифруется и, в отличие от Samba, не обеспечивает аутентификацию пользователя. Доступ к серверу ограничен IP-адресами клиентов или именами хостов.

На сервере под управлением CentOS 8 мы настроим сервер NFS и другие машины, которые будут действовать как клиенты NFS. Сервер и клиенты должны иметь возможность общаться друг с другом через частную сеть. Если ваш хостинг-провайдер не предлагает частные IP-адреса, вы можете использовать общедоступные IP-адреса и настроить брандмауэр сервера так, чтобы трафик на порт 2049 передавался только из надежных источников.

Машины в этом примере имеют следующие IP-адреса:

Настройте сервер NFS

Теперь рассмотрим, как установить необходимые пакеты, создать и экспортировать каталоги NFS и настроить брандмауэр.

Установка сервера NFS

Пакет «nfs-utils» предоставляет утилиты и демоны NFS для сервера NFS. Для его установки выполните следующую команду:


После завершения установки включите и запустите службу NFS, введя:


По умолчанию в CentOS 8 NFS версии 3 и 4.x включены, версия 2 отключена. NFSv2 сейчас довольно старый, и нет никаких причин для его включения. Чтобы проверить это, выполните следующую cat команду:


Параметры конфигурации сервера NFS установлены в /etc/nfsmount.conf и /etc/nfs.conf файлы. На данном этапе подойдут настройки по умолчанию.


Создание файловых систем

При настройке сервера NFSv4 рекомендуется использовать глобальный корневой каталог NFS и привязать фактические каталоги к точке подключения общего ресурса. В этом примере мы будем использовать /srv/nfs4 директор в качестве корня NFS.

Чтобы лучше объяснить, как можно настроить монтирование NFS, мы собираемся использовать два каталога ( /var/www и /opt/backups ) с разными настройками конфигурации.

Это /var/www/ принадлежит пользователю и группе apache и /opt/backups принадлежит root .

Создайте файловую систему экспорта с помощью mkdir команды:

Смонтируйте фактические каталоги:

Чтобы сделать привязку монтируемой постоянной, добавьте в /etc/fstab файл следующие записи :

Экспорт файловых систем

Следующим шагом является определение файловых систем, которые будут экспортированы сервером NFS, параметров общего ресурса и клиентов, которым разрешен доступ к этим файловым системам. Для этого откройте /etc/exports файл:


Экспорт www и backups каталогов и разрешить доступ только от клиентов по 192.168.33.0/24 сети:

Первая строка содержит fsid=0 каталог, определяющий корневой каталог NFS /srv/nfs . Доступ к этому тому NFS разрешен только клиентам из 192.168.33.0/24 подсети. Этот crossmnt параметр необходим для совместного использования каталогов, которые являются подкаталогами экспортируемого каталога.

Во второй строке показано, как задать несколько правил экспорта для одной файловой системы. Он экспортирует /srv/nfs4/backups каталог и предоставляет доступ только для чтения ко всему 192.168.33.0/24 диапазону, а также для чтения и записи 192.168.33.3 . Опция sync сообщает NFS на изменения записи на диск , прежде чем ответить.

Последняя строка должна быть понятной. Для получения дополнительной информации обо всех доступных опциях введите man exports свой терминал.

Сохраните файл и экспортируйте общие ресурсы:


Вам нужно запускать команду выше каждый раз, когда вы изменяете /etc/exports файл. Если есть какие-либо ошибки или предупреждения, они будут показаны на терминале.

Чтобы просмотреть текущий активный экспорт и его состояние, используйте:


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


root_squash является одним из наиболее важных параметров безопасности NFS. Он не позволяет корневым пользователям, подключенным с клиентов, иметь права root на подключенных общих ресурсах. Он будет отображать корень UID и GID в nobody / nogroup UID / GID .

Чтобы пользователи на клиентских компьютерах имели доступ, NFS ожидает, что идентификаторы пользователя и группы клиента совпадают с идентификаторами на сервере. Другой вариант - использовать функцию отображения NFSv4, которая переводит идентификаторы пользователей и групп в имена и наоборот.

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

Конфигурация брандмауэра

FirewallD является решением брандмауэра по умолчанию в Centos 8 .

Служба NFS включает в себя предопределенные правила для доступа к серверу NFS.

Следующие команды будут постоянно разрешать доступ из 192.168.33.0/24 подсети:


Настройка клиентов NFS

Теперь, когда сервер NFS настроен и общие ресурсы экспортированы, на следующем шаге настройте клиенты и подключите удаленные файловые системы.

Вы также можете смонтировать общий ресурс NFS на компьютерах MacOS и Windows, но мы сосредоточимся на системах Linux.

Установка клиента NFS

На клиентских компьютерах установите инструменты, необходимые для монтирования удаленных файловых систем NFS.

Установите клиент NFS в Debian и Ubuntu

Название пакета, включающего программы для монтирования файловых систем NFS в дистрибутивах на основе Debian, - nfs-common . Чтобы установить его, запустите:

Установите клиент NFS в CentOS и Fedora

На Red Hat и ее производных установите nfs-utils пакет:

Монтирование файловых систем

Мы будем работать на клиентском компьютере с IP-адресом 192.168.33.110 , который имеет доступ для чтения и записи к /srv/nfs4/www файловой системе и доступ только для чтения к /srv/nfs4/backups файловой системе.

Создайте две новые директории для точек монтирования. Вы можете создавать эти каталоги в любом месте, которое вы хотите.


Смонтируйте экспортированные файловые системы с помощью mount команды:


Где 192.168.33.148 находится IP-адрес сервера NFS. Вы также можете использовать имя хоста вместо IP-адреса, но оно должно быть разрешено клиентским компьютером. Обычно это делается путем сопоставления имени хоста с IP в /etc/hosts файле.

При монтировании файловой системы NFSv4 вам нужно пропустить корневой каталог NFS, поэтому вместо /srv/nfs4/backups него нужно использовать /backups .


Убедитесь, что удаленные файловые системы успешно смонтированы, используя команду mount или df :


Команда напечатает все смонтированные файловые системы. Последние две строки являются смонтированными долями:


Чтобы сделать монтирование постоянным при перезагрузке, откройте /etc/fstab файл:

и добавьте следующие строки:

Чтобы узнать больше информации о доступных опциях при монтировании файловой системы NFS, введите man nfs свой терминал.

Другой вариант для монтирования удаленных файловых систем - использовать autofs инструмент или создать системный модуль.

Тестирование NFS Access

Давайте проверим доступ к общим ресурсам, создав новый файл в каждом из них.

Сначала попробуйте создать тестовый файл в /backups каталоге с помощью touch команды:


Далее попробуйте создать тестовый файл в /srv/www каталоге как корень, используя sudo команду:


/var/www каталог принадлежат к apache пользователю, и эта доля имеет root_squash вариант набор, который отображает привилегированный пользователь к nobody пользователю и nogroup группе , которая не имеет права записи на удаленный ресурс.

Предполагая, что пользователь apache существует на клиентском компьютере с тем же UID и GID на удаленном сервере (что должно быть в случае, если, например, вы установили apache на обоих компьютерах), вы можете протестировать создание файла как пользователя apache с:

Команда не будет отображать вывод, что означает, что файл был успешно создан.

Для проверки перечислите файлы в /srv/www каталоге:

Выходные данные должны показать вновь созданный файл:


Размонтирование файловой системы NFS

Если вам больше не нужен удаленный общий ресурс NFS, вы можете размонтировать его, как любую другую смонтированную файловую систему, с помощью команды umount. Например, чтобы размонтировать /backup ресурс, который вы запустите:


Вывод

В этой статье мы показали, как настроить сервер NFS и как монтировать удаленные файловые системы на клиентских компьютерах. Если вы внедряете NFS в производство и делитесь разумными данными, рекомендуется включить аутентификацию Kerberos.

В качестве альтернативы NFS вы можете использовать SSHFS для монтирования удаленных каталогов через SSH-соединение. SSHFS зашифрован по умолчанию и намного проще в настройке и использовании.

Ошибка автозапуска службы nfs-server.service - exportfs: Failed to resolve в CentOS Linux 7.2

При перезагрузке файлового NFS-сервера на базе CentOS Linux 7.2 служба nfs-server не стартует, хотя настроена на автоматический запуск. Соответсвенно NFS-клиентам недоступны NFS-шары с этого файлового срвера. При этом ручной запуск службы nfs-server происходит успешно. После перезагрузки сервера, если заглянуть в текущий статус службы то увидим то, что она не запущена и ошибку типа «exportfs: Failed to resolve…»

Проблема может быть связана с тем, что в процессе загрузки служба nfs-server.service пытается запуститься до того, как закончена полная инициализация сети, и соответственно из-за ошибки разрешения имени из файла /etc/exports служба не стартует.

Для решения этой проблемы можно использовать разные методы.

Метод №1

Включить службу NetworkManager-wait-online, в случае если в вашей системе используется NetworkManager :

Метод №2

Отредактировать конфигурационный файл службы /usr/lib/systemd/system/nfs-server.service, добавив в секцию [Unit] строки:

После этого выполняем команду:

Эти методы можно пробовать использовать как раздельно так и вместе. После применения методов несколько раз перезагружаем сервер и убеждаемся в том, что каждый раз служба nfs-server запускается автоматически при загрузке системы.

Если ничего не помогает, то можно попробовать поставить жёсткий «костыль», а именно добавить в конец файла /etc/rc.d/rc.local проверку наличия сети путём отсылки ping, и как только сеть появится - запускать службу. Этот фрагмент добавим в файл /etc/rc.d/rc.local (в переменную checkip вставьте IP другого сервера, например шлюза):

И не забудем сделать исполняемым файл

Дополнительные источники информации


Автор первичной редакции:
Алексей Максимов
Время публикации: 09.09.2016 11:10

Понимание процедуры загрузки в Linux RHEL7/CentOS

Следующие шаги суммируют, как процедура загрузки происходит в Linux.

1. Выполнение POST: машина включена. Из системного ПО, которым может быть UEFI или классический BIOS, выполняется самотестирование при включении питания (POST) и аппаратное обеспечение, необходимое для запуска инициализации системы.

2. Выбор загрузочного устройства: В загрузочной прошивке UEFI или в основной загрузочной записи находится загрузочное устройство.

3. Загрузка загрузчика: с загрузочного устройства находится загрузчик. На Red Hat/CentOS это обычно GRUB 2.

4. Загрузка ядра: Загрузчик может представить пользователю меню загрузки или может быть настроен на автоматический запуск Linux по умолчанию. Для загрузки Linux ядро загружается вместе с initramfs . Initramfs содержит модули ядра для всего оборудования, которое требуется для загрузки, а также начальные сценарии, необходимые для перехода к следующему этапу загрузки. На RHEL 7/CentOS initramfs содержит полную операционную систему (которая может использоваться для устранения неполадок).

5. Запуск /sbin/init: Как только ядро загружено в память, загружается первый из всех процессов, но все еще из initramfs . Это процесс /sbin/init , который связан с systemd . Демон udev также загружается для дальнейшей инициализации оборудования. Все это все еще происходит из образа initramfs .

6. Обработка initrd.target: процесс systemd выполняет все юниты из initrd.target , который подготавливает минимальную операционную среду, в которой корневая файловая система на диске монтируется в каталог /sysroot . На данный момент загружено достаточно, чтобы перейти к установке системы, которая была записана на жесткий диск.

7. Переключение на корневую файловую систему: система переключается на корневую файловую систему, которая находится на диске, и в этот момент может также загрузить процесс systemd с диска.

8. Запуск цели по умолчанию (default target): Systemd ищет цель по умолчанию для выполнения и запускает все свои юниты. В этом процессе отображается экран входа в систему, и пользователь может проходить аутентификацию. Обратите внимание, что приглашение к входу в систему может быть запрошено до успешной загрузки всех файлов модуля systemd . Таким образом, просмотр приглашения на вход в систему не обязательно означает, что сервер еще полностью функционирует.
На каждом из перечисленных этапов могут возникнуть проблемы из-за неправильной настройки или других проблем. Таблица суммирует, где настроена определенная фаза и что вы можете сделать, чтобы устранить неполадки, если что-то пойдет не так.

Передача аргементов в GRUB 2 ядру во время загрузки

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

Когда сервер загружается, вы кратко видите меню GRUB 2. Смотри быстро, потому что это будет длиться всего несколько секунд. В этом загрузочном меню вы можете ввести e, чтобы войти в режим, в котором вы можете редактировать команды, или c, чтобы ввести полную командную строку GRUB.


После передачи e в загрузочное меню GRUB вы увидите интерфейс, показанный на скриншоте ниже. В этом интерфейсе прокрутите вниз, чтобы найти раздел, начинающийся с linux16 /vmlinuz , за которым следует множество аргументов. Это строка, которая сообщает GRUB, как запустить ядро, и по умолчанию это выглядит так:



После ввода параметров загрузки, которые вы хотите использовать, нажмите Ctrl + X, чтобы запустить ядро с этими параметрами. Обратите внимание, что эти параметры используются только один раз и не являются постоянными. Чтобы сделать их постоянными, вы должны изменить содержимое файла конфигурации /etc/default/grub и использовать grub2-mkconfig -o /boot/grub2/grub.cfg , чтобы применить изменение.

Когда у вас возникли проблемы, у вас есть несколько вариантов (целей), которые вы можете ввести в приглашении загрузки GRUB:

■ rd.break Это останавливает процедуру загрузки, пока она еще находится в стадии initramfs .
Эта опция полезна, если у вас нет пароля root.

■ init=/bin/sh или init=/bin/bash Указывает, что оболочка должна быть запущена сразу после загрузки ядра и initrd . Это полезный вариант, но не лучший, потому что в некоторых случаях вы потеряете консольный доступ или пропустите другие функции.

■ systemd.unit=rescue.target Команда запускает еще несколько системных юнитов, чтобы привести вас в более полный рабочий режим. Требуется пароль root.
Чтобы увидеть, что загружено только очень ограниченное количество юнит-файлов, вы можете ввести команду systemctl list-units .

Запуск целей(targets) устранения неполадок в Linux

1. (Пере)загружаем Linux. Когда отобразиться меню GRUB, нажимаем e ;

2. Находим строку, которая начинается на linux16 /vmlinuz. В конце строки вводим systemd.unit=rescue.target и удаляем rhgb quit ;

3. Жмем Ctrl+X, чтобы начать загрузку с этими параметрами. Вводим пароль от root;

4. Вводим systemctl list-units и смотрим. Будут показаны все юнит-файлы, которые загружены в данный момент и соответственно загружена базовая системная среда;

5. Вводим systemctl show-environment . Видим переменные окружения в режиме rescue.target;

6. Перезагружаемся reboot ;

7. Когда отобразится меню GRUB, нажимаем e . Находим строку, которая начинается на linux16 /vmlinuz. В конце строки вводим systemd.unit=emergency.target и удаляем rhgb quit ;

8. Снова вводим пароль от root;

9. Система загрузилась в режиме emergency.target;

10. Вводим systemctl list-units и видим, что загрузился самый минимум из юнит-файлов.

Устранение неполадок с помощью загрузочного диска Linux

Еще один способ восстановления работоспособности Linux использовать образ операционки.

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

  • Install CentOS 7 in Basic Graphics Mode: эта опция переустанавливает систему. Не используйте её, если не хотите устранить неполадки в ситуации, когда обычная установка не работает и вам необходим базовый графический режим. Как правило, вам никогда не нужно использовать эту опцию для устранения неисправностей при установке.
  • Rescue a CentOS System: это самая гибкая система спасения. Это должен быть первый вариант выбора при использовании аварийного диска.
  • Run a Memory Test: если вы столкнулись с ошибками памяти, это позволяет пометить плохие микросхемы памяти, чтобы ваша машина могла нормально загружаться.
  • Boot from local drive: здесь я думаю всё понятно.

Пример использования "Rescue a CentOS System"

1. Перезагружаем сервер с установочным диском Centos 7. Загружаемся и выбираем "Troubleshooting".

2. В меню траблшутинга выбираем "Rescue a CentOS System" и загружаемся.


3. Система восстановления теперь предлагает вам найти установленную систему Linux и смонтировать ее в /mnt/sysimage . Выберите номер 1, чтобы продолжить:
4. Если была найдена правильная установка CentOS, вам будет предложено, чтобы система была смонтирована в /mnt/sysimage . В этот момент вы можете дважды нажать Enter, чтобы получить доступ к оболочке восстановления.



5. Ваша система Linux на данный момент доступна через каталог /mnt/sysimage . Введите chroot /mnt/sysimage . На этом этапе у вас есть доступ к корневой файловой системе, и вы можете получить доступ ко всем инструментам, которые необходимы для восстановления доступа к вашей системе.

Переустановка GRUB с помощью аварийного диска

Одна из распространенных причин, по которой вам нужно запустить аварийный диск, заключается в том, что загрузчик GRUB 2 не работает. Если это произойдет, вам может понадобиться установить его снова. После того, как вы восстановили доступ к своему серверу с помощью аварийного диска, переустановить GRUB 2 несложно, и он состоит из двух этапов:

  1. Убедитесь, что вы поместили содержимое каталога /mnt/sysimage в текущую рабочую среду.
  2. Используйте команду grub2-install , а затем имя устройства, на котором вы хотите переустановить GRUB 2. Если это виртуальная машина KVM используйте команду grub2-install /dev/vda и на физическом сервере или виртуальная машина VMware, HyperV или Virtual Box, это grub2-install /dev/sda .

Повторное создание Initramfs с помощью аварийного диска

Иногда initramfs также может быть поврежден. Если это произойдет, вы не сможете загрузить свой сервер в нормальном рабочем режиме. Чтобы восстановить образ initramfs после загрузки в среду восстановления, вы можете использовать команду dracut . Если используется без аргументов, эта команда создает новый initramfs для загруженного в данный момент ядра.
Кроме того, вы можете использовать команду dracut с несколькими опциями для создания initramfs для конкретных сред ядра. Существует также файл конфигурации с именем /etc/dracut.conf , который можно использовать для включения определенных параметров при повторном создании initramfs .

  • /usr/lib/dracut/dracut.conf.d/*.conf содержит системные файлы конфигурации по умолчанию.
  • /etc/dracut.conf.d содержит пользовательские файлы конфигурации dracut.
  • /etc/dracut.conf используется в качестве основного файла конфигурации.

Исправление общих проблем

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

Переустановка GRUB 2

Код загрузчика не исчезает просто так, но иногда может случиться, что загрузочный код GRUB 2 будет поврежден. В этом случае вам лучше знать, как переустановить GRUB 2. Точный подход зависит от того, находится ли ваш сервер в загрузочном состоянии. Если это так, то довольно просто переустановить GRUB 2. Просто введите grub2-install и имя устройства, на которое вы хотите его установить. У команды есть много различных опций для точной настройки того, что именно будет установлено, но вам, вероятно, они не понадобятся, потому что по умолчанию команда устанавливает все необходимое, чтобы ваша система снова загрузилась. Становится немного сложнее, если ваш сервер не загружается.

Если это произойдет, вам сначала нужно запустить систему восстановления и восстановить доступ к вашему серверу из системы восстановления. После монтирования файловых систем вашего сервера в /mnt/sysimage и использования chroot /mnt/sysimage , чтобы сделать смонтированный образ системы вашим корневым образом: Просто запустите grub2-install , чтобы установить GRUB 2 на желаемое установочное устройство. Но если вы находитесь на виртуальной машине KVM, запустите grub2-install /dev/vda , а если вы находитесь на физическом диске, запустите grub2-install /dev/sda .

Исправление Initramfs

В редких случаях может случиться так, что initramfs будет поврежден. Если вы тщательно проанализируете процедуру загрузки, вы узнаете, что у вас есть проблема с initramfs , потому что вы никогда не увидите, как корневая файловая система монтируется в корневой каталог, и при этом вы не увидите запуска каких-либо системных модулей. Если вы подозреваете, что у вас есть проблема с initramfs , ее легко создать заново. Чтобы воссоздать его, используя все настройки по умолчанию (что в большинстве случаев нормально), вы можете просто запустить команду dracut --force . (Без --force команда откажется перезаписать ваши существующие initramfs .)
При запуске команды dracut вы можете использовать файл конфигурации /etc/dracut.conf , чтобы указать, что именно записывается в initramfs . В этом файле конфигурации вы можете увидеть такие параметры, как lvmconf = «no» , которые можно использовать для включения или выключения определенных функций. Используйте эти параметры, чтобы убедиться, что у вас есть все необходимые функции в initramfs .

Восстановление после проблем с файловой системой

Не в первый раз уже сталкиваюсь со странными и досадными граблями, побороть кои не в силах. Было сие и на 7-й версии Фри, а вот теперь проявилось на 9.1.

Есть у меня в системе терабайтный SATA-винт. Разделён он пополам на две партиции. Одна из них отдана под файлопомойку и используется исключительно из-под рабочей станции Windows. Монтируется партиция в каталог /export, а дальше он подцепляется виндовым NFS-клиентом. Вот соответствующая запись в /etc/exports:

Так вот, иногда случается нечто. Во время работы с подмонтированным диском (напр., при просмотре фрагментов какого-нибудь фильма с файлопомойки) всё вдруг зависает. Плеер виснет, NFS-диск перестаёт отвечать. Как будто связь прервалась. Но связь на самом деле не прервалась, у меня инет на рабочую станцию идёт через ту же Фрю, и всё ок.

Перебегаю на Фрю (у меня KVM на две машины). И что же я вижу? Фря зависла. Единственная доступная мне операция - это переключение между консолями посредством Alt + Fn. На другие сочетания клавиш реакции нет. Ввести что-либо в командной строке невозможно. Всё зависло намертво. В том числе и демоны, обслуживающие подключение NFS. ssh-доступ к машине тоже не работает. То есть, имею в итоге труп, который пропускает через себя инет и позволяет посмотреть свои консоли, но не более того. Всё, что можно сделать с машиной, - перезагрузить. Это и приходится делать. После перезагрузки причину произошедшего установить проблематично. В логах - зияющая пустота.

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

Я подумал было, что по каким-нибудь причинам нагрузка на проц стала настолько неимоверной, что из-за него всё залипло. Либо какие-то проблемы с диском. Чтобы проверить эти версии, вывел в отдельные консоли непрерывно работающие top и gstat в обычном режиме автоматического обновления экрана. Когда всё в очередной раз зависло, я полез смотреть эти консоли. Ни фига! Обе программы тоже зависли, т.е. на экранах отображалось их состояние на момент возникновения проблемы, а дальнейшего обновления не было. И показания на этот момент были вполне благолепны. 95% проца свободно, загрузка винта менее 1%. В top'е было видно, что nfsd находится в состоянии rpcsvc. Фиг его знает, что это значит.

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

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

Пришлось изучать внутренние механизмы Git и сетевой файловой системы NFS. В итоге мы нашли баг в клиенте Linux v4.0 NFS, Тронд Мюклебуст (Trond Myklebust) написал патч для ядра, и с 26 октября этот патч входит в основное ядро Linux.

В этом посте я расскажу, как мы изучали проблему, в каком направлении думали и какие инструменты использовали, чтобы отследить баг. Мы вдохновлялись отличной детективной работой Олега Дашевского, описанной в посте «Как я две недели охотился за утечкой памяти в Ruby».




А еще это отличный пример того, что отладка ПО с открытым исходным кодом — это командный спорт, в котором участвует много людей, компаний и стран. Девиз GitLab «Каждый может внести свой вклад» справедлив не только для самого GitLab, но и для других проектов с открытым исходным кодом, например для ядра Linux.

Воспроизведение бага

  1. Полный текст ошибки: fatal: Couldn't read ./packed-refs: Stale file handle .
  2. Судя по всему, проблема возникала, когда клиент вручную запускал сборку мусора в Git командой git gc .
  3. Ошибка пропадала, когда системный администратор запускал утилиту ls в каталоге.
  4. Ошибка пропадала, когда процесс git gc завершался.

Понятно, что первые два пункта связаны. Когда вы отправляете изменения в ветку Git, Git создает слабую ссылку — длинное имя файла, которое указывает имя ветки для коммита. Например, при отправке в master будет создан файл с именем refs/heads/master в репозитории:

Команда git gc выполняет несколько задач. Например, собирает эти слабые ссылки (refs) и упаковывает их в один файл с именем packed-refs . Это немного ускоряет работу, ведь прочитать один большой файл проще, чем много маленьких. Например, после запуска команды git gc файл packed-refs может выглядеть как-то так:

Как создается файл packed-refs ? Чтобы это узнать, мы запустили команду strace git gc там, где у нас была слабая ссылка. Вот строки, которые относятся к делу:

Системные вызовы показали, что команда git gc :

  1. Открыла packed-refs.lock . Это говорит другим процессам, что файл packed-refs заблокирован и не может меняться.
  2. Открыла packed-refs.new .
  3. Записала слабые ссылки в packed-refs.new .
  4. Переименовала packed-refs.new в packed-refs .
  5. Удалила packed-refs.lock .
  6. Удалила слабые ссылки.

Ключевой пункт здесь — четвертый, то есть переименование, где Git вводит файл packed-refs . git gc не только собирает слабые ссылки, но и выполняет куда более ресурсоемкую задачу — ищет и удаляет неиспользуемые объекты. В больших репозиториях это может длиться больше часа.

И мы спросили себя: а в больших репозиториях держит ли git gc файл открытым во время очистки? Мы изучили логи strace , запустили утилиту lsof , и вот что узнали о процессе git gc :

Как видно, файл packed-refs закрывается в самом конце, после потенциально долгого процесса Garbage collect objects .

Так возник следующий вопрос: как ведет себя NFS, когда на одной ноде открыт файл packed-refs , а другая в это время переименовывает его?

"В научных целях" мы попросили клиента провести один эксперимент на двух разных машинах (Элис и Боб):
1) В общем томе NFS создайте два файла: test1.txt и test2.txt с разным содержимым, чтобы их было проще различать:

2) На машине Элис файл test1.txt должен быть открыт:

3) На машине Элис непрерывно показывайте содержимое test1.txt :

4) Затем на машине Боб выполните команду:

Последний шаг воспроизводит то, что делает git gc с файлом packed-refs , когда перезаписывает существующий файл.
На машине клиента результат выглядел примерно так:

Есть! Кажется, мы контролируемо воспроизвели проблему. Но в этом же эксперименте на Linux NFS-сервере такая проблема не возникла. Результат был ожидаемым — после переименования принималось новое содержимое:

Откуда эта разница в поведении? Оказывается, клиент использовал хранилище Isilon NFS, которое поддерживало только NFS v4.0. Когда мы изменили параметры подключения на v4.0 с помощью параметра vers=4.0 в /etc/fstab , тест показал другой результат для Linux NFS-сервера:

Вместо устаревшего дескриптора файла Stale file handle сервер Linux NFS v4.0 показывал устаревшее содержимое. Оказывается, разницу в поведении можно объяснить спецификациями NFS. Из RFC 3010:

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

Иными словами, NFS-серверы могут выбирать, как себя вести, когда файл переименован, и NFS-сервер вполне обоснованно возвращает ошибку Stale file error в таких случаях. Мы предположили, что причина проблемы — та же, хотя результаты и были разными. Мы подозревали, что дело в проверке кэша, ведь утилита ls в каталоге убирала ошибку. Теперь у нас был воспроизводимый тестовый сценарий, и мы обратились к экспертам — мейнтейнерам Linux NFS.

Ложный след: делегирование на NFS-сервере

Когда мы сумели пошагово воспроизвести ошибку, я написал контактам по Linux NFS о том, что мы узнали. Неделю я переписывался с Брюсом Филдсом, мейнтейнером Linux NFS-сервера, и он предположил, что баг в NFS и нужно изучить сетевой трафик. Он думал, что проблема в делегировании задач на NFS-сервере.

Что такое делегирование на NFS-сервере?

В двух словах, в версии NFS v4 появилась функция делегирования для ускорения доступа к файлам. Сервер может делегировать доступ на чтение или запись клиенту, чтобы клиенту не пришлось постоянно спрашивать у сервера, не изменен ли файл другим клиентом. Проще говоря, делегирование записи — это как одолжить кому-то свой блокнот и сказать: «Ты пока тут пиши, а я заберу его, когда буду готов». И человеку не придется просить блокнот каждый раз, когда нужно что-то записать — у него есть полная свобода действий, пока блокнот не отнимут. В NFS просьба вернуть блокнот называется отзывом делегирования.

Баг в отзыве делегирования в NFS мог бы объяснить проблему Stale file handle . Помните, как в нашем эксперименте у Элис был открыт test1.txt , а потом его заменил test2.txt . Может быть, серверу не удалось отозвать делегирование для test1.txt , и это привело к неверному статусу. Чтобы проверить эту теорию, мы записали трафик NFC утилитой tcpdump и визуализировали его с помощью Wireshark.

Wireshark — это отличный инструмент с открытым исходным кодом для анализа сетевого трафика, особенно для изучения NFS в действии. Мы записали трассировку с помощью следующей команды на NFS-сервере:

Эта команда записывает весь NFS-трафик, который обычно проходит через порт TCP 2049. Раз наш эксперимент удался с NFS v4.1, но не с NFS v4.0, мы могли сравнить поведение NFS в рабочем и нерабочем случае. С Wireshark мы увидели следующее поведение:

NFS v4.0 (устаревший файл)

NFS v4.1 (рабочий случай)

Почему Элис вдруг решается на дополнительный LOOKUP ? Судя по всему, отзыв делегирования прошел успешно, но какая-то проблема, видимо, осталась. Например, пропущен шаг инвалидации. Чтобы это проверить, мы исключили делегирование NFS на самом NFS-сервере этой командой:

Мы повторили эксперимент, но проблема никуда не делась. Мы убедились, что проблема не в NFS-сервере или делегировании, и решили посмотреть на NFS-клиент в ядре.

Копаем глубже: Linux NFS-клиент

Первый вопрос, на который мы должны были ответить мейнтейнерам NFS:

Эта проблема сохраняется в последней версии ядра?

Проблема возникала в ядрах CentOS 7.2 и Ubuntu 16.04 с версиями 3.10.0-862.11.6 и 4.4.0-130 соответственно. Но оба ядра отставали от последней версии, которой на тот момент была 4.19-rc2.

Мы развернули новую виртуальную машину Ubuntu 16.04 на платформе Google Cloud Platform (GCP), клонировали последнее ядро Linux и настроили среду разработки ядра. Мы создали файл .config с помощью menuconfig и проверили, что:

  1. Драйвер NFS скомпилирован как модуль ( CONFIG_NFSD=m ). указаны верно.

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

Мы убедились, что проблема устаревшего файла никуда не делась в последней версии ядра. Мы спросили себя:

  1. Где именно возникает проблема?
  2. Почему это происходит в NFS v4.0, но не в v4.1?

Чтобы ответить на эти вопросы, мы углубились в исходный код NFS. Отладчика ядра у нас не было, так что мы посылали в исходный код вызовы двух типов:

  1. pr_info() ( раньше это был printk ).
  2. dump_stack() : он показывает трассировку стека для текущего вызова функции.

Например, первым делом мы подключились к функции nfs4_file_open() в fs/nfs/nfs4file.c :

После каждого изменения мы перекомпилировали модуль и переустанавливали его в ядро с помощью команд:

Что это за вызовы do_dentry_open и vfs_open ? В Linux есть виртуальная файловая система (virtual filesystem, VFS) — слой абстракции, который предоставляет общий интерфейс для всех файловых систем. В документации по VFS говорится:

VFS реализует open(2), stat(2), chmod(2) и другие системные вызовы. Система VFS использует аргумент имени пути, который им передается, для поиска по кэшу записей каталога (dentry-кэш, или dcache). Это обеспечивает очень быстрый механизм поиска, который преобразует имя пути (или имя файла) в конкретный dentry. Dentry находятся в RAM и никогда не сохраняются на диске — они существуют только для производительности.

И нас осенило — а что если проблема в dentry-кэше?

Мы заметили, что dentry-кэш обычно проверяется в fs/nfs/dir.c . Особенно нас заинтересовала функция nfs4_lookup_revalidate() , и в качестве эксперимента мы заставили ее сработать раньше:

И в этом эксперименте проблема устаревшего файла не возникла! Наконец, мы напали на след.

Чтобы узнать, почему проблема не возникала в NFS v4.1, мы добавили вызовы pr_info() в каждый блок if в этой функции. Мы поэкспериментировали с NFS v4.0 и v4.1 и нашли особое условие в версии v4.1:

Что такое NFS_CAP_ATOMIC_OPEN_V1 ? В этом патче к ядру говорится, что это функция версии NFS v4.1, и код в fs/nfs/nfs4proc.c подтвердил, что этот параметр есть в v4.1, но отсутствует в v4.0:

Поэтому версии вели себя по-разному — в v4.1 goto no_open вызывает больше проверок в функции nfs_lookup_revalidate() , а в v4.0 функция nfs4_lookup_revalidate() возвращается раньше. И как мы в итоге решили проблему?

Решение

Оказывается, исправление для бага NFS v4.0 было глубже в базе кода, чем мы думали. Тронд хорошо описал это в патче:

Нужно сделать так, чтобы inode и dentry правильно перепроверялись, когда открывается уже открытый файл. Сейчас мы не перепроверяем ни то, ни другое в NFSv4.0, потому что открытый файл кэширован. Давайте это исправим и будем кэшировать открытые файлы только в особых случаях — для восстановления открытых файлов и возврата делегирования.

Мы убедились, что это исправление решило проблему устаревшего файла, и отправили отчеты о багах командам Ubuntu и RedHat.

Мы прекрасно понимали, что изменения окажутся в стабильной версии ядра еще не скоро, так что добавили временное решение этой проблемы в Gitaly. Мы поэкспериментировали и проверили, что вызов stat() в файле packed-refs заставляет ядро перепроверять переименованный файл в dentry-кэше. Для простоты мы реализовали это в Gitaly для любых файловых систем, не только для NFS. Проверка выполняется только один раз, прежде чем Gitaly открывает репозиторий, а для других файлов уже есть другие вызовы stat() .

Чему мы научились

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

Огромное спасибо Тронду Мюклебусту за то, что исправил проблему, и Брюсу Филдсу за то, что ответил на наши вопросы и помог разобраться в NFS. Именно за такую отзывчивость и профессионализм мы ценим сообщество разработчиков открытого исходного кода.

Читайте также: