So verkleinern Sie das Root-Dateisystem, ohne eine Live-CD zu booten

93

Ich muss die Partitionen eines Systems neu anordnen, um Daten, die sich zuvor unter dem Root-Dateisystem befanden, in dedizierte Einhängepunkte zu verschieben. Die Volumes befinden sich alle in LVM, daher ist dies relativ einfach: Erstellen Sie neue Volumes, verschieben Sie Daten in diese, verkleinern Sie das Root-Dateisystem und hängen Sie die neuen Volumes an den entsprechenden Stellen ein.

Das Problem ist Schritt 3, bei dem das Root-Dateisystem verkleinert wird. Die beteiligten Dateisysteme sind ext4, daher wird die Online-Größenänderung unterstützt. Im eingebundenen Zustand können die Dateisysteme jedoch nur vergrößert werden. Um die Partition zu verkleinern, müssen Sie sie aushängen, was für die Root-Partition im normalen Betrieb natürlich nicht möglich ist.

Bei den Antworten im Internet geht es anscheinend darum, eine Live-CD oder ein anderes Rettungsmedium zu booten, den Verkleinerungsvorgang auszuführen und anschließend das installierte System neu zu starten. Es handelt sich jedoch um ein Remote-System, auf das ich nur über SSH zugreifen kann. Ich kann neu starten, aber das Booten einer Rettungsdisk und das Ausführen von Vorgängen über die Konsole ist nicht möglich.

Wie kann ich das Root-Dateisystem aushängen, während der Remote-Shell-Zugriff erhalten bleibt?

Tom Hunt
quelle
Gibt es eine Möglichkeit, das Root-Dateisystem vorübergehend auf einem anderen Server bereitzustellen? zB eine andere VM hochfahren und ihr dieses Laufwerk präsentieren?
Steve
Der Server ist physisch, also nein.
Tom Hunt
4
Kopiere root nach tmpfs und pivot_rootdort hinein. Ein Beispiel hier dreamlayers.blogspot.co.uk/2012/10/running-linux-from-ram.html - es ist schwierig, aber wenn Sie eine Testbox zum Anprobieren haben, ist es eine Überlegung wert.
Steve
1
Ein weiteres Beispiel hier, wo Remote - Zugriff über SSH wird als ivarch.com/blogs/oss/2007/01/...
steve
2
Wenn das Root-LVM klein genug ist, können Sie es auf ein anderes LVM klonen und ein Boot-Restaurant (temp new default) in grub erstellen, um es zu verwenden, und dann von dort booten (es zu Ihrem "Live-System" machen)
Rabin

Antworten:

170

Bei der Lösung dieses Problems waren die Informationen unter http://www.ivarch.com/blogs/oss/2007/01/resize-a-live-root-fs-a-howto.shtml von entscheidender Bedeutung. Dieser Leitfaden bezieht sich jedoch auf eine sehr alte Version von RHEL, und verschiedene Informationen waren veraltet.

Die folgenden Anweisungen sind für die Arbeit mit CentOS 7 konzipiert, sollten jedoch problemlos auf jede Distribution übertragbar sein, die systemd ausführt. Alle Befehle werden als root ausgeführt.

  1. Stellen Sie sicher, dass sich das System in einem stabilen Zustand befindet

    Stellen Sie sicher, dass niemand anderes es benutzt und nichts anderes Wichtiges vor sich geht. Es ist wahrscheinlich eine gute Idee, Service-Providing-Einheiten wie httpd oder ftpd zu stoppen, um sicherzustellen, dass externe Verbindungen die Dinge in der Mitte nicht stören.

    systemctl stop httpd
    systemctl stop nfs-server
    # and so on....
    
  2. Hängen Sie alle nicht verwendeten Dateisysteme aus

    umount -a
    

    Hiermit wird eine Reihe von Warnungen ausgegeben, dass das Ziel beschäftigt ist, und zwar für das Root-Volume selbst und für verschiedene temporäre / System-FSs. Diese können vorerst ignoriert werden. Wichtig ist, dass außer dem Root-Dateisystem keine Dateisysteme auf der Festplatte gemountet bleiben. Überprüfen Sie dies:

    # mount alone provides the info, but column makes it possible to read
    mount | column -t
    

    Wenn Sie feststellen, dass noch Dateisysteme auf der Festplatte eingehängt sind, wird noch etwas ausgeführt, das nicht ausgeführt werden sollte. Überprüfen Sie, was es verwendet fuser:

    # if necessary:
    yum install psmisc
    # then:
    fuser -vm <mountpoint>
    systemctl stop <whatever>
    umount -a
    # repeat as required...
    
  3. Machen Sie die temporäre Wurzel

    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/
    

    Dies schafft ein sehr minimales Root-System, das (unter anderem) die Anzeige von Manpages (nein /usr/share), Anpassungen auf Benutzerebene (nein /rootoder /home) und so weiter unterbricht. Dies ist beabsichtigt, da es eine Ermutigung darstellt, nicht länger als nötig in einem solchen von Geschworenen manipulierten Wurzelsystem zu bleiben.

    An dieser Stelle sollten Sie auch sicherstellen, dass die gesamte erforderliche Software installiert ist, da dies den Paketmanager sicher zum Erliegen bringt. Sehen Sie sich alle Schritte durch und stellen Sie sicher, dass Sie über die erforderlichen ausführbaren Dateien verfügen.

  4. Pivot in die Wurzel

    mount --make-rprivate / # necessary for pivot_root to work
    pivot_root /tmp/tmproot /tmp/tmproot/oldroot
    for i in dev proc sys run; do mount --move /oldroot/$i /$i; done
    

    systemd bewirkt, dass Bereitstellungen standardmäßig die Freigabe von Teilstrukturen zulassen (wie bei mount --make-shared), und dies schlägt pivot_rootfehl. Daher schalten wir dies global mit ab mount --make-rprivate /. System- und temporäre Dateisysteme werden vollständig in das neue Stammverzeichnis verschoben. Dies ist notwendig, damit es überhaupt funktioniert. Die Sockets für die Kommunikation mit systemd befinden sich unter anderem in diesem Ordner, /runsodass laufende Prozesse nicht geschlossen werden können.

  5. Stellen Sie sicher, dass der Remotezugriff die Umstellung überstanden hat

    systemctl restart sshd
    systemctl status sshd
    

    Stellen Sie nach dem Neustart von sshd sicher, dass Sie einsteigen können, indem Sie ein anderes Terminal öffnen und sich erneut über ssh mit dem Computer verbinden. Wenn Sie das nicht können, beheben Sie das Problem, bevor Sie fortfahren.

    Sobald Sie überprüft haben, dass Sie wieder eine Verbindung herstellen können, beenden Sie die aktuell verwendete Shell und stellen Sie die Verbindung wieder her. Dies ermöglicht das sshdVerlassen der verbleibenden Gabel und stellt sicher, dass die neue Gabel nicht hält /oldroot.

  6. Schließe alles mit der alten Wurzel

    fuser -vm /oldroot
    

    Dadurch wird eine Liste der Prozesse gedruckt, die sich noch im alten Stammverzeichnis befinden. Auf meinem System sah es so aus:

                 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 F..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
    

    Sie müssen sich mit jedem dieser Prozesse befassen, bevor Sie die Bereitstellung aufheben können /oldroot. Der Brute-Force-Ansatz ist einfach kill $PIDfür jeden, aber dies kann die Dinge zerbrechen. Um es sanfter zu machen:

    systemctl | grep running
    

    Dadurch wird eine Liste der ausgeführten Dienste erstellt. Sie sollten in der Lage sein, dies mit der Liste der Prozesse zu korrelieren, die gehalten werden, /oldrootund diese dann systemctl restartfür jeden von ihnen ausgeben . Einige Dienste lehnen es ab, im temporären Stammverzeichnis aufzurufen und in einen fehlerhaften Status zu wechseln. diese sind für den Moment nicht wirklich wichtig.

    Wenn es sich bei dem Root-Laufwerk, dessen Größe Sie ändern möchten, um ein LVM-Laufwerk handelt, müssen Sie möglicherweise auch einige andere ausgeführte Dienste neu starten, auch wenn sie nicht in der von erstellten Liste angezeigt werden fuser -vm /oldroot. Wenn Sie feststellen, dass Sie die Größe eines LVM-Laufwerks unter Schritt 7 nicht ändern können, versuchen Sie es systemctl restart systemd-udevd.

    Einige Vorgänge lassen sich nicht einfach abwickeln systemctl restart. Für mich gehörten diese dazu auditd(die nicht gerne über getötet werden systemctl, und wollten also nur ein kill -15). Diese können individuell behandelt werden.

    Der letzte Prozess, den Sie normalerweise finden, ist systemdselbst. Führen Sie dazu systemctl daemon-reexec.

    Sobald Sie fertig sind, sollte die Tabelle folgendermaßen aussehen:

                 USER        PID ACCESS COMMAND
    /oldroot:    root     kernel mount /oldroot
    
  7. Hänge die alte Wurzel ab

    umount /oldroot
    

    An dieser Stelle können Sie alle erforderlichen Manipulationen durchführen. Die ursprüngliche Frage erforderte einen einfachen resize2fsAufruf, aber Sie können hier tun, was Sie wollen. Ein weiterer Anwendungsfall ist die Übertragung des Root-Dateisystems von einer einfachen Partition nach LVM / RAID / whatever.

  8. Drehen Sie die Wurzel zurück

    mount <blockdev> /oldroot
    mount --make-rprivate / # again
    pivot_root /oldroot /oldroot/tmp/tmproot
    for i in dev proc sys run; do mount --move /tmp/tmproot/$i /$i; done
    

    Dies ist eine einfache Umkehrung von Schritt 4.

  9. Entsorgen Sie die temporäre Wurzel

    Wiederholen Sie die Schritte 5 und 6, außer wenn Sie /tmp/tmprootanstelle von verwenden /oldroot. Dann:

    umount /tmp/tmproot
    rmdir /tmp/tmproot
    

    Da es ein tmpfs ist, löst sich die temporäre Wurzel an diesem Punkt im Äther auf, um nie wieder gesehen zu werden.

  10. Bringen Sie die Dinge wieder an ihren Platz

    Hängen Sie die Dateisysteme erneut ein:

    mount -a
    

    Zu diesem Zeitpunkt sollten Sie auch die in Schritt 7 vorgenommenen Anpassungen aktualisieren /etc/fstabund diesen grub.cfgentsprechend anpassen.

    Starten Sie alle fehlgeschlagenen Dienste neu:

    systemctl | grep failed
    systemctl restart <whatever>
    

    Freigegebene Teilbäume wieder zulassen:

    mount --make-rshared /
    

    Starten Sie die gestoppten Service Units - Sie können diesen einzelnen Befehl verwenden:

    systemctl isolate default.target
    

Und du bist fertig.

Vielen Dank an Andrew Wood, der diese Entwicklung auf RHEL4 ausgearbeitet hat, und Steve, der mir die Verbindung zu ersteren zur Verfügung gestellt hat.

Tom Hunt
quelle
11
Geniale Antwort. Fast magisch und sehr klar und unkompliziert. Verwendet es mit Debian VPS ohne irgendwelche Probleme (musste umount /oldroot/bootnatürlich auf Stufe 6 sein). Ich verknüpfe Ihre Antwort mit anderen SE-Fragen, die keine Antwort oder negative Antwort hatten.
Vaab
3
Und gelöst, das Problem war wie @vaab angegeben; Sie müssen umount /oldroot/bootvor Ihnenumount /oldroot
ToBeReplaced
3
Es geht darum, das Root-Dateisystem ohne physische Konsole zu deaktivieren und zu manipulieren. Soweit ich weiß, gibt es keine Möglichkeit, einen Dienst, der von einer Partition liest, geöffnet zu halten, während die Bereitstellung dieser Partition aufgehoben wird. Wenn Ihr Dienst den Stamm-FS nicht berührt, können Sie ihn mit mount --movetmpfs geöffnet lassen , dies wird jedoch nicht unterstützt.
Tom Hunt
2
Sie müssen die Betriebssystemfunktionen verwenden, um den init-Daemon neu zu starten. Ich habe noch nie Emporkömmling verwendet, aber wiki.ubuntu.com/FoundationsTeam/Specs/… schlägt vor, dass telinit udas tun könnte, was Sie wollen.
Tom Hunt
3
Ein weiterer Fehler, auf den ich gestoßen bin: / tmp ist eine Ramdisk auf meinem System, daher habe ich eine Ramdisk auf geladen /oldroot/tmp, die mich daran gehindert hat, die Bereitstellung aufzuheben /oldroot, aber weder in fusernoch in der lsofAusgabe angezeigt wird . Ich habe ein bisschen auf systemd gestarrt, um das herauszufinden ...
Chris Kitching
7

Wenn Sie sicher sind, was Sie tun - also nicht experimentieren, können Sie sich in initrd einklinken, was der nicht interaktive und schnelle Weg ist.

Auf einem Debian-basierten System ist dies wie.

Siehe Code: https://github.com/szepeviktor/debian-server-tools/blob/master/debian-resizefs.sh

Es gibt ein anderes Beispiel: https://github.com/szepeviktor/debian-server-tools/blob/master/debian-convert-ext3-ext4.sh

Szépe Viktor
quelle
Wenn Sie eine Antwort geben, ist es vorzuziehen, eine Erklärung zu geben , WARUM Ihre Antwort die richtige ist.
Stephen Rauch
1
Dies ist ein vernünftiger Ansatz. Ich mag meine, weil ich die notwendigen Manipulationen interaktiv ausführen kann; Dieser ist jedoch wahrscheinlich schneller. Es kann sinnvoll sein, einige Details in die Antwort selbst einzuarbeiten oder andere Plattformen in Betracht zu ziehen (anscheinend funktioniert dieser allgemeine Ansatz immer noch mit dracut oder mkinitcpio oder einem anderen vage modernen initramfs-Generator).
Tom Hunt
Sorry @ stephen-rauch Ich habe nur auf die Idee hingewiesen, nicht auf die Ausführung.
Szépe Viktor