Docker-compose: node_modules không có trong một tập sau khi cài đặt npm thành công


184

Tôi có một ứng dụng với các dịch vụ sau:

  • web/ - giữ và chạy một máy chủ web 3 bình python trên cổng 5000. Sử dụng sqlite3.
  • worker/- có một index.jstệp là một công nhân cho một hàng đợi. máy chủ web tương tác với hàng đợi này bằng API json qua cổng 9730. Công nhân sử dụng redis để lưu trữ. Công nhân cũng lưu trữ dữ liệu cục bộ trong thư mụcworker/images/

Bây giờ câu hỏi này chỉ liên quan đến worker.

worker/Dockerfile

FROM node:0.12

WORKDIR /worker

COPY package.json /worker/
RUN npm install

COPY . /worker/

docker-compose.yml

redis:
    image: redis
worker:
    build: ./worker
    command: npm start
    ports:
        - "9730:9730"
    volumes:
        - worker/:/worker/
    links:
        - redis

Khi tôi chạy docker-compose build, mọi thứ hoạt động như mong đợi và tất cả các mô-đun npm được cài đặt /worker/node_modulesnhư tôi mong đợi.

npm WARN package.json unfold@1.0.0 No README data

> phantomjs@1.9.2-6 install /worker/node_modules/pageres/node_modules/screenshot-stream/node_modules/phantom-bridge/node_modules/phantomjs
> node install.js

<snip>

Nhưng khi tôi làm docker-compose up, tôi thấy lỗi này:

worker_1 | Error: Cannot find module 'async'
worker_1 |     at Function.Module._resolveFilename (module.js:336:15)
worker_1 |     at Function.Module._load (module.js:278:25)
worker_1 |     at Module.require (module.js:365:17)
worker_1 |     at require (module.js:384:17)
worker_1 |     at Object.<anonymous> (/worker/index.js:1:75)
worker_1 |     at Module._compile (module.js:460:26)
worker_1 |     at Object.Module._extensions..js (module.js:478:10)
worker_1 |     at Module.load (module.js:355:32)
worker_1 |     at Function.Module._load (module.js:310:12)
worker_1 |     at Function.Module.runMain (module.js:501:10)

Hóa ra không có mô-đun nào có mặt /worker/node_modules(trên máy chủ hoặc trong thùng chứa).

Nếu trên máy chủ, tôi npm install, sau đó mọi thứ hoạt động tốt. Nhưng tôi không muốn làm điều đó. Tôi muốn container để xử lý các phụ thuộc.

Có chuyện gì ở đây vậy?

(Không cần phải nói, tất cả các gói đều nằm trong package.json.)


Bạn đã kết thúc việc tìm kiếm một giải pháp?
Justin Stayton

Tôi nghĩ bạn nên sử dụng hướng dẫn ONBUILD ... Giống như thế này: github.com/nodejs/docker-node/blob/master/0.12/onbuild/
Lỗi

1
Làm thế nào bạn có thể phát triển trên máy chủ khi IDE không biết các phụ thuộc của node_module?
André

2
Cố gắng loại bỏ volumes: - worker/:/worker/khối khỏi docker-compose.ymltập tin. Dòng này ghi đè lên thư mục bạn thực hiện bằng lệnh COPY.
Stepan

When I run docker-compose build, everything works as expected and all npm modules are installed in /worker/node_modules as I'd expect.- Làm thế nào bạn kiểm tra điều này?
Vallie

Câu trả lời:


272

Điều này xảy ra bởi vì bạn đã thêm workerthư mục của mình dưới dạng một ổ đĩa docker-compose.yml, vì âm lượng không được gắn kết trong quá trình xây dựng.

Khi docker xây dựng hình ảnh, node_modulesthư mục được tạo trong workerthư mục và tất cả các phụ thuộc được cài đặt ở đó. Sau đó, trong thời gian chạy, workerthư mục từ docker bên ngoài được gắn vào phiên bản docker (không được cài đặt node_modules), ẩn node_modulesbạn vừa cài đặt. Bạn có thể xác minh điều này bằng cách loại bỏ âm lượng được gắn từ của bạn docker-compose.yml.

Một cách giải quyết là sử dụng một khối lượng dữ liệu để lưu trữ tất cả node_modules, vì khối lượng dữ liệu sao chép trong dữ liệu từ hình ảnh docker được xây dựng trước khi workerthư mục được gắn kết. Điều này có thể được thực hiện trong docker-compose.ymlnhư thế này:

redis:
    image: redis
worker:
    build: ./worker
    command: npm start
    ports:
        - "9730:9730"
    volumes:
        - ./worker/:/worker/
        - /worker/node_modules
    links:
        - redis

Tôi không hoàn toàn chắc chắn liệu điều này có đặt ra bất kỳ vấn đề nào cho tính di động của hình ảnh hay không, nhưng có vẻ như bạn chủ yếu sử dụng docker để cung cấp môi trường thời gian chạy, đây không phải là vấn đề.

Nếu bạn muốn đọc thêm về khối lượng, có một hướng dẫn sử dụng tuyệt vời có sẵn ở đây: https://docs.docker.com/userguide/dockervolume/

EDIT: Docker đã thay đổi cú pháp của nó để yêu cầu dẫn đầu ./để gắn vào các tệp liên quan đến tệp docker-compose.yml.


7
câu trả lời tuyệt vời, ít xâm phạm và giải thích tuyệt vời!
kkemple

41
Tôi đã thử phương pháp này và đập vào tường khi sự phụ thuộc thay đổi. Tôi đã xây dựng lại hình ảnh, bắt đầu bộ chứa mới và khối lượng /worker/node_modulesvẫn giữ nguyên như trước (với các phụ thuộc cũ). Có mẹo nào để sử dụng âm lượng mới khi xây dựng lại hình ảnh không?
Ondrej Slinták

11
Dường như docker compose không loại bỏ khối lượng nếu các container khác sử dụng chúng (ngay cả khi chúng đã chết). Vì vậy, nếu có một số container chết cùng loại (vì bất kỳ lý do gì), kịch bản tôi đã mô tả trong bình luận trước đây sẽ xảy ra. Từ những gì tôi đã thử, sử dụng docker-compose rmdường như để khắc phục vấn đề này, nhưng tôi tin rằng phải có một giải pháp tốt hơn và dễ dàng hơn.
Ondrej Slinták

15
Có một giải pháp trong năm 2018 mà không cần phải rebuild --no-cachethay đổi mọi lúc?
Eelke

7
bây giờ bạn có thể sử dụng `--renew-anon-volume` để tạo lại các khối ẩn danh thay vì dữ liệu từ các thùng chứa trước đó.
Mohammed Essehemy

37

Các node_modulesthư mục được ghi đè bởi khối lượng và không dễ tiếp cận hơn trong container. Tôi đang sử dụng chiến lược tải mô-đun gốc để lấy thư mục ra khỏi ổ đĩa:

/data/node_modules/ # dependencies installed here
/data/app/ # code base

Dockerfile:

COPY package.json /data/
WORKDIR /data/
RUN npm install
ENV PATH /data/node_modules/.bin:$PATH

COPY . /data/app/
WORKDIR /data/app/

Thư mục node_moduleskhông thể truy cập từ bên ngoài container vì nó được bao gồm trong hình ảnh.


1
Có bất kỳ nhược điểm của phương pháp này? Có vẻ làm việc tốt cho tôi.
Bret Fisher

1
node_moduleskhông thể truy cập từ bên ngoài container nhưng không thực sự là nhược điểm;)
jsan

và mỗi khi bạn thay đổi gói.json, bạn cần xây dựng lại toàn bộ vùng chứa bằng --no-cache phải không?
Lu-ca

Bạn cần xây dựng lại hình ảnh khi bạn thay đổi gói.json, vâng, nhưng - không cần bộ đệm. Nếu bạn chạy, docker-compose run app npm installbạn sẽ tạo một node_modules trong thư mục hiện tại và bạn không cần phải xây dựng lại hình ảnh nữa.
jsan

9
Nhược điểm là: không còn tự động hoàn thành IDE, không trợ giúp, không có kinh nghiệm phát triển tốt. Mọi thứ cũng cần phải được cài đặt trên máy chủ ngay bây giờ, nhưng không phải là lý do để sử dụng docker ở đây mà máy chủ dev không cần gì để có thể làm việc với một dự án?
Michael B.

31

Giải pháp được cung cấp bởi @FrederikNS hoạt động, nhưng tôi thích đặt tên rõ ràng cho âm lượng node_modules của mình.

project/docker-compose.ymlTệp của tôi (docker-compose phiên bản 1.6+):

version: '2'
services:
  frontend:
    ....
    build: ./worker
    volumes:
      - ./worker:/worker
      - node_modules:/worker/node_modules
    ....
volumes:
  node_modules:

cấu trúc tập tin của tôi là:

project/
   │── worker/
        └─ Dockerfile
   └── docker-compose.yml

Nó tạo ra một khối lượng được đặt tên project_node_modules và sử dụng lại mỗi lần tôi tải ứng dụng của mình.

Tôi docker volume lstrông như thế này:

DRIVER              VOLUME NAME
local               project1_mysql
local               project1_node_modules
local               project2_postgresql
local               project2_node_modules

3
Trong khi điều này làm việc với nhau, bạn sẽ hiểu được một khái niệm thực sự trong docker: tất cả các phụ thuộc sẽ được đưa vào để có thể di chuyển tối đa. bạn không thể di chuyển hình ảnh này mà không chạy các lệnh khác, điều này sẽ xảy ra
Javier Buzzi

4
đã cố gắng giải quyết vấn đề tương tự trong nhiều giờ và tự mình đưa ra giải pháp tương tự. Câu trả lời của bạn phải được đánh giá cao nhất, nhưng tôi đoán, ppl không nhận được câu trả lời của bạn vì bạn đã đặt tên cho tập của mình là "node_modules" và tất cả các độc giả đều thiếu thực tế rằng điều này tạo ra một tập mới. Khi đọc mã của bạn, tôi nghĩ rằng bạn chỉ cần "gắn lại" thư mục node_modules cục bộ và loại bỏ ý tưởng đó. Có lẽ bạn nên chỉnh sửa tên âm lượng thành một cái gì đó như "container_node_modules" để làm rõ điều đó. :)
Fabian

1
Tôi cũng thấy đây là giải pháp tao nhã nhất, dễ dàng cho phép tham khảo cùng một âm lượng cho các mô-đun nút trong một số giai đoạn xây dựng. Bài viết tuyệt vời Bài học từ Xây dựng ứng dụng nút trong Docker cũng sử dụng cách tiếp cận tương tự.
kf06925

1
@ kf06925 người đàn ông bạn thực sự đã cứu tôi, tôi đã dành hàng giờ cố gắng để giải quyết điều này và nhờ vào bài viết tôi có thể !! Tôi sẽ mua cho bạn một cốc bia nếu tôi có thể cảm ơn rất nhiều
helado

21

Gần đây tôi có một vấn đề tương tự. Bạn có thể cài đặt node_modulesở nơi khác và đặt NODE_PATHbiến môi trường.

Trong ví dụ dưới đây tôi đã cài đặt node_modulesvào/install

công nhân / Dockerfile

FROM node:0.12

RUN ["mkdir", "/install"]

ADD ["./package.json", "/install"]
WORKDIR /install
RUN npm install --verbose
ENV NODE_PATH=/install/node_modules

WORKDIR /worker

COPY . /worker/

docker-compose.yml

redis:
    image: redis
worker:
    build: ./worker
    command: npm start
    ports:
        - "9730:9730"
    volumes:
        - worker/:/worker/
    links:
        - redis

6
Giải pháp được bình chọn hàng đầu bởi @FrederikNS rất hữu ích, là cách tôi giải quyết một vấn đề khác về khối lượng cục bộ của mình ghi đè lên bộ chứa node_modulesdựa trên bài viết này . Nhưng nó đã dẫn tôi đến trải nghiệm vấn đề này . Giải pháp này ở đây là tạo một thư mục riêng để sao chép package.jsonvào, chạy npm installở đó, sau đó chỉ định NODE_PATHbiến môi trường docker-compose.ymlđể trỏ đến node_modulesthư mục của thư mục đó hoạt động và cảm thấy đúng.
cwnewhouse

Bao gồm ENV NODE_PATH = / install / node_modules trong Dockerfile cuối cùng cũng là giải pháp cho tôi sau nhiều giờ thử các cách tiếp cận khác nhau. Cảm ơn ngài.
Benny Meade

Nếu bạn chạy npm installtrên máy chủ thì sao? Nó dường như node_modulessẽ xuất hiện trên máy chủ và sẽ được phản ánh vào container, ưu tiên hơn NODE_PATH. Vì vậy, container sẽ sử dụng node_modules từ máy chủ.
trọng

19

Có giải pháp tao nhã:

Chỉ cần gắn kết không phải toàn bộ thư mục, mà chỉ thư mục ứng dụng. Bằng cách này, bạn sẽ không gặp rắc rối vớinpm_modules .

Thí dụ:

  frontend:
    build:
      context: ./ui_frontend
      dockerfile: Dockerfile.dev
    ports:
    - 3000:3000
    volumes:
    - ./ui_frontend/src:/frontend/src

Dockerfile.dev:

FROM node:7.2.0

#Show colors in docker terminal
ENV COMPOSE_HTTP_TIMEOUT=50000
ENV TERM="xterm-256color"

COPY . /frontend
WORKDIR /frontend
RUN npm install update
RUN npm install --global typescript
RUN npm install --global webpack
RUN npm install --global webpack-dev-server
RUN npm install --global karma protractor
RUN npm install
CMD npm run server:dev

Xuất sắc. Không thể hiểu tại sao nó không phải là câu trả lời được chấp nhận.
Jivan

2
Đây là giải pháp tốt và nhanh chóng nhưng nó đòi hỏi phải xây dựng lại & cắt tỉa sau khi cài đặt phụ thuộc mới.
Kunok

Bây giờ tôi làm điều đó một cách khác biệt, cần có một âm lượng dành riêng cho node_modules và âm lượng mà bạn gắn kết từ máy chủ. Sau đó, không có vấn đề gì cả
holms

14

CẬP NHẬT: Sử dụng giải pháp được cung cấp bởi @FrederikNS.

Tôi gặp phải vấn đề tương tự. Khi thư mục/worker được gắn vào thùng chứa - tất cả nội dung của nó sẽ được đồng bộ hóa (vì vậy thư mục node_modules sẽ biến mất nếu bạn không có nó cục bộ.)

Do các gói npm không tương thích dựa trên HĐH, tôi không thể chỉ cài đặt các mô-đun cục bộ - sau đó khởi chạy bộ chứa, vì vậy ..

Giải pháp của tôi cho vấn đề này là bọc nguồn trong một srcthư mục, sau đó liên kết node_modulesvào thư mục đó, sử dụng tệp index.js này . Vì vậy, index.jstập tin bây giờ là điểm khởi đầu của ứng dụng của tôi.

Khi tôi chạy container, tôi gắn /app/srcthư mục vào srcthư mục cục bộ của mình .

Vì vậy, thư mục container trông giống như thế này:

/app
  /node_modules
  /src
    /node_modules -> ../node_modules
    /app.js
  /index.js

Nó là xấu xí , nhưng nó hoạt động ..


3
Ôi, chúa ơi ... tôi không thể tin rằng tôi cũng bị mắc kẹt với điều đó!
Lucas Pottersky

10

Do cách Node.js tải các mô-đun , node_modulescó thể ở bất kỳ đâu trong đường dẫn đến mã nguồn của bạn. Ví dụ, đặt nguồn của bạn tại /worker/srcvà bạn package.jsontrong /worker, do đó /worker/node_moduleslà nơi họ đang được cài đặt.


7

Việc cài đặt node_modules trong vùng chứa khác với thư mục dự án và việc đặt NODE_PATH vào thư mục node_modules giúp tôi (bạn cần xây dựng lại vùng chứa).

Tôi đang sử dụng docker-compose. Cấu trúc tập tin dự án của tôi:

-/myproject
--docker-compose.yml
--nodejs/
----Dockerfile

docker-compose.yml:

version: '2'
services:
  nodejs:
    image: myproject/nodejs
    build: ./nodejs/.
    volumes:
      - ./nodejs:/workdir
    ports:
      - "23005:3000"
    command: npm run server

Dockerfile trong thư mục nodejs:

FROM node:argon
RUN mkdir /workdir
COPY ./package.json /workdir/.
RUN mkdir /data
RUN ln -s /workdir/package.json /data/.
WORKDIR /data
RUN npm install
ENV NODE_PATH /data/node_modules/
WORKDIR /workdir

1
Đây là giải pháp tốt nhất tôi tìm thấy. NODE_PATHlà chìa khóa cho tôi
cdignam

Tôi nghĩ rằng điều này có ý nghĩa nhưng bằng cách đặt NODE_PATH, khi chạy hình ảnh, CMD npm start không sử dụng NODE_PATH được chỉ định.
Acton

6

Ngoài ra còn có một số giải pháp đơn giản mà không cần ánh xạ node_modulethư mục vào một ổ đĩa khác. Sắp sửa chuyển các gói npm vào lệnh CMD cuối cùng.

Nhược điểm của phương pháp này:

  • chạy npm installmỗi khi bạn chạy container (chuyển từ npmsang yarncũng có thể tăng tốc quá trình này một chút).

công nhân / Dockerfile

FROM node:0.12
WORKDIR /worker
COPY package.json /worker/
COPY . /worker/
CMD /bin/bash -c 'npm install; npm start'

docker-compose.yml

redis:
    image: redis
worker:
    build: ./worker
    ports:
        - "9730:9730"
    volumes:
        - worker/:/worker/
    links:
        - redis

3

Có hai yêu cầu riêng biệt mà tôi thấy đối với môi trường phát triển nút ... gắn mã nguồn của bạn VÀO vùng chứa và gắn nút_modules TỪ vùng chứa (đối với IDE của bạn). Để thực hiện việc đầu tiên, bạn thực hiện gắn kết thông thường, nhưng không phải tất cả mọi thứ ... chỉ là những thứ bạn cần

volumes:
    - worker/src:/worker/src
    - worker/package.json:/worker/package.json
    - etc...

(lý do để không làm - /worker/node_modules là vì docker-compose sẽ duy trì âm lượng giữa các lần chạy, nghĩa là bạn có thể chuyển hướng khỏi những gì thực sự có trong hình ảnh (đánh bại mục đích không chỉ gắn kết gắn kết từ máy chủ của bạn)).

Cái thứ hai thực sự khó hơn. Giải pháp của tôi là một chút hackish, nhưng nó hoạt động. Tôi có một tập lệnh để cài đặt thư mục node_modules trên máy chủ của mình và tôi chỉ cần nhớ gọi nó bất cứ khi nào tôi cập nhật pack.json (hoặc thêm nó vào mục tiêu chạy docker-compose build cục bộ).

install_node_modules:
    docker build -t building .
    docker run -v `pwd`/node_modules:/app/node_modules building npm install

2

Theo tôi, chúng ta không nên RUN npm installtrong Dockerfile. Thay vào đó, chúng ta có thể bắt đầu một container bằng cách sử dụng bash để cài đặt các phụ thuộc trước khi chạy dịch vụ nút chính thức

docker run -it -v ./app:/usr/src/app  your_node_image_name  /bin/bash
root@247543a930d6:/usr/src/app# npm install

Tôi thực sự đồng ý với bạn về điều này. Các tập có nghĩa là được sử dụng khi bạn muốn chia sẻ dữ liệu giữa container và máy chủ. Khi bạn quyết định node_moduleskiên trì ngay cả sau khi gỡ bỏ container, bạn cũng nên biết khi nào hoặc khi nào không nên thực hiện npm installthủ công. OP đề nghị làm điều đó trên mọi hình ảnh xây dựng . Bạn có thể làm điều đó, nhưng bạn cũng không cần sử dụng âm lượng cho điều đó. Trên mỗi bản dựng, các mô-đun sẽ được cập nhật bất kỳ.
phil294

@Blauhirn thật hữu ích khi gắn một khối lượng máy chủ cục bộ vào vùng chứa khi thực hiện, ví dụ như đồng hồ gulp (hoặc các lệnh tương tự) - bạn muốn nút_modules tồn tại trong khi vẫn cho phép thay đổi các nguồn khác (js, css, v.v.). npm khăng khăng sử dụng một ngụm cục bộ, vì vậy nó phải tồn tại (hoặc được cài đặt thông qua các phương pháp khác khi khởi động)
tbm

2

Bạn có thể thử một cái gì đó như thế này trong Dockerfile của bạn:

FROM node:0.12
WORKDIR /worker
CMD bash ./start.sh

Sau đó, bạn nên sử dụng Âm lượng như thế này:

volumes:
  - worker/:/worker:rw

Bản bắt đầu phải là một phần của kho công nhân của bạn và trông như thế này:

#!/bin/sh
npm install
npm start

Vì vậy, node_modules là một phần của khối công nhân của bạn và được đồng bộ hóa và các tập lệnh npm được thực thi khi mọi thứ đã hết.


Điều này sẽ thêm một chi phí lớn để bắt đầu container.
tbm

2
Nhưng chỉ lần đầu tiên vì node_modules sẽ tồn tại trên máy cục bộ.
Parav01d

Hoặc cho đến khi hình ảnh được xây dựng lại, hoặc âm lượng bị xóa :). Điều đó nói rằng, tôi đã không tìm thấy một giải pháp tốt hơn cho mình.
tbm

0

Bạn cũng có thể bỏ Dockerfile của mình, vì tính đơn giản của nó, chỉ cần sử dụng một hình ảnh cơ bản và chỉ định lệnh trong tệp soạn thảo của bạn:

version: '3.2'

services:
  frontend:
    image: node:12-alpine
    volumes:
      - ./frontend/:/app/
    command: sh -c "cd /app/ && yarn && yarn run start"
    expose: [8080]
    ports:
      - 8080:4200

Điều này đặc biệt hữu ích đối với tôi, vì tôi chỉ cần môi trường của hình ảnh, nhưng hoạt động trên các tệp của tôi bên ngoài vùng chứa và tôi nghĩ đây cũng là điều bạn muốn làm.

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.