本文へスキップ本文へスキップ
開発環境

Dockerって結局何?「開発環境の差分を減らすやつ」くらいしか知らないので、ちゃんと理解してみた

Dockerって結局何?「開発環境の差分を減らすやつ」くらいしか知らないので、ちゃんと理解してみた
目次

Dockerって結局何?「開発環境の差分を減らすやつ」くらいしか知らないので、ちゃんと理解してみた

Dockerを使っていると、こんな説明をすることがあります。

「開発環境の差分を減らせるやつです。」

これは間違っていません。

Node.jsのバージョンが違う。 PHPの拡張が足りない。 ローカルにPostgreSQLが入っていない。 「自分のPCでは動くのに、別の人のPCでは動かない」。

Dockerは、こういう環境差分をかなり減らしてくれます。

ただ、自分も普段Dockerを使っているわりに、

Dockerfileとimageとcontainerって、結局どうつながってる?
volumeって何で必要?
Composeは何をまとめてる?
そもそもDockerの中では何が起きてる?

と聞かれると、少し説明が怪しいところがありました。

そこで今回は、Dockerを初めて触る人でも流れがつながるように、できるだけ難しい言葉を減らして整理してみます。


Dockerは「アプリが動く環境ごと持ち運びやすくする」ための仕組み

Docker公式では、Dockerをアプリケーションの開発・配布・実行のためのプラットフォームとして説明しています。

ポイントは、アプリケーションを container(コンテナ) と呼ばれる隔離された環境で動かせることです。

参考: https://docs.docker.com/get-started/docker-overview/⁠(外部サイト)

たとえばNode.jsのアプリを動かすとして、普通にPCへ直接環境を作るなら、

  • Node.jsを入れる

  • 必要なライブラリを入れる

  • 環境変数を設定する

  • ポートを合わせる

  • DBも用意する

といった準備が必要です。

そして人によって、

「Node 22です」 「いや自分はNode 24です」 「そのライブラリ入ってません」 「そのポート、別のアプリが使ってます」

みたいなことが起きます。

Dockerでは、アプリを動かすために必要な環境をある程度まとめて定義し、その定義から同じような実行環境を作れるようにします。

だから「開発環境の差分を減らす」という理解は、かなり核心に近いです。

ただし、後で触れますが PCの差分を完全になくす魔法ではありません。


まず覚えたいのは Dockerfile → image → container の3つ

Dockerを理解しようとすると、いきなり用語が大量に出てきます。

でも最初は、この3つをつなげるだけでかなり分かりやすくなります。

Dockerfile
→ image
→ container

ここは、マンションを作る話で考えてみます。

Dockerfileは「この部屋こうしといて」という業者への指示書

Dockerfileは、imageを作るための手順を書いたテキストファイルです。

参考: https://docs.docker.com/reference/dockerfile/⁠(外部サイト)

たとえば、

FROM node:24

WORKDIR /app

COPY package.json pnpm-lock.yaml ./

RUN corepack enable && pnpm install

COPY . .

CMD ["pnpm", "dev"]

なら、かなりざっくり言えば、

Node 24の部屋を用意して。
作業場所は /app にして。
必要なファイルを運び込んで。
pnpm installして。
最後はpnpm devで起動して。

という工事指示書です。

Dockerfile自体が動いている環境ではありません。

あくまで「こういう環境を作ってください」という定義です。

imageは、指示書どおり作った「モデルルームの型」

Dockerfileをもとに docker build を実行すると、imageを作れます。

Docker公式ではimageを、containerを作るための読み取り専用テンプレートとして説明しています。

つまり、まだ誰も住んでいない完成済みモデルルームの型みたいなものです。

Node 24が入っていて、必要なパッケージが入っていて、アプリのファイルも入っている。

この型があるから、同じimageから何個でもcontainerを作れます。

containerは、実際に人が入って動き始めた部屋

imageから実際に起動したものがcontainerです。

docker runを実行すると、imageをもとにcontainerが作られ、その中で指定されたプロセスが動きます。

つまり、

Dockerfile = 工事指示書
image = モデルルームの型
container = 実際に稼働している部屋

くらいで考えると、最初はかなり分かりやすいです。

docker

docker buildとdocker runで何が起きているのか

ここまで分かると、いつも打っているコマンドの意味も見えてきます。

docker build

docker build -t my-app .

これは、

このDockerfileを読んで、my-appというimageを作ってください

という操作です。

Dockerfileの各命令を順番に処理して、実行環境の型であるimageを組み立てます。

Dockerのimageはレイヤー構造になっているので、変更されていない部分はビルド時に再利用されることがあります。

だからDockerfileの書き方によって、ビルド速度が大きく変わることもあります。

docker run

docker run -p 3000:3000 my-app

これは、my-app imageからcontainerを作って起動する操作です。

公式Docsの説明では、docker runを実行すると、必要ならimageを取得し、containerを作成し、書き込み可能なファイルシステムやネットワークを用意して、最後にcontainer内のプロセスを起動します。

「runだから何となく起動している」ではなく、

imageから実行可能なcontainerを作って、その中のプロセスを動かしている

と考えると理解しやすいです。


containerと仮想マシンは似ているようで違う

ここもDockerで混乱しやすいところです。

「隔離された環境なら、仮想マシンと同じでは?」

と思います。

かなり大きな違いは、containerごとに丸ごと別のOSを持つわけではないことです。

Linux上のDocker Engineでは、Linuxカーネルのnamespaceなどの仕組みを使って、プロセスやネットワークなどを隔離します。

そのため、一般的な仮想マシンより軽量に扱いやすいのが特徴です。

一方で、MacやWindowsでDocker Desktopを使う場合は少し事情が違います。

Docker Desktopでは、Linux containerを動かすためのLinux環境として、軽量なLinux VMの中でDocker Engineが動きます。

参考: https://docs.docker.com/desktop/features/networking/⁠(外部サイト)

つまり、

containerそのものが毎回VMを1台持っているわけではない。
でもDocker Desktopでは、そのcontainerを動かす土台としてLinux VMが使われている。

という理解が近いです。

ここを知らないと、

「DockerはVMじゃないって聞いたのに、Docker DesktopはVM使ってるじゃん」

となります。

ここは、Dockerを学び始めたときに混乱しやすいポイントです。


containerを消したらデータも消える。そこでvolumeが出てくる

Dockerを使い始めると、次に出てくるのがvolumeです。

containerには書き込み可能な領域があります。

ただし、containerを削除すると、そのcontainer内だけに保存していたデータも基本的には消えます。

Webアプリだけなら作り直せばいいこともありますが、DBのデータまで毎回消えたら困ります。

そこで使うのがvolumeです。

Docker公式では、volumeをDockerが管理する永続データの保存先として説明しています。

参考: https://docs.docker.com/engine/storage/volumes/⁠(外部サイト)

マンションの例でいうと、

container = 部屋
volume = 部屋の外にある貸倉庫

みたいな感じです。

部屋を壊して新しくしても、倉庫に置いた荷物は残ります。

たとえばPostgreSQL containerのデータをvolumeへ保存しておけば、containerを作り直してもDBデータを残せます。

bind mountとは何が違う?

開発環境ではbind mountもよく使います。

bind mountは、ホストPC側の実際のフォルダをcontainer内へ直接見せる仕組みです。

参考: https://docs.docker.com/engine/storage/bind-mounts/⁠(外部サイト)

たとえばソースコードをbind mountしておけば、

PC側でコードを書く
→ container側からも同じファイルが見える

という開発環境を作れます。

ざっくり分けると、

volume = Dockerに管理してもらう倉庫
bind mount = 自分のPCの部屋とcontainerに直通のドアを付ける

くらいで考えると分かりやすいです。


networkはcontainer同士の「内線電話」

アプリをDocker化すると、containerが1個とは限りません。

たとえば、

  • Webアプリ

  • API

  • PostgreSQL

  • Redis

を別々のcontainerで動かすことがあります。

そこで必要になるのがnetworkです。

Dockerのnetworkに接続されたcontainer同士は、設定に応じて通信できます。

Composeでは、特に指定しなくてもアプリ用のデフォルトnetworkが作られ、同じnetworkに参加したservice同士はservice名で見つけられます。

参考: https://docs.docker.com/compose/how-tos/networking/⁠(外部サイト)

たとえばComposeに、

services:
app:
build: .
depends_on:
- db

db:
image: postgres

とあれば、app側からdbというservice名を使って接続できる構成にできます。

マンションでいうなら、

network = container同士だけで使える内線電話

みたいなものです。

同じ建物にいるからといって勝手に全部つながるのではなく、どのnetworkへ参加させるかで通信範囲を整理できます。


Docker Composeは「複数の部屋をまとめて開ける管理表」

ここまで来るとComposeがかなり分かりやすくなります。

Docker Composeは、複数containerで構成されるアプリを定義してまとめて動かすための仕組みです。

参考: https://docs.docker.com/compose/⁠(外部サイト)

たとえば、

services:
app:
build: .
ports:
- "3000:3000"
volumes:
- .:/app

db:
image: postgres
volumes:
- db-data:/var/lib/postgresql/data

volumes:
db-data:

と書いて、

docker compose up

を実行すると、Composeファイルをもとに必要なservice、network、volumeなどをまとめて扱えます。

つまり、

appの部屋を開ける
DBの部屋も開ける
2つを内線でつなぐ
DB用の倉庫も付ける

みたいな準備をまとめてやってくれます。

Dockerを使っていて、

「docker runを何個も覚えるよりComposeの方が楽」

となる理由はここです。

Dockerって結局何?「開発環境の差分を減らすやつ」くらいしか知らないので、ちゃんと理解してみた

Dockerは起動するだけじゃない。知ると便利な機能がかなりある

Dockerを「環境を作って起動するだけのもの」と思っていたのですが、実際にはデバッグや確認でもかなり便利です。

docker logs:まず中で何が起きたか見る

containerが起動しない、APIが500になる、処理が止まる。

そんなときはログを見ることが多いです。

docker logs my-app

追いかけながら見るなら、

docker logs -f my-app

のようにできます。

「動かない → 何となく再起動」より、まずログを見るだけで原因が見つかることがあります。

docker exec:動いているcontainerの中へ入って確認する

docker execは、動いているcontainerの中で別のコマンドを実行できます。

公式Docsでも、running container内で新しいコマンドを実行するものとして説明されています。

参考: https://docs.docker.com/reference/cli/docker/container/exec/⁠(外部サイト)

docker exec -it my-app sh

これで、

  • ファイルが本当にあるか

  • 環境変数が入っているか

  • コマンドが使えるか

  • 設定ファイルがどうなっているか

などをcontainer内から確認できます。

自分のPCを見ているつもりで調べていたら、問題が起きていたのはcontainerの中だった、という切り分けにも便利です。

healthcheck:「起動している」と「使える」は別

containerがrunningになっていても、アプリが正常に応答できるとは限りません。

たとえばプロセス自体は動いているけど、内部で固まっていることもあります。

DockerfileのHEALTHCHECKを使うと、containerが正常に動作しているかをチェックし、healthy / unhealthyといった状態を持たせられます。

参考: https://docs.docker.com/reference/dockerfile/#healthcheck⁠(外部サイト)

ここで大事なのは、healthcheckを書いただけで自動的に全部復旧するわけではないことです。

まず「起動しているように見えるけど、実は壊れている」を検知しやすくする仕組みです。

Docker Desktopの画面も意外と便利

Docker Desktopでは、container、image、volume、ログ、リソース使用量などをGUIから確認できます。

参考: https://docs.docker.com/desktop/use-desktop/⁠(外部サイト)

CLIですべて操作できるとしても、

「今どのcontainerが生きてる?」
「このvolume何だっけ?」
「メモリ食ってるcontainerある?」

みたいな確認はGUIの方が早いこともあります。

Docker=黒い画面だけ、と考えず、確認内容に応じてGUIも使い分けられます。


「DockerならどのPCでも完全に同じ」は少し言いすぎ

ここまで見ると、

Dockerを使えば環境差分は全部なくなるのでは?

と思いたくなります。

でも、そこまではいきません。

たとえば、

  • IntelとArmなどCPUアーキテクチャの違い

  • Mac / Windows / LinuxのホストOS差

  • bind mountしたファイルシステムの違い

  • ファイル権限

  • ポート競合

  • Docker Desktopへ割り当てたCPU・メモリ

  • .envなど外から渡す設定値

  • 接続先APIやDBの状態

  • imageのタグや更新タイミング

などは差分になり得ます。

Dockerが揃えやすくしてくれるのは、アプリを動かす環境の大きな部分です。

「PCそのものを完全コピーする」のではありません。

だから最初の説明も、

「PCの差分をなくす」

より、

「開発・実行環境の差分をかなり減らし、同じ構成を再現しやすくする」

の方が正確です。


Dockerを使う意味は「環境構築を楽にする」だけではなかった

今回整理してみて、Dockerの見え方が少し変わりました。

最初は、

環境差分を減らすための便利ツール

くらいの認識でした。

でも実際には、

Dockerfileで環境を定義する
→ imageとして同じ型を作る
→ containerとして実行する
→ volumeでデータを外へ逃がす
→ networkでcontainer同士をつなぐ
→ Composeでアプリ全体をまとめる

という、一連の仕組みになっています。

これが分かると、Dockerfileやcompose.yamlを見たときも、

「よく分からない呪文」

ではなく、

このアプリは、どんな環境を作って、何個のcontainerを動かして、どこにデータを残して、どう通信しているのか

という設計図に見えてきます。

Dockerを使えることと、Dockerを説明できることはやっぱり別でした。

でも最初からnamespaceやcgroupを全部覚える必要はないと思います。

まずは、

Dockerfile → image → container

をつなげる。

次に、

volume / network / Compose

を足す。

そして困ったら、

logs / exec / healthcheck

を見る。

この順番なら、何となく使っていたDockerがかなり理解しやすくなりました。

次にDockerを触るときは、compose upが通ったことだけで安心せず、

「今、どのimageから何のcontainerが動いて、データはどこに残ってる?」

くらいは一度見てみると、Dockerの理解が一気につながると思います。

過去の記事から 1 本

全 10 本のうち 3 本目の記事

about

このブログについて

Web 開発・インフラ・AI を手を動かして試した記録が中心です。うまくいった話もハマった話も、仕事や日々のこともそのまま書きます。