Susunan Docker: node_modules tidak ada dalam volume setelah npm berhasil diinstal

184

Saya memiliki aplikasi dengan layanan berikut:

  • web/ - memegang dan menjalankan server web python 3 flask pada port 5000. Menggunakan sqlite3.
  • worker/- Memiliki index.jsfile yang merupakan pekerja untuk antrian. server web berinteraksi dengan antrian ini menggunakan json API over port 9730. Pekerja menggunakan redis untuk penyimpanan. Pekerja juga menyimpan data secara lokal di folderworker/images/

Sekarang pertanyaan ini hanya menyangkut 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

Ketika saya menjalankan docker-compose build, semuanya berfungsi seperti yang diharapkan dan semua modul npm diinstal /worker/node_modulesseperti yang saya harapkan.

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>

Tetapi ketika saya melakukannya docker-compose up, saya melihat kesalahan ini:

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)

Ternyata tidak ada modul yang ada di /worker/node_modules(di host atau di wadah).

Jika di tuan rumah, saya npm install, maka semuanya bekerja dengan baik. Tetapi saya tidak ingin melakukan itu. Saya ingin wadah untuk menangani dependensi.

Apa yang salah di sini?

(Tak perlu dikatakan, semua paket sudah masuk package.json.)

KGo
sumber
Apakah Anda akhirnya menemukan solusi?
Justin Stayton
Saya pikir Anda harus menggunakan instruksi ONBUILD ... Seperti ini: github.com/nodejs/docker-node/blob/master/0.12/onbuild/…
Lucas Pottersky
1
Bagaimana Anda melakukan pengembangan pada host ketika IDE tidak tahu dependensi node_module?
André
2
Cobalah untuk menghapus volumes: - worker/:/worker/blokir dari docker-compose.ymlfile. Baris ini menimpa folder yang Anda buat dengan perintah 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.- Bagaimana Anda memeriksa ini?
Vallie

Jawaban:

272

Ini terjadi karena Anda telah menambahkan workerdirektori Anda sebagai volume ke Anda docker-compose.yml, karena volume tidak dipasang selama pembuatan.

Ketika buruh pelabuhan membangun gambar, node_modulesdirektori dibuat di dalam workerdirektori, dan semua dependensi diinstal di sana. Kemudian pada saat runtime workerdirektori dari luar buruh pelabuhan dipasang ke instance buruh pelabuhan (yang tidak memiliki diinstal node_modules), menyembunyikan node_modulesAnda baru saja diinstal. Anda dapat memverifikasi ini dengan menghapus volume yang terpasang dari Anda docker-compose.yml.

Solusinya adalah dengan menggunakan volume data untuk menyimpan semua node_modules, karena volume data menyalin data dari gambar buruh pelabuhan yang dibangun sebelum workerdirektori dipasang. Ini bisa dilakukan docker-compose.ymlseperti ini:

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

Saya tidak sepenuhnya yakin apakah ini menimbulkan masalah untuk portabilitas gambar, tetapi karena tampaknya Anda terutama menggunakan buruh pelabuhan untuk menyediakan lingkungan runtime, ini seharusnya tidak menjadi masalah.

Jika Anda ingin membaca lebih lanjut tentang volume, ada panduan pengguna yang bagus tersedia di sini: https://docs.docker.com/userguide/dockervolumes/

EDIT: Docker sejak itu mengubah sintaksinya untuk meminta yang terdepan ./untuk pemasangan file relatif terhadap file docker-compose.yml.

FrederikNS
sumber
7
jawaban yang bagus, paling tidak mengganggu dan penjelasan yang bagus!
kkemple
41
Saya sudah mencoba metode ini dan menabrak dinding ketika dependensi berubah. Saya telah membangun kembali gambar, mulai wadah baru dan volume /worker/node_modulestetap sama seperti sebelumnya (dengan dependensi lama). Apakah ada trik bagaimana cara menggunakan volume baru setelah membangun kembali gambar?
Ondrej Slinták
11
Tampaknya komposisi buruh pelabuhan tidak menghapus volume jika wadah lain menggunakannya (bahkan jika mereka sudah mati). Jadi jika ada beberapa wadah mati dengan jenis yang sama (untuk alasan apa pun) skenario yang saya jelaskan di komentar sebelumnya mengikuti. Dari apa yang saya coba, menggunakan docker-compose rmtampaknya untuk memperbaiki masalah ini, tetapi saya percaya pasti ada solusi yang lebih baik dan lebih mudah.
Ondrej Slinták
15
Apakah ada solusi di 2018 tanpa harus rebuild --no-cachesetiap kali deps berubah?
Eelke
7
Anda sekarang dapat menggunakan `--renew-anon-volume` yang akan membuat ulang volume anonim alih-alih data dari wadah sebelumnya.
Mohammed Essehemy
37

The node_modulesfolder ditimpa oleh volume dan tidak ada lebih mudah diakses dalam wadah. Saya menggunakan strategi pemuatan modul asli untuk mengeluarkan folder dari volume:

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

The node_modulesdirektori tidak dapat diakses dari luar wadah karena disertakan dalam gambar.

jsan
sumber
1
apakah ada kerugian dari pendekatan ini? Tampaknya bekerja dengan baik untuk saya.
Bret Fisher
1
node_modulestidak dapat diakses dari luar wadah tetapi tidak benar-benar downside;)
jsan
dan setiap kali Anda mengubah package.json Anda perlu membangun kembali seluruh wadah dengan --no-cache bukan?
Luke
Anda perlu membangun kembali gambar ketika Anda mengubah package.json, ya, tetapi --no-cache tidak diperlukan. Jika Anda menjalankan docker-compose run app npm installAnda akan membuat node_modules di direktori saat ini dan Anda tidak perlu membangun kembali gambar.
jsan
9
Kelemahannya adalah: tidak ada lagi pelengkapan otomatis IDE, tidak ada bantuan, tidak ada pengalaman pengembang yang baik. Semuanya juga perlu diinstal pada host sekarang, tetapi bukankah alasan untuk menggunakan buruh pelabuhan di sini bahwa dev-host tidak memerlukan apa pun untuk dapat bekerja dengan proyek?
Michael B.
31

Solusi yang disediakan oleh @FrederikNS berfungsi, tetapi saya lebih suka untuk secara eksplisit memberi nama volume node_modules saya.

project/docker-compose.ymlFile saya (versi docker-compose 1.6+):

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

struktur file saya adalah:

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

Itu menciptakan volume bernama project_node_modulesdan menggunakannya kembali setiap kali saya aplikasi saya.

docker volume lsPenampilan saya seperti ini:

DRIVER              VOLUME NAME
local               project1_mysql
local               project1_node_modules
local               project2_postgresql
local               project2_node_modules
Guillaume Vincent
sumber
3
sementara ini "bekerja", Anda menghindari konsep nyata di buruh pelabuhan: semua dependensi harus dipanggang untuk portabilitas maksimum. Anda tidak dapat memindahkan gambar ini tanpa menjalankan perintah lain yang agak berembus
Javier Buzzi
4
mencoba memecahkan masalah yang sama selama berjam-jam dan muncul dengan solusi yang sama dengan diriku. Jawaban Anda harus diberi nilai tertinggi, tetapi saya kira, tolong jangan dapatkan jawaban Anda karena Anda menamai volume Anda "node_modules" dan semua pembaca kehilangan fakta bahwa ini menciptakan volume baru. Saat membaca kode Anda, saya pikir Anda hanya "memasang kembali" folder lokal node_modules dan membuang ide itu. Mungkin Anda harus mengedit nama volume menjadi sesuatu seperti "container_node_modules" untuk memperjelasnya. :)
Fabian
1
Saya juga menemukan ini menjadi solusi yang paling elegan, yang dengan mudah memungkinkan merujuk pada volume yang sama untuk modul simpul di beberapa tahap pembangunan. Artikel bagus Pelajaran dari Aplikasi Node Bangunan di Docker juga menggunakan pendekatan yang sama.
kf06925
1
@ kf06925 Anda benar-benar menyelamatkan saya, saya menghabiskan berjam-jam mencoba untuk menyelesaikan ini dan terima kasih untuk artikel yang saya bisa !! Saya akan membelikan Anda bir jika saya bisa mengucapkan terima kasih banyak
helado
21

Baru-baru ini saya memiliki masalah yang sama. Anda dapat menginstal di node_modulestempat lain dan mengatur NODE_PATHvariabel lingkungan.

Pada contoh di bawah ini saya instal node_moduleske/install

pekerja / 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
ericstolten
sumber
6
Solusi pilihan teratas oleh @FrederikNS bermanfaat, adalah bagaimana saya memecahkan masalah yang berbeda dari volume lokal saya menimpa wadah node_modulesberdasarkan artikel ini . Tapi itu membuat saya mengalami masalah ini . Solusi ini di sini untuk membuat direktori terpisah untuk menyalin ke Anda package.json, jalankan di npm installsana, kemudian tentukan NODE_PATHvariabel lingkungan docker-compose.ymluntuk menunjuk ke node_modulesfolder direktori yang berfungsi dan terasa benar.
cwnewhouse
Termasuk ENV NODE_PATH = / install / node_modules di Dockerfile akhirnya menjadi solusi bagi saya setelah berjam-jam mencoba berbagai pendekatan. Terima kasih Pak.
Benny Meade
Bagaimana jika Anda menjalankan npm installhost? Tampaknya node_modulesakan muncul di host dan akan direfleksikan ke wadah, mengambil prioritas NODE_PATH. Jadi container akan menggunakan node_modules dari host.
Vitalets
19

Ada solusi yang elegan:

Hanya memasang tidak seluruh direktori, tetapi hanya direktori aplikasi. Dengan cara ini Anda tidak akan mengalami kesulitan npm_modules.

Contoh:

  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
holms
sumber
Cemerlang. Tidak mengerti mengapa itu bukan jawaban yang diterima.
Jivan
2
Ini adalah solusi yang baik dan cepat tetapi membutuhkan pembangunan kembali & prem setelah instalasi dependensi baru.
Kunok
Saya melakukannya secara berbeda sekarang, harus ada volume khusus untuk node_modules dan volume yang Anda pasang dari host. Maka tidak ada masalah sama sekali
holms
14

UPDATE: Gunakan solusi yang disediakan oleh @FrederikNS.

Saya mengalami masalah yang sama. Ketika folder di /worker-mount ke wadah - semua kontennya akan disinkronkan (sehingga folder node_modules akan hilang jika Anda tidak memilikinya secara lokal.)

Karena paket npm yang tidak kompatibel berdasarkan OS, saya tidak bisa hanya menginstal modul secara lokal - kemudian meluncurkan wadah, jadi ..

Solusi saya untuk ini, adalah dengan membungkus sumber dalam srcfolder, lalu menautkan node_moduleske folder itu, menggunakan file index.js ini . Jadi, index.jsfile tersebut sekarang menjadi titik awal aplikasi saya.

Ketika saya menjalankan wadah, saya memasang /app/srcfolder ke srcfolder lokal saya .

Jadi folder penampung terlihat seperti ini:

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

Itu jelek , tapi berhasil ..

SELAI
sumber
3
oh, tuanku ... aku tidak percaya aku juga terjebak dengan itu!
Lucas Pottersky
10

Karena cara Node.js memuat modul , node_modulesbisa di mana saja di jalur ke kode sumber Anda. Misalnya, letakkan sumber Anda di /worker/srcdan package.jsondi Anda /worker, begitu /worker/node_modulesjuga di mana mereka diinstal.

Justin Stayton
sumber
7

Menginstal node_modules dalam wadah ke berbeda dari folder proyek, dan pengaturan NODE_PATH ke folder node_modules membantu saya (Anda perlu membangun kembali wadah).

Saya menggunakan komposisi buruh pelabuhan. Struktur file proyek saya:

-/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 di folder 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
sergeysynergy
sumber
1
Ini adalah solusi terbaik yang saya temukan. NODE_PATHadalah kunci untuk saya.
cdignam
Saya pikir ini masuk akal tetapi dengan menetapkan NODE_PATH, saat menjalankan gambar, CMD npm start tidak menggunakan NODE_PATH yang ditentukan.
Acton
6

Ada juga beberapa solusi sederhana tanpa memetakan node_moduledirektori ke volume lain. Ini akan pindah menginstal paket npm ke perintah CMD akhir.

Kerugian dari pendekatan ini:

  • jalankan npm installsetiap kali Anda menjalankan kontainer (beralih dari npmke yarnmungkin juga mempercepat proses ini sedikit).

pekerja / 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
Egel
sumber
3

Ada dua persyaratan terpisah yang saya lihat untuk lingkungan dev node ... mount kode sumber Anda ke dalam wadah, dan mount node_modules DARI wadah (untuk IDE Anda). Untuk mencapai yang pertama, Anda melakukan mount biasa, tetapi tidak semuanya ... hanya hal-hal yang Anda butuhkan

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

(Alasan untuk tidak melakukan - /worker/node_modulesini adalah karena docker-compose akan mempertahankan volume di antara run, yang berarti Anda dapat menyimpang dari apa yang sebenarnya ada dalam gambar (mengalahkan tujuan tidak hanya mengikat pemasangan dari host Anda)).

Yang kedua sebenarnya lebih sulit. Solusi saya agak retas, tetapi berhasil. Saya memiliki skrip untuk menginstal folder node_modules pada mesin host saya, dan saya hanya harus ingat untuk memanggilnya setiap kali saya memperbarui package.json (atau, tambahkan ke target make yang menjalankan build docker-compose build secara lokal).

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

Menurut pendapat saya, kita tidak boleh RUN npm installdi Dockerfile. Sebagai gantinya, kita dapat memulai sebuah wadah menggunakan bash untuk menginstal dependensi sebelum menjalankan layanan simpul formal

docker run -it -v ./app:/usr/src/app  your_node_image_name  /bin/bash
root@247543a930d6:/usr/src/app# npm install
salamander
sumber
Saya sebenarnya setuju dengan Anda tentang hal ini. Volume dimaksudkan untuk digunakan ketika Anda ingin berbagi data antara wadah dan host. Ketika Anda memutuskan untuk node_modulestetap gigih bahkan setelah wadah dikeluarkan, Anda juga harus tahu kapan atau kapan tidak melakukannya npm installsecara manual. OP menyarankan untuk melakukannya pada setiap build gambar . Anda dapat melakukannya, tetapi Anda tidak perlu juga menggunakan volume untuk itu. Pada setiap build, modul akan menjadi yang terbaru.
phil294
@Blauhirn sangat membantu untuk me-mount volume host lokal ke dalam wadah ketika melakukan misal gulp watch (atau perintah analog) - Anda ingin agar node_modules tetap ada sementara masih memungkinkan perubahan ke sumber lain (js, css dll). npm bersikeras menggunakan tegukan lokal, sehingga harus bertahan (atau dipasang melalui metode lain saat start-up)
tbm
2

Anda dapat mencoba sesuatu seperti ini di Dockerfile Anda:

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

Maka Anda harus menggunakan Volume seperti ini:

volumes:
  - worker/:/worker:rw

Skrip awal harus menjadi bagian dari repositori pekerja Anda dan terlihat seperti ini:

#!/bin/sh
npm install
npm start

Jadi node_modules adalah bagian dari volume pekerja Anda dan akan disinkronkan dan skrip npm dieksekusi ketika semuanya habis.

Parav01d
sumber
Ini akan menambah overhead yang besar untuk memulai wadah.
tbm
2
Tetapi hanya pertama kali karena node_modules akan bertahan pada mesin lokal.
Parav01d
Atau sampai gambar dibangun kembali, atau volume dihapus :). Karena itu, saya sendiri belum menemukan solusi yang lebih baik.
tbm
0

Anda juga dapat membuang Dockerfile Anda, karena kesederhanaannya, cukup gunakan gambar dasar dan tentukan perintah dalam file penulisan Anda:

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

Ini sangat berguna bagi saya, karena saya hanya perlu lingkungan gambar, tetapi beroperasi pada file saya di luar wadah dan saya pikir ini adalah apa yang ingin Anda lakukan juga.

itmuckel
sumber