В решении этой проблемы решающее значение имела информация, представленная на http://www.ivarch.com/blogs/oss/2007/01/resize-a-live-root-fs-a-howto.shtml. Однако, это руководство относится к очень старой версии RHEL, и различная информация была устаревшей.
Приведенные ниже инструкции предназначены для работы с CentOS 7, но они должны быть достаточно легко переносимы на любой дистрибутив, в котором работает systemd. Все команды выполняются от имени root.
Убедитесь, что система находится в стабильном состоянии
Убедитесь, что никто больше не использует ее и не происходит ничего важного. Возможно, хорошей идеей будет остановить такие предоставляющие сервисы устройства, как httpd или ftpd, просто для того, чтобы внешние соединения не нарушили работу в середине.
systemctl stop httpd
systemctl stop nfs-server
# и так далее....
Размонтируйте все неиспользуемые файловые системы
umount -a
Это выведет несколько предупреждений 'Target is busy', для самого корневого тома и для различных временных/системных ФС. На данный момент их можно проигнорировать. Важно то, что ни одна файловая система на диске не остается смонтированной, кроме самой корневой файловой системы. Проверьте это:
# mount сам по себе предоставляет информацию, но column делает возможным чтение
mount | column -t
Если вы видите, что файловые системы на диске все еще смонтированы, значит, что-то все еще работает, чего не должно быть. Проверьте, что это, используя fuser
:
#, если необходимо:
yum install psmisc
# затем:
fuser -vm
systemctl stop
umount -a
# повторите по мере необходимости...
Сделайте временного корня
mkdir /tmp/tmproot
mount -t tmpfs none /tmp/tmproot
mkdir /tmp/tmproot/{proc,sys,dev,run,usr,var,tmp,oldroot}
cp -ax /{bin,etc,mnt,sbin,lib,lib64} /tmp/tmproot/
cp -ax /usr/{bin,sbin,lib,lib64} /tmp/tmproot/usr/
cp -ax /var/{account, empty, lib, local, lock, nis, opt, preserve, run, spool, tmp, yp} /tmp/tmproot/var/
Это создает очень минимальную корневую систему, что нарушает (среди прочего) просмотр manpage (нет /usr/share
), настройки на уровне пользователя (нет /root
или /home
) и так далее. Это сделано намеренно, так как является стимулом не оставаться в такой придуманной корневой системе дольше, чем это необходимо.
На этом этапе вы также должны убедиться, что все необходимое программное обеспечение установлено, поскольку это также наверняка приведет к поломке менеджера пакетов. Просмотрите все шаги и убедитесь, что у вас есть необходимые исполняемые файлы.
Поворот в корень
mount --make-rprivate / # необходимо для работы pivot_root
pivot_root /tmp/tmproot /tmp/tmproot/oldroot
for i in dev proc sys run; do mount --move /oldroot/$i /$i; done
systemd заставляет монтирование разрешать совместное использование поддеревьев по умолчанию (как в mount --make-shared
), и это вызывает ошибку pivot_root
. Следовательно, мы отключаем это глобально с помощью mount --make-rprivate /
. Системная и временная файловые системы оптом перемещаются в новый корень. Это необходимо для того, чтобы система вообще работала; сокеты для связи с systemd, среди прочего, живут в /run
, и поэтому нет способа заставить запущенные процессы закрыть его.
Обеспечение удаленного доступа пережило переключение
systemctl restart sshd
systemctl status sshd
После перезапуска sshd убедитесь, что вы можете войти в систему, открыв другой терминал и снова подключившись к машине по ssh. Если вы не можете, устраните проблему, прежде чем двигаться дальше.
Убедившись, что вы можете снова подключиться, выйдите из оболочки, которую вы сейчас используете, и подключитесь снова. Это позволит оставшемуся форку sshd
выйти и гарантирует, что новый форк не удерживает /oldroot
.
Закройте все, что все еще использует старый root
fuser -vm /oldroot
Это выведет список процессов, которые все еще держатся за старый корневой каталог. В моей системе он выглядел так:
USER PID ACCESS COMMAND
/oldroot: root kernel mount /oldroot
root 1 ...e. systemd
root 549 ...e. systemd-journal
root 563 ...e. lvmetad
root 581 f...e. systemd-udevd
root 700 ...e. auditd
root 723 ...e. NetworkManager
root 727 ...e. irqbalance
root 730 F...e. tuned
root 736 ...e. smartd
root 737 F...e. rsyslogd
root 741 ...e. abrtd
chrony 742 ...e. chronyd
root 743 ...e. abrt-watch-log
libstoragemgmt 745 ...e. lsmd
root 746 ...e. systemd-logind
dbus 747 ...e. dbus-daemon
root 753 ...ce. atd
root 754 ...e. crond
root 770 ...e. agetty
polkitd 782 ...e. polkitd
root 1682 F.ce. master
postfix 1714 ...ce. qmgr
postfix 12658 ..ce. pickup
Вам нужно разобраться с каждым из этих процессов, прежде чем вы сможете размонтировать /oldroot
. Грубый подход - просто убить $PID
для каждого, но это может все сломать. Чтобы сделать это более мягко:
systemctl | grep running
Это создаст список запущенных служб. Вы должны быть в состоянии соотнести его со списком процессов, хранящихся в /oldroot
, а затем выполнить systemctl restart
для каждого из них. Некоторые службы откажутся запускаться во временном корне и перейдут в состояние отказа; в данный момент это не имеет значения.
Если корневой диск, размер которого вы хотите изменить, является диском LVM, вам также может понадобиться перезапустить некоторые другие запущенные службы, даже если они не отображаются в списке, созданном командой fuser -vm /oldroot
. Если вы обнаружили, что не можете изменить размер диска LVM в шаге 7, попробуйте systemctl restart systemd-udevd
.
С некоторыми процессами нельзя справиться с помощью простого systemctl restart
. У меня это был auditd
(который не любит быть убитым через systemctl
, и поэтому просто хотел kill -15
). С ними можно разобраться по отдельности.
Последним процессом, который вы обнаружите, обычно является systemd
. Для этого выполните systemctl daemon-reexec
.
После этого таблица должна выглядеть так:
USER PID ACCESS COMMAND
/oldroot: root kernel mount /oldroot
Размонтируйте старый root
umount /oldroot
На этом этапе вы можете выполнить любые необходимые манипуляции. В исходном вопросе требовался простой вызов resize2fs
, но здесь вы можете делать все, что захотите; еще один случай использования - перенос корневой файловой системы с простого раздела на LVM/RAID/что угодно.
Перенос корня назад
mount /oldroot
mount --make-rprivate / # снова
pivot_root /oldroot /oldroot/tmp/tmproot
for i in dev proc sys run; do mount --move /tmp/tmproot/$i /$i; done
Это прямое изменение шага 4.
Удалите временный корень
Повторите шаги 5 и 6, только вместо /tmp/tmproot
используйте /oldroot
. Затем:
umount /tmp/tmproot
rmdir /tmp/tmproot
Поскольку это tmpfs, в этот момент временный корень растворяется в эфире, чтобы никогда больше не появиться.
Верните все на свои места
Снова смонтируйте файловые системы:
mount -a
На этом этапе вам также следует обновить /etc/fstab
и grub.cfg
в соответствии с любыми корректировками, сделанными на шаге 7.
Перезапустите все неработающие службы:
systemctl | grep failed
systemctl restart
Снова разрешите общие поддеревья:
mount --make-rshared /
Запустите остановленные блоки обслуживания - вы можете использовать эту единственную команду:
systemctl isolate default.target
И все готово.
Большое спасибо Andrew Wood, который отработал эту эволюцию на RHEL4, и steve, который предоставил мне ссылку на первую версию.
Вероятно, есть менее подробные способы сделать это, но классическим решением будет что-то вроде:
#!/bin/bash
trap 'rm $TMP' 0
TMP=$(mktemp)
rm $TMP
mkfifo $TMP
tee < $TMP ${log:-/tmp/log.txt} &
exec > $TMP 2>&1
Само собой разумеется, что здесь есть серьезные проблемы с безопасностью и надежностью, поскольку любой другой процесс может читать или записывать из или в FIFO. Если вы хотите сделать что-то подобное, вам лучше использовать простую оболочку, которая направляет вывод вашего скрипта в tee
.