Skip to content

Docker コンテナ化

はじめに

「私のマシンでは動く」は開発者にとって最も古典的な言い訳ですが、Docker はその言い訳を完全に消し去ります。 コンテナ化技術はアプリケーションとそのすべての依存関係を標準化された単位にパッケージングし、あらゆる環境で一貫した実行を保証します。現代のソフトウェアデリバリーの基石です。

この記事で学べること

この章を終えると、次のことが身につきます:

  • コア概念:イメージ、コンテナ、レジストリの3つのコア概念を理解
  • アーキテクチャ比較:コンテナと仮想マシンの本質的な違いを理解
  • 実践スキル:Dockerfile の記述と一般的なコマンドを習得
  • オーケストレーション基礎:Docker Compose でマルチサービスアプリケーションを管理
  • ベストプラクティス:イメージ最適化、セキュリティ強化などの本番級プラクティス
内容コア概念
第1章なぜコンテナが必要か環境の一貫性、リソース効率、標準化されたデリバリー
第2章コア概念イメージ、コンテナ、レジストリ、Dockerfile
第3章Docker ライフサイクル記述、ビルド、プッシュ、実行、管理
第4章Docker Composeマルチサービスオーケストレーション、ネットワーク、ボリューム
第5章ベストプラクティスイメージ最適化、セキュリティ、マルチステージビルド

1. なぜコンテナが必要なのか?

コンテナが登場する前、アプリケーションをデプロイするにはサーバー上でランタイムを手動インストールし、環境変数を設定し、依存関係の競合を処理する必要がありました。異なる環境(開発、テスト、本番)間の差異はバグの温床でした。

Virtual Machines vs Containers
Switch between both virtualization models to compare their architecture
App A / App B / App C
App A + Bins/Libs
App B + Bins/Libs
App C + Bins/Libs
Docker Engine
Host OS
Physical hardware
Startup speedSeconds
Resource usageShared host OS kernel (MB scale)
IsolationProcess-level isolation
DensityHundreds of containers per host
Image sizeMB scale

コンテナはどんな問題を解決するのか?

問題従来のアプローチコンテナアプローチ
環境の不一致「私のマシンでは動く」すべての依存関係をパッケージ、どこでも一貫
依存関係の競合アプリAは Node 14、アプリBは Node 18各コンテナが独立した環境
リソースの無駄各 VM に完全な OSカーネルを共有、MB レベルのオーバーヘッド
デプロイが遅い手動インストールと設定docker run コマンド一つ
スケーリングが難しい新しい VM を作成、環境をインストール、デプロイ数秒で新しいコンテナを起動

コンテナの本質

コンテナは軽量な仮想マシンではありません。その本質は隔離されたプロセスです。Linux カーネルは2つのメカニズムでコンテナを実現します:

  • Namespace:プロセスの可視範囲を隔離(PID、ネットワーク、ファイルシステムなど)
  • Cgroups:プロセスのリソース使用量を制限(CPU、メモリ、IO)

コンテナ内のプロセスはホストマシン上の通常のプロセスと本質的に違いはありません。ただ「外が見えない部屋に閉じ込められている」だけです。


2. コア概念

Docker の世界は3つのコア概念を中心に展開します:イメージ(Image)、コンテナ(Container)、レジストリ(Registry)。

概念例え説明
イメージ(Image)クラス / テンプレート読み取り専用のアプリケーションテンプレート。コード、ランタイム、ライブラリ、設定を含む
コンテナ(Container)インスタンス / オブジェクトイメージの実行インスタンス。読み書き可能、独立したライフサイクルを持つ
レジストリ(Registry)アプリストアイメージの保存と配布を行うサービス(Docker Hub、ACR、ECR)
Dockerfileレシピ / 設計図イメージのビルド方法を定義するテキストファイル
ボリューム(Volume)外付けハードディスク永続データ。コンテナ削除後もデータは失われない

イメージのレイヤー構造

Docker イメージは複数の読み取り専用レイヤー(Layer)で構成され、各 Dockerfile 命令が1つのレイヤーを作成します:

┌─────────────────────────┐
│  CMD ["node", "app.js"] │  ← 起動コマンドレイヤー
├─────────────────────────┤
│  COPY . /app            │  ← アプリケーションコードレイヤー(頻繁に変更)
├─────────────────────────┤
│  RUN npm install        │  ← 依存関係インストールレイヤー(時々変更)
├─────────────────────────┤
│  FROM node:18-alpine    │  ← ベースイメージレイヤー(ほとんど変更されない)
└─────────────────────────┘

なぜレイヤー構造が重要なのか?

Docker は各レイヤーをキャッシュします。レイヤーに変更がない場合、ビルド時にキャッシュが再利用されます。そのため Dockerfile では変更頻度の低い命令を前に(依存関係のインストールなど)、変更頻度の高い命令を後に(コードのコピーなど)配置すべきです。これにより、ほとんどのビルドでキャッシュがヒットし、大幅に高速化されます。


3. Docker ライフサイクル

Dockerfile の記述からコンテナの実行まで、Docker のワークフローは明確なパイプラインです。

Docker Lifecycle
Click each stage to inspect the details
📝
Write a Dockerfile
🔨
Build the image
☁️
Push to registry
▶️
Run the container
⚙️
Manage containers
Write a Dockerfile
A Dockerfile is the recipe for building an image. It starts from a base image and defines how to assemble the application environment step by step. Each instruction creates an image layer that Docker can cache for later builds.
Common commands
FROM node:18-alpineChoose the base image
WORKDIR /appSet the working directory
COPY package*.json ./Copy dependency files for cache reuse
RUN npm installInstall dependencies
COPY . .Copy application code
EXPOSE 3000Declare the port
CMD ["node", "server.js"]Start command

Dockerfile 一般的な命令リファレンス

命令目的
FROMベースイメージを指定FROM node:18-alpine
WORKDIR作業ディレクトリを設定WORKDIR /app
COPYファイルをイメージにコピーCOPY package.json ./
RUNビルド時にコマンドを実行RUN npm install
ENV環境変数を設定ENV NODE_ENV=production
EXPOSEポートを宣言(ドキュメントのみ)EXPOSE 3000
CMDコンテナ起動コマンドCMD ["node", "app.js"]
ENTRYPOINTコンテナのエントリポイント(オーバーライドが困難)ENTRYPOINT ["nginx"]

4. Docker Compose:マルチサービスオーケストレーション

実際のプロジェクトでは通常、複数のコンテナが必要です。Web アプリケーションにはアプリケーションサーバー + データベース + Redis + Nginx が必要になるかもしれません。Docker Compose は1つの YAML ファイルで複数のコンテナを定義・管理します。

docker-compose.yml の例

yaml
version: '3.8'
services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DB_HOST=db
      - REDIS_HOST=redis
    depends_on:
      - db
      - redis

  db:
    image: postgres:15-alpine
    volumes:
      - db-data:/var/lib/postgresql/data
    environment:
      - POSTGRES_PASSWORD=secret

  redis:
    image: redis:7-alpine

volumes:
  db-data:

Compose のコア概念

概念説明
services個々のコンテナサービスを定義app、db、redis
volumes永続データボリュームdb-data にデータベースファイルを保存
networksカスタムネットワーク(デフォルトで自動作成)サービス間はサービス名でアクセス
depends_on起動順序の依存関係app は db と redis に依存
environment環境変数データベースのパスワード、接続アドレス

サービスディスカバリー

Docker Compose では、サービス名がホスト名になります。app コンテナは db:5432 でデータベースに、redis:6379 で Redis に直接アクセスでき、IP アドレスを知る必要はありません。これは Docker の内蔵 DNS のおかげです。


5. ベストプラクティス

5.1 マルチステージビルド(Multi-stage Build)

マルチステージビルドはイメージサイズを最適化する強力なツールです。ビルドステージですべてのツールと依存関係をインストールし、最終ステージではランタイムに必要なファイルのみを残します。

dockerfile
# ビルドステージ
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# ランタイムステージ
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/server.js"]

5.2 イメージ最適化チェックリスト

最適化項目アプローチ効果
小さいベースイメージを選択ubuntu ではなく alpine を使用イメージが ~200MB から ~50MB に
RUN 命令を統合複数のコマンドを && で接続イメージのレイヤー数を削減
.dockerignore を使用node_modules、.git などを除外ビルドの高速化、コンテキストの削減
マルチステージビルドビルド環境とランタイム環境を分離最終イメージにビルドツールを含まない
バージョン番号を固定node:latest ではなく node:18.17-alpine再現可能なビルド

5.3 セキュリティプラクティス

プラクティス説明
root で実行しないUSER node で非 root ユーザーを指定
脆弱性スキャンdocker scout または Trivy でイメージをスキャン
最小権限必要なパッケージのみインストール、デバッグツールは入れない
シークレットをハードコードしない環境変数または Docker Secrets を使用
ベースイメージを定期的に更新セキュリティ脆弱性を迅速に修正

まとめ

Docker コンテナ化は現代のソフトウェアデリバリーの基盤であり、すべての開発者にとって理解が不可欠です。

この章の重要ポイントを振り返ります:

  1. コンテナ vs VM:コンテナはホストカーネルを共有し、より軽量で高速だが、VM より隔離性はやや弱い
  2. コア3点セット:イメージ(テンプレート)、コンテナ(インスタンス)、レジストリ(配布)
  3. Dockerfile:レイヤービルド、キャッシュの活用、変更の少ない命令を前に配置
  4. Docker Compose:YAML でマルチサービスアプリケーションを定義、サービス名がホスト名
  5. 本番プラクティス:マルチステージビルドでイメージを小型化、alpine ベースイメージ、非 root で実行

参考文献