はじめに
「自分の環境では動くのに、本番環境ではなぜか動かない」。開発者なら一度は経験したことがあるこの問題は、長年にわたって開発現場を悩ませてきました。この課題を根本から解決するのが、Dockerを中心としたコンテナ技術です。
本記事では、コンテナ技術の基本概念からDockerのアーキテクチャまで、初心者にもわかりやすく解説します。この記事を読むことで、以下のことが理解できるようになります。
- コンテナ技術が生まれた背景と解決する問題
- 仮想マシンとコンテナの根本的な違い
- Docker Engine、イメージ、コンテナの関係性
- Dockerを使うメリットと活用シーン
「自分の環境では動くのに」問題
環境の差異が生む悲劇
開発者の間で「It works on my machine(自分のマシンでは動く)」という言葉は、半ば冗談として、しかし深刻な問題として知られています。この問題が発生する原因を整理してみましょう。
環境差異の主な原因
| 項目 | 開発環境 | 本番環境 |
|---|---|---|
| OS | macOS 15 | Amazon Linux 2023 |
| 言語バージョン | Python 3.12.1 | Python 3.11.4 |
| ライブラリ | 最新版 | 古いバージョン |
| 環境変数 | 開発用設定 | 本番用設定 |
| ファイルパス | /Users/dev/app |
/var/www/app |
このように、開発環境と本番環境では様々な要素が異なります。たった1つのライブラリのバージョン違いでも、アプリケーションが正常に動作しなくなることがあります。
従来の解決策とその限界
この問題に対して、従来は以下のようなアプローチが取られてきました。
ドキュメント化による解決
|
|
しかし、この方法には問題があります。
- 手順書の更新漏れ
- 人による解釈の違い
- OSごとのコマンドの差異
- 暗黙の前提条件の存在
仮想マシンによる解決
次に登場したのが仮想マシン(VM)を使った方法です。VirtualBoxやVMwareを使って、本番環境と同じOS・設定の仮想マシンを構築します。これにより環境差異の問題はある程度解決しましたが、新たな課題が生まれました。
- 起動に数分かかる
- 数GB〜数十GBのディスク容量を消費
- CPUやメモリのオーバーヘッドが大きい
- VMイメージの共有・配布が困難
仮想マシンとコンテナの違い
仮想マシンのアーキテクチャ
仮想マシンは、ハードウェアをソフトウェアでエミュレートする技術です。各VMは独立したOS(ゲストOS)を持ち、ハイパーバイザーと呼ばれるソフトウェア層を介して物理ハードウェアにアクセスします。
block-beta
columns 3
block:vm1:1
columns 1
AppA["App A"]
BinsA["Bins/Libs"]
GuestA["Guest OS\n(Ubuntu)"]
end
block:vm2:1
columns 1
AppB["App B"]
BinsB["Bins/Libs"]
GuestB["Guest OS\n(CentOS)"]
end
block:vm3:1
columns 1
AppC["App C"]
BinsC["Bins/Libs"]
GuestC["Guest OS\n(Debian)"]
end
Hypervisor["ハイパーバイザー"]:3
HostOS["ホスト OS"]:3
Hardware["物理ハードウェア"]:3各VMが完全なOSを持つため、以下のような特徴があります。
- 高い分離性(セキュリティ面で有利)
- 異なるOSカーネルを動作可能
- リソース消費が大きい(各VMにOS分のメモリ・ディスクが必要)
- 起動時間が長い(OSブートが必要)
コンテナのアーキテクチャ
コンテナは、ホストOSのカーネルを共有しながら、アプリケーションの実行環境を分離する技術です。Linuxカーネルの機能であるnamespaces(名前空間)とcgroups(コントロールグループ)を利用して実現されています。
block-beta
columns 3
block:container1:1
columns 1
AppA["App A"]
BinsA["Bins/Libs"]
end
block:container2:1
columns 1
AppB["App B"]
BinsB["Bins/Libs"]
end
block:container3:1
columns 1
AppC["App C"]
BinsC["Bins/Libs"]
end
DockerEngine["Docker Engine"]:3
HostOS["ホスト OS"]:3
Hardware["物理ハードウェア"]:3VMと比較して、Guest OSの層がなくなっていることがわかります。これにより以下のメリットが得られます。
- 軽量(数MB〜数百MB程度)
- 高速起動(数秒以内)
- 効率的なリソース利用
- 高いポータビリティ
VMとコンテナの比較表
| 観点 | 仮想マシン | コンテナ |
|---|---|---|
| 起動時間 | 数分 | 数秒 |
| イメージサイズ | 数GB〜数十GB | 数MB〜数百MB |
| メモリオーバーヘッド | 大(各VMにOS分) | 小(カーネル共有) |
| 分離レベル | 高(完全分離) | 中(カーネル共有) |
| OS互換性 | 異なるOS可 | 同一カーネルのみ |
| 起動可能数 | 数個〜数十個 | 数百〜数千個 |
| ユースケース | 異種OS環境、高セキュリティ | マイクロサービス、CI/CD |
Dockerのアーキテクチャ概要
Dockerとは何か
Dockerは、コンテナ技術を誰でも簡単に使えるようにしたプラットフォームです。2013年にDotCloud社(現Docker社)によって公開され、コンテナ技術の普及に大きく貢献しました。
Dockerは以下の要素で構成されています。
- Docker Engine - コンテナの作成・実行を担う中核エンジン
- Docker Image - コンテナの設計図となるテンプレート
- Docker Container - イメージから作成された実行インスタンス
- Docker Registry - イメージを保存・共有するリポジトリ
Docker Engineの仕組み
Docker Engineは、クライアント・サーバーアーキテクチャで動作します。
flowchart LR
subgraph CLI["Docker CLI"]
run["docker run"]
ps["docker ps"]
other["docker ..."]
end
subgraph Daemon["Docker Daemon (dockerd)"]
subgraph Containerd["containerd"]
runc["runc"]
end
end
CLI -->|"REST API"| Daemon各コンポーネントの役割
| コンポーネント | 役割 |
|---|---|
| Docker CLI | ユーザーがDockerを操作するためのコマンドラインツール |
| Docker Daemon | コンテナ、イメージ、ネットワーク、ボリュームを管理するバックグラウンドプロセス |
| containerd | コンテナのライフサイクル管理を担当する高レベルランタイム |
| runc | OCI準拠のコンテナ実行を担当する低レベルランタイム |
Dockerイメージとは
Dockerイメージは、コンテナを作成するためのテンプレートです。アプリケーションのコード、ランタイム、ライブラリ、環境変数、設定ファイルなど、コンテナ実行に必要なすべてを含んでいます。
イメージはレイヤー構造になっており、各レイヤーは読み取り専用です。
block-beta
columns 1
AppCode["アプリケーションコード (最上位レイヤー)"]
PyPackages["Python パッケージ"]
Python["Python 3.12"]
Ubuntu["Ubuntu 24.04 (ベースイメージ)"]このレイヤー構造には以下のメリットがあります。
- 効率的なストレージ: 共通レイヤーは1回だけ保存
- 高速なビルド: 変更されたレイヤーのみ再構築
- 高速な転送: 差分のみをダウンロード
Dockerコンテナとは
Dockerコンテナは、イメージから作成された実行可能なインスタンスです。イメージが「設計図」なら、コンテナは「その設計図から建てられた建物」のようなものです。
flowchart LR
subgraph Image["Docker イメージ"]
nginx["nginx:latest\n(読み取り専用)"]
end
subgraph Containers["Docker コンテナ"]
ContainerA["Container A\n(Running)"]
ContainerB["Container B\n(Running)"]
end
nginx -->|"docker run"| ContainerA
nginx -->|"docker run"| ContainerBコンテナの特徴
- 分離された環境: 他のコンテナやホストから分離されたプロセス空間
- 書き込み可能レイヤー: イメージの上に書き込み可能なレイヤーを追加
- 一時性: コンテナ削除時に書き込みレイヤーも削除(永続化にはボリュームを使用)
- 起動の高速性: 新しいプロセスを起動するだけなので数秒で起動
Docker Registryとイメージの共有
Docker Registryは、Dockerイメージを保存・配布するためのサービスです。最も広く使われているのがDocker Hubで、公式イメージや多数のコミュニティイメージが公開されています。
flowchart LR
Dev["開発マシン"]
Hub["Docker Hub\n(Registry)\nnginx, python,\nnode, mysql..."]
Prod["本番サーバー"]
Dev -->|"push"| Hub
Hub -->|"pull"| Dev
Hub -->|"pull"| ProdDockerを使う5つのメリット
1. 環境の完全な再現性
Dockerを使えば、開発・テスト・本番環境で全く同じコンテナを実行できます。「自分の環境では動くのに」問題は、Dockerの登場によって解決されました。
2. 迅速な環境構築
新しいチームメンバーが参加した場合でも、docker compose up コマンド一発で開発環境を構築できます。従来は数時間〜数日かかっていた環境構築が、数分で完了します。
3. リソースの効率的な利用
コンテナは軽量なため、1台のマシンで多数のコンテナを同時に実行できます。マイクロサービスアーキテクチャとの相性が非常に良く、サービスごとに独立したコンテナとして運用できます。
4. CI/CDパイプラインとの親和性
コンテナはイミュータブル(不変)であるため、CI/CDパイプラインに最適です。ビルドされたイメージは、テスト環境から本番環境まで同一のものを使用できます。
5. スケーラビリティ
Kubernetesなどのコンテナオーケストレーションツールと組み合わせることで、負荷に応じてコンテナを自動的にスケールアウト/スケールインできます。
まとめ
本記事では、Dockerとコンテナ技術の基本概念について解説しました。
学んだポイント
- 「自分の環境では動くのに」問題は、環境差異が原因
- 仮想マシンは完全な分離を提供するが、オーバーヘッドが大きい
- コンテナはOSカーネルを共有することで軽量・高速を実現
- Docker Engineはクライアント・サーバーアーキテクチャで動作
- イメージはレイヤー構造の読み取り専用テンプレート
- コンテナはイメージから作成された実行インスタンス
- Docker Registryでイメージを共有し、環境の再現性を確保
次のステップとして、Docker Desktopをインストールし、実際にコンテナを動かしてみることをおすすめします。