So fügen Sie einem laufenden Docker-Container dynamisch ein Volume hinzu

So fügen Sie einem laufenden Docker-Container dynamisch ein Volume hinzu

Jemand hat mich schon einmal gefragt, ob es möglich ist, Volumes zu mounten, nachdem der Docker-Container gestartet wurde. Angesichts der Funktionsweise des mnt-Namespace dachte ich zunächst, dass dies schwierig zu erreichen wäre. Aber ich denke, jetzt ist es geschafft.

  • Kurz gesagt, um ein Datenträgervolume in einen laufenden Container einzubinden, müssen wir Folgendes tun:
  • Verwenden Sie nsenter, um das gesamte Dateisystem, das dieses Datenträgervolume enthält, an einem temporären Einhängepunkt einzuhängen.
  • Erstellen Sie eine Bind-Mount von dem bestimmten Ordner, den wir als Datenträgervolume verwenden möchten, zum Speicherort dieses Datenträgervolumes.

Umounten Sie den im ersten Schritt erstellten temporären Einhängepunkt.

Vorsichtsmaßnahmen

In den folgenden Beispielen habe ich absichtlich das $-Zeichen eingefügt, um anzuzeigen, dass es sich um eine Shell-Befehlszeilenaufforderung handelt, damit Sie leichter unterscheiden können, was Sie eingeben müssen und was der Computer antwortet. Für einige mehrzeilige Befehle verwende ich weiterhin >. Mir ist bewusst, dass die Befehle in den Beispielen dadurch nicht so einfach kopiert und eingefügt werden können. Wenn Sie den Code kopieren und einfügen möchten, sehen Sie sich das Beispielskript am Ende des Artikels an.

Detaillierte Schritte

In den folgenden Beispielen wird davon ausgegangen, dass Sie einen einfachen Container namens „Charlie“ mit dem folgenden Befehl gestartet haben:

$ docker run --name charlie -ti ubuntu bash

Was wir tun müssen, ist, den Hostordner /home/jpetazzo/Work/DOCKER/docker in das Verzeichnis /src im Container einzubinden. Okay, fangen wir an.

nsenter

Zuerst benötigen wir nsenter und das Hilfsskript docker-enter. Warum? Weil wir das Dateisystem aus dem Container mounten wollen. Aus Sicherheitsgründen lässt der Container dies nicht zu. Mit nsenter können wir die oben genannten Sicherheitsbeschränkungen umgehen und beliebige Befehle im Kontext des Containers (genauer gesagt des Namespace) ausführen. Dies erfordert natürlich Root-Rechte auf dem Docker-Host.

Die einfachste Möglichkeit, nsenter zu installieren, besteht darin, es zusammen mit dem Skript „docker-enter“ auszuführen:

$ docker run --rm -v /usr/local/bin:/target jpetazzo/nsenter

Weitere Einzelheiten finden Sie auf der Homepage des nsenter-Projekts.

Suchen Sie das Dateisystem

Wir möchten das Dateisystem, das den Hostordner (/home/jpetazzo/Work/DOCKER/docker) enthält, im Container mounten. Dann müssen wir herausfinden, welches Dateisystem dieses Verzeichnis enthält.

Zuerst müssen wir die Datei kanonisieren (oder dereferenzieren), falls es sich um einen symbolischen Link handelt oder ihr Pfad symbolische Links enthält:

$ readlink --canonicalize /home/jpetazzo/Arbeit/DOCKER/docker
/home/jpetazzo/go/src/github.com/docker/docker

Ha, es ist tatsächlich ein symbolischer Link! Lassen Sie uns dies in eine Umgebungsvariable einfügen:

$ HOSTPATH=/home/jpetazzo/Arbeit/DOCKER/docker
$REALPATH=$(readlink --kanonisieren Sie $HOSTPATH)

Als nächstes müssen wir herausfinden, welches Dateisystem diesen Pfad enthält. Wir tun dies mit einem etwas unerwarteten Tool: df:

$ df $REALPATH
Dateisystem 1K-Blöcke Verwendet Verfügbare Nutzung% Eingebunden auf
/sda2 245115308 156692700 86157700 65 % /home/jpetazzo

Verwenden Sie das Flag -P (um das POSIX-Format zu erzwingen, falls Sie ein exotisches df haben oder das df einer anderen Person, wenn Sie Docker auf Solaris oder BSD installieren) und fügen Sie das Ergebnis ebenfalls in eine Variable ein:

$ FILESYS=$(df -P $REALPATH | tail -n 1 | awk '{print $6}')

Suchen Sie das Gerät (und das Unterverzeichnis) des Dateisystems

Jetzt gibt es keine Bind-Mounts oder BTRFS-Subvolumes auf dem System, wir müssen nur in /proc/mounts nachsehen und das Gerät finden, das dem Dateisystem /home/jpetazzo entspricht. Aber in meinem System ist /home/jpetazzo ein Subvolume des BTRFS-Pools. Um die Subvolume-Informationen (oder Bind-Mount-Informationen) zu erhalten, müssen Sie /proc/self/moutinfo überprüfen.

Wenn Sie noch nie von Mountinfo gehört haben, sehen Sie sich die Kerneldokumentation für proc.txt an.

Holen Sie sich zunächst die Geräteinformationen des Dateisystems:

$ während des Lesens DEV MOUNT JUNK
> mache [ $MOUNT = $FILESYS ] und breche ab
> fertig </proc/mounts
$ echo $DEV
/dev/sda2

Als nächstes holen Sie sich die Subroot-Informationen (also den Pfad zum gemounteten Dateisystem):

$ während ABC SUBROOT MOUNT JUNK gelesen wird
> mache [ $MOUNT = $FILESYS ] und breche ab
> fertig < /proc/self/mountinfo 
$ echo $SUBROOT
/jpetazzo

sehr gut. Jetzt wissen wir, dass wir /dev/sda2 mounten müssen. Gehen Sie im Dateisystem zu /jpetazzo. Von hier aus können Sie den Rest des Pfads zur erforderlichen Datei abrufen (in diesem Beispiel /go/src/github.com/docker/docker ).
Berechnen wir den verbleibenden Weg:

$ SUBPATH=$(echo $REALPATH | sed s,^$FILESYS,,)

Hinweis: Diese Methode funktioniert nur, wenn der Pfad kein "," enthält. Wenn Ihr Pfad ein „“, „“ enthält und Sie das Verzeichnis mit der Methode in diesem Artikel mounten möchten, lassen Sie es mich bitte wissen. (Um dieses Problem zu lösen, muss ich die Shell-Triade aufrufen: Jessie, Soulshake, Tianon?)

Als letztes müssen Sie vor dem Betreten des Containers die Haupt- und Nebennummern des Blockgeräts ermitteln. Sie können stat verwenden:

$ stat --format "%t %T" $DEV
8 2

Beachten Sie, dass diese beiden Zahlen im Hexadezimalformat vorliegen. Wir werden sie später im Binärformat benötigen. Dies kann folgendermaßen umgerechnet werden:

$ DEVDEC=$(printf "%d %d" $(stat --format "0x%t 0x%T" $DEV))

Zusammenfassen

Es gibt noch einen letzten Schritt. Aus einem mir unerklärlichen Grund aktualisieren manche Dateisysteme (einschließlich BTRFS) das Gerätefeld in /proc/mounts, nachdem sie mehrmalig gemountet wurden. Das heißt, wenn wir im Container ein temporäres Blockgerät namens /tmpblkdev erstellen und es zum Mounten unseres eigenen Dateisystems verwenden, wird das Dateisystem (auf dem Hostcomputer!) als /tmpblkdev und nicht als /dev/sda2 angezeigt. Dies mag harmlos klingen, führt in der Praxis jedoch dazu, dass nachfolgende Versuche, auf das Blockgerät des Dateisystems zuzugreifen, fehlschlagen.

Kurz gesagt, wir möchten sicherstellen, dass sich die Blockgeräteknoten im Container am gleichen Pfad befinden wie auf dem Hostcomputer.

Sie müssen Folgendes tun:

$ docker-enter charlie --sh -c \
> "[ -b $DEV ] || mknod --mode 0600 $DEV b $DEVDEC"

Erstellen Sie einen temporären Einhängepunkt zum Einhängen des Dateisystems:

$ docker-enter charlie --mkdir /tmpmnt
$ docker-enter charlie --mount $DEV /tmpmnt

Stellen Sie sicher, dass der Volume-Einhängepunkt vorhanden ist, und hängen Sie das Volume per Bind-Mount ein:

$ docker-enter charlie --mkdir -p /src
$ docker-enter charlie --mount -o bind /tmpmnt/$SUBROOT/$SUBPATH /src

Löschen Sie den temporären Einhängepunkt:

$ docker-enter charlie --umount /tmpmnt
$ docker-enter charlie --rmdir /tmpmnt

(Wir löschen den Geräteknoten nicht. Es wäre vielleicht überflüssig, zunächst zu prüfen, ob das Gerät überhaupt existiert, aber es ist schon kompliziert genug.)

Mission erfüllt!

Automatisieren Sie alles

Der folgende Absatz kann direkt kopiert und eingefügt werden.

#!/bin/sh
setze -e
CONTAINER=charlie
HOSTPATH=/home/jpetazzo/Arbeit/DOCKER/docker
CONTPATH=/src

REALPATH=$(readlink --kanonisieren Sie $HOSTPATH)
FILESYS=$(df -P $REALPATH | tail -n 1 | awk '{print $6}')

während gelesen DEV MOUNT JUNK
mache [ $MOUNT = $FILESYS ] und breche ab 
fertig </proc/mounts
[ $MOUNT = $FILESYS ] # Plausibilitätsprüfung!

\während Sie ABC SUBROOT MOUNT JUNK lesen
\do [ $MOUNT = $FILESYS ] && break
\done < /proc/self/mountinfo 
[ $MOUNT = $FILESYS ] # Mount-Sanitätsprüfung!

SUBPATH=$(echo $REALPATH | sed s,^$FILESYS,,)
DEVDEC=$(printf "%d %d" $(stat --format "0x%t 0x%T" $DEV))

docker-enter $CONTAINER -- sh -c \
   "[ -b $DEV ] || mknod --mode 0600 $DEV b $DEVDEC"
docker-enter $CONTAINER --mkdir /tmpmnt
docker-enter $CONTAINER --mount $DEV /tmpmnt
docker-enter $CONTAINER --mkdir -p $CONTPATH
docker-enter $CONTAINER --mount -o bind /tmpmnt/$SUBROOT/$SUBPATH $CONTPATH
docker-enter $CONTAINER --umount /tmpmnt
docker-enter $CONTAINER --rmdir /tmpmnt

Status und Einschränkungen

Die obige Methode gilt nicht für Dateisysteme, die nicht auf Blockgeräten basieren. Sie funktioniert nur, wenn /proc/mounts den Blockgeräteknoten korrekt abrufen kann (wie oben erwähnt, ist dies nicht immer möglich). Außerdem habe ich dies nur in meiner eigenen Umgebung getestet, nicht in einer Cloud-Instanz oder so etwas, aber es würde mich interessieren, ob es dort auch zutrifft.

Das Obige ist der vollständige Inhalt dieses Artikels. Ich hoffe, er wird für jedermanns Studium hilfreich sein. Ich hoffe auch, dass jeder 123WORDPRESS.COM unterstützen wird.

Das könnte Sie auch interessieren:
  • Eine kurze Diskussion zum Docker-Lernen: Docker-Datenvolumen (Volumen)
  • Detaillierte Erläuterung der Container-Datenmengen und der Datenverwaltung in Docker
  • Detaillierte Erläuterung der Docker-Datenvolumenverwaltung
  • So implementieren Sie die Docker-Volume-Montage
  • Eine kurze Einführung in die Dockerfile-Anweisung VOLUME
  • Zwei Möglichkeiten zum Verwalten von Volumes in Docker

<<:  Detaillierte Erklärung des Cocoscreater-Prefabs

>>:  Sequenzimplementierungsmethode basierend auf MySQL

Artikel empfehlen

Löschen von Dateien mit Leerzeichen in Linux (keine Verzeichnisse)

In unserer täglichen Arbeit kommen wir oft mit Da...

Detaillierte Erklärung von mktemp, einem grundlegenden Linux-Befehl

mktemp Erstellen Sie auf sichere Weise temporäre ...

Schreiben Sie eine dynamische Uhr auf einer Webseite in HTML

Verwenden Sie HTML, um eine dynamische Web-Uhr zu...

Beispielcode zur Implementierung einer schwebenden Seitenbox basierend auf JS

Wenn die Bildlaufleiste nach unten gezogen wird, ...

Installationstutorial für Docker unter Linux

Das Docker-Paket ist bereits im Standard-Reposito...

js implementiert einen einzigen Klick zum Ändern der Tabelle

Pure js implementiert eine mit einem Klick bearbe...

Verwendung des Linux-Befehls ln

1. Befehlseinführung Mit dem Befehl ln werden Lin...

Detailliertes Tutorial zur Installation und Konfiguration von Nginx unter Centos7

Hinweis: Der grundlegende Verzeichnispfad für die...

Codebeispiele für allgemeine Vorgänge bei der Docker-Image-Verwaltung

Spiegelung ist auch eine der Kernkomponenten von ...

Installations-Tutorial zur neuesten MySQL-Version 8.0.17 mit Dekomprimierung

Ich persönlich denke, dass die dekomprimierte Ver...

So entfernen Sie Wagenrücklaufzeichen aus Text in Linux

Machen Sie sich keine Sorgen, wenn Sie das Wagenr...