当 docker 构建 Dockerfile 时,如何缓存 RUN npm 安装指令

我目前正在为我的应用程序开发一个 Node 后端。 当对其进行对接(docker build .)时,最长的阶段是 RUN npm installRUN npm install指令在每个小的服务器代码更改上运行,这通过增加构建时间来阻碍生产力。

我发现,在应用程序代码所在的地方运行 npm install 并使用 ADD 指令将 node _ module 添加到容器中可以解决这个问题,但是这远非最佳实践。它打破了整个停靠的想法,这使得集装箱重量更大。

还有别的办法吗?

126745 次浏览

好的,我找到了 这篇伟大的文章关于写码头文件时的效率。

这是一个在运行 RUN npm install指令之前添加应用程序代码的错误码头文件的示例:

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

通过将应用程序的副本分成2个 COPY 指令(一个用于 package.json 文件,另一个用于其余文件) ,并在添加实际代码之前运行 npm 安装指令,任何代码更改都不会触发 RUN npm 安装指令,只有 package.json 的更改才会触发。更好的实践码头文件:

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 文件添加的地方,安装它的依赖项并将它们复制到应用程序所在的 WORKDIR 容器中:

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

为了避免在每个 docker 构建中都出现 npm 安装阶段,只需复制这些行并将 ^/opt/app ^ 更改为应用程序在容器中的位置。

我想您可能已经知道了,但是您可以在包含

node_modules
npm-debug.log

以避免您的形象膨胀时,您推到码头枢纽

我发现最简单的方法是利用 Docker 的复制语义:

COPY 指令从中复制新的文件或目录,并将它们添加到路径处容器的文件系统中。

这意味着,如果首先显式地复制 package.json文件,然后运行可以缓存它的 npm install步骤,那么就可以复制源目录的其余部分。如果 package.json文件已经更改,那么这将是新的,它将重新运行 npm 安装缓存,以备将来的构建使用。

Dockerfile 末尾的代码片段如下:

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


# install application
COPY     . /usr/app

奇怪! 没人提到 多阶段建造多阶段建造

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

这里有一个很棒的 tuto: https://codefresh.io/docker-tutorial/node_docker_multistage/

您不需要使用 tmp 文件夹,只需将 package.json 复制到容器的应用程序文件夹中,做一些安装工作,然后复制所有文件。

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

我希望使用卷,而不是复制,并且继续使用 docker compose,我可以在最后将命令链接起来

FROM debian:latest
RUN apt -y update \
&& apt -y install curl \
&& curl -sL https://deb.nodesource.com/setup_12.x | bash - \
&& apt -y install nodejs
RUN apt -y update \
&&  apt -y install wget \
build-essential \
net-tools
RUN npm install pm2 -g


RUN mkdir -p /home/services_monitor/ && touch /home/services_monitor/
RUN chown -R root:root /home/services_monitor/


WORKDIR /home/services_monitor/


CMD npm install \
&& pm2-runtime /home/services_monitor/start.json