Docker | 2週間で実務レベルに到達するDocker学習:1週間まとめ(ミニ課題)(Day7)

Docker Docker
スポンサーリンク

Day7:1週間まとめミニ課題の全体像

Day7は「知識をバラバラで終わらせず、一本の線にする日」です。
テーマは Node.js のAPIサーバを「完全にDocker化」すること
ここまで学んだ Dockerfile、ポート公開、ボリュームを全部使って、単体アプリを“実務レベル”の形に仕上げます。

ゴールはシンプルです。
あなたのPCに Node.js が入っていなくても、
docker builddocker 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-apidocker 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化する」ということの中身です。

タイトルとURLをコピーしました