Docker bir Dockerfile oluştururken RUN npm yükleme talimatını önbelleğe alma


86

Şu anda uygulamam için bir Node arka ucu geliştiriyorum. Dockerize ederken ( docker build .) en uzun aşama RUN npm install. RUN npm installArtan yapı zaman içinde her küçük sunucu kodu değişikliği talimat çalışır, hangi Engeller verimlilik.

Uygulama kodunun bulunduğu yerde npm kurulumunu çalıştırmanın ve ADD komutuyla kapsayıcıya node_modules eklemenin bu sorunu çözdüğünü buldum, ancak en iyi uygulama olmaktan uzak. Bu, onu dockerize etme fikrini bir nevi kırıyor ve konteynerin çok daha fazla ağırlık almasına neden oluyor.

Başka çözüm var mı?

Yanıtlar:


125

Tamam, bir docker dosyası yazarken verimlilikle ilgili bu harika makaleyi buldum .

Bu, RUN npm installtalimatı çalıştırmadan önce uygulama kodunu ekleyen hatalı bir docker dosyası örneğidir :

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"]

Uygulamanın kopyasını 2 COPY talimatına bölerek (biri package.json dosyası için diğeri diğer dosyalar için) ve gerçek kodu eklemeden önce npm kurulum talimatını çalıştırarak, herhangi bir kod değişikliği RUN npm kurulumunu tetiklemeyecektir. talimat, yalnızca package.json'daki değişiklikler onu tetikleyecektir. Daha iyi uygulama docker dosyası:

FROM ubuntu
MAINTAINER David Weinstein <david@bitjudo.com>

# 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"]

Package.json dosyasının eklendiği, bağımlılıklarını yüklediği ve bunları uygulamanın bulunduğu WORKDIR kapsayıcısına kopyaladığınız yer burasıdır:

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

Her docker derlemesinde npm yükleme aşamasından kaçınmak için bu satırları kopyalayın ve ^ / opt / app ^ öğesini uygulamanızın kapsayıcı içinde bulunduğu konuma değiştirin.


2
Bu çalışır. Yine de bazı noktalar. afaik ADDlehine cesareti kırıldı COPY. COPYdaha da etkilidir. IMO, son iki paragraf gerekli değildir, çünkü bunlar yinelenir ve ayrıca uygulama açısından WORKDIR, ayarlandığı sürece uygulamanın dosya sisteminde nerede yaşadığı önemli değildir .
eljefedelrodeodeljefe

2
Daha da iyisi, apt-get komutlarının tümünü bir apt-get clean. Ayrıca, çalışma dizininizi yerleşik kapsayıcınıza kopyalamaktan kaçınmak ve derlemenin derleme bağlamı kopyalama adımını hızlandırmak için ./node_modules'i .dockerignore dosyanıza ekleyin.
Simetrik

1
Aynı yaklaşım, ancak sadece package.jsonson dinlenme pozisyonuna eklemek de iyi çalışıyor (herhangi bir cp / mv'yi ortadan kaldırarak).
J. Fritz Barnes

27
Ben anlamadım Neden bir geçici dizine kuruyorsunuz ve sonra onu uygulama dizinine taşıyorsunuz? Neden sadece uygulama dizinine yüklemiyorsunuz? Burada neyi özlüyorum?
joniba

1
Bu muhtemelen öldü, ancak gelecekteki okuyucular için bundan bahsettiğimi düşündüm. @joniba bunu yapmanın bir nedeni, geçici klasörü yerel ana bilgisayar dosya sisteminin node_modüllerine müdahale etmeden derlemede kalıcı bir birim olarak bağlamak olabilir. Yani uygulamamı yerel olarak ama aynı zamanda bir kapsayıcıda çalıştırmak ve yine de package.json değiştiğinde
node_modüllerimin

41

Tuhaf! Hiç kimse çok aşamalı yapıdan bahsetmiyor .

# ---- 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

Harika öğretici burada: https://codefresh.io/docker-tutorial/node_docker_multistage/


2
COPYSonra bir açıklama yapmanın ENTRYPOINTne anlamı var ?
lindhe

Harika, bu Dockerfile'ınızı Dockerfile'ınızı her düzenlediğinizde bağımlılıkları yeniden yüklemeden test ederken de iyi bir avantaj sağlıyor
Xavier Brassoud

31

En basit yaklaşımın Docker'ın kopyalama anlamlarından yararlanmak olduğunu buldum:

COPY komutu, içinden yeni dosya veya dizinleri kopyalar ve bunları yoldaki konteynerin dosya sistemine ekler.

Bu, önce package.jsondosyayı açıkça kopyalarsanız ve ardından npm installönbelleğe alınabilecek adımı çalıştırırsanız ve ardından kaynak dizinin geri kalanını kopyalayabileceğiniz anlamına gelir. Eğer package.jsondosya değişti, o yeni olacak ve npm gelecek inşa için bu önbelleğe yüklemek yeniden çalıştırmak olacaktır.

Dockerfile'ın sonundaki bir snippet şöyle görünür:

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

# install application
COPY     . /usr/app

7
cd /usr/appSenin yerine kullanabilirsin / kullanmalısın WORKDIR /usr/app.
Vladimir Vukanac

1
@VladimirVukanac: +1: WORKDIR kullanarak; Bunu hesaba katmak için yukarıdaki cevabı güncelledim.
J. Fritz Barnes

1
@ user557657 WORKDIR, komutun çalıştırılacağı gelecekteki görüntü içindeki dizini ayarlar. Yani bu durumda, npm kurulumundan yüklenen bağımlılıkları /usr/appoluşturacak olan npm kurulumunu imaj içinden çalıştırıyor /usr/app/node_modules.
J. Fritz Barnes

1
@ J.FritzBarnes çok teşekkürler. isnt COPY . /usr/appkopyalayacaktır package.jsondosyayı yeniden /usr/appdosyalarının geri kalanı ile?
user557657

1
Docker, değişirse npm installkomutu yeniden çalıştırmaz package.json, RUN komutu sonucunu önbelleğe alır ve aynı RUN komutunun aynı sonucu verdiğini varsayar. Önbelleği geçersiz kılmak için docker build--no-cache bayrağıyla çalıştırmalı veya RUN komutunu bir şekilde değiştirmelisiniz.
Mikhail Zhuravlev

3

Sanırım zaten biliyorsunuzdur, ancak aynı klasöre bir .dockerignore dosyası dahil edebilirsiniz.

node_modules
npm-debug.log

docker hub'a ittiğinizde görüntünüzün şişmesini önlemek için


1

tmp klasörünü kullanmanıza gerek yoktur, sadece package.json dosyasını kapsayıcınızın uygulama klasörüne kopyalayın, biraz yükleme işi yapın ve tüm dosyaları daha sonra kopyalayın.

COPY app/package.json /opt/app/package.json
RUN cd /opt/app && npm install
COPY app /opt/app

yani / opt / app konteyner dizininde npm kurulumunu çalıştırıp ardından tüm dosyaları yerel makineden / opt / app'a kopyalıyorsunuz?
user557657
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.