最近、MERN スタック アプリケーションを Docker 化 (開発用とその後の運用用の両方) しようとしていますが、Node.js (特に node_modules) と Docker 間の相互作用がちょっと気になります。質問全体では、開発に使用するコンピューターを「ホスト マシン」として指定します。
要約
開発時にホストマシンの node_modules フォルダーをコンテナーにマウントせずに、Node.js アプリを Docker 化する、それほど非現実的ではない方法はありますか?
私が試みたアプローチ(そして、どれも私を満足させない理由)
開発にDocker(およびそのdocker-composeユーティリティ)を使用することの3つの大きな利点が考えられます(これらを次のように呼びます)。ポイント1、2、3) :
- Docker 自体以外のものを手動でインストールする必要がないため、新しいマシンに開発環境をセットアップしたり、プロジェクトに新しいメンバーを統合したりすることが容易になります。
- このアプリは、開発中にデバッグするために簡単に実行できます (簡単に実行すれば
docker-compose up
、DB、バックエンド、フロントエンドが起動して実行されます)。 - Docker コンテナは独自の小さな Linux 仮想マシンのようなものであるため、ランタイム環境はすべてのマシンとアプリの最終的な運用環境にわたって一貫しています。
最初の 2 つのポイントは、Node.js アプリケーションを Docker 化する際に問題にはなりません。ただし、Node での依存関係の動作と node_modules の機能により、開発環境では 3 番目のポイントを実現するのがより困難であると感じています。説明しましょう。
これは簡略化されたプロジェクト フォルダー構造です。
project
│ docker-compose.yml
│
└───node-backend
│ │ Dockerfile
│ │ package.json
│ │ server.js
│ │
│ └───src
│ │ │ ...
│ │
│ └───node_modules
│ │ ...
│
└───react-frontend
│ ...
私が試したことや、インターネット上の記事やチュートリアルで見たことから、Docker で Node.js を開発するには基本的に 3 つのアプローチがあります。3 つのアプローチすべてにおいて、アプリのイメージを記述するために次の Dockerfile を使用していると想定しています。
# node-backend/Dockerfile
FROM node:lts-alpine
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . ./
EXPOSE 8000
CMD [ "npm", "start" ]
アプローチ 1: 素早く汚い
開発中は、ホスト マシンのコード フォルダー全体 (node_modules を含む) をコンテナーにマウントします。
docker-compose.yml
ファイルは通常、次のようになります (わかりやすくするために、データベースや React アプリの構成は含まれていません)。# ./docker-compose.yml version: "3" services: backend: build: ./node-backend/ ports: - 8000:8000 volumes: - ./node-backend:/usr/src/app
このアプローチは、開発のためのセットアップと使用が最も簡単です。コードはホストとコンテナ間で同期され、ホットリロード(ノードモンたとえば、ホストとコンテナ間で依存関係が同期され、
npm install some-module
ホストマシン上でコンテナを再構築する必要がなくなります。しかし、それは尊重されないポイント3: ホストマシンのnode_modulesもコンテナにマウントされるため、プラットフォーム固有の部分が含まれる場合があります(たとえば、ノード-gypコンテナの OS (Alpine Linux) 用ではなく、ホスト マシンの OS (私の場合は macOS または Windows) 用にコンパイルされたアドオンなどです。
アプローチ 2: よりクリーンだが、新しい依存関係をインストールするのが面倒
ホスト マシンのソース コード フォルダーをマウントしますが、今回はコンテナーの node_modules を保持するボリューム (名前付きまたは匿名) を作成し、ホスト マシンの node_modules によって隠されないようにします。
# ./docker-compose.yml version: "3" services: backend: build: ./node-backend/ ports: - 8000:8000 volumes: - ./node-backend:/usr/src/app - /usr/src/app/node_modules
このアプローチでは、ポイント3尊重されるようになりました:開発中のコンテナで使用されるnode_modulesフォルダが作成されたことを確認しますコンテナ専用、適切なプラットフォーム固有のコードが含まれています。
ただし、新しい依存関係をインストールするのは面倒です。
npm install
をコンテナ内で直接実行するか( を使用docker exec
)、その場合、毎回手動で実行しない限り、依存関係はホスト マシンにインストールされません。IDE (私の場合は VSCode) で自動補完、リンティングを提供し、「モジュールが見つかりません」という警告を回避するには、それらをローカルにインストールすることも重要です。- ホスト マシン上で を実行する場合は
npm install
、コンテナーの node_modules が最新になるように、毎回 Docker イメージを再構築する必要があり、時間がかかります。
アプローチ3: おそらく最もクリーンだが、セットアップが難しい
最終的なアプローチは直接開発することです内でコンテナは、ホストのマウントを行う必要がないことを意味します。コードと node_modules への変更が永続的になるように、ボリュームを作成するだけです (今回は名前を付けますが、匿名でも機能すると思います)。このアプローチはまだ試していないので、ファイルがどのよう
docker-compose.yml
になるかわかりませんが、おそらく次の行のいずれかになります。# ./docker-compose.yml version: "3" services: backend: build: ./node-backend/ ports: - 8000:8000 volumes: - backend-data:/usr/src/app volumes: backend-data:
このアプローチはまた、ポイント3しかし、コンテナ内でのリモート開発は、ホストマシンでの通常の開発よりもセットアップが難しいです(ただし、VSCodeはプロセスを簡素化する)。また、ソースコードのバージョン管理(つまりGit を使用する場合、リモート リポジトリへのアクセスを許可するには、ホスト マシンの SSH ID をコンテナーに渡す必要があるため、少し面倒に思えます。
結論
ご覧のとおり、私が求めているすべての利点を兼ね備えたアプローチをまだ見つけていません。セットアップが簡単で開発に使用できるアプローチが必要です。ポイント3これはコンテナ化の哲学と目的の重要な側面であるためです。これにより、IDE のすべての機能 (Intellisense、linting など) を維持しながら、コンテナとホスト マシン間で node_modules を同期することが面倒にならなくなります。
私が完全に見逃している何かがあるのでしょうか? この問題に対する皆さんの解決策は何ですか?
ベストアンサー1
このプロジェクトを見ることをお勧めしますhttps://github.com/BretFisher/node-docker-good-defaults、トリックを使用することでローカルとコンテナの両方の node_modules をサポートしますが、一部のフレームワーク (例: Strapi) とは互換性がありません。
- これらは
container node_modules
アプリから1つ上のフォルダレベルに配置され(依存関係を解決するためのノードのアルゴリズムは、アプリのフォルダを再帰的に検索してnode_modulesを探します)、アプリフォルダから削除されます。 - これらは
host node_modules
アプリフォルダ内に配置されます(変更はありません) bind-mount the package.json + lock
コンテナ(アプリから 1 つのフォルダを思い出す)とホスト(アプリ フォルダ)の間に特別な追加ファイルが存在するため、それらのファイルは常に同期された状態に保たれます => そのため、必要なのはホスト上で実行し、最終的に Docker イメージを再構築するdocker exec npm install dep
ことだけです。npm install dep