2026年10月8日に、Node.js公式Dockerイメージの現行READMEを再読しました。本番アプリはLTSリリースを使うこと、node:ltsなどのタグ、node:alpineが小さい一方でmusl libcを使うためDebian系のglibc向け依存と互換性を確認すること、node:slimは実行に必要な最小構成であることが案内されています。Nodeの新リリース後はOS・アーキテクチャ別イメージの公開が遅れる場合もあり、タグ名だけで起動性、脆弱性、費用を保証できません。
30秒で要点
コンテナは、アプリと必要なファイルをイメージという配布単位にまとめ、その内容から分離したプロセスを動かす仕組みです。開発と本番の差を減らせますが、外部DB、権限、保存データ、ホストの更新まで同じになるわけではありません。
- 揃える対象は、イメージ、実行設定、外部サービスとの接続条件です。
- 消して作り直すアプリと、失ってはいけないデータを分けます。
- Dockerの採用とKubernetesの採用は別の判断です。配布の再現性に加え、更新と復旧の担当を決めます。
「自分のPCでは動くのに、別の場所では動かない」。この問題に対し、どこまでを箱に入れ、どこからを運用で支えるかを考えるための入門です。
イメージとコンテナを分けて考える
イメージは実行に使うファイル一式、コンテナはそれを使って作られた実行環境です。同じイメージから複数のコンテナを作れます。コンテナを止めても、イメージそのものが書き換わるわけではありません。
たとえば集計プログラムとNode.jsをイメージに入れて配布します。実行時には、どのデータを読むか、どれだけメモリを使えるか、外部へ通信できるかを決めます。実行後の結果を残す場所も必要です。
Dockerfileはイメージを作る手順、レジストリはイメージを保存・配布する場所です。Dockerはこれらを扱う代表的な道具であり、コンテナという考え方そのものと同義ではありません。
仮想マシンとの違いは、カーネルを共有するか
一般的なLinuxコンテナはホストのカーネルを共有し、プロセスやファイルの見え方などを分離します。仮想マシンは仮想化されたハードウェア上でゲストOSを動かします。コンテナを仮想マシンの中で動かす構成もあります。Dockerのコンテナ入門が、この違いを説明しています。
表が収まらない場合は、横にスクロールできます
| 観点 | Linuxコンテナ | 仮想マシン |
|---|---|---|
| 主な分離の単位 | カーネルを共有するプロセス | ゲストOSを含む環境 |
| 配布・更新で扱うもの | アプリとユーザー空間の依存関係 | OSを含むマシンイメージ等 |
| 合う要件の例 | 同じアプリを再作成・複数配置したい | 異なるOSやカーネルの要件がある |
| 別途必要な管理 | ホスト、権限、ネットワーク、データ | 仮想化基盤、ゲストOS、権限、データ |
「必ず数秒」「完全に隔離」「VMより安全」とは言えません。イメージ取得、初期化、ストレージ、権限設定によって起動時間も影響範囲も変わります。実行するコードをどこまで信頼できるかを先に決めます。
小さな集計処理を、配布できる形にする
まずWebサーバーやDBを使わず、架空の注文2件を集計します。同じフォルダに次の3ファイルを置く例です。Node.js 24系のローカル実行を確認しています。Dockerでのビルド・実行は本稿の検証では行っていません。
orders.jsonは練習用データです。実データや秘密情報をイメージに含めるための例ではありません。
[
{ "totalYen": 1200 },
{ "totalYen": 800 }
]
report.mjsは、非負の安全な整数だけを受け付け、合計も同じ範囲に収まるか確認します。
import { readFile } from 'node:fs/promises';
const orders = JSON.parse(
await readFile(new URL('./orders.json', import.meta.url), 'utf8')
);
if (!Array.isArray(orders)) throw new Error('orders must be an array');
let totalYen = 0;
for (const order of orders) {
const value = order?.totalYen;
if (!Number.isSafeInteger(value) || value < 0) {
throw new Error('totalYen must be a non-negative safe integer');
}
totalYen += value;
if (!Number.isSafeInteger(totalYen)) throw new Error('total overflow');
}
console.log(JSON.stringify({ count: orders.length, totalYen }));
node report.mjsで実行すると、{"count":2,"totalYen":2000}を出力します。値を文字列の"1200"に変えるとエラーで終了します。黙って型を変換しないことも確認できます。
Dockerfileでは、実行環境とコピーするファイルを明示します。
FROM node:24-bookworm-slim
WORKDIR /app
COPY report.mjs orders.json ./
USER node
CMD ["node", "report.mjs"]
Nodeの公式イメージで定義されている構成を使っています。対応するバリアント一覧は2026年10月4日に確認しました。すでに利用を許可されたDocker環境がある場合の操作例は次の通りです。初回のビルドではベースイメージを外部から取得します。
docker build -t order-report:local .
docker run --rm --network none order-report:local
--rmは終了したコンテナを削除します。今回の結果は標準出力に出るだけで、永続保存しません。--network noneは実行するコンテナに外部通信用のネットワークを与えない設定で、ビルド時の取得を止める指定ではありません。none network driverを参照してください。
この例で確認するのは、ファイル、実行コマンド、実行ユーザーを明示する流れです。本番用の権限制限、資源上限、監査、イメージ検査が完成した例ではありません。
同じイメージでも、環境差は残る
再現性を上げるには、アプリのコードだけでなく、動作に関わる条件も記録します。
表が収まらない場合は、横にスクロールできます
| 条件 | 起きる差 | 記録・確認するもの |
|---|---|---|
| CPUとOS | 対応する実行形式がない | 対象プラットフォームとイメージの対応 |
| 依存ライブラリ | 開発機だけにあるライブラリへ依存 | ロックファイル、ビルド手順、実行時の依存 |
| 実行設定 | 接続先やタイムゾーン等が異なる | 必須設定、既定値、設定不足時のエラー |
| 外部サービス | DBの版・権限・応答が異なる | 接続条件、互換性、失敗時の処理 |
| マウント | イメージ内のファイルを別内容で覆う | 保存先、所有者、読み書きの範囲 |
ベースイメージの24-bookworm-slimというタグも固定内容ではありません。配備した内容を特定するにはdigestを記録し、必要に応じてdigestで固定します。ただし固定したまま放置すれば修正版は取り込まれません。更新を検出し、テストして採用する流れが必要です。Dockerのイメージ固定の説明
依存があるNodeアプリでは、ロックファイルに基づくインストールと、実際のビルド成果物・起動コマンドを揃えます。マルチステージビルドはビルド用の道具を実行用イメージから分ける方法です。ただし、ビルド側からnode_modulesをコピーすれば常に動くわけではありません。Debian系のglibcとAlpineのmuslでは、ネイティブ依存の互換性を確認します。Node公式イメージのバリアント説明
権限、ポート、永続データを別々に決める
非rootユーザーとrootlessを混同しない
例のUSER nodeはコンテナ内のプロセスの実行ユーザーです。Docker daemon自体を非rootで動かすrootless modeとは異なります。どちらも「設定したので安全」とは判断せず、書き込めるディレクトリ、ホストのマウント、ネットワーク到達範囲を点検します。
秘密鍵やトークンをDockerfile、ソース、ビルド引数へ埋め込まないことも必要です。イメージやビルド履歴に残れば、後から実行環境で隠しても回収できません。ビルド用の秘密と、実行時に渡す秘密の管理方法を分けます。
EXPOSEは、ホストへのポート公開ではない
EXPOSE 3000は想定するポートの情報です。ホスト側のポートへ公開する操作はdocker run -pなどで指定します。ローカル開発だけで使うなら、公開先のアドレスも確認します。認証のない管理画面を外部へ出さないためです。DockerfileのEXPOSE・USER・ARG
volumeがあっても、バックアップは別に必要
コンテナの書き込み可能な層にだけ保存したデータは、そのコンテナを削除すると失われます。永続データはvolumeや外部ストレージへ分けます。Dockerのvolumeの説明
ただし同じホスト上にvolumeが残ることと、ホスト故障や誤削除から復元できることは別です。DBなら整合性を保ったバックアップ、保存先の分離、復元にかかる時間を確認します。アプリを前のイメージへ戻しても、変更済みのDBスキーマまで自動で戻るわけではありません。
ComposeとKubernetesは、運用要件に合わせて選ぶ
Composeは複数のサービスとネットワーク、volumeなどの構成を宣言して扱う道具です。開発用のアプリとDBを揃える際に役立ちます。ただしdepends_onで順序を指定しただけでは、DBが接続を受け付ける準備まで待ちません。必要ならhealthcheckとservice_healthy条件を組み合わせます。起動後にDBが落ちた際の再接続は、さらに別の問題です。Composeの起動順制御
Kubernetesでは、コンテナをまとめるPod、望ましい複製数や更新を管理するDeployment、通信先をまとめるServiceなどを扱います。複数ホストで配置や復旧を管理したい場合の選択肢ですが、ネットワーク、権限、証明書、監視、クラスタ更新の責任も増えます。
特に「健康か」という一つの言葉で、異なる判定をまとめないことが大切です。
表が収まらない場合は、横にスクロールできます
| 判定 | 何を知りたいか | 失敗時の意味 |
|---|---|---|
| Docker HEALTHCHECK | 健康状態の確認結果はどうか | unhealthy等の状態を付ける。これ自体は再起動命令ではない |
| Kubernetes readiness | 今、通信を受けてよいか | 対象Serviceの配送先から外す |
| Kubernetes liveness | 停滞したプロセスを再起動すべきか | 失敗条件に達すると再起動方針に従って対処する |
| Kubernetes startup | 起動処理を終えたか | 成功するまで他のprobeを待たせ、起動失敗を判定する |
DBが一時停止しただけで全Podのlivenessが落ちる設計では、再起動が重なって復旧を妨げる場合があります。判定コマンドがイメージ内に存在するかも確認します。小さいイメージにはcurlがないこともあります。Kubernetesのprobeの役割と注意点
採用前に、再作成と復元を一度分けて試す
最初から多くのサービスを移す必要はありません。小さな処理を一つ選び、次を確認すると不足が見つかります。
- 同じイメージを別の許可された環境で動かし、設定不足を検出できるか確認する。
- アプリのコンテナを削除・再作成しても、必要なデータが残る構成か確認する。
- バックアップから別の保存先へ復元し、内容と所要時間を確認する。
- 前のイメージへ戻す操作と、DB変更の互換性を別々に確認する。
- 更新、監視、障害連絡、費用確認の担当を決める。
これらは読者の検証手順であり、本稿でDockerやクラスタの復旧を実行した記録ではありません。今回実行したのは、先の集計プログラムの正常入力・不正入力の確認です。
評価には、配備失敗件数を全配備件数で割った割合、障害発生から復旧までの時間、更新作業の工数を使えます。ホスト、レジストリ、ストレージ、監視、運用作業の費用も含めて比較します。コンテナ数が増えたこと自体を成功とはしません。
配布後の検証と公開操作を整理するならCI/CD入門、サービスを分ける判断ならマイクロサービス入門へ進んでください。サイト全体の更新・復旧体制は運用保守ガイドでも扱っています。