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


184

次のサービスを備えたアプリがあります。

  • web/ -ポート5000でPython 3フラスコWebサーバーを保持して実行します。sqlite3を使用します。
  • worker/- index.jsキューのワーカーであるファイルがあります。Webサーバーは、ポート経由でjson APIを使用してこのキューと対話します9730。ワーカーはストレージに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 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>

しかし、私がそうするとき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ます。)


最終的に解決策を見つけましたか?
Justin Stayton 2015年

私はあなたがこのように... ONBUILD命令を使うべきだと思う:github.com/nodejs/docker-node/blob/master/0.12/onbuild/...
ルーカスPottersky

1
IDEがnode_moduleの依存関係を知らない場合、ホスト上でどのように開発しますか?
アンドレ・

2
ファイルvolumes: - worker/:/worker/からブロックを削除してみてくださいdocker-compose.yml。この行は、COPYコマンドで作成したフォルダを上書きします。
ステパン2017年

When I run docker-compose build, everything works as expected and all npm modules are installed in /worker/node_modules as I'd expect.-どうやってこれをチェックしたの?
Vallie

回答:


272

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

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

回避策は、データボリュームを使用してすべての node_modulesworkerは、ディレクトリがマウントされる前に、構築されたDockerイメージからデータをコピーします。これは次のように行うことができます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/にある便利なユーザーガイドをご覧ください。

編集:Dockerはそれ以来./、docker-compose.ymlファイルに関連するファイルをマウントするためのリーディングを要求するように構文を変更しました。


7
素晴らしい答え、邪魔にならない、素晴らしい説明!
kkemple

41
私はこの方法を試し、依存関係が変わったときに壁にぶつかりました。イメージを再構築し、新しいコンテナーを開始し、ボリューム/worker/node_modulesは以前と同じままでした(古い依存関係を使用)。イメージの再構築時に新しいボリュームを使用する方法はありますか?
OndrejSlinták16年

11
他のコンテナーがボリュームを使用している場合(たとえそれらがデッドであっても)、Docker Composeはボリュームを削除しないようです。したがって、(何らかの理由で)同じタイプのデッドコンテナがいくつかある場合は、前のコメントで説明したシナリオが続きます。私が試したものから、を使用docker-compose rmするとこの問題が解決するようですが、より優れた簡単な解決策があるはずです。
OndrejSlinták16年

15
2018年にrebuild --no-cache、担当者が変わるたびに解決する必要のない解決策はありますか?
Eelke

7
以前のコンテナのデータの代わりに匿名ボリュームを再作成する`--renew-anon-volumes`を使用できるようになりました。
Mohammed Essehemy

37

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それは、画像に含まれているため、ディレクトリは、容器の外側からアクセスすることはできません。


1
このアプローチには欠点がありますか?私にとってはうまくいくようです。
Bret Fisher

1
node_modulesコンテナーの外部からアクセスすることはできませんが、実際にはマイナス面ではありません;)
jsan

そしてpackage.jsonを変更するたびに、コンテナ全体を--no-cacheで再構築する必要がありますか?
ルーク

はい、package.jsonを変更するときにイメージを再構築する必要がありますが、-no-cacheは必要ありません。実行docker-compose run app npm installすると、現在のディレクトリにnode_modulesが作成され、イメージを再構築する必要がなくなります。
jsan

9
欠点は、IDEのオートコンプリートがなくなること、ヘルプがないこと、優れた開発経験がないことです。ここでもすべてをホストにインストールする必要がありますが、ここでdockerを使用する理由は、dev-hostがプロジェクトで機能するために何も必要ないということではありませんか?
マイケルB.

31

@FrederikNSが提供するソリューションは機能しますが、node_modulesボリュームに明示的に名前を付けることを好みます。

私のproject/docker-compose.ymlファイル(docker-composeバージョン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

3
これが「機能する」一方で、Dockerの実際の概念を回避しています。最大限の移植性のためにすべての依存関係を組み込む必要があります。ちょっと吹く他のコマンドを実行せずにこの画像を移動することはできません
Javier Buzzi 2018年

4
同じ問題を何時間も解決しようと試み、同じ解決策を自分で考えました。あなたの答えは最高の評価でなければなりませんが、ボリュームに「node_modules」という名前を付けたため、pplは答えを取得できません。すべてのリーダーは、これによって新しいボリュームが作成されるという事実を見逃しています。コードを読んだとき、ロケールのnode_modulesフォルダーを単に「再マウント」すると思い、そのアイデアを破棄しました。ボリューム名を「container_node_modules」のように編集して、それを明確にする必要があるかもしれません。:)
Fabian

1
また、これが最も洗練されたソリューションであることがわかりました。これにより、複数のビルド段階でノードモジュールの同じボリュームを簡単に参照できます。優れた記事「Dockerでのノードアプリのビルドからのレッスン」でも同じアプローチを使用しています。
kf06925

1
@ kf06925あなたは本当に私を救った男、私はこれを解決するために何時間も費やしました、そして私ができる記事のおかげで!! 本当に感謝できたらビールを買いたいと思います
helado

21

最近、同様の問題が発生しました。node_modules他の場所にインストールして、NODE_PATH環境変数を設定できます。

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

worker / 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
@FrederikNSによって上位投票された解決策は有用です。この記事にnode_modules基づいて、ローカルボリュームがコンテナーの上書きを行う別の問題を解決する方法でした。しかし、それがこの問題を経験するようになりました。ここにコピーする別のディレクトリを作成し、そこで実行し、環境変数を指定して、そのディレクトリのフォルダを指すようにするこのソリューションは、正しく機能していると感じています。package.jsonnpm installNODE_PATHdocker-compose.ymlnode_modules
cwnewhouse 2018

ENV NODE_PATH = / install / node_modulesをDockerfileに含めることは、さまざまなアプローチを何時間も試した結果、最終的に私にとって解決策となりました。ありがとうございます。
Benny Meade

npm installホストで実行するとどうなりますか?それはそうですnode_modulesホスト上に表示され、優先度を超えるを取って、コンテナに反映されますNODE_PATH。したがって、コンテナはホストからのnode_modulesを使用します。
バイタレット

19

エレガントなソリューションがあります:

ディレクトリ全体ではなく、アプリディレクトリのみをマウントします。これにより、で問題が発生しなくなります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

鮮やかさ。なぜそれが受け入れられない答えなのか理解できません。
ジバン

2
これは優れた高速なソリューションですが、新しい依存関係のインストール後に再構築とプルーニングが必要です。
Kunok

今は別の方法で行います。node_modules専用のボリュームと、ホストからマウントするボリュームがあるはずです。その後、まったく問題はありません
Holms

14

更新:@FrederikNSが提供するソリューションを使用してください。

同じ問題が発生しました。フォルダー/workerがコンテナーにマウントされると、その内容がすべて同期されます(ローカルになければ、node_modulesフォルダーは表示されなくなります)。

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

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

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

したがって、コンテナフォルダは次のようになります。

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

それは醜いが、それは動作します。..


3
ああ、愛する主よ...私もそれで立ち往生しているとは信じられない!
Lucas Pottersky、2015


7

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

私は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

1
これは私が見つけた最高のソリューションです。NODE_PATH私にとって鍵でした。
cdignam 2018年

これは理にかなっていると思いますが、NODE_PATHを設定することで、イメージを実行するときに CMD npm start 、指定されたNODE_PATHを使用しません。
アクトン

6

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

このアプローチの欠点:

  • npm installコンテナを実行するたびに実行します(からnpmに切り替えると、yarnこのプロセスが少し高速化する場合もあります)。

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

ノード開発環境には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ビルドを実行するmakeターゲットに追加します)。

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

2

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

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

私は実際にこれに同意します。ボリュームは、コンテナーとホスト間でデータを共有するときに使用することを目的としています。あなたが持っていることを決定するときに、あなたのnode_modules場合、またはときに実行しないようにしても、コンテナ削除した後、あなたも知っておくべき永続的でnpm install手動を。OPは、すべてのイメージビルドでそれを行うことをお勧めします。それはできますが、そのためにボリュームを使用する必要はありません。すべてのビルドで、モジュールは常に最新の状態になります。
phil294 2018年

@Blauhirn gulp watch(または類似のコマンド)を実行するときは、ローカルホストボリュームをコンテナーにマウントすると便利です。他のソース(js、cssなど)への変更を許可しながら、node_modulesを永続化します。npmはローカルgulpの使用を要求するため、永続化する必要があります(または起動時に他の方法でインストールする必要があります)
tbm

2

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

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

次に、次のようにボリュームを使用する必要があります。

volumes:
  - worker/:/worker:rw

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

#!/bin/sh
npm install
npm start

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


これにより、コンテナの起動に大きなオーバーヘッドが追加されます。
tbm

2
しかし、node_modulesがローカルマシンに永続化されるため、初めてです。
Parav01d

または、イメージが再構築されるか、ボリュームが削除されるまで:)。とは言っても、私にはもっと良い解決策がありません。
tbm

0

Dockerfileを破棄することもできます。シンプルであるため、基本的なイメージを使用して、作成ファイルでコマンドを指定するだけです。

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

これは私にとって特に便利です。イメージの環境が必要なだけなのですが、コンテナーの外で私のファイルを操作するので、これもあなたがやりたいことだと思います。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.