はじめに
この記事では、Dockerfile からビルドしたイメージを元に Node.js / TypeScript のコンテナを作成する手順について記載します。
以下の記事の Node.js / TypeScript バージョンです。
開発環境
開発環境は以下の通りです。
- Windows11
- Docker Engine 26.1.1
- Node.js 20.15.1
- npm 10.8.2
- @types/node 20.14.10
- ts-node 10.9.2
- TypeScript 5.5.3
Node.js / TypeScript アプリケーションの作成
まずは、Node.js / TypeScript アプリケーションを作成します。
package.json
の作成
package.json
を作成します。
コマンド実行中の質問をスキップするため、-y
をつけて実行します。
npm init -y
package.json
が作成されます。
{
"name": "node",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": ""
}
TypeScript のインストールと設定
TypeScript 及び以下のパッケージをインストールします。
-
ts-node
: TypeScript を Node.js で直接実行できるようにする -
@types/node
: Node.js の型定義
npm install typescript ts-node @types/node --save-dev
package.json
にインストールしたパッケージが表示されます。
{
...
"devDependencies": {
"@types/node": "^20.14.10",
"ts-node": "^10.9.2",
"typescript": "^5.5.3"
}
}
また、node_modules
が作成されるので .gitignore
を作成し、node_modules
を追加して、git 管理対象外とします。
node_modules
次に TypeScirpt 設定ファイルを作成します。
npx tsc --init
tsconfig.json
が作成されます。
デフォルトでは、CommonJS
を利用する設定になっていますが、ES Modules
(ECMA Script Modules
) を利用したいので、設定を変更します。今回は Node.js 20 系を利用するので、20 系に対応した lib
、module
、target
を指定します。
{
"compilerOptions": {
/* Language and Environment */
"target": "ES2022" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
"lib": [
"ES2023"
] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,
/* Modules */
"module": "node16" /* Specify what module code is generated. */,
/* Interop Constraints */
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
/* Type Checking */
"strict": true /* Enable all strict type-checking options. */,
/* Completeness */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}
アプリケーションコードの実装
src/index.ts
ファイルを作成し、ログを出力するコードを実装します。
import http from "http";
const HOSTNAME = "0.0.0.0";
const PORT = 3000;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader("Content-Type", "text/plain");
res.end("Hello World");
});
server.listen(PORT, HOSTNAME, () => {
console.log(`Server running at http://${HOSTNAME}:${PORT}`);
});
エントリーポイントの変更と実行スクリプトの追加
package.json
でエントリーポイントの変更とアプリケーションを実行するスクリプトの追加を行います。
{
...
"main": "src/index.ts",
"scripts": {
"start": "ts-node src/index.ts",
...
...
}
スクリプトを実行して、一度動作確認します。
npm start
実装通りログが表示されることを確認します。
Dockerfile の作成
Dockerfile を作成し、イメージをビルドします。
ベースイメージ FROM
ベースイメージはタグが 20-alpine3.19 の Node.js イメージを利用します。
FROM node:20-alpine3.19
作業ディレクトリ WORKDIR
作業ディレクトリを /app
にします。
WORKDIR /app
デフォルトでもルートディレクトリ(/
)が作業ディレクトリとなるので、このインストラクションは任意です。ただ、作業ディレクトリを指定することで、Dockerfile のこれ以降の COPY
や RUN
などのインストラクションによって生成されるディレクトリやファイルが app
内に配置されます。そのため、コンテナ内の他のファイルと区別しやすくなります。
ホストマシンのファイルをコピー COPY
ホストマシンの package.json
及び package-lock.json
をコンテナの作業ディレクトリにコピーします。
COPY package*.json ./
現時点で本インストラクションを記載しておくことで、次に実施する依存関係のインストールでキャッシュを利用でき、ビルドスピードが向上します。
※package.json
や package-lock.json
に変更があった時だけイメージビルド時、依存関係がインストールされます。
依存関係のインストール RUN
先ほどコピーした package.json
に記載されたパッケージ及びその依存関係をコンテナにインストールします。
RUN npm install
ホストマシンの全ファイルをコピー COPY
ホストマシンの全ファイルをコンテナの作業ディレクトリにコピーします。
COPY . .
コンテナのポートを指定 EXPOSE
3000 ポートを開けます。
EXPOSE 3000
コンテナ起動時のコマンドを指定 CMD
コンテナ起動時に package.json
の scripts
の start
を実行します。
CMD ["npm", "start"]
イメージのビルド
完成した Dockerfile は以下の通りです。
FROM node:20-alpine3.19
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
こちらの Dockerfile からイメージをビルドします。
docker image build --tag node-app:0.0.1 .
イメージがビルドできたか確認します。
docker image ls node-app
コンテナの起動
ビルドしたイメージからコンテナを起動します。
docker container run `
--name app `
--rm `
--publish 3000:3000 `
node-app:0.0.1
Dockerfile 作成前にホストマシンで動作確認したときと同じログが表示されます。
参考
関連リンク