web-dev-qa-db-ja.com

Docker-compose:npmのインストールが成功した後にnode_modulesがボリュームに存在しない

私は以下のサービスを備えたアプリを持っています。

  • web/ - ポート5000でPython 3フラスコのWebサーバーを保持して実行します。sqlite3を使用します。
  • worker/ - キューのワーカーであるindex.jsファイルがあります。 Webサーバーはポート9730上でjson APIを使用してこのキューと対話します。作業者はredisを使って保管します。ワーカーは、データをローカルでフォルダーworker/images/にも保存します。

今この質問は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

docker-compose buildを実行すると、期待通りにすべてが動作し、すべてのnpmモジュールが/worker/node_modulesにインストールされます。

npm WARN package.json [email protected] No README data

> [email protected] install /worker/node_modules/pageres/node_modules/screenshot-stream/node_modules/phantom-bridge/node_modules/phantomjs
> node install.js

<snip>

しかしdocker-compose upを実行すると、このエラーが表示されます。

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)

/worker/node_modules(ホスト上またはコンテナ内)にモジュールが存在しないことが判明しました。

ホスト上であれば、私はnpm installです、それからすべてはうまく働きます。しかし、私はそれをしたくありません。コンテナーに依存関係を処理させたい。

ここで何がおかしいのですか?

(言うまでもなく、すべてのパッケージはpackage.jsonにあります。)

142
Karan Goel

これは、ビルド中にボリュームがマウントされていないため、workerディレクトリをボリュームとしてdocker-compose.ymlに追加したためです。

Dockerがイメージをビルドすると、node_modulesディレクトリがworkerディレクトリ内に作成され、そこにすべての依存関係がインストールされます。その後、実行時に、dockerの外部からworkerディレクトリがdockerインスタンスにマウントされ(インストールされたnode_modulesはありません)、インストールしたばかりのnode_modulesは隠されます。 docker-compose.ymlからマウントされているボリュームを削除することでこれを確認できます。

workerディレクトリがマウントされる前にデータボリュームが構築されたdockerイメージからデータをコピーするので、回避策はデータボリュームを使用してすべてのnode_modulesを格納することです。これはdocker-compose.ymlでこのようにすることができます:

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

これがイメージの移植性に何らかの問題があるかどうかは完全にはわかりませんが、ランタイム環境を提供するために主にdockerを使用しているように思われるので、これは問題にならないはずです。

あなたがボリュームについてもっと読みたいならば、ここで利用可能なニースユーザガイドがあります: https://docs.docker.com/userguide/dockervolumes/

210
FrederikNS

node_modulesフォルダはボリュームによって上書きされ、コンテナ内でアクセスできなくなります。私はボリュームからフォルダを取り出すために ネイティブモジュールロード戦略 を使っています。

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

node_modulesはイメージに含まれているため、コンテナーの外側からアクセスすることはできません。

27
jsan

@FrederikNSが提供する解決策は機能しますが、私は明示的に自分のnode_modulesボリュームに名前を付けることを好みます。

私のproject/docker-compose.ymlファイル(docker-compose version 1.6以降):

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

私のファイル構造は次のとおりです。

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

これはproject_node_modulesという名前のボリュームを作成し、アプリケーションを起動するたびにそれを再利用します。

私のdocker volume lsはこのようになります:

DRIVER              VOLUME NAME
local               project1_mysql
local               project1_node_modules
local               project2_postgresql
local               project2_node_modules
24

私は最近同様の問題を抱えていました。 node_modulesを他の場所にインストールして、NODE_PATH環境変数を設定することができます。

以下の例で、私はnode_modules/installにインストールしました

ワーカー/ 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
17
ericstolten

エレガントな解決策があります。

ディレクトリ全体ではなく、appディレクトリだけをマウントするだけです。こうすればnpm_modulesで問題を起こすことはありません。

例:

  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
14
holms

更新:@FrederikNSが提供する solution を使ってください。

私は同じ問題に遭遇しました。フォルダ/workerがコンテナにマウントされると、そのすべてのコンテンツが同期されます(したがって、ローカルに持っていなければnode_modulesフォルダは消えます)。

OSに基づく互換性のないnpmパッケージのせいで、モジュールをローカルにインストールすることはできませんでした - それからコンテナを起動します。

これに対する私の解決策は、ソースをsrcフォルダーにラップしてから、 this index.jsファイル を使用してnode_modulesをそのフォルダーにリンクすることでした。だから、index.jsファイルは私のアプリケーションの出発点になりました。

コンテナを実行すると、/app/srcフォルダをローカルのsrcフォルダにマウントしました。

そのため、コンテナフォルダは次のようになります。

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

それは醜いですが、動きます..

11
JAM

Node.jsがモジュールを読み込む方法 により、node_modulesはソースコードへのパスのどこにでも置くことができます。例えば、あなたのソースを/worker/srcに、そしてあなたのpackage.json/workerに置くので、/worker/node_modulesはそれらがインストールされる場所です。

9
Justin Stayton

Node_modulesをプロジェクトフォルダーとは異なるコンテナーにインストールし、node_modulesフォルダーにNODE_PATHを設定すると便利です(コンテナーを再構築する必要があります)。

私はdocker-composeを使っています。私のプロジェクトファイル構造:

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

Nodejsフォルダー内のDockerfile:

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
6
sergeysynergy

node_moduleディレクトリを別のボリュームにマッピングしない簡単な解決策もあります。 npmパッケージのインストールを最終的なCMDコマンドに移行しようとしています。

このアプローチの欠点:

  • containerを実行するたびにnpm installを実行します(npmからyarnに切り替えると、このプロセスが少しスピードアップすることもあります)。

ワーカー/ 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
5
Egel

Node dev環境には、2つの異なる要件があります。ソースコードをコンテナにマウントし、node_modulesをコンテナからマウントします(IDE用)。最初の作業を実行するには、通常のマウントを行いますが、すべてではありません。必要なものだけを実行します。

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

- /worker/node_modulesを実行しないのは、docker-composeが実行間でそのボリュームを保持するためです。つまり、実際にイメージ内にあるものとは異なる可能性があるからです(ホストからのバインドマウントだけでなく目的を無効にする)。

2番目のものは実際には難しいです。私の解決策は少し厄介ですが、うまくいきます。私は自分のホストマシンにnode_modulesフォルダをインストールするためのスクリプトを持っています、そしてpackage.jsonを更新する時はいつでもそれを呼び出すことを忘れないでください(あるいは、それをdocker-compose buildをローカルで実行するmakeターゲットに追加します)。

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

Dockerfileでこのようなことを試すことができます。

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

それならあなたはこのVolumeをこのように使うべきです:

volumes:
  - worker/:/worker:rw

開始スクリプトはワーカーリポジトリの一部である必要があり、次のようになります。

#!/bin/sh
npm install
npm start

そのため、node_modulesはワーカーボリュームの一部であり、同期化され、すべてが起動したときにnpmスクリプトが実行されます。

1
Parav01d

私の意見では、DockerfileにRUN npm installを入れてはいけません。代わりに、正式なノードサービスを実行する前に、依存関係をインストールするためにbashを使用してコンテナを起動できます。

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