Freigeben über


Anleitung: Dauerhaftes Speichern von Daten in einer Container-App mithilfe von Volumes in VS Code

In diesem Lernprogramm lernen Sie, Daten in einer Containeranwendung zu speichern. Wenn Sie sie ausführen oder aktualisieren, sind die Daten weiterhin verfügbar. Es gibt zwei Haupttypen von Volumes, die zum Speichern von Daten verwendet werden. Dieses Tutorial konzentriert sich auf benannte Volumes.

Außerdem erfahren Sie mehr über Bindungsbereitstellungen, die den genauen Bereitstellungspunkt auf dem Host steuern. Sie können Bindungsbereitstellungen verwenden, um Daten persistent zu speichern, aber es können auch weitere Daten in Containern hinzugefügt werden. Wenn Sie an einer Anwendung arbeiten, können Sie eine Bindungsbereitstellung verwenden, um Quellcode in Container einzubinden, damit Codeänderungen erkannt werden können und darauf reagiert werden kann und damit Ihnen die Änderungen sofort angezeigt werden.

In diesem Tutorial werden auch Image-Layering, die Zwischenspeicherung von Layern und mehrstufige Builds vorgestellt.

In diesem Lernprogramm erfahren Sie, wie Sie:

  • Grundlegendes zu containerübergreifenden Daten
  • Persistentes Speichern von Daten mithilfe benannter Volumes
  • Verwenden von Bindungsbereitstellungen
  • Bildebene anzeigen.
  • Cacheabhängigkeiten.
  • Mehrstufige Builds verstehen.

Voraussetzungen

In diesem Lernprogramm wird das vorherige Lernprogramm zum Erstellen und Freigeben einer Container-App mit Visual Studio Code fortgesetzt. Beginnen Sie mit diesem, was Voraussetzungen enthält.

Verstehen von Daten in Containern

In diesem Abschnitt beginnen Sie zwei Container und erstellen jeweils eine Datei. Die in einem Container erstellten Dateien sind in einem anderen Nicht verfügbar.

  1. Starten Sie einen ubuntu Container mit diesem Befehl:

    docker run -d ubuntu bash -c "shuf -i 1-10000 -n 1 -o /data.txt && tail -f /dev/null"
    

    Dieser Befehl ruft mithilfe von && zwei Befehle auf. Der erste Teil wählt eine einzelne Zufallszahl aus und schreibt sie in /data.txt. Der zweite Befehl überwacht eine Datei, um den Container am Laufen zu halten.

  2. Klicken Sie im Container-Explorer im VS-Code mit der rechten Maustaste auf den Ubuntu-Container, und wählen Sie "Shell anfügen" aus.

    Screenshot der Erweiterung

    Ein Terminal wird geöffnet, das eine Shell im Ubuntu-Container ausführt.

  3. Führen Sie den folgenden Befehl aus, um den Inhalt der /data.txt Datei anzuzeigen.

    cat /data.txt
    

    Das Terminal zeigt eine Zahl zwischen 1 und 10000 an.

    Wenn Sie die Befehlszeile verwenden möchten, um dieses Ergebnis anzuzeigen, rufen Sie die Container-ID mithilfe des Befehls docker ps ab, und führen Sie den folgenden Befehl aus.

    docker exec <container-id> cat /data.txt
    
  4. Starten Sie einen weiteren ubuntu Container.

    docker run -d ubuntu bash -c "shuf -i 1-10000 -n 1 -o /data.txt && tail -f /dev/null"
    
  5. Verwenden Sie diesen Befehl, um den Ordnerinhalt anzuzeigen.

    docker run -it ubuntu ls /
    

    Es sollte keine data.txt-Datei vorhanden sein, da sie nur für den ersten Container in den Scratchspace geschrieben wurde.

  6. Wählen Sie diese beiden Ubuntu-Container aus. Klicken Sie mit der rechten Maustaste, und wählen Sie Entfernenaus. Über die Befehlszeile können Sie sie mithilfe des Befehls docker rm -f entfernen.

Persistentes Speichern Ihrer To-do-Daten mit benannten Volumes

Standardmäßig speichert die Todo-App ihre Daten in einer SQLite-Datenbank bei /etc/todos/todo.db. SQLite-Datenbank ist eine relationale Datenbank, die alle Daten in einer einzigen Datei speichert. Dieser Ansatz funktioniert für kleine Projekte.

Sie können die einzelne Datei auf dem Host speichern. Wenn Sie sie für den nächsten Container verfügbar machen, kann die Anwendung an der Stelle weitermachen, an der sie unterbrochen wurde. Indem Sie ein Volume erstellen und an den Ordner anfügen oder einbinden, in dem die Daten gespeichert sind, können Sie die Daten persistent speichern. Der Container schreibt in die Datei todo.db, und diese Daten verbleiben auf dem Host im Volume.

Verwenden Sie für diesen Abschnitt ein benanntes Volume. Docker verwaltet den physischen Speicherort des Volumes auf dem Datenträger. Verweisen Sie auf den Namen des Datenvolumens, und Docker stellt die richtigen Daten bereit.

  1. Erstellen Sie ein Volume mithilfe des Befehls docker volume create.

    docker volume create todo-db
    
  2. Wählen Sie unter CONTAINERS die Option getting-started aus, und klicken Sie mit der rechten Maustaste darauf. Wählen Sie Stopp aus, um den App-Container anzuhalten.

    Verwenden Sie den Befehl docker stop, um den Container über die Befehlszeile zu beenden.

  3. Starten Sie den Getting-Started--Container mit dem folgenden Befehl.

    docker run -dp 3000:3000 -v todo-db:/etc/todos getting-started
    

    Der Volumenparameter gibt das Volume an, das eingehängt werden soll, und den Speicherort /etc/todos.

  4. Aktualisieren Sie Ihren Browser, um die App neu zu laden. Wenn Sie das Browserfenster geschlossen haben, wechseln Sie zu http://localhost:3000/. Fügen Sie Ihrer Aufgabenliste einige Elemente hinzu.

    Screenshot zeigt die Beispiel-App mit mehreren Elementen, die der Liste hinzugefügt wurden.

  5. Entfernen Sie den Container getting-started für die To-do-App. Klicken Sie im Container-Explorer mit der rechten Maustaste auf den Container, und wählen Sie "Entfernen" aus, oder verwenden Sie die docker stop Befehle.docker rm

  6. Starten Sie einen neuen Container mit demselben Befehl:

    docker run -dp 3000:3000 -v todo-db:/etc/todos getting-started
    

    Mit diesem Befehl wird dasselbe Laufwerk wie zuvor bereitgestellt. Aktualisieren Sie Ihren Browser. Die hinzugefügten Elemente befinden sich weiterhin in Ihrer Liste.

  7. Entfernen Sie den Container getting-started erneut.

Die unten erläuterten, benannten Volumes und Bindungsbereitstellungen sind die Haupttypen von Volumes, die von einer Standardinstallation der Docker-Engine unterstützt werden.

Eigentum Benannte Volumes Bindungsbereitstellungen
Standort des Gastgebers Auswahl durch Docker Sie steuern
Mount-Beispiel (mit -v) my-volume:/usr/local/data /path/to/data:/usr/local/data
Auffüllen des neuen Volumes mit Containerinhalten Ja Nein
Unterstützung für Volumetreiber Ja Nein

Es gibt viele Volumetreiber-Plug-Ins zur Unterstützung von NFS, SFTP, NetApp und mehr. Diese Plug-Ins sind besonders wichtig, Um Container auf mehreren Hosts in einer clusterierten Umgebung wie Schwarm oder Kubernetes auszuführen.

Wenn Sie sich fragen, wo Docker tatsächlich Ihre Daten speichert, führen Sie den folgenden Befehl aus.

docker volume inspect todo-db

Die Ausgabe sollte diesem Ergebnis ähneln.

[
    {
        "CreatedAt": "2019-09-26T02:18:36Z",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/todo-db/_data",
        "Name": "todo-db",
        "Options": {},
        "Scope": "local"
    }
]

Der Mountpoint ist der tatsächliche Speicherort, an dem die Daten gespeichert werden. Auf den meisten Computern benötigen Sie Stammzugriff, um vom Host aus auf dieses Verzeichnis zuzugreifen.

Verwenden von Bindungsbereitstellungen

Mithilfe von Bindungsbereitstellungen können Sie den exakten Bereitstellungspunkt auf dem Host bestimmen. Dieser Ansatz speichert Daten, wird jedoch häufig verwendet, um mehr Daten in Containern bereitzustellen. Sie können ein Bind-Mount verwenden, um Quellcode in den Container einzubinden, damit dieser Codeänderungen erkennt und darauf reagiert, sodass Sie die Änderungen sofort sehen können.

Um Ihren Container zur Unterstützung eines Entwicklungsworkflows auszuführen, führen Sie die folgenden Schritte aus:

  1. Entfernen Sie alle getting-started Container.

  2. Führen Sie im Ordner app den folgenden Befehl aus.

    docker run -dp 3000:3000 -w /app -v ${PWD}:/app node:lts-alpine sh -c "yarn install && yarn run dev"
    

    Dieser Befehl enthält die folgenden Parameter.

    • -dp 3000:3000 Wie zuvor. Führen Sie den getrennten Modus aus, und erstellen Sie eine Portzuordnung.
    • -w /app Arbeitsverzeichnis innerhalb des Containers.
    • -v ${PWD}:/app": Hier wird eine Bindungsbereitstellung für das aktuelle Verzeichnis vom Host im Container in das /app-Verzeichnis durchgeführt.
    • node:lts-alpine Das zu verwendende Bild. Dieses Image ist das Basisimage für Ihre App aus dem Dockerfile .
    • sh -c "yarn install && yarn run dev": Dies ist ein Befehl. Sie startet eine Shell mit sh und führt yarn install aus, um alle Abhängigkeiten zu installieren. Anschließend wird yarn run dev ausgeführt. Wenn Sie die package.json-Datei genauer ansehen, wird dev vom Skript nodemon gestartet.
  3. Sie können die Protokolle mithilfe von docker logsansehen.

    docker logs -f <container-id>
    
    $ nodemon src/index.js
    [nodemon] 2.0.20
    [nodemon] to restart at any time, enter `rs`
    [nodemon] watching path(s): *.*
    [nodemon] watching extensions: js,mjs,json
    [nodemon] starting `node src/index.js`
    Using sqlite database at /etc/todos/todo.db
    Listening on port 3000
    

    Wenn der letzte Eintrag in dieser Liste angezeigt wird, wird die App ausgeführt.

    Wenn Sie mit der Überwachung der Protokolle fertig sind, wählen Sie eine beliebige Taste im Terminalfenster aus, oder wählen Sie STRG+C- in einem externen Fenster aus.

  4. Öffnen Sie in VS Code src/static/js/app.js. Ändern Sie den Text der Schaltfläche Element hinzufügen in Zeile 109.

    - {submitting ? 'Adding...' : 'Add Item'}
    + {submitting ? 'Adding...' : 'Add'}
    

    Speichern Sie Ihre Änderung.

  5. Aktualisieren Sie Ihren Browser. Du solltest die Änderung sehen.

    Screenshot zeigt die Beispiel-App mit dem neuen Text auf der Schaltfläche.

  6. Entfernen Sie den node:lts-alpine Container.

  7. Führen Sie im app Ordner den folgenden Befehl aus, um den node_modules Ordner zu entfernen, der in den vorherigen Schritten erstellt wurde.

    rm -r node_modules
    

Bildebenen anzeigen

Sie können sich die Ebenen ansehen, aus denen ein Bild besteht. Führen Sie den Befehl docker image history aus, um den Befehl anzuzeigen, der zum Erstellen der einzelnen Ebenen in einem Image verwendet wurde.

  1. Verwenden Sie docker image history, um die Ebenen im Getting-Started Bild anzuzeigen, das Sie zuvor im Tutorial erstellt haben.

    docker image history getting-started
    

    Ihr Ergebnis sollte diesem Ergebnis ähneln.

    IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
    a78a40cbf866        18 seconds ago      /bin/sh -c #(nop)  CMD ["node" "/app/src/ind…   0B                  
    f1d1808565d6        19 seconds ago      /bin/sh -c yarn install --production            85.4MB              
    a2c054d14948        36 seconds ago      /bin/sh -c #(nop) COPY dir:5dc710ad87c789593…   198kB               
    9577ae713121        37 seconds ago      /bin/sh -c #(nop) WORKDIR /app                  0B                  
    b95baba1cfdb        13 days ago         /bin/sh -c #(nop)  CMD ["node"]                 0B                  
    <missing>           13 days ago         /bin/sh -c #(nop)  ENTRYPOINT ["docker-entry…   0B                  
    <missing>           13 days ago         /bin/sh -c #(nop) COPY file:238737301d473041…   116B                
    <missing>           13 days ago         /bin/sh -c apk add --no-cache --virtual .bui…   5.35MB              
    <missing>           13 days ago         /bin/sh -c #(nop)  ENV YARN_VERSION=1.21.1      0B                  
    <missing>           13 days ago         /bin/sh -c addgroup -g 1000 node     && addu…   74.3MB              
    <missing>           13 days ago         /bin/sh -c #(nop)  ENV NODE_VERSION=12.14.1     0B                  
    <missing>           13 days ago         /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B                  
    <missing>           13 days ago         /bin/sh -c #(nop) ADD file:e69d441d729412d24…   5.59MB   
    

    Jede der Linien stellt eine Ebene im Bild dar. Die Ausgabe zeigt die Basis unten mit der neuesten Ebene oben an. Mithilfe dieser Informationen können Sie die Größe jeder Ebene sehen und dabei helfen, große Bilder zu diagnostizieren.

  2. Mehrere der Zeilen werden abgeschnitten. Wenn Sie den parameter --no-trunc hinzufügen, erhalten Sie die vollständige Ausgabe.

    docker image history --no-trunc getting-started
    

Cacheabhängigkeiten

Sobald sich eine Ebene ändert, müssen auch alle nachgelagerten Schichten neu erstellt werden. Hier sehen Sie erneut das Dockerfile:

FROM node:lts-alpine
WORKDIR /app
COPY . .
RUN yarn install --production
CMD ["node", "/app/src/index.js"]

Jeder Befehl in der Dockerfile- wird zu einer neuen Ebene im Image. Um die Anzahl der Ebenen zu minimieren, können Sie Ihre Dockerfile- umstrukturieren, um das Zwischenspeichern von Abhängigkeiten zu unterstützen. Bei Knotenbasierten Anwendungen werden diese Abhängigkeiten in der Datei package.json definiert.

Der Ansatz besteht darin, zuerst nur diese Datei zu kopieren; die Abhängigkeiten zu installieren und dann alles andere zu kopieren. In diesem Prozess werden die Yarn-Abhängigkeiten nur erneut erstellt, wenn eine Änderung an package.json vorgenommen wurde.

  1. Aktualisieren Sie das Dockerfile, damit zuerst package.json kopiert wird, installieren Sie anschließend die Abhängigkeiten, und kopieren Sie daraufhin den Rest. Hier ist die neue Datei:

    FROM node:lts-alpine
    WORKDIR /app
    COPY package.json yarn.lock ./
    RUN yarn install --production
    COPY . .
    CMD ["node", "/app/src/index.js"]
    
  2. Erstellen Sie ein neues Image mit docker build.

    docker build -t getting-started .
    

    Die Ausgabe sollte wie die folgenden Ergebnisse aussehen:

    Sending build context to Docker daemon  219.1kB
    Step 1/6 : FROM node:lts-alpine
    ---> b0dc3a5e5e9e
    Step 2/6 : WORKDIR /app
    ---> Using cache
    ---> 9577ae713121
    Step 3/6 : COPY package* yarn.lock ./
    ---> bd5306f49fc8
    Step 4/6 : RUN yarn install --production
    ---> Running in d53a06c9e4c2
    yarn install v1.17.3
    [1/4] Resolving packages...
    [2/4] Fetching packages...
    info fsevents@1.2.9: The platform "linux" is incompatible with this module.
    info "fsevents@1.2.9" is an optional dependency and failed compatibility check. Excluding it from installation.
    [3/4] Linking dependencies...
    [4/4] Building fresh packages...
    Done in 10.89s.
    Removing intermediate container d53a06c9e4c2
    ---> 4e68fbc2d704
    Step 5/6 : COPY . .
    ---> a239a11f68d8
    Step 6/6 : CMD ["node", "/app/src/index.js"]
    ---> Running in 49999f68df8f
    Removing intermediate container 49999f68df8f
    ---> e709c03bc597
    Successfully built e709c03bc597
    Successfully tagged getting-started:latest
    

    Alle Ebenen wurden neu erstellt. Dieses Ergebnis wird erwartet, da Sie das Dockerfile geändert haben.

  3. Nehmen Sie eine Änderung an src/static/index.html vor. Ändern Sie beispielsweise den Titel in "The Awesome Todo App".

  4. Erstellen Sie das Docker-Image jetzt mit docker build erneut. Diesmal sollte Ihre Ausgabe etwas anders aussehen.

    Sending build context to Docker daemon  219.1kB
    Step 1/6 : FROM node:lts-alpine
    ---> b0dc3a5e5e9e
    Step 2/6 : WORKDIR /app
    ---> Using cache
    ---> 9577ae713121
    Step 3/6 : COPY package* yarn.lock ./
    ---> Using cache
    ---> bd5306f49fc8
    Step 4/6 : RUN yarn install --production
    ---> Using cache
    ---> 4e68fbc2d704
    Step 5/6 : COPY . .
    ---> cccde25a3d9a
    Step 6/6 : CMD ["node", "/app/src/index.js"]
    ---> Running in 2be75662c150
    Removing intermediate container 2be75662c150
    ---> 458e5c6f080c
    Successfully built 458e5c6f080c
    Successfully tagged getting-started:latest
    

    Da Sie den Buildcache verwenden, sollte es viel schneller gehen.

Mehrstufige Builds

Mehrstufige Builds sind ein unglaublich leistungsfähiges Tool, mit dem Sie mehrere Phasen zum Erstellen eines Images verwenden können. Es gibt mehrere Vorteile für sie:

  • Abgrenzen von Abhängigkeiten zur Buildzeit von Laufzeitabhängigkeiten
  • Verringern der Gesamtbildgröße, indem Sie nur das versenden, was Ihre App ausführen muss

Dieser Abschnitt enthält kurze Beispiele.

Beispiel für Maven/Tomcat

Wenn Sie Java-basierte Anwendungen erstellen, wird ein JDK benötigt, um den Quellcode in Java-Bytecode zu kompilieren. Das JDK wird in der Produktion nicht benötigt. Möglicherweise verwenden Sie Tools wie Maven oder Gradle, um die App zu erstellen. Diese Tools werden auch in Ihrem endgültigen Image nicht benötigt.

FROM maven AS build
WORKDIR /app
COPY . .
RUN mvn package

FROM tomcat
COPY --from=build /app/target/file.war /usr/local/tomcat/webapps 

In diesem Beispiel wird eine Phase buildverwendet, um den tatsächlichen Java-Build mit Maven auszuführen. Die zweite Phase, beginnend mit "FROM tomcat", kopiert Dateien aus der build-Phase. Das endgültige Bild ist nur die letzte Erstellungsphase, die mit dem parameter --target überschrieben werden kann.

React-Beispiel

Beim Erstellen von React-Anwendungen benötigen Sie eine Node-Umgebung, um den JavaScript-Code, Sass-Stylesheets und vieles mehr in statisches HTML, JavaScript und CSS zu kompilieren. Wenn Sie kein serverseitiges Rendering durchführen, benötigen Sie nicht einmal eine Node-Umgebung für den Produktionsbuild.

FROM node:lts-alpine AS build
WORKDIR /app
COPY package* yarn.lock ./
RUN yarn install
COPY public ./public
COPY src ./src
RUN yarn run build

FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html

In diesem Beispiel wird ein node:lts-alpine-Image verwendet, um den Build auszuführen, wodurch die Layerzwischenspeicherung maximiert wird, und anschließend wird die Ausgabe in einen nginx- container kopiert.

Bereinigen von Ressourcen

Bewahren Sie alles auf, was Sie bisher getan haben, um diese Reihe von Tutorials fortzusetzen.

Nächste Schritte

Sie haben die Optionen zum Speichern von Daten für Container-Apps kennengelernt.

Was möchten Sie als Nächstes tun?