So zwischenspeichern Sie die RUN npm-Installationsanweisung, wenn Docker eine Docker-Datei erstellt

86

Ich entwickle derzeit ein Node-Backend für meine Anwendung. Beim Andocken ( docker build .) ist die längste Phase die RUN npm install. Die RUN npm installAnweisung wird bei jeder kleinen Änderung des Servercodes ausgeführt, was die Produktivität durch eine längere Erstellungszeit beeinträchtigt.

Ich habe festgestellt, dass das Ausführen von npm install, wo sich der Anwendungscode befindet, und das Hinzufügen der node_modules zum Container mit der ADD-Anweisung dieses Problem löst, aber es ist alles andere als eine bewährte Methode. Es bricht irgendwie die ganze Idee, es anzudocken, und es bewirkt, dass der Behälter viel mehr wiegt.

Irgendwelche anderen Lösungen?

ohadgk
quelle

Antworten:

124

Ok, also habe ich diesen großartigen Artikel über Effizienz beim Schreiben einer Docker-Datei gefunden.

Dies ist ein Beispiel für eine fehlerhafte Docker-Datei, die den Anwendungscode hinzufügt, bevor die RUN npm installAnweisung ausgeführt wird:

FROM ubuntu

RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
RUN apt-get update
RUN apt-get -y install python-software-properties git build-essential
RUN add-apt-repository -y ppa:chris-lea/node.js
RUN apt-get update
RUN apt-get -y install nodejs

WORKDIR /opt/app

COPY . /opt/app
RUN npm install
EXPOSE 3001

CMD ["node", "server.js"]

Durch Teilen der Kopie der Anwendung in 2 COPY-Anweisungen (eine für die Datei package.json und die andere für die restlichen Dateien) und Ausführen der npm-Installationsanweisung vor dem Hinzufügen des eigentlichen Codes wird bei jeder Codeänderung die RUN npm-Installation nicht ausgelöst Anweisung, nur Änderungen der package.json werden es auslösen. Besser üben Docker-Datei:

FROM ubuntu
MAINTAINER David Weinstein <[email protected]>

# install our dependencies and nodejs
RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
RUN apt-get update
RUN apt-get -y install python-software-properties git build-essential
RUN add-apt-repository -y ppa:chris-lea/node.js
RUN apt-get update
RUN apt-get -y install nodejs

# use changes to package.json to force Docker not to use the cache
# when we change our application's nodejs dependencies:
COPY package.json /tmp/package.json
RUN cd /tmp && npm install
RUN mkdir -p /opt/app && cp -a /tmp/node_modules /opt/app/

# From here we load our application's code in, therefore the previous docker
# "layer" thats been cached will be used if possible
WORKDIR /opt/app
COPY . /opt/app

EXPOSE 3000

CMD ["node", "server.js"]

Hier wurde die Datei package.json hinzugefügt, ihre Abhängigkeiten installiert und in den Container WORKDIR kopiert, in dem sich die App befindet:

ADD package.json /tmp/package.json
RUN cd /tmp && npm install
RUN mkdir -p /opt/app && cp -a /tmp/node_modules /opt/app/

Um die npm-Installationsphase bei jedem Docker-Build zu vermeiden, kopieren Sie einfach diese Zeilen und ändern Sie ^ / opt / app ^ an den Ort, an dem sich Ihre App im Container befindet.

ohadgk
quelle
2
Das funktioniert. Einige Punkte. ADDwird zugunsten von COPYafaik entmutigt . COPYist noch effektiver. IMO, die letzten beiden Absätze sind nicht erforderlich, da es sich um Duplikate handelt. Auch aus Sicht der App spielt es keine Rolle, wo sich die App auf dem Dateisystem befindet, solange sie festgelegt WORKDIRist.
eljefedelrodeodeljefe
2
Besser noch ist es, alle apt-get-Befehle auf einem RUN zu kombinieren, einschließlich eines apt-get clean. Fügen Sie außerdem ./node_modules zu Ihrem .dockerignore hinzu, um zu vermeiden, dass Ihr Arbeitsverzeichnis in Ihren erstellten Container kopiert wird, und um den Schritt zum Erstellen des Buildkontexts des Builds zu beschleunigen.
Symmetric
1
Der gleiche Ansatz, aber nur das Hinzufügen package.jsonzur endgültigen Ruheposition, funktioniert ebenfalls gut (ohne cp / mv).
J. Fritz Barnes
26
Ich verstehe es nicht Warum installieren Sie in einem temporären Verzeichnis und verschieben es dann in das App-Verzeichnis? Warum nicht einfach im App-Verzeichnis installieren? Was vermisse ich hier?
Joniba
1
Dies ist wahrscheinlich tot, aber ich dachte, ich erwähne es für zukünftige Leser. @joniba Ein Grund dafür wäre, den temporären Ordner als beständiges Volume in compose bereitzustellen, ohne die node_modules des lokalen Host-Dateisystems zu beeinträchtigen. Das heißt, ich möchte meine App möglicherweise lokal, aber auch in einem Container ausführen und trotzdem die Möglichkeit behalten, dass meine node_modules nicht ständig erneut heruntergeladen werden, wenn sich package.json ändert
Dancypants
40

Seltsam! Niemand erwähnt mehrstufigen Build .

# ---- Base Node ----
FROM alpine:3.5 AS base
# install node
RUN apk add --no-cache nodejs-current tini
# set working directory
WORKDIR /root/chat
# Set tini as entrypoint
ENTRYPOINT ["/sbin/tini", "--"]
# copy project file
COPY package.json .

#
# ---- Dependencies ----
FROM base AS dependencies
# install node packages
RUN npm set progress=false && npm config set depth 0
RUN npm install --only=production 
# copy production node_modules aside
RUN cp -R node_modules prod_node_modules
# install ALL node_modules, including 'devDependencies'
RUN npm install

#
# ---- Test ----
# run linters, setup and tests
FROM dependencies AS test
COPY . .
RUN  npm run lint && npm run setup && npm run test

#
# ---- Release ----
FROM base AS release
# copy production node_modules
COPY --from=dependencies /root/chat/prod_node_modules ./node_modules
# copy app sources
COPY . .
# expose port and define CMD
EXPOSE 5000
CMD npm run start

Tolles Tuto hier: https://codefresh.io/docker-tutorial/node_docker_multistage/

Abdennour TOUMI
quelle
2
Was ist los mit einer COPYErklärung danach ENTRYPOINT?
Lindhe
Großartig, das bietet auch einen guten Vorteil, wenn Sie Ihre Docker-Datei testen, ohne Abhängigkeiten jedes Mal neu zu installieren, wenn Sie Ihre Docker-Datei bearbeiten
Xavier Brassoud
29

Ich habe festgestellt, dass der einfachste Ansatz darin besteht, die Kopiersemantik von Docker zu nutzen:

Die Anweisung COPY kopiert neue Dateien oder Verzeichnisse aus und fügt sie dem Dateisystem des Containers am Pfad hinzu.

Dies bedeutet, dass Sie den Rest des Quellverzeichnisses kopieren können , wenn Sie die package.jsonDatei zuerst explizit kopieren und dann den npm installSchritt ausführen , dass sie zwischengespeichert werden kann. Wenn sich die package.jsonDatei geändert hat, ist diese neu und führt das npm-Installations-Caching für zukünftige Builds erneut aus.

Ein Ausschnitt aus dem Ende einer Docker-Datei würde folgendermaßen aussehen:

# install node modules
WORKDIR  /usr/app
COPY     package.json /usr/app/package.json
RUN      npm install

# install application
COPY     . /usr/app
J. Fritz Barnes
quelle
5
Anstelle von cd /usr/appkönnen / sollten Sie verwenden WORKDIR /usr/app.
Vladimir Vukanac
1
@VladimirVukanac: +1: bei Verwendung von WORKDIR; Ich habe die Antwort oben aktualisiert, um dies zu berücksichtigen.
J. Fritz Barnes
Ist die npm-Installation im Verzeichnis / usr / app oder ausgeführt? ?
user557657
1
@ user557657 Das WORKDIR legt das Verzeichnis im zukünftigen Image fest, von dem aus der Befehl ausgeführt wird. In diesem Fall wird die npm-Installation /usr/appim Image ausgeführt, wodurch eine /usr/app/node_modulesmit den von der npm-Installation installierten Abhängigkeiten erstellt wird.
J. Fritz Barnes
1
@ J.FritzBarnes vielen Dank. Wird COPY . /usr/appdie package.jsonDatei nicht erneut /usr/appmit den restlichen Dateien kopiert ?
user557657
3

Ich kann mir vorstellen, dass Sie es bereits wissen, aber Sie könnten eine .dockerignore-Datei in denselben Ordner aufnehmen, der Folgendes enthält

node_modules
npm-debug.log

um zu vermeiden, dass Ihr Bild aufgebläht wird, wenn Sie auf den Docker-Hub drücken

usrrname
quelle
1

Sie müssen nicht den Ordner tmp verwenden, sondern einfach package.json in den Anwendungsordner Ihres Containers kopieren, einige Installationsarbeiten ausführen und alle Dateien später kopieren.

COPY app/package.json /opt/app/package.json
RUN cd /opt/app && npm install
COPY app /opt/app
Mike Zhang
quelle
Sie führen also die npm-Installation im Containerverzeichnis / opt / app aus und kopieren dann alle Dateien vom lokalen Computer nach / opt / app?
user557657