Zum Inhalt springen

Docker/Image/Erstellung: Unterschied zwischen den Versionen

Aus Foxwiki
Keine Bearbeitungszusammenfassung
Keine Bearbeitungszusammenfassung
Zeile 2: Zeile 2:
Image layering
Image layering
Using the docker image history command, you can see the command that was used to create each layer within an image.
Using the docker image history command, you can see the command that was used to create each layer within an image.
    1. Use the docker image history command to see the layers in the getting-started image you created.
 
; 1. Use the docker image history command to see the layers in the getting-started image you created.
  docker image history getting-started
  docker image history getting-started
You should get output that looks something like the following.
ou should get output that looks something like the following.
    • IMAGE              CREATED            CREATED BY                                      SIZE                COMMENT
 
IMAGE              CREATED            CREATED BY                                      SIZE                COMMENT
a78a40cbf866        18 seconds ago      /bin/sh -c #(nop)  CMD ["node" "src/index.j…    0B                   
a78a40cbf866        18 seconds ago      /bin/sh -c #(nop)  CMD ["node" "src/index.j…    0B                   
f1d1808565d6        19 seconds ago      /bin/sh -c yarn install --production            85.4MB               
f1d1808565d6        19 seconds ago      /bin/sh -c yarn install --production            85.4MB               
Zeile 19: Zeile 21:
<missing>          13 days ago        /bin/sh -c #(nop)  CMD ["/bin/sh"]              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   
<missing>          13 days ago        /bin/sh -c #(nop) ADD file:e69d441d729412d24…  5.59MB   
Each of the lines represents a layer in the image. The display here shows the base at the bottom with the newest layer at the top. Using this, you can also quickly see the size of each layer, helping diagnose large images.
Each of the lines represents a layer in the image. The display here shows the base at the bottom with the newest layer at the top. Using this, you can also quickly see the size of each layer, helping diagnose large images.
    • You'll notice that several of the lines are truncated. If you add the --no-trunc flag, you'll get the full output.
* You'll notice that several of the lines are truncated. If you add the --no-trunc flag, you'll get the full output.
    2. docker image history --no-trunc getting-started
 
; 2. docker image history --no-trunc getting-started
Layer caching
Layer caching
Now that you've seen the layering in action, there's an important lesson to learn to help decrease build times for your container images. Once a layer changes, all downstream layers have to be recreated as well.
Now that you've seen the layering in action, there's an important lesson to learn to help decrease build times for your container images. Once a layer changes, all downstream layers have to be recreated as well.
Look at the following Dockerfile you created for the getting started app.
Look at the following Dockerfile you created for the getting started app.
# syntax=docker/dockerfile:1
 
FROM node:lts-alpine
# syntax=docker/dockerfile:1
WORKDIR /app
FROM node:lts-alpine
COPY . .
WORKDIR /app
RUN yarn install --production
COPY . .
CMD ["node", "src/index.js"]
RUN yarn install --production
CMD ["node", "src/index.js"]
 
Going back to the image history output, you see that each command in the Dockerfile becomes a new layer in the image. You might remember that when you made a change to the image, the yarn dependencies had to be reinstalled. It doesn't make much sense to ship around the same dependencies every time you build.
Going back to the image history output, you see that each command in the Dockerfile becomes a new layer in the image. You might remember that when you made a change to the image, the yarn dependencies had to be reinstalled. It doesn't make much sense to ship around the same dependencies every time you build.
To fix it, you need to restructure your Dockerfile to help support the caching of the dependencies. For Node-based applications, those dependencies are defined in the package.json file. You can copy only that file in first, install the dependencies, and then copy in everything else. Then, you only recreate the yarn dependencies if there was a change to the package.json.
To fix it, you need to restructure your Dockerfile to help support the caching of the dependencies. For Node-based applications, those dependencies are defined in the package.json file. You can copy only that file in first, install the dependencies, and then copy in everything else. Then, you only recreate the yarn dependencies if there was a change to the package.json.
    1. Update the Dockerfile to copy in the package.json first, install dependencies, and then copy everything else in.
 
    • # syntax=docker/dockerfile:1
1. Update the Dockerfile to copy in the package.json first, install dependencies, and then copy everything else in.
FROM node:lts-alpine
# syntax=docker/dockerfile:1
WORKDIR /app
FROM node:lts-alpine
COPY package.json yarn.lock ./
WORKDIR /app
RUN yarn install --production
COPY package.json yarn.lock ./
COPY . .
RUN yarn install --production
CMD ["node", "src/index.js"]
COPY . .
    • Build a new image using docker build.
CMD ["node", "src/index.js"]
docker build -t getting-started .
    • Build a new image using docker build.
  docker build -t getting-started .
You should see output like the following.
You should see output like the following.
    • [+] Building 16.1s (10/10) FINISHED
[+] Building 16.1s (10/10) FINISHED
=> [internal] load build definition from Dockerfile
=> [internal] load build definition from Dockerfile
=> => transferring dockerfile: 175B
=> => transferring dockerfile: 175B
=> [internal] load .dockerignore
=> [internal] load .dockerignore
=> => transferring context: 2B
=> => transferring context: 2B
=> [internal] load metadata for docker.io/library/node:lts-alpine
=> [internal] load metadata for docker.io/library/node:lts-alpine
=> [internal] load build context
=> [internal] load build context
=> => transferring context: 53.37MB
=> => transferring context: 53.37MB
=> [1/5] FROM docker.io/library/node:lts-alpine
=> [1/5] FROM docker.io/library/node:lts-alpine
=> CACHED [2/5] WORKDIR /app
=> CACHED [2/5] WORKDIR /app
=> [3/5] COPY package.json yarn.lock ./
=> [3/5] COPY package.json yarn.lock ./
=> [4/5] RUN yarn install --production
=> [4/5] RUN yarn install --production
=> [5/5] COPY . .
=> [5/5] COPY . .
=> exporting to image
=> exporting to image
=> => exporting layers
=> => exporting layers
=> => writing image    sha256:d6f819013566c54c50124ed94d5e66c452325327217f4f04399b45f94e37d25
=> => writing image    sha256:d6f819013566c54c50124ed94d5e66c452325327217f4f04399b45f94e37d25
=> => naming to docker.io/library/getting-started
=> => naming to docker.io/library/getting-started
    • Now, make a change to the src/static/index.html file. For example, change the <title> to "The Awesome Todo App".
    • Now, make a change to the src/static/index.html file. For example, change the <title> to "The Awesome Todo App".
    • Build the Docker image now using docker build -t getting-started . again. This time, your output should look a little different.
    • Build the Docker image now using docker build -t getting-started . again. This time, your output should look a little different.
    4. [+] Building 1.2s (10/10) FINISHED
    4. [+] Building 1.2s (10/10) FINISHED
      => [internal] load build definition from Dockerfile
        => [internal] load build definition from Dockerfile
      => => transferring dockerfile: 37B
        => => transferring dockerfile: 37B
      => [internal] load .dockerignore
        => [internal] load .dockerignore
      => => transferring context: 2B
        => => transferring context: 2B
      => [internal] load metadata for docker.io/library/node:lts-alpine
        => [internal] load metadata for docker.io/library/node:lts-alpine
      => [internal] load build context
        => [internal] load build context
      => => transferring context: 450.43kB
        => => transferring context: 450.43kB
      => [1/5] FROM docker.io/library/node:lts-alpine
        => [1/5] FROM docker.io/library/node:lts-alpine
      => CACHED [2/5] WORKDIR /app
        => CACHED [2/5] WORKDIR /app
      => CACHED [3/5] COPY package.json yarn.lock ./
        => CACHED [3/5] COPY package.json yarn.lock ./
      => CACHED [4/5] RUN yarn install --production
        => CACHED [4/5] RUN yarn install --production
      => [5/5] COPY . .
        => [5/5] COPY . .
      => exporting to image
        => exporting to image
      => => exporting layers
        => => exporting layers
      => => writing image    sha256:91790c87bcb096a83c2bd4eb512bc8b134c757cda0bdee4038187f98148e2eda
        => => writing image    sha256:91790c87bcb096a83c2bd4eb512bc8b134c757cda0bdee4038187f98148e2eda
      => => naming to docker.io/library/getting-started
        => => naming to docker.io/library/getting-started
      First off, you should notice that the build was much faster. And, you'll see that several steps are using previously cached layers. Pushing and pulling this image and updates to it will be much faster as well.
 
Multi-stage builds
First off, you should notice that the build was much faster. And, you'll see that several steps are using previously cached layers. Pushing and pulling this image and updates to it will be much faster as well.
 
; Multi-stage builds
Multi-stage builds are an incredibly powerful tool to help use multiple stages to create an image. There are several advantages for them:
Multi-stage builds are an incredibly powerful tool to help use multiple stages to create an image. There are several advantages for them:
     • Separate build-time dependencies from runtime dependencies
     • Separate build-time dependencies from runtime dependencies
Zeile 117: Zeile 128:
Next steps
Next steps
In the next section, you'll learn about additional resources you can use to continue learning about containers.
In the next section, you'll learn about additional resources you can use to continue learning about containers.
= TMP =
== Image-Erstellung ==
; Bewährte Praktiken für die Image-Erstellung
=== [https://docs.docker.com/get-started/workshop/09_image_best/#image-layering Image-Schichtung] ===
Mit dem Befehl docker image history können Sie den Befehl sehen, mit dem jede Schicht innerhalb eines Images erstellt wurde.# Verwenden Sie den Befehl docker image history, um die Schichten in dem von Ihnen erstellten Getting-Started-Image anzuzeigen
docker image history getting-started
Sie sollten eine Ausgabe erhalten, die in etwa wie die folgende aussieht.* IMAGE CREATED CREATED BY GRÖSSE KOMMENTAR
a78a40cbf866 vor 18 Sekunden /bin/sh -c #(nop) CMD ["node" "src/index.j... 0B
f1d1808565d6 vor 19 Sekunden /bin/sh -c yarn install --production 85.4MB
a2c054d14948 Vor 36 Sekunden /bin/sh -c #(nop) COPY dir:5dc710ad87c789593... 198kB
9577ae713121 vor 37 Sekunden /bin/sh -c #(nop) WORKDIR /app 0B
b95baba1cfdb vor 13 Tagen /bin/sh -c #(nop) CMD ["node"] 0B
<fehlt> vor 13 Tagen /bin/sh -c #(nop) ENTRYPOINT ["docker-entry... 0B
<fehlt> Vor 13 Tagen /bin/sh -c #(nop) COPY file:238737301d473041... 116B
<fehlt> vor 13 Tagen /bin/sh -c apk add --no-cache --virtual .bui... 5.35MB
<fehlt> vor 13 Tagen /bin/sh -c #(nop) ENV YARN_VERSION=1.21.1 0B
<Fehlt> Vor 13 Tagen /bin/sh -c addgroup -g 1000 node && addu... 74.3MB
<fehlt> Vor 13 Tagen /bin/sh -c #(nop) ENV NODE_VERSION=12.14.1 0B
<fehlt> Vor 13 Tagen /bin/sh -c #(nop) CMD ["/bin/sh"] 0B
<fehlt> vor 13 Tagen /bin/sh -c #(nop) ADD file:e69d441d729412d24... 5.59MB
Jede der Zeilen steht für eine Ebene im Bild. Die Anzeige hier zeigt die Basis am unteren Rand und die neueste Ebene am oberen Rand. Auf diese Weise können Sie auch schnell die Größe der einzelnen Ebenen erkennen, was bei der Diagnose großer Bilder hilfreich ist.* Sie werden feststellen, dass einige der Zeilen abgeschnitten sind. Wenn Sie das --no-trunc-Flag hinzufügen, erhalten Sie die vollständige Ausgabe
# docker image history --no-trunc getting-started
=== [https://docs.docker.com/get-started/workshop/09_image_best/#layer-caching Layer-Caching] ===
Nachdem Sie nun die Schichtung in Aktion gesehen haben, gibt es eine wichtige Lektion zu lernen, die dazu beiträgt, die Erstellungszeiten für Ihre Container-Abbilder zu verkürzen. Sobald sich eine Schicht ändert, müssen alle nachgelagerten Schichten ebenfalls neu erstellt werden
Sehen Sie sich das folgende Dockerfile an, das Sie für die Getting Started App erstellt haben
<nowiki># syntax=docker/dockerfile:1</nowiki>
[https://docs.docker.com/reference/dockerfile/#from FROM] node:lts-alpine
[https://docs.docker.com/reference/dockerfile/#workdir WORKDIR] /app
[https://docs.docker.com/reference/dockerfile/#copy COPY] .
[https://docs.docker.com/reference/dockerfile/#run RUN] yarn install --production
[https://docs.docker.com/reference/dockerfile/#cmd CMD] ["node", "src/index.js"]
Wenn Sie sich die Ausgabe der Image-Historie ansehen, sehen Sie, dass jeder Befehl im Dockerfile zu einer neuen Ebene im Image wird. Sie erinnern sich vielleicht daran, dass bei einer Änderung am Image die Garn-Abhängigkeiten neu installiert werden mussten. Es ist nicht sehr sinnvoll, bei jedem Build die gleichen Abhängigkeiten mitzuschleppen
Um dies zu beheben, müssen Sie Ihr Dockerfile umstrukturieren, um das Zwischenspeichern der Abhängigkeiten zu unterstützen. Bei Node-basierten Anwendungen werden diese Abhängigkeiten in der package.json-Datei definiert. Sie können zuerst nur diese Datei hineinkopieren, die Abhängigkeiten installieren und dann alles andere hineinkopieren. Dann erstellen Sie die Garn-Abhängigkeiten nur dann neu, wenn es eine Änderung in der package.json gibt.# Aktualisieren Sie die Dockerdatei, um zuerst die package.json zu kopieren, die Abhängigkeiten zu installieren und dann alles andere hineinzukopieren
* <nowiki># syntax=docker/dockerfile:1</nowiki>
[https://docs.docker.com/reference/dockerfile/#from FROM] node:lts-alpine
[https://docs.docker.com/reference/dockerfile/#workdir WORKDIR] /app
[https://docs.docker.com/reference/dockerfile/#copy COPY] package.json yarn.lock ./
[https://docs.docker.com/reference/dockerfile/#run RUN] yarn install --production
[https://docs.docker.com/reference/dockerfile/#copy COPY] .
[https://docs.docker.com/reference/dockerfile/#cmd CMD] ["node", "src/index.js"]* Erstellen Sie ein neues Image mit docker build
docker build -t getting-started
Sie sollten eine Ausgabe wie die folgende sehen.* [+] Bauen 16.1s (10/10) BEENDET
<nowiki>=> [intern] Build-Definition aus Dockerfile laden</nowiki>
<nowiki>=> Übertragen des Dockerfiles: 175B</nowiki>
<nowiki>=> [internal] load .dockerignore</nowiki>
<nowiki>=> Übertragen von Kontext: 2B</nowiki>
<nowiki>=> [intern] lade Metadaten für docker.io/library/node:lts-alpine</nowiki>
<nowiki>=> [internal] load build context</nowiki>
<nowiki>=> Übertragen des Kontextes: 53.37MB</nowiki>
<nowiki>=> [1/5] FROM docker.io/library/node:lts-alpine</nowiki>
<nowiki>=> CACHED [2/5] WORKDIR /app</nowiki>
<nowiki>=> [3/5] COPY package.json yarn.lock ./</nowiki>
<nowiki>=> [4/5] RUN yarn install --production</nowiki>
<nowiki>=> [5/5] COPY .</nowiki>
<nowiki>=> exportieren nach image</nowiki>
<nowiki>=> Exportieren von Schichten</nowiki>
<nowiki>=> Schreiben des Images sha256:d6f819013566c54c50124ed94d5e66c452325327217f4f04399b45f94e37d25</nowiki>
<nowiki>=> => Umbenennung in docker.io/library/getting-started</nowiki>* Nehmen Sie nun eine Änderung an der Datei src/static/index.html vor. Ändern Sie zum Beispiel den <title> in "The Awesome Todo App"
* Erstellen Sie nun das Docker-Image mit docker build -t getting-started . erneut. Diesmal sollte die Ausgabe ein wenig anders aussehen
# [+] Bauen 1.2s (10/10) BEENDET
<nowiki>=> [intern] lade Build-Definition aus Dockerfile</nowiki>
<nowiki>=> Übertragen des Dockerfiles: 37B</nowiki>
<nowiki>=> [internal] load .dockerignore</nowiki>
<nowiki>=> Übertragen von Kontext: 2B</nowiki>
<nowiki>=> [intern] lade Metadaten für docker.io/library/node:lts-alpine</nowiki>
<nowiki>=> [internal] load build context</nowiki>
<nowiki>=> Übertragen des Kontextes: 450.43kB</nowiki>
<nowiki>=> [1/5] FROM docker.io/library/node:lts-alpine</nowiki>
<nowiki>=> CACHED [2/5] WORKDIR /app</nowiki>
<nowiki>=> CACHED [3/5] COPY package.json yarn.lock ./</nowiki>
<nowiki>=> CACHED [4/5] RUN yarn install --production</nowiki>
<nowiki>=> [5/5] COPY .</nowiki>
<nowiki>=> exportieren nach image</nowiki>
<nowiki>=> Exportieren von Schichten</nowiki>
<nowiki>=> Schreiben des Images sha256:91790c87bcb096a83c2bd4eb512bc8b134c757cda0bdee4038187f98148e2eda</nowiki>
<nowiki>=> Benennung in docker.io/library/getting-started</nowiki>
Zunächst einmal sollten Sie feststellen, dass die Erstellung viel schneller war. Und Sie werden sehen, dass mehrere Schritte zuvor zwischengespeicherte Ebenen verwenden. Das Pushen und Ziehen dieses Bildes und die Aktualisierungen werden ebenfalls viel schneller sein
=== [https://docs.docker.com/get-started/workshop/09_image_best/#multi-stage-builds Mehrstufige Builds] ===
Mehrstufige Builds sind ein unglaublich leistungsfähiges Werkzeug, um ein Bild in mehreren Stufen zu erstellen. Sie bieten mehrere Vorteile
* Trennung von Build-Abhängigkeiten und Laufzeit-Abhängigkeiten
* Verringern Sie die Gesamtgröße des Images, indem Sie nur das ausliefern, was Ihre Anwendung zum Laufen braucht
==== [https://docs.docker.com/get-started/workshop/09_image_best/#maventomcat-example Beispiel Maven/Tomcat] ====
Wenn Sie Java-basierte Anwendungen erstellen, benötigen Sie ein JDK, um den Quellcode in Java-Bytecode zu kompilieren. In der Produktion wird dieses JDK jedoch nicht benötigt. Möglicherweise verwenden Sie auch Tools wie Maven oder Gradle, um die Anwendung zu erstellen. Auch diese werden in Ihrem endgültigen Image nicht benötigt. Mehrstufige Builds helfen
<nowiki># syntax=docker/dockerfile:1</nowiki>
[https://docs.docker.com/reference/dockerfile/#from FROM] maven AS build
[https://docs.docker.com/reference/dockerfile/#workdir WORKDIR] /app
[https://docs.docker.com/reference/dockerfile/#copy COPY] .
[https://docs.docker.com/reference/dockerfile/#run RUN] mvn paket
[https://docs.docker.com/reference/dockerfile/#from FROM] tomcat
[https://docs.docker.com/reference/dockerfile/#copy COPY] --from=build /app/target/file.war /usr/local/tomcat/webapps
In diesem Beispiel verwenden Sie eine Stufe (genannt build), um den eigentlichen Java-Build mit Maven durchzuführen. In der zweiten Phase (beginnend mit FROM tomcat) kopieren Sie die Dateien aus der build-Phase hinein. Das endgültige Bild ist nur die letzte Stufe, die erstellt wird, was mit dem Flag --target überschrieben werden kann
==== [https://docs.docker.com/get-started/workshop/09_image_best/#react-example React-Beispiel] ====
Bei der Erstellung von React-Anwendungen benötigen Sie eine Node-Umgebung, um den JS-Code (typischerweise JSX), SASS-Stylesheets und mehr in statisches HTML, JS und CSS zu kompilieren. Wenn Sie kein serverseitiges Rendering durchführen, benötigen Sie nicht einmal eine Node-Umgebung für Ihren Produktions-Build. Sie können die statischen Ressourcen in einem statischen Nginx-Container ausliefern
<nowiki># syntax=docker/dockerfile:1</nowiki>
[https://docs.docker.com/reference/dockerfile/#from FROM] node:lts AS build
[https://docs.docker.com/reference/dockerfile/#workdir WORKDIR] /app
[https://docs.docker.com/reference/dockerfile/#copy COPY] paket* yarn.lock ./
[https://docs.docker.com/reference/dockerfile/#run RUN] yarn install
[https://docs.docker.com/reference/dockerfile/#copy COPY] public ./public
[https://docs.docker.com/reference/dockerfile/#copy COPY] src ./src
[https://docs.docker.com/reference/dockerfile/#run RUN] yarn run build
[https://docs.docker.com/reference/dockerfile/#from FROM] nginx:alpine
[https://docs.docker.com/reference/dockerfile/#copy COPY] --from=build /app/build /usr/share/nginx/html
Im vorherigen Dockerfile-Beispiel wird das node:lts-Image verwendet, um den Build durchzuführen (Maximierung der Zwischenspeicherung von Schichten), und dann wird die Ausgabe in einen nginx-Container kopiert
=== [https://docs.docker.com/get-started/workshop/09_image_best/#summary Zusammenfassung] ===
In diesem Abschnitt haben Sie einige Best Practices für die Image-Erstellung kennengelernt, darunter Layer-Caching und mehrstufige Builds
Verwandte Informationen
* [https://docs.docker.com/reference/dockerfile/ Dockerfile-Referenz]
* [https://docs.docker.com/build/building/best-practices/ Bewährte Verfahren für Dockerfile]
=== [https://docs.docker.com/get-started/workshop/09_image_best/#next-steps Nächste Schritte] ===
[https://docs.docker.com/get-started/workshop/10_what_next/ Im nächsten Abschnitt erfahren Sie mehr über zusätzliche Ressourcen, die Sie nutzen können, um weiter über Container zu lernen.]
[[Kategorie:Docker/Workshop]]

Version vom 23. Oktober 2025, 15:39 Uhr

Image-building best practices Image layering Using the docker image history command, you can see the command that was used to create each layer within an image.

1. Use the docker image history command to see the layers in the getting-started image you created.
docker image history getting-started

ou should get output that looks something like the following.

IMAGE CREATED CREATED BY SIZE COMMENT a78a40cbf866 18 seconds ago /bin/sh -c #(nop) CMD ["node" "src/index.j… 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

Each of the lines represents a layer in the image. The display here shows the base at the bottom with the newest layer at the top. Using this, you can also quickly see the size of each layer, helping diagnose large images.

  • You'll notice that several of the lines are truncated. If you add the --no-trunc flag, you'll get the full output.
2. docker image history --no-trunc getting-started

Layer caching

Now that you've seen the layering in action, there's an important lesson to learn to help decrease build times for your container images. Once a layer changes, all downstream layers have to be recreated as well. Look at the following Dockerfile you created for the getting started app.

# syntax=docker/dockerfile:1
FROM node:lts-alpine
WORKDIR /app
COPY . .
RUN yarn install --production
CMD ["node", "src/index.js"]

Going back to the image history output, you see that each command in the Dockerfile becomes a new layer in the image. You might remember that when you made a change to the image, the yarn dependencies had to be reinstalled. It doesn't make much sense to ship around the same dependencies every time you build.

To fix it, you need to restructure your Dockerfile to help support the caching of the dependencies. For Node-based applications, those dependencies are defined in the package.json file. You can copy only that file in first, install the dependencies, and then copy in everything else. Then, you only recreate the yarn dependencies if there was a change to the package.json.

1. Update the Dockerfile to copy in the package.json first, install dependencies, and then copy everything else in.

# syntax=docker/dockerfile:1
FROM node:lts-alpine
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install --production
COPY . .
CMD ["node", "src/index.js"]
    • Build a new image using docker build.
 docker build -t getting-started .

You should see output like the following.

[+] Building 16.1s (10/10) FINISHED
=> [internal] load build definition from Dockerfile
=> => transferring dockerfile: 175B
=> [internal] load .dockerignore
=> => transferring context: 2B
=> [internal] load metadata for docker.io/library/node:lts-alpine
=> [internal] load build context
=> => transferring context: 53.37MB
=> [1/5] FROM docker.io/library/node:lts-alpine
=> CACHED [2/5] WORKDIR /app
=> [3/5] COPY package.json yarn.lock ./
=> [4/5] RUN yarn install --production
=> [5/5] COPY . .
=> exporting to image
=> => exporting layers
=> => writing image     sha256:d6f819013566c54c50124ed94d5e66c452325327217f4f04399b45f94e37d25
=> => naming to docker.io/library/getting-started
    • Now, make a change to the src/static/index.html file. For example, change the <title> to "The Awesome Todo App".
    • Build the Docker image now using docker build -t getting-started . again. This time, your output should look a little different.
    4. [+] Building 1.2s (10/10) FINISHED
       => [internal] load build definition from Dockerfile
       => => transferring dockerfile: 37B
       => [internal] load .dockerignore
       => => transferring context: 2B
       => [internal] load metadata for docker.io/library/node:lts-alpine
       => [internal] load build context
       => => transferring context: 450.43kB
       => [1/5] FROM docker.io/library/node:lts-alpine
       => CACHED [2/5] WORKDIR /app
       => CACHED [3/5] COPY package.json yarn.lock ./
       => CACHED [4/5] RUN yarn install --production
       => [5/5] COPY . .
       => exporting to image
       => => exporting layers
       => => writing image     sha256:91790c87bcb096a83c2bd4eb512bc8b134c757cda0bdee4038187f98148e2eda
       => => naming to docker.io/library/getting-started

First off, you should notice that the build was much faster. And, you'll see that several steps are using previously cached layers. Pushing and pulling this image and updates to it will be much faster as well.

Multi-stage builds

Multi-stage builds are an incredibly powerful tool to help use multiple stages to create an image. There are several advantages for them:

   • Separate build-time dependencies from runtime dependencies
   • Reduce overall image size by shipping only what your app needs to run

Maven/Tomcat example When building Java-based applications, you need a JDK to compile the source code to Java bytecode. However, that JDK isn't needed in production. Also, you might be using tools like Maven or Gradle to help build the app. Those also aren't needed in your final image. Multi-stage builds help.

  1. syntax=docker/dockerfile:1

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

FROM tomcat COPY --from=build /app/target/file.war /usr/local/tomcat/webapps In this example, you use one stage (called build) to perform the actual Java build using Maven. In the second stage (starting at FROM tomcat), you copy in files from the build stage. The final image is only the last stage being created, which can be overridden using the --target flag. React example When building React applications, you need a Node environment to compile the JS code (typically JSX), SASS stylesheets, and more into static HTML, JS, and CSS. If you aren't doing server-side rendering, you don't even need a Node environment for your production build. You can ship the static resources in a static nginx container.

  1. syntax=docker/dockerfile:1

FROM node:lts 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 the previous Dockerfile example, it uses the node:lts image to perform the build (maximizing layer caching) and then copies the output into an nginx container. Summary In this section, you learned a few image building best practices, including layer caching and multi-stage builds. Related information:

   • Dockerfile reference
   • Dockerfile best practices

Next steps In the next section, you'll learn about additional resources you can use to continue learning about containers.