Quantcast
Channel: Node.jsタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 8691

DockerでNode.js環境構築する際、コンテナ生成時にpackage.jsonからパッケージを展開する方法

$
0
0
環境 Windows10 Home 20H2 WSL2インストール済み Docker Desktop for Windows VSCode 参考にさせて頂いた記事、サイト Docker、ボリューム(Volume)について真面目に調べた - Qiita Dockerを使ってNode開発環境を作る DockerfileのCMD、docker-compose.yamlのcommandに、複数コマンドを複数行で書く - miuuuu’s blog 当記事のきっかけ 前の記事で、自分なりにdocker使えるような気がし始めてきたところですが、疑問が生じました。 前の記事(※)では、空のnode.jsコンテナがあり、そのコンテナ内でnpm initでpackage.jsonを初期化、順次必要なパッケージをインストールしていき、package.jsonに依存関係を記録していきました。 あらかじめ依存関係を記録したpackage.jsonや初期のコードがあり、それをコンテナに展開させるにはどうするのかを、自分なりに試そうと思いました。 ※自分がDockerについて調べて、やっと使えるようになった気がした手順 - Qiita やりたいこと 初期のコード、依存関係が登録されたpackage.jsonが先にある。 コンテナ生成時に必要なパッケージ、初期コードがコンテナに展開される。 コンテナを削除しても、ソースは残る。(永続化) 実践 ファイル構造(ホスト) (root) | |--src/ (←このフォルダ以下をコンテナにマウントする。) | |--dist/(※distフォルダ以下は、typescriptをトランスパイルした際に作成される。初めは存在しない。) | | |--server.js | | | |--nodo_modules(※npm install実行時にpackage.jsonをもとに展開される。初めは存在しない。) | | | |--src/ | | |-server.ts | | | |--nodemon.json | |--package.json | |--tsconfig.json | |--webpack.config.json | |--docker.compose.yml dcoker-compose.yml docker-compose.yml version: '3' services: web-server: image: node:14 container_name: docker-node-c1 volumes: - ./src:/app ports: - 3000:3000 working_dir: /app command: > bash -c "npm install && npm run build && npm run dev" その他ファイルを準備する (root)/src/src/server.ts import * as Express from 'express'; const app = Express(); app.get('/', (req: Express.Request, res: Express.Response, next: Express.NextFunction) => { res.send('Hello World.'); }); const portNo = process.env.PORT || 3000; app.listen(portNo, () => { console.log(`app running on port ${portNo}.`); }); export default app; (root)/src/nodemon.json { "watch": [ "dist" ], "ext": "ts, js, json", "exec": "node ./dist/server.js" } (root)/src/package.json { "name": "app", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "build": "webpack --config webpack.config.js", "dev": "nodemon -L", "start": "node ./dist/server.js", "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "express": "^4.17.1" }, "devDependencies": { "@types/express": "^4.17.12", "nodemon": "^2.0.7", "ts-loader": "^9.2.3", "typescript": "^4.3.2", "webpack": "^5.39.0", "webpack-cli": "^4.7.2", "webpack-node-externals": "^3.0.0" } } (root)/src/tsconfig.json { "compilerOptions": { "sourceMap": true, "noImplicitAny": true, "module": "es2015", "target": "es2017", "jsx": "react", "lib": ["es2018", "dom"], "moduleResolution": "node", "removeComments": true, "strict": true, "noUnusedLocals": false, "noUnusedParameters": false, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "strictFunctionTypes": false } } webpack.config.js const path = require('path'); const nodeExternals = require('webpack-node-externals'); module.exports = { mode: 'development', target: 'node', externals: [ nodeExternals() ], devtool: 'eval-source-map', module: { rules: [ { loader: 'ts-loader', test: /\.ts$/, exclude: [/node_modules/], options: { configFile: 'tsconfig.json'} } ] }, resolve: { extensions: ['.ts', '.js', '.json'] }, entry: './src/server.ts', output: { filename: 'server.js', path: path.resolve(__dirname, 'dist') }, node: { __dirname: false } }; コンテナを生成する ターミナルで以下のコマンドを実行します。 docker compose up VSCodeのエクスプローラ部を確認すると、srcフォルダにnode-modulesフォルダが生成され、配下にnpm installによりウンロードされたファイルが表示されます。 初期コードのTypeScriptファイルserver.tsがトランスパイルされ、distフォルダが生成され、server.jsファイルが出来上がります。 生成されたコンテナ内でnode.jsサーバが起動します。 ブラウザにて、localhost:3000にアクセスすると、"Hello World."が表示されます。 実験 コードの修正が反映するかの実験 server.tsを修正します。 (root)/src/server.ts //...省略... app.get('/', (req: Express.Request, res: Express.Response, next: Express.NextFunction) => { //res.send('Hello World.'); res.send('hogehoge.'); }); //...省略... VSCodeで別ターミナルを開き、コンテ内に入りトランスパイルします。 # コンテナ内に入る docker exec -it docker-node-c1 /bin/bash # 以下のような表示になる。 root@c34ee48d4afb:/app# # トランスパイルを実行する。 # nodemonが変更を検知し、node.jsサーバが再起動します。 root@c34ee48d4afb:/app# npm run build ブラウザをリロードし、表示が"hogehoge."になることを確認します。 コンテナ再起動後も修正が保持されているかの実験 docker compose upを実行したターミナルでctrl+cを押下し、コンテナを停止します。 ブラウザでlocalhost:3000にアクセスすると、 "このサイトにアクセスできません" 等の表示になります。 再度、docker compose upを実行し、コンテナを起動します。 ブラウザでlocalhost:3000にアクセスすると、"hogehoge."と表示され、コンテナにコードの修正が保持されていることが確認できます。 以上です。 奮闘記 ああでもない、こうでもないと、試した記録です。 Dockerfileを使用する ホストに完成package.json、ソースコードを持ち、それらをコンテ内にコピーしたイメージを考えました。 フォルダ構成 (root) | |--src/ (←このフォルダ以下をデータコンテナにマウントする。) | |--dist/(※distフォルダ以下は、typescriptをトランスパイルした際に作成される。初めは存在しない。) | | |--server.js | | | |--nodo_modules(※npm install実行時にpackage.jsonをもとに展開される。初めは存在しない。) | | | |--src/ | | |-server.ts | | | |--nodemon.json | |--package.json | |--tsconfig.json | |--webpack.config.json | |--Dockerfile Dockerfile Dockerfile FROM node:14 # コンテナ内の作業フォルダはapp WORKDIR /app # ./srcフォルダをコンテナの/appにコピーする。 # 依存関係が記録済みのpackage.json、その他ソースコードをコピーされる。 COPY ./src ./ # コンテナ内でコピーされたpackage.jsonを元に、パッケージがインストールされる。 RUN npm install # typescriptをトランスパイルする。 RUN npm run build # nodemonでnode.jsサーバを起動する。 CMD npm run dev このDockerfileをもとにイメージ"docker-node-image1"を作成します。 Dockerfileがあるディレクトリで以下のコマンドを実行します。 docker build . -t docker-node-image1 イメージ"docker-node-image1"をもとにコンテナ"docker-node-c1"を作成します。 docker run --name docker-node-c1 -p 3000:3000 docker-node-image1 # コンテナの生成、パッケージのインストール、タイプスクリプトのビルド、nodemonの起動まで行われます。 # 当方のターミナルでは以下のような表示になります。 > app@1.0.0 dev /app > nodemon -L [nodemon] 2.0.7 [nodemon] to restart at any time, enter `rs` [nodemon] watching path(s): dist/**/* [nodemon] watching extensions: ts,js,json [nodemon] starting `node ./dist/server.js` app running on port 3000. ブラウザでlocalhost:3000にアクセスし、コンテナが起動していることを確認します。 続いてコンテナ内に入り、ファイルが展開されているかを確認します。 別のターミナルを開き、以下のコマンドを実行します。 docker exec -it docker-node-c1 /bin/bash # 以下のような表示になります。 root@932173c904c6:/app# # dirコマンドでディレクトリを確認します。 root@932173c904c6:/app#dir # 結果 dist nodemon.json package.json tsconfig.json node_modules package-lock.json src webpack.config.js コンテナ内にホストからコピーしたファイルが展開されていることが確認できました。 確認ができたので、このコンテナは削除します。 docker rm docker-node-c1 ここで私は、「よし、あとはコンテナ内のappフォルダにホストのフォルダをマウントすれば成功だ!」と思い、以下のコマンドを実行しました。 # -vオプションの"C:\hoge\fuga"の部分はプロジェクトフォルダのルートです。 # ※-vオプション部分のホスト側のパス部分、フルパスでないと期待した挙動になりませんでした(/srcというような相対パスでは不可でした)。 docker run -p 3000:3000 -v C:\hoge\fuga\src:/app --name docker-node-c1 docker-node-image1 成功を確信していましたが結果はエラーでした。 # 結果 > app@1.0.0 dev /app > nodemon -L sh: 1: nodemon: not found npm ERR! code ELIFECYCLE npm ERR! syscall spawn npm ERR! file sh npm ERR! errno ENOENT npm ERR! app@1.0.0 dev: `nodemon -L` npm ERR! spawn ENOENT npm ERR! npm ERR! Failed at the app@1.0.0 dev script. npm ERR! This is probably not a problem with npm. There is likely additional logging output above. npm WARN Local package.json exists, but node_modules missing, did you mean to install? npm ERR! A complete log of this run can be found in: npm ERR! /root/.npm/_logs/2021-07-01T15_54_06_909Z-debug.log エラーメッセージの Local package.json exists, but node_modules missing, did you mean to install? をGoogle翻訳にかけると、 ローカルのpackage.jsonは存在しますが、node_modulesがありません。インストールするつもりでしたか? とのことです。Dockerfileで「RUN npm install」を指定してたのになぜ?と思いました。 おそらくコンテナ生成時、以下のようなことが起こったのではないかと想像しています。 - イメージdocker-node-image1からコンテナを生成すると、/appディレクトリ内にはnode_modues、distフォルダが存在する。 - docker runコマンドの-v オプションにより、ホストの./srcフォルダの内容がコンテ内の/appに上書きされる。この時、ホスト側の./srcフォルダにはnode_modules、distフォルダはないので、コンテナ内の/appディレクトリのnode_moduels、distも消える。 エラーは発生しましたが、dokcer ps -aコマンドを確認すると、コンテナは生成されていました。 後のために削除します。 # コンテナ一覧確認 docker ps -a # 結果 エラーは発生したが、コンテナ"docker-node-c1"は生成されている。 CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 5429c0746eb9 docker-node-image1 "docker-entrypoint.s…" 21 minutes ago Exited (1) 21 minutes ago docker-node-c1 # 削除する。 docker rm docker-node-c1 次は、コンテナ生成後、bashを起動し、そこでnpm install等を試しました。 docker run -it -p 3000:3000 -v C:\hoge\fuga\src:/app --name docker-node-c1 docker-node-image1 /bin/bash # コンテナ生成後、コンテナ内に入り、以下のような表示になります。 root@2cde3c6232ad:/app# # ここでnpmコマンドを実行します。 root@2cde3c6232ad:/app# npm install root@2cde3c6232ad:/app# npm run build root@2cde3c6232ad:/app# npm run dev ホスト側の./srcフォルダにnode_modules、distが現れ、node.jsサーバも立ち上がり、localhost:3000にアクセスすることができました。 コンテナ停止、再起動、を確認。 # コンテナ停止 docker stop docker-node-c1 # コンテナ再起動 docker start docker-node-c1 # コンテナ内に入り、node.jsサーバを起動 docker exec -it docker-node-c1 /bin/bahs # 以下はコンテナ内 # nodemonでnode.jsサーバを起動。 root@932173c904c6:/app#npm run dev 期待した挙動になりました。 実は、この部分記事の下書きを書きながら気づきました。これで、当初やりたかったことができてしまいました。 以上を振り返ると、 1. Dockerイメージにpackage.json、初期コードを持たせている。 2. Docerfileの"RUN npm install"でコンテナ生成時にnpm installを行っている。 3. しかし、docker run -vオプションで、ホストのsrcを上書きしている。 ということは、この例では、Dockerfileでソースのコピー等不要でした。 もっと言うと、Dockerfile用いてイメージの作成は不要でした。 Dockerhubに上がっているNode.jsのイメージを用いてコンテナを生成し、ホストのsrcフォルダをマウントすれば、やりたいことができました。 # 先の例(再掲) # doker run -it -p 3000:3000 -v C:\hoge\fuga\src:/app --name docker-node-c1 docker-node-image1 /bin/bash # イメージでdocker-node-image1ではなくDockerhub上のイメージ"node:14"を用いる。 doker run -it -p 3000:3000 -v C:\hoge\fuga\src:/app --name docker-node-c1 node:14 /bin/bash # コンテナ生成後、コンテナ内のbashが起動し、以下のような表示になります。 root@638e25fd74f5:/# # appディレクトリに移動します。 root@638e25fd74f5:/# cd app # パッケージをインストールします。 root@638e25fd74f5:/app# npm install # 以下トランスパイルや、npm start等の実行…省略 # コンテナの削除 docker rm docker-node-c1 当例ではDockerfile用いる必要ありませんでしたが、個人的には実際にDockerfile記述し、挙動を確認、体験してみるという意味では、大変意義がありました。 感想 何度も、コンテナの生成、破棄をコマンドを実行し、行いました。 自分は、ドキュメントを読んで理屈を理解するよりも、実際に実行してみて、感覚を掴むほうが理解が進みました。いや、完全に理解はしていないと思いますが、dockerいいなと思いました。

Viewing all articles
Browse latest Browse all 8691

Trending Articles