438 Stimmen

Was ist der (beste) Weg, Berechtigungen für gemeinsam genutzte Docker-Volumes zu verwalten?

Ich habe eine Weile mit Docker experimentiert und stoße immer wieder auf das gleiche Problem im Umgang mit persistenten Daten.

Ich erstelle mein Dockerfile und exponiere ein Volume oder verwende --volumes-from, um einen Host-Ordner in meinem Container einzubinden.

Welche Berechtigungen soll ich dem gemeinsam genutzten Volume auf dem Host zuweisen?

Ich kann mir zwei Optionen vorstellen:

  • Bisher habe ich allen Lese- und Schreibzugriff gewährt, damit ich vom Docker-Container aus in den Ordner schreiben kann.

  • Die Benutzer vom Host in den Container mappen, um genauere Berechtigungen zu vergeben. Ich bin mir jedoch nicht sicher, ob dies möglich ist und habe dazu nicht viel gefunden. Bisher kann ich nur den Container als bestimmten Benutzer ausführen: docker run -i -t -user="meinbenutzer" postgres, aber dieser Benutzer hat eine andere UID als mein Host-meinbenutzer, sodass die Berechtigungen nicht funktionieren. Außerdem bin ich mir unsicher, ob das Zuordnen der Benutzer Sicherheitsrisiken birgt.

Gibt es andere Alternativen?

Wie geht ihr mit diesem Problem um?

174voto

Raman Punkte 14828

UPDATE 2016-03-02: Mit Docker 1.9.0 hat Docker benannte Volumes, welche Data-only Container ersetzen. Die unten stehende Antwort, sowie mein verlinkter Blog-Beitrag, haben weiterhin Wert im Sinne von wie man über Daten innerhalb von Docker nachdenkt, aber erwägen Sie die Verwendung von benannten Volumes, um das unten beschriebene Muster umzusetzen, anstatt Data-Container zu verwenden.


Ich glaube, der kanonische Weg, dies zu lösen, besteht darin, Data-only-Container zu verwenden. Mit diesem Ansatz erfolgt der gesamte Zugriff auf die Volumendaten über Container, die -volumes-from den Data-Container verwenden, sodass die Host-UID/GID keine Rolle spielt.

Ein Beispiel, das in der Dokumentation genannt wird, ist das Sichern eines Datenvolumens. Dazu wird ein weiterer Container verwendet, um das Backup über tar durchzuführen, und auch dieser verwendet -volumes-from, um das Volumen einzubinden. Daher denke ich, dass der Schlüsselpunkt ist zu verstehen: Anstatt darüber nachzudenken, wie man Zugriff auf die Daten auf dem Host mit den richtigen Berechtigungen bekommt, überlegen Sie, wie Sie was auch immer Sie benötigen -- Backups, durchsuchen usw. -- über einen anderen Container durchführen. Die Container selbst müssen konsistente UID/GIDs verwenden, aber sie müssen nicht auf etwas auf dem Host gemappt werden, um portabel zu bleiben.

Das ist auch für mich relativ neu, aber wenn Sie einen bestimmten Anwendungsfall haben, fühlen Sie sich frei zu kommentieren und ich werde versuchen, die Antwort zu erweitern.

UPDATE: Für den in den Kommentaren genannten Anwendungsfall könnten Sie ein Image some/graphite haben, um Graphite auszuführen, und ein Image some/graphitedata als Data-Container. Also, unter Vernachlässigung von Ports und dergleichen, sieht das Dockerfile des Images some/graphitedata

VON debian:jessie
# Fügen Sie zuerst unseren Benutzer und unsere Gruppe hinzu, um sicherzustellen, dass ihre IDs unabhängig von anderen später hinzugefügten Abhängigkeiten konsistent zugewiesen werden
RUN groupadd -r graphite \
  && useradd -r -g graphite graphite
RUN mkdir -p /data/graphite \
  && chown -R graphite:graphite /data/graphite
VOLUME /data/graphite
USER graphite
CMD ["echo", "Data-Container für Graphite"]

Erstellen und erstellen Sie den Data-Container:

docker build -t some/graphitedata Dockerfile
docker run --name graphitedata some/graphitedata

Das some/graphite Dockerfile sollte auch die gleichen UID/GIDs erhalten, daher könnte es ungefähr so aussehen:

VON debian:jessie
# Fügen Sie zuerst unseren Benutzer und unsere Gruppe hinzu, um sicherzustellen, dass ihre IDs unabhängig von anderen später hinzugefügten Abhängigkeiten konsistent zugewiesen werden
RUN groupadd -r graphite \
  && useradd -r -g graphite graphite
# ... Graphite-Installation ...
VOLUME /data/graphite
USER graphite
CMD ["/bin/graphite"]

Und es würde folgendermaßen ausgeführt:

docker run --volumes-from=graphitedata some/graphite

Ok, das gibt uns also unseren Graphite-Container und den dazugehörigen Data-Only-Container mit dem richtigen Benutzer/Gruppe (beachten Sie, dass Sie den some/graphite-Container auch für den Data-Container verwenden könnten, indem Sie den entrypoing/CMD beim Ausführen überschreiben, aber sie als separate Images zu haben ist meiner Meinung nach klarer).

Nun, sagen wir, Sie möchten etwas im Datenordner bearbeiten. Anstatt das Volumen an den Host zu binden und es dort zu bearbeiten, erstellen Sie einen neuen Container, um diese Aufgabe zu erledigen. Nennen wir ihn some/graphitetools. Erstellen wir auch den entsprechenden Benutzer/Gruppe, genauso wie beim some/graphite-Image.

VON debian:jessie
# Fügen Sie zuerst unseren Benutzer und unsere Gruppe hinzu, um sicherzustellen, dass ihre IDs unabhängig von anderen später hinzugefügten Abhängigkeiten konsistent zugewiesen werden
RUN groupadd -r graphite \
  && useradd -r -g graphite graphite
VOLUME /data/graphite
USER graphite
CMD ["/bin/bash"]

Sie könnten dies DRY machen, indem Sie im Dockerfile von some/graphite oder some/graphitedata erben, oder anstatt ein neues Image zu erstellen, nur eines der vorhandenen wiederverwenden (entrypoint/CMD nach Bedarf überschreiben).

Jetzt führen Sie einfach aus:

docker run -ti --rm --volumes-from=graphitedata some/graphitetools

und dann vi /data/graphite/whatever.txt. Das funktioniert perfekt, weil alle Container denselben Graphite-Benutzer mit übereinstimmender UID/GID haben.

Da Sie /data/graphite nie vom Host einbinden, spielt es keine Rolle, wie die Host-UID/GID auf die in den Containern graphite und graphitetools definierten UID/GID abgebildet werden. Diese Container können nun auf jeden Host bereitgestellt werden und werden weiterhin perfekt funktionieren.

Das Schöne daran ist, dass graphitetools alle möglichen nützlichen Dienstprogramme und Skripte enthalten könnte, die Sie jetzt auch auf portable Weise bereitstellen können.

UPDATE 2: Nachdem ich diese Antwort geschrieben habe, habe ich beschlossen, einen umfassenderen Blog-Beitrag zu diesem Ansatz zu schreiben. Ich hoffe, es hilft.

UPDATE 3: Ich habe diese Antwort korrigiert und weitere Details hinzugefügt. Sie enthielt zuvor einige falsche Annahmen über den Besitz und Berechtigungen -- der Besitz wird normalerweise zum Zeitpunkt der Volumenerstellung d. h. im Data-Container zugewiesen, weil dies der Zeitpunkt ist, zu dem das Volumen erstellt wird. Siehe diesen Blog. Dies ist jedoch keine Voraussetzung -- Sie können den Data-Container einfach als "Referenz/Handle" verwenden und den Besitz/Berechtigungen in einem anderen Container über chown in einem entrypoint setzen, der mit gosu endet, um den Befehl als den richtigen Benutzer auszuführen. Wenn sich jemand für diesen Ansatz interessiert, kommentieren Sie bitte, und ich kann Links zu einem Beispiel bereitstellen, das diesen Ansatz verwendet.

84voto

Dimitris Punkte 1196

Eine sehr elegante Lösung kann im offiziellen Redis-Image und im Allgemeinen in allen offiziellen Images gesehen werden.

Schritt-für-Schritt beschrieben:

  • Erstellen Sie den Redis-Benutzer/die Redis-Gruppe, bevor Sie etwas anderes tun

Wie in den Dockerfile-Kommentaren zu sehen:

Fügen Sie unseren Benutzer und unsere Gruppe zuerst hinzu, um sicherzustellen, dass ihre IDs unabhängig von den hinzugefügten Abhängigkeiten konsistent zugewiesen werden

  • Installieren Sie gosu mit dem Dockerfile

gosu ist eine Alternative zu su / sudo für einfaches Absteigen vom Root-Benutzer. (Redis wird immer mit dem Benutzer redis ausgeführt)

  • Konfigurieren Sie das /data-Volume und setzen Sie es als Arbeitsverzeichnis

Indem wir das /data-Volume mit dem Befehl VOLUME /data konfigurieren, haben wir jetzt ein separates Volume, das entweder ein Docker-Volume ist oder an ein Host-Verzeichnis gebunden werden kann.

Indem wir es als Arbeitsverzeichnis festlegen (WORKDIR /data), wird es zum Standardverzeichnis, von dem aus Befehle ausgeführt werden.

  • Fügen Sie die Datei docker-entrypoint hinzu und setzen Sie sie als ENTRYPOINT mit dem Standard-CMD redis-server

Dies bedeutet, dass alle Containerausführungen über das docker-entrypoint-Skript laufen und standardmäßig der Befehl redis-server ausgeführt wird.

docker-entrypoint ist ein Skript, das eine einfache Funktion ausführt: Ändert den Besitz des aktuellen Verzeichnisses (/data) und steigt vom Benutzer root auf den Benutzer redis herab, um redis-server auszuführen. (Wenn der ausgeführte Befehl nicht redis-server ist, wird der Befehl direkt ausgeführt.)

Dies hat folgende Auswirkung

Wenn das /data-Verzeichnis an das Host-Verzeichnis gebunden ist, bereitet der docker-entrypoint die Benutzerberechtigungen vor, bevor redis-server unter dem Benutzer redis ausgeführt wird.

Dies gibt Ihnen die Gewissheit, dass keinerlei Einrichtung erforderlich ist, um den Container unter einer beliebigen Volumenkonfiguration auszuführen.

Natürlich müssen Sie sicherstellen, dass Sie, wenn Sie das Volume zwischen verschiedenen Images teilen möchten, dieselbe Benutzer-ID/Gruppen-ID verwenden, da andernfalls der letzte Container die Benutzerberechtigungen des vorherigen übernimmt.

40voto

Hamy Punkte 19372

Dies ist wohl nicht unbedingt der beste Weg für die meisten Umstände, aber es wurde bisher noch nicht erwähnt, also könnte es vielleicht jemandem helfen.

  1. Bind-Mount des Host-Volumes

    Der Host-Ordner FOOBAR ist im Container unter /volume/FOOBAR gemountet

  2. Ändern Sie das Startskript Ihres Containers, um die GID des gewünschten Volumes herauszufinden

    $ TARGET_GID=$(stat -c "%g" /volume/FOOBAR)

  3. Stellen Sie sicher, dass Ihr Benutzer zu einer Gruppe mit dieser GID gehört (möglicherweise müssen Sie eine neue Gruppe erstellen). In diesem Beispiel gebe ich vor, dass meine Software als Benutzer nobody innerhalb des Containers ausgeführt wird, daher möchte ich sicherstellen, dass nobody zu einer Gruppe mit einer Gruppen-ID gleich TARGET_GID gehört

    EXISTS=$(cat /etc/group | grep $TARGET_GID | wc -l)

    Neue Gruppe mit der Ziel-GID erstellen und Benutzer nobody hinzufügen

    if [ $EXISTS == "0" ]; then groupadd -g $TARGET_GID tempgroup usermod -a -G tempgroup nobody else

    GID existiert, Gruppennamen finden und hinzufügen

    GROUP=$(getent group $TARGET_GID | cut -d: -f1)
    usermod -a -G $GROUP nobody

    fi

Ich mag das, weil ich die Gruppenberechtigungen für meine Host-Volumes einfach ändern kann und weiß, dass diese aktualisierten Berechtigungen auch im Docker-Container gelten. Dies geschieht ohne irgendwelche Berechtigungs- oder Besitzänderungen an meinen Host-Ordnern/Dateien, was mich glücklich macht.

Ich mag das nicht, weil es davon ausgeht, dass es keine Gefahr gibt, sich selbst zu beliebigen Gruppen innerhalb des Containers hinzuzufügen, die zufällig eine GID verwenden, die Sie möchten. Es kann nicht mit einer USER-Klausel in einem Dockerfile verwendet werden (es sei denn, dieser Benutzer hat Root-Rechte, nehme ich an). Außerdem schreit es nach Flickschusterei ;-)

Wenn Sie hart im Nehmen sind, können Sie dies natürlich auf viele Arten erweitern - z. B. nach allen Gruppen in Unterverzeichnissen, mehreren Volumes usw. suchen.

28voto

Alex Myznikov Punkte 820

Wie du, war auch ich auf der Suche nach einer Möglichkeit, Benutzer/Gruppen von host zu Docker-Containern zuzuordnen, und dies ist der kürzeste Weg, den ich bisher gefunden habe:

  version: "3"
  services:
    my-service:
      .....
      volumes:
        # uid/gid-Listen vom Host übernehmen
        - /etc/passwd:/etc/passwd:ro
        - /etc/group:/etc/group:ro
        # Konfigurationsordner einbinden
        - Pfad-zu-meinen-Konfigs/my-service:/etc/my-service:ro
        .....

Dies ist ein Auszug aus meiner docker-compose.yml.

Die Idee ist, (im Nur-Lesen-Modus) Benutzer/Gruppen-Listen vom Host in den Container einzubinden. Nachdem der Container gestartet wurde, entspricht die Zuordnung von uid->Benutzername (sowie für Gruppen) im Container dem des Hosts. Jetzt kannst du die Benutzer/Gruppen-Einstellungen für deinen Service innerhalb des Containers konfigurieren, als ob er auf deinem Host-System ausgeführt würde.

Wenn du entscheidest, deinen Container auf einen anderen Host zu verschieben, musst du nur den Benutzernamen in der Service-Konfigurationsdatei auf das ändern, was auf diesem Host vorhanden ist.

21voto

FDisk Punkte 7253

Versuchen Sie, einen Befehl zur Dockerfile hinzuzufügen

RUN usermod -u 1000 www-data

Kredit geht an https://github.com/denderello/symfony-docker-example/issues/2#issuecomment-94387272

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X