Docker アーキテクチャと核心概念
Docker のアーキテクチャと核心となる概念は、Docker がどのように動作するのか、そしてコンテナ化をいかに効率的に進めるかを理解するための土台です。これらの低レイヤーの仕組みをマスターすることで、コンテナ化されたアプリケーションの構築、管理、デプロイをより自在に行えるようになります。
本章では、Docker の主要なコンポーネント、それらの相互作用、そしてコンテナ技術を支える重要なキーワードについて深く掘り下げていきます。
1. Docker アーキテクチャ
Docker はクライアント・サーバー(Client-Server)アーキテクチャを採用しています。Docker クライアント (Client) が Docker デーモン (Daemon) と通信を行い、デーモンが Docker コンテナのビルド、実行、配布といった重い処理を一手に引き受けます。
クライアントとデーモンは、同一システム上で動作させることも、リモートのデーモンに接続させることも可能です。これらは通常、REST API、UNIX ソケット、またはネットワークインターフェースを介して通信します。
1.1 Docker クライアント (Docker Client)
Docker クライアントは、ユーザーが Docker とやり取りするための主要な手段です。CLI(コマンドラインインターフェース)を提供し、Docker デーモンに対して命令を送信します。これには、イメージのビルド、コンテナの実行、レジストリからのプル/プッシュ、ボリュームやネットワークの管理などが含まれます。クライアントは、入力されたコマンドをデーモンが理解できる API リクエストに変換する役割を担います。
例: docker run hello-world コマンドを実行すると、クライアントはデーモンに対し、hello-world イメージに基づいてコンテナを実行するようリクエストを送信します。
1.2 Docker デーモン (Docker Daemon)
Docker デーモン(dockerd)は、バックグラウンドで常駐するプロセスであり、Docker イメージ、コンテナ、ネットワーク、ボリュームを管理します。API リクエストをリッスンし、それに応じた操作を実行します。デーモンが処理する主なタスクは以下の通りです:
- イメージ管理: レジストリからのプル、Dockerfile によるビルド、ローカルストレージへの保存。
- コンテナ管理: コンテナの作成、起動、停止、削除。
- ネットワーク管理: コンテナ間、および外部ネットワークと通信するための仮想ネットワークの作成と管理。
- データボリューム管理: データの永続化のためのストレージボリュームの管理。
Docker デーモンは、ホスト OS カーネルの機能である namespaces(名前空間) や cgroups(コントロールグループ) を利用して、コンテナ間およびコンテナとホストシステム間の隔離(アイソレーション)を実現しています。
1.3 Docker レジストリ (Docker Registry)
Docker レジストリは、Docker イメージを保存および配布するためのシステムです。イメージを一元管理するリポジトリであり、ここからイメージを保存(プッシュ)したり取得(プル)したりできます。
- Docker Hub: Docker 公式が提供するパブリックレジストリであり、膨大な数のプリビルドイメージが存在します。デフォルトで利用されるレジストリです。
- プライベートレジストリ: 独自の管理下にあるレジストリで、機密性の高いイメージを安全に保管できます。例として Docker Trusted Registry (DTR)、Harbor、あるいはクラウドベンダーが提供する Amazon ECR、Google GCR などがあります。
docker pull <image> を実行すると、デーモンは設定されたレジストリ(デフォルトは Docker Hub)からイメージを取得します。逆に docker push <image> では、イメージをレジストリへ送信します。
2. Docker の核心概念
Docker の仕組みを理解する上で、以下の 5 つのコアコンセプトを把握しておくことが不可欠です。
2.1 Docker イメージ (Docker Images)
Docker イメージは、コンテナを作成するための読み取り専用(Read-only)のテンプレートです。アプリケーションの実行に必要なコード、ライブラリ、依存関係、ツール、設定ファイルがすべて含まれています。
- レイヤー構造 (Layers): イメージは複数のレイヤーで構成されています。Dockerfile の各命令が新しいレイヤーを作成し、それらが積み重なって一つのイメージを形成します。この構造により、異なるイメージ間でベースレイヤーを共有できるため、ストレージと配布の効率が劇的に向上します。
- イミュータビリティ (Immutability): イメージは一度ビルドされると変更できません。コンテナを実行する際、Docker はイメージの上に「書き込み可能レイヤー」を作成し、変更はその層にのみ保存されます。これにより、元のイメージは常にクリーンな状態に保たれます。
2.2 Docker コンテナ (Docker Containers)
Docker コンテナは、イメージの実行可能なインスタンスです。軽量で隔離された環境であり、その中でアプリケーションが動作します。
- 隔離性 (Isolation): コンテナ同士、およびコンテナとホストは分離されており、安全で予測可能な実行環境が提供されます。
- ポータビリティ (Portability): Docker がインストールされている環境であれば、インフラを問わずどこでも同じように動作します。
- ライフサイクル (Lifecycle): 作成、起動、停止、再起動、削除という独自のライフサイクルを持ちます。
2.3 Dockerfile
Dockerfile は、Docker イメージを構築するための一連の命令を記述したプレーンテキストファイルです。
- 自動化 (Automation): 手作業を排除し、一貫性のある再現可能なイメージビルドを可能にします。
- 主要な命令:
FROM(ベースイメージ)、RUN(コマンド実行)、COPY(ファイルコピー)、CMD(実行コマンド)など。
Node.js アプリの Dockerfile 例:
# 公式の Node.js イメージをベースに使用
FROM node:14
# ワークディレクトリを設定
WORKDIR /app
# package.json と package-lock.json をコピー
COPY package*.json ./
# 依存関係をインストール
RUN npm install
# アプリケーションのソースコードをコピー
COPY . .
# 3000番ポートを公開
EXPOSE 3000
# アプリケーションを起動
CMD ["npm", "start"]2.4 Docker ボリューム (Docker Volumes)
Docker ボリュームは、コンテナによって生成・利用されるデータを永続化するためのメカニズムです。データはコンテナのライフサイクルとは独立して管理されるため、コンテナを削除してもデータは失われません。
- データ共有: 複数のコンテナ間で同一のボリュームをマウントし、データを共有することも可能です。
2.5 Docker ネットワーク (Docker Networks)
コンテナ間、およびコンテナと外部の世界を繋ぐ通信経路を提供します。
- Bridge Network: デフォルトのプライベートネットワーク。
- Host Network: ホストのネットワークスタックを直接共有。
- Overlay Network: 異なるホスト間で動作するコンテナ同士の通信を可能にします。
3. 実戦例とデモンストレーション
具体的な例を通じて、これらの概念がどのように連携するかを見ていきましょう。
3.1 シンプルな "Hello, World!" コンテナの実行
docker run hello-worldこのコマンドの裏側では以下の処理が行われています:
- クライアントがデーモンに対し、
hello-worldイメージの実行をリクエスト。 - ホスト上にイメージがない場合、デーモンが Docker Hub からプル。
- デーモンがイメージから新しいコンテナを作成。
- コンテナ内のプログラムが実行され、"Hello from Docker!" を出力。
- プログラム終了後、コンテナは停止。
3.2 シンプルな Docker イメージの構築
my-app というディレクトリを作成し、その中に app.py と Dockerfile を用意します。
app.py:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello, Docker!"
if __name__ == "__main__":
app.run(debug=True, host='0.0.0.0')Dockerfile:
# 軽量な Python 3.9 ベースイメージを使用
FROM python:3.9-slim-buster
# ワークディレクトリを設定
WORKDIR /app
# app.py をコンテナ内にコピー
COPY app.py .
# flask フレームワークをインストール
RUN pip install flask
# 5000番ポートを公開
EXPOSE 5000
# Python スクリプトを実行
CMD ["python", "app.py"]イメージのビルド:
docker build -t my-app .コンテナの実行:
docker run -d -p 5000:5000 my-appこの例は、Dockerfile を用いたイメージの定義から、ポートマッピング(5000:5000)を伴うコンテナの実行までの一連の流れを示しています。コンテナ技術の真髄は、この「一度定義すれば、どこでも動く」という一貫性にあります。