私は以下のサービスを備えたアプリを持っています。
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
にあります。)
これは、ビルド中にボリュームがマウントされていないため、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/
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
はイメージに含まれているため、コンテナーの外側からアクセスすることはできません。
@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
私は最近同様の問題を抱えていました。 node_modules
を他の場所にインストールして、NODE_PATH
環境変数を設定することができます。
以下の例で、私はnode_modules
を/install
にインストールしました
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/
redis:
image: redis
worker:
build: ./worker
command: npm start
ports:
- "9730:9730"
volumes:
- worker/:/worker/
links:
- redis
エレガントな解決策があります。
ディレクトリ全体ではなく、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
私は同じ問題に遭遇しました。フォルダ/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
それは醜いですが、動きます..
Node.jsがモジュールを読み込む方法 により、node_modules
はソースコードへのパスのどこにでも置くことができます。例えば、あなたのソースを/worker/src
に、そしてあなたのpackage.json
を/worker
に置くので、/worker/node_modules
はそれらがインストールされる場所です。
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
node_module
ディレクトリを別のボリュームにマッピングしない簡単な解決策もあります。 npmパッケージのインストールを最終的なCMDコマンドに移行しようとしています。
このアプローチの欠点:
- containerを実行するたびに
npm install
を実行します(npm
からyarn
に切り替えると、このプロセスが少しスピードアップすることもあります)。
FROM node:0.12
WORKDIR /worker
COPY package.json /worker/
COPY . /worker/
CMD /bin/bash -c 'npm install; npm start'
redis:
image: redis
worker:
build: ./worker
ports:
- "9730:9730"
volumes:
- worker/:/worker/
links:
- redis
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
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スクリプトが実行されます。
私の意見では、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