Day7:1週間まとめミニ課題の全体像
Day7は「知識をバラバラで終わらせず、一本の線にする日」です。
テーマは Node.js のAPIサーバを「完全にDocker化」すること。
ここまで学んだ Dockerfile、ポート公開、ボリュームを全部使って、単体アプリを“実務レベル”の形に仕上げます。
ゴールはシンプルです。
あなたのPCに Node.js が入っていなくても、docker build と docker run だけで API が動き、
コードやデータの扱いも意図どおりにコントロールできる状態を作ることです。
前提となるシンプルな Node.js API のイメージ
ここでは、次のような超シンプルな API を前提にします。
// app.js
const express = require("express");
const fs = require("fs");
const app = express();
const DATA_FILE = "./data.txt";
app.use(express.json());
app.get("/message", (req, res) => {
const text = fs.existsSync(DATA_FILE)
? fs.readFileSync(DATA_FILE, "utf-8")
: "no data";
res.json({ message: text });
});
app.post("/message", (req, res) => {
const text = req.body.text || "";
fs.writeFileSync(DATA_FILE, text);
res.json({ status: "saved", text });
});
app.listen(3000, () => {
console.log("API server running on port 3000");
});
このAPIは /message に対して
GET で保存済みメッセージを返し、
POST でメッセージを保存します。
保存先は data.txt というローカルファイルです。
ここに対して、
「Docker化」「ポート公開」「ボリューム利用」を組み込んでいきます。
ステップ1:DockerfileでNode.js APIをDocker化する
Dockerfileの全体像
まずは、このAPIを動かすための Dockerfile を作ります。
FROM node:18
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "app.js"]
Dockerfileここに、これまで学んだ要素がすべて詰まっています。
FROM と WORKDIR の意味の再確認
FROM node:18 は「Node.js 18 が入った公式環境を使う」という宣言です。WORKDIR /app は「このコンテナの作業場所は /app にする」という指定です。
以降の COPY や RUN はすべて /app の中で行われます。
COPY と RUN の役割
COPY package*.json ./ と RUN npm install によって、
依存関係(express など)がコンテナ内にインストールされます。
その後の COPY . . で app.js などのアプリ本体がコピーされます。
ここで重要なのは、
依存関係はイメージに閉じ込めておき、
コードは後でマウントして差し替えることもできる、という設計が可能になる点です。
CMD と EXPOSE の意味
CMD ["node", "app.js"] は「コンテナ起動時に node app.js を実行する」という設定です。EXPOSE 3000 は「このコンテナは3000番ポートで待ち受ける」という“宣言”です。
EXPOSE 自体はポートを公開しませんが、
「このコンテナはここで待っているよ」というメタ情報として重要です。
ステップ2:ポート公開で外部からAPIにアクセスできるようにする
コンテナの中と外のポートの関係
このAPIはコンテナの中で 3000 番ポートを使って動きます。
しかし、そのままではホスト(あなたのPC)からアクセスできません。
そこで、Day3で学んだポートフォワーディングを使います。
docker run -d -p 8080:3000 my-api
ここで my-api は docker build -t my-api . で作ったイメージ名です。
このコマンドの意味は
「ホストの8080番ポート → コンテナの3000番ポート」
という橋を作ることです。
ブラウザや curl からはhttp://localhost:8080/message
にアクセスすればOKです。
裏側では、Dockerがコンテナの3000番ポートに転送しています。
ステップ3:ボリュームでデータを永続化する
なぜこのAPIにボリュームが必要なのか
このAPIは data.txt にメッセージを保存します。
もしボリュームを使わずにコンテナの中に保存すると、
コンテナを削除した瞬間に data.txt も消えます。
Day6で学んだとおり、
「コンテナは使い捨て、データは残したい」
というのが正しい設計です。
そこでボリュームを使います。
ボリュームの作成
docker volume create apidata
これで apidata という名前のデータ倉庫ができます。
ボリュームをコンテナに接続する
このAPIは data.txt をアプリのカレントディレクトリ(/app)に保存します。
つまり、/app をボリュームに接続すれば、data.txt もボリューム側に保存されます。
docker run -d -p 8080:3000 -v apidata:/app my-api
これで、
コンテナの /app にあるファイルはすべて apidata に保存されます。
コンテナを削除しても、apidata は残り続けます。
ステップ4:実際の動作イメージを頭の中でシミュレーションする
1回目の起動とデータ保存
コンテナを起動し、POST /message に JSON で { "text": "hello" } を送ると、
コンテナ内の /app/data.txt に “hello” が保存されます。
しかし実際には、これは apidata ボリュームに保存されています。
コンテナを削除してもデータが残ることの確認
docker rm -f コンテナID でコンテナを削除します。
その後、同じコマンドで新しいコンテナを起動します。
docker run -d -p 8080:3000 -v apidata:/app my-api
再度 GET /message を叩くと、
“hello” が返ってきます。
これは、
コンテナは新しくなっているのに、データはボリュームに残っている
ということの証明です。
ここまでできれば、
「コンテナ削除してもデータ保持」という Day6 のゴールも、
「単体アプリを完全Docker化」という Day7 のゴールも同時に達成できています。
ステップ5:開発時のマウントと本番用ボリュームの使い分け
開発時の理想構成
開発中は、コードを頻繁に変更します。
そのため、Day5で学んだマウントを組み合わせると快適になります。
例えば次のような構成です。
Dockerfile で依存関係をインストールしておき、
実行時はコードだけマウントする。
docker run \
-d \
-p 8080:3000 \
-v $(pwd):/app \
-v apidata:/app/data \
my-api
※上記コードの \ は \ を表している
このようにすれば、
コードはホストと同期され、
データはボリュームに保存されます。
開発中にコードを変えても即反映され、
コンテナを作り直してもデータは残る、
という理想的な状態になります。
本番環境の理想構成
本番では、
コードも依存関係もイメージに閉じ込め、
ボリュームだけをデータ用に使うのが一般的です。
マウントは使わず、-v apidata:/app のようにボリュームだけを使うことで、
セキュリティと安定性が高まります。
Day7のゴールの再確認:「単体アプリを完全Docker化」
ここまでの流れを、自分の言葉で説明できれば完璧です。
Node.js のAPIサーバを Dockerfile でコンテナ化し、
ポートフォワーディングで外部からアクセスできるようにし、
ボリュームでデータを永続化する。
開発時はマウントでコードを同期し、
本番時はボリュームだけでデータを守る。
これが「単体アプリを完全Docker化する」ということの中身です。
