Cách lưu vào bộ nhớ cache lệnh cài đặt RUN npm khi docker xây dựng một Dockerfile


86

Tôi hiện đang phát triển chương trình phụ trợ Node cho ứng dụng của mình. Khi cô đặc nó ( docker build .) giai đoạn dài nhất là RUN npm install. Các RUN npm installchạy hướng dẫn về mọi thay đổi mã máy chủ nhỏ, mà cản trở năng suất thông qua việc tăng thời gian xây dựng.

Tôi nhận thấy rằng chạy npm install nơi mã ứng dụng sống và thêm node_modules vào vùng chứa bằng hướng dẫn ADD giải quyết được vấn đề này, nhưng nó không phải là phương pháp hay nhất. Nó phá vỡ toàn bộ ý tưởng về việc đông đặc nó và nó làm cho vật chứa nặng hơn nhiều.

Bất kỳ giải pháp nào khác?

Câu trả lời:


124

Ok vì vậy tôi đã tìm thấy bài viết tuyệt vời này về hiệu quả khi viết tệp docker.

Đây là một ví dụ về một tệp docker không hợp lệ thêm mã ứng dụng trước khi chạy RUN npm installhướng dẫn:

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

Bằng cách chia bản sao của ứng dụng thành 2 hướng dẫn SAO CHÉP (một cho tệp package.json và một cho các tệp còn lại) và chạy hướng dẫn cài đặt npm trước khi thêm mã thực, bất kỳ thay đổi mã nào sẽ không kích hoạt cài đặt npm RUN hướng dẫn, chỉ những thay đổi của package.json mới kích hoạt nó. Tệp docker thực hành tốt hơn:

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

Đây là nơi tệp package.json được thêm vào, cài đặt các phụ thuộc của nó và sao chép chúng vào vùng chứa WORKDIR, nơi ứng dụng hoạt động:

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

Để tránh giai đoạn cài đặt npm trên mọi bản dựng docker, chỉ cần sao chép các dòng đó và thay đổi ^ / opt / app ^ thành vị trí ứng dụng của bạn nằm bên trong vùng chứa.


2
Điều đó hoạt động. Một số điểm mặc dù. ADDkhông khuyến khích ủng hộ COPY, afaik. COPYthậm chí còn hiệu quả hơn. IMO, hai đoạn cuối là không cần thiết, vì chúng là bản sao và cũng từ quan điểm ứng dụng, nó không quan trọng ở vị trí nào trên hệ thống tệp mà ứng dụng tồn tại, miễn là WORKDIRđược đặt.
eljefedelrodeodeljefe

2
Vẫn tốt hơn là kết hợp tất cả các lệnh apt-get vào một RUN, bao gồm một apt-get clean. Ngoài ra, hãy thêm ./node_modules vào .dockerignore của bạn, để tránh sao chép thư mục làm việc của bạn vào vùng chứa được xây dựng và để tăng tốc bước sao chép ngữ cảnh của bản dựng.
xứng

1
Cách tiếp cận tương tự nhưng chỉ cần thêm package.jsonvào vị trí nghỉ cuối cùng cũng hoạt động tốt (loại bỏ bất kỳ cp / mv nào).
J. Fritz Barnes

27
Tôi không hiểu. Tại sao bạn cài đặt trong một thư mục tạm thời và sau đó chuyển nó vào thư mục ứng dụng? Tại sao không chỉ cài đặt trong thư mục ứng dụng? Tôi còn thiếu gì ở đây?
joniba

1
Điều này có lẽ đã chết, nhưng tôi nghĩ rằng tôi đề cập đến nó cho độc giả trong tương lai. @joniba một lý do để làm điều này là gắn kết thư mục tạm thời như một ổ đĩa liên tục trong quá trình soạn mà không can thiệp vào node_modules của hệ thống tệp máy chủ cục bộ. Tức là tôi có thể muốn chạy ứng dụng của tôi tại địa phương mà còn trong một container mà vẫn giữ khả năng có node_modules tôi không ngừng tái tải khi thay đổi package.json
dancypants

41

Kỳ dị! Không ai đề cập đến xây dựng nhiều giai đoạn .

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

Hướng dẫn tuyệt vời tại đây: https://codefresh.io/docker-tutorial/node_docker_multistage/


2
Có gì với việc có một COPYtuyên bố sau ENTRYPOINT?
lindhe

Tuyệt vời, điều đó cũng mang lại lợi thế tốt khi bạn đang kiểm tra Dockerfile của mình mà không cần cài đặt lại các phụ thuộc mỗi khi bạn chỉnh sửa Dockerfile của mình
Xavier Brassoud

30

Tôi thấy rằng cách tiếp cận đơn giản nhất là tận dụng ngữ nghĩa bản sao của Docker:

Hướng dẫn COPY sao chép các tệp hoặc thư mục mới từ và thêm chúng vào hệ thống tệp của vùng chứa tại đường dẫn.

Điều này có nghĩa là nếu trước tiên bạn sao chép package.jsontệp một cách rõ ràng và sau đó chạy npm installbước mà tệp có thể được lưu vào bộ nhớ cache và sau đó bạn có thể sao chép phần còn lại của thư mục nguồn. Nếu package.jsontệp đã thay đổi, thì tệp đó sẽ là tệp mới và nó sẽ chạy lại bộ nhớ đệm cài đặt npm cho các bản dựng trong tương lai.

Đoạn mã từ phần cuối của Dockerfile sẽ giống như sau:

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

# install application
COPY     . /usr/app

6
Thay vì cd /usr/appbạn có thể / nên sử dụng WORKDIR /usr/app.
Vladimir Vukanac

1
@VladimirVukanac: +1: khi sử dụng WORKDIR; Tôi đã cập nhật câu trả lời ở trên để tính đến điều đó.
J. Fritz Barnes

là npm install chạy trong thư mục / usr / app hay. ?
user557657

1
@ user557657 WORKDIR đặt thư mục trong hình ảnh tương lai mà từ đó lệnh sẽ được chạy. Vì vậy, trong trường hợp này, nó đang chạy cài đặt npm từ /usr/appbên trong hình ảnh sẽ tạo ra một tệp /usr/app/node_modulesphụ thuộc được cài đặt từ cài đặt npm.
J. Fritz Barnes

1
@ J.FritzBarnes cảm ơn rất nhiều. Không COPY . /usr/appsao chép package.jsonlại tệp /usr/appvới phần còn lại của tệp?
user557657

3

Tôi tưởng tượng bạn có thể đã biết, nhưng bạn có thể bao gồm tệp .dockerignore trong cùng một thư mục chứa

node_modules
npm-debug.log

để tránh làm phồng hình ảnh của bạn khi bạn đẩy đến trung tâm docker


1

bạn không cần sử dụng thư mục tmp, chỉ cần sao chép package.json vào thư mục ứng dụng của vùng chứa, thực hiện một số công việc cài đặt và sao chép tất cả các tệp sau đó.

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

vậy bạn đang thực hiện cài đặt npm trong thư mục container / opt / app rồi sao chép tất cả các tệp từ máy cục bộ sang / opt / app?
user557657
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.