Docker入門:コンテナ技術の基礎をわかりやすく紹介

はい、承知いたしました。Docker入門に関する約5000語の詳細な解説記事を記述します。


Docker入門:コンテナ技術の基礎をわかりやすく紹介

ソフトウェア開発や運用において、「開発環境では動いたのに、本番環境では動かない…」「依存関係のインストールが大変…」「複数のプロジェクトで環境を分けるのが難しい…」といった問題に直面したことはありませんか?

これらの課題を解決する強力なツールとして、近年注目を集めているのがコンテナ技術、特にその代表格であるDockerです。

本記事では、Dockerがなぜ必要とされているのか、コンテナとは一体何なのかという基礎の基礎から、実際に手を動かしてDockerを使い始めるための基本的な操作、さらにはより実践的なDockerfileの書き方、ネットワーク、ストレージ、複数コンテナ管理(Docker Compose)といった応用的なトピックまで、詳しく解説していきます。

Dockerの基本をしっかりと理解し、あなたの開発・運用のワークフローを改善するための一歩を踏み出しましょう。

1. はじめに:なぜ今、Dockerが必要なのか?

かつて、アプリケーションをサーバーにデプロイする際、OSのインストール、ミドルウェア(Webサーバー、データベースなど)のインストールと設定、アプリケーションコードの配置、依存ライブラリのインストール…といった一連の作業を手作業で行うのが一般的でした。しかし、この方法は多くの問題を抱えていました。

  • 「Works on my machine」問題: 開発者のPCでは問題なく動作するのに、テスト環境や本番環境では動かない。これは、OSのバージョン、インストールされているライブラリ、環境変数などの違いによって発生します。
  • 環境構築の手間と時間: 新しいプロジェクトを開始したり、新しいメンバーを迎えたりするたびに、開発環境を一から構築する必要があり、多くの時間と労力がかかります。
  • 依存関係の衝突: 複数のアプリケーションを同じサーバーで動かそうとすると、それぞれが異なるバージョンのライブラリを要求し、衝突してしまうことがあります。
  • デプロイの不安定性: 環境の違いや手作業によるミスが原因で、デプロイが失敗したり、予期せぬ不具合が発生したりすることがあります。

これらの問題を解決するために登場したのが仮想化技術です。

仮想化技術の変遷:VMからコンテナへ

仮想化技術には、大きく分けて「仮想マシン (VM)」と「コンテナ」の2種類があります。

仮想マシン (VM: Virtual Machine)

VMware, VirtualBox, Hyper-Vなどが代表的です。
VMは、ホストOSの上にハイパーバイザーを介して、ゲストOSを含めた仮想的なコンピュータ全体をエミュレートします。

  • 利点:
    • ホストOSから完全に独立した環境を提供できる。
    • 異なるOS(例えばLinux上でWindows)を動作させることができる。
  • 欠点:
    • ゲストOS自体がリソース(CPU, メモリ, ディスク)を消費するため、オーバーヘッドが大きい。
    • VMイメージのサイズが大きく(数GB以上)、起動に時間がかかる。
    • イメージの配布や管理が比較的煩雑。

VMはOSレベルでの完全な隔離を実現しますが、その分どうしても重くなりがちです。

コンテナ

DockerやKubernetesなどが代表的です。
コンテナは、VMとは異なり、ホストOSのカーネルを共有します。コンテナの中にはゲストOSは含まれません。その代わりに、アプリケーションとその実行に必要なライブラリ、設定ファイルなど、OS以外のすべてをパッケージングします。

  • 利点:
    • ゲストOSの起動が不要なため、VMに比べて非常に軽量で起動が速い(数秒〜数十秒)。
    • コンテナイメージのサイズが小さい(数十MB〜数百MB)。
    • OSカーネルを共有するため、リソースの効率が良い。
    • 開発環境、テスト環境、本番環境など、どこでも同じように動く(ポータビリティが高い)。
    • 環境構築が容易になり、開発者間の環境差異をなくせる。
    • アプリケーションとその依存関係をまとめて管理できるため、デプロイが安定する。
  • 欠点:
    • ホストOSと同じ種類のOSしか実行できない(厳密にはカーネルが互換性があればよい)。例えば、Linuxホスト上でWindowsコンテナを直接実行することはできない(Windows版Docker DesktopはHyper-VなどのVM上でLinuxを動かしてその上でコンテナを実行している)。
    • OSレベルでの完全な隔離ではないため、セキュリティ面でVMより注意が必要な場合がある(ただし、現代のコンテナ技術の隔離レベルは非常に高い)。

このように、コンテナ技術はVMの欠点を克服し、アプリケーションの開発・テスト・デプロイのワークフローを劇的に改善する可能性を秘めています。その中でも、コンテナ技術を爆発的に普及させた立役者がDockerです。

2. コンテナ技術とは何か? Dockerの基礎概念

コンテナ技術を一言でいうと、「アプリケーションとその実行に必要なものをすべて1つにパッケージングし、ホストOSから隔離された独立した環境で実行する技術」です。

より具体的に、Dockerにおけるコンテナ技術の主要な概念を見ていきましょう。

  • イメージ (Image)
    • コンテナを起動するための設計図であり、テンプレートです。
    • アプリケーションコード、ランタイム(例: Node.js, Python, Java)、ライブラリ、依存関係、環境変数、設定ファイルなど、アプリケーションを実行するために必要なすべてのファイルシステムと設定が読み取り専用の状態で含まれています。
    • イメージは複数のレイヤーが積み重なって構成されています。これにより、共通のベースイメージ(例: OSの最小限のファイル群)を複数のイメージで共有でき、ストレージ容量を節約したり、ビルド時間を短縮したりできます。
    • イメージは不変 (Immutable) です。一度作成されたイメージは変更されません。
    • Dockerでは、Dockerfileというテキストファイルにイメージの作成手順を記述します。
  • コンテナ (Container)
    • イメージを実行した実行中のインスタンスです。
    • イメージは静的なファイル群ですが、コンテナはメモリやCPUといったリソースを使って実際に動作している状態です。
    • コンテナはホストOSから隔離されており、独自のファイルシステム(イメージの読み取り専用レイヤーの上に、書き込み可能なレイヤーが追加される)、プロセス空間、ネットワークインターフェースを持ちます。
    • この隔離技術により、「Works on my machine」問題を解決し、どの環境でも同じようにアプリケーションを実行できます。
    • コンテナは簡単に起動、停止、削除ができます。また、停止しても状態(書き込み可能なレイヤーの変更内容)を保持しておくことも可能です(ただし、通常は永続化には後述のVolumeなどを使います)。
  • Dockerfile
    • イメージを自動的に構築するためのテキストファイルです。
    • イメージを作成するための手順(どのベースイメージを使うか、ファイルをコピーするか、コマンドを実行するか、ポートを開けるか、起動時に何を実行するかなど)を、特定の構文に従って記述します。
    • Dockerfileがあることで、イメージ作成手順がコードとして管理され、再現性が保証されます。
  • Registry (レジストリ)
    • Dockerイメージを保管・共有するためのサービスです。
    • 自分で作成したイメージをRegistryにアップロードしたり、他の人が作成したイメージをRegistryからダウンロードしたりできます。
    • 最も有名なRegistryはDocker Hubですが、Google Container Registry (GCR) やAmazon Elastic Container Registry (ECR) といったクラウドベンダーが提供するものや、プライベートなRegistryを構築することも可能です。

これらの要素が連携して、Dockerを使ったアプリケーションの開発・運用ワークフローを構築します。

3. Dockerのアーキテクチャ

Dockerはクライアントサーバー型のアーキテクチャを採用しています。主要なコンポーネントは以下の通りです。

  • Docker Daemon (dockerd)
    • Dockerの心臓部です。ホストOS上で常にバックグラウンドで動作しています。
    • イメージの構築、コンテナの実行、ネットワークやストレージの管理、Docker APIへのリクエスト処理など、Dockerの主要な機能を提供します。
  • Docker Client (docker)
    • ユーザーがDockerと対話するためのコマンドラインインターフェース (CLI) ツールです。
    • docker build, docker run, docker ps といったコマンドを入力すると、Docker ClientがDocker DaemonにAPIリクエストを送信し、Daemonがその処理を実行します。
    • Docker ClientとDaemonは、同じホスト上にある場合はUNIXソケットや名前付きパイプで通信し、リモートホストにある場合はREST APIで通信します。
  • Docker Registry
    • 前述の通り、イメージを保存・共有する場所です。Docker ClientはDaemonを介してRegistryと通信し、イメージのダウンロード (docker pull) やアップロード (docker push) を行います。

アーキテクチャの仕組み

  1. ユーザーがDocker Client (dockerコマンド) を実行します。
  2. Docker Clientは、ローカルまたはリモートのDocker DaemonにAPIリクエストを送信します。
  3. Docker Daemonはリクエストを受け取り、必要な処理を実行します。
    • docker build: Dockerfileを読み込み、イメージを構築します。構築したイメージはローカルのイメージキャッシュに保存されます。
    • docker run: 指定されたイメージを使って新しいコンテナを起動します。必要であればRegistryからイメージをダウンロードします。
    • docker pull: 指定されたイメージをRegistryからダウンロードし、ローカルに保存します。
    • docker push: ローカルのイメージをRegistryにアップロードします。
    • docker ps: 実行中または停止中のコンテナリストを取得します。
  4. Docker Daemonは処理結果をDocker Clientに返し、Clientがユーザーに結果を表示します。

レイヤー構造 (Union File System)

Dockerイメージは、読み取り専用のレイヤーが積み重なった構造をしています。

例えば、ubuntuイメージをベースに、そこに特定のライブラリをインストールし、さらにアプリケーションコードを追加したイメージを作成するとします。

  1. ubuntuベースイメージ(複数のレイヤー)
  2. ライブラリをインストールしたことによる新しいレイヤー
  3. アプリケーションコードをコピーしたことによる新しいレイヤー

これらのレイヤーが組み合わされて、1つのイメージが構成されます。

コンテナを起動する際には、この読み取り専用のイメージレイヤーの上に、書き込み可能なコンテナレイヤーが追加されます。コンテナ内でファイルを作成・変更・削除すると、その変更はすべてこのコンテナレイヤーに記録されます。元のイメージレイヤーは変更されません。

このレイヤー構造には以下の利点があります。

  • ストレージ効率: 複数のイメージが同じベースイメージを共有している場合、そのベースイメージのレイヤーはディスク上で1つだけ保持されます。
  • ビルド効率: Dockerfileの各命令は新しいレイヤーを作成します。イメージをビルドする際、Dockerは前のビルドから変更されていないレイヤーを再利用します(キャッシュ)。これにより、変更があった部分だけをビルドし直せばよく、ビルド時間が短縮されます。

4. Dockerのインストールと基本的な使い方

では、実際にDockerをインストールし、基本的なコマンドを使ってみましょう。

インストールの準備

お使いのOSに合わせてDocker Desktopをインストールするのが最も簡単です。

  • Windows: Docker Desktop for Windowsをインストールします。Windows 10 Pro/Enterprise/Education (バージョン 1903 以降) または Windows 11 が必要で、通常はWSL2 (Windows Subsystem for Linux 2) を使用します。Homeエディションの場合はWSL2を有効にする必要があります。
  • macOS: Docker Desktop for Macをインストールします。Intelチップ搭載MacまたはAppleシリコン搭載Macに対応しています。
  • Linux: 各ディストリビューション(Ubuntu, Debian, CentOS, Fedoraなど)向けに公式のインストール手順が提供されています。通常はリポジトリを追加してパッケージマネージャーでインストールします。Docker Engine単体で動作します。

各OS向けの最新のインストール手順は、Docker公式サイトのドキュメントを参照してください。

インストールが完了したら、ターミナル(またはコマンドプロンプト)を開き、Dockerコマンドが実行できるか確認します。

bash
docker --version

もしDocker Daemonが起動していない場合は、Docker Desktopアプリケーションを起動するか、Linuxの場合はサービスとして起動してください(例: sudo systemctl start docker)。

最初のコンテナを実行する (docker run)

Dockerが正しくインストールされたかを確認するための最も簡単な方法は、hello-worldイメージを実行することです。

bash
docker run hello-world

このコマンドを実行すると、以下のことが起こります。

  1. Docker ClientがDocker Daemonにhello-worldコンテナの実行を依頼します。
  2. Docker Daemonは、ローカルにhello-worldという名前のイメージがあるか確認します。
  3. もしイメージがなければ、Docker Hub (デフォルトのRegistry) からhello-worldイメージを自動的にダウンロード (docker pullに相当) します。
  4. ダウンロードしたイメージを使って、新しいコンテナを作成し、実行します。
  5. コンテナは単純に「Hello from Docker!」のようなメッセージを表示して終了します。
  6. Docker Daemonはコンテナの実行結果をClientに返します。

成功すると、以下のような出力が表示されます。

“`
Unable to find image ‘hello-world:latest’ in the local registry
latest: Pulling from library/hello-world
… (ダウンロードの進行状況) …
Digest: sha256:…
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the “hello-world” image from the Docker Hub.
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.

To further experiment with Docker, run a Ubuntu container:
$ docker run -it ubuntu bash
“`

これで、Dockerが正しく動作していることが確認できました。

次に、もう少し実用的なコンテナを動かしてみましょう。軽量なLinuxディストリビューションであるAlpine Linuxを使ってみます。

bash
docker run alpine echo "Hello from Alpine!"

これも同様に、Alpineイメージがなければダウンロードされ、そのコンテナ内でecho "Hello from Alpine!"コマンドが実行されます。

コンテナを対話的に実行する (docker run -it)

多くのコンテナは、バックグラウンドでサービスを起動したり、単一のコマンドを実行して終了したりします。しかし、コンテナ内でシェルを起動して操作したい場合もあります。その場合は -it オプションを使います。

bash
docker run -it ubuntu bash

  • -i: 標準入力を開いたままにします。
  • -t: 疑似ターミナル (pseudo-TTY) を割り当てます。

このコマンドを実行すると、Ubuntuイメージ(なければダウンロード)を使ったコンテナが起動し、その中でbashシェルが実行されます。プロンプトがコンテナ内のものに変わるはずです。

bash
root@<container_id>:/# ls /
bin boot dev etc home lib lib32 lib64 libx32 media mnt opt proc root run sbin srv sys tmp usr var
root@<container_id>:/# exit

exitと入力すると、コンテナ内のシェルが終了し、それに伴ってコンテナも停止します。(-itを使わない単一コマンドの場合は、コマンド終了と同時にコンテナが終了します)。

実行中のコンテナ一覧を表示する (docker ps)

現在実行中のコンテナを確認するには docker ps コマンドを使います。

bash
docker ps

-a オプションを付けると、停止しているコンテナも含めてすべて表示できます。

bash
docker ps -a

出力例:

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a3f0498b2a3c ubuntu "bash" About a minute ago Exited (0) 35 seconds ago nostalgic_golick
<container_id> <image_name> "<command>" <elapsed_time> <status> (<exit_code>) <time_ago> <ports> <names>

各列の意味:

  • CONTAINER ID: コンテナの一意なID (短縮形)。
  • IMAGE: コンテナが作成されたイメージの名前。
  • COMMAND: コンテナ起動時に実行されたコマンド。
  • CREATED: コンテナが作成されてからの経過時間。
  • STATUS: コンテナの状態 (Up, Exited, Createdなど)。
  • PORTS: ポートマッピングの情報(後述)。
  • NAMES: コンテナに付けられた名前 (自動生成または手動指定)。

コンテナの停止・起動・再起動 (docker stop, docker start, docker restart)

実行中のコンテナを停止するには docker stop <container_idまたはname> を使います。

bash
docker stop nostalgic_golick

停止したコンテナを再開するには docker start <container_idまたはname> を使います。

bash
docker start nostalgic_golick

実行中のコンテナを再起動するには docker restart <container_idまたはname> を使います。

bash
docker restart nostalgic_golick

コンテナの削除 (docker rm)

停止したコンテナは、docker ps -aで確認できますが、ディスク容量を消費します。不要になったコンテナは削除しましょう。

bash
docker rm nostalgic_golick

実行中のコンテナを強制的に削除するには -f オプションを使います。

bash
docker rm -f <running_container_id>

停止しているすべてのコンテナをまとめて削除するには、フィルター機能を使います。

bash
docker container prune

実行中のコンテナ内でコマンドを実行する (docker exec)

既に起動しているコンテナ内で、追加でコマンドを実行したい場合があります。例えば、Webサーバーコンテナが動いている最中に、ログファイルを確認したり、特定のツールを実行したりしたい場合などです。

これには docker exec コマンドを使います。

bash
docker exec -it <container_idまたはname> bash

このコマンドは、指定したコンテナ内で新しいプロセス(この場合はbashシェル)を起動します。コンテナ自体は停止せず、元のプロセス(コンテナ起動時に指定されたコマンド)は引き続き実行され続けます。

コンテナのログを確認する (docker logs)

コンテナ内で実行されているアプリケーションの標準出力や標準エラー出力は、Dockerによってログとして収集されます。これを確認するには docker logs コマンドを使います。

bash
docker logs <container_idまたはname>

ログをリアルタイムで追尾したい場合は -f (follow) オプションを使います。

bash
docker logs -f <container_idまたはname>

ポートマッピング (docker run -p)

コンテナ内でWebサーバーなどのネットワークサービスを動かした場合、ホストOSからそのサービスにアクセスできるようにする必要があります。これにはポートマッピングを行います。

コンテナの特定のポートを、ホストOSの特定のポートに紐付けます。

bash
docker run -p 8080:80 nginx

このコマンドの意味は以下の通りです。

  • docker run nginx: nginxイメージを使ってコンテナを実行する。
  • -p 8080:80: ホストOSのポート8080へのアクセスを、コンテナ内のポート80に転送する。

これにより、ホストOS上で http://localhost:8080 にアクセスすると、コンテナ内のNginxウェブサーバー(デフォルトでは80番ポートで待機している)に接続できるようになります。

書式は ホストポート:コンテナポート または IPアドレス::コンテナポート または IPアドレス:ホストポート:コンテナポート となります。

コンテナ名の指定 (docker run --name)

docker run コマンドでコンテナを起動する際に、- -name オプションで任意の名前を付けることができます。名前を付けると、コンテナIDの代わりに名前でコンテナを指定できるようになり、管理が容易になります。

bash
docker run --name my-nginx -d -p 8080:80 nginx

  • -d: デタッチドモード(バックグラウンド実行)でコンテナを起動します。
  • --name my-nginx: コンテナに my-nginx という名前を付けます。

これで、docker stop my-nginxdocker logs my-nginx のように、名前で操作できるようになります。

5. Dockerイメージの操作

Dockerイメージはコンテナの設計図であり、これなしにコンテナは起動できません。イメージの操作はDockerを使いこなす上で非常に重要です。

イメージとは?

前述の通り、イメージはアプリケーションとその実行に必要なファイルシステム、ライブラリ、設定などをパッケージングした読み取り専用のテンプレートです。

イメージを取得する (docker pull)

Docker HubなどのRegistryからイメージをダウンロードするには docker pull コマンドを使います。

bash
docker pull ubuntu:latest
docker pull python:3.9-slim
docker pull postgres:13

タグ(:latest, :3.9-slim, :13)を指定することで、特定のバージョンのイメージを取得できます。タグを省略した場合、デフォルトで :latest タグが使用されます。しかし、latestタグは常に最新版とは限らず、ビルドごとに上書きされる可能性があるため、本番環境などで使うイメージは特定のバージョンタグを指定することが推奨されます。

ローカルのイメージ一覧を表示する (docker images)

ダウンロードしたり、自分でビルドしたりして、ローカルに保存されているイメージの一覧を確認するには docker images コマンドを使います。

bash
docker images

出力例:

REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest a2d2544364ce 2 weeks ago 142MB
ubuntu latest ba6267758213 3 weeks ago 72.8MB
hello-world latest d2c94e258dcb 4 months ago 13.3kB

  • REPOSITORY: イメージが所属するRegistryやユーザー名、イメージ名。
  • TAG: イメージのバージョンやバリエーションを示すタグ。
  • IMAGE ID: イメージの一意なID (短縮形)。
  • CREATED: イメージが作成されてからの経過時間。
  • SIZE: イメージのサイズ。

イメージの削除 (docker rmi)

不要になったイメージは削除してディスク容量を解放しましょう。

bash
docker rmi <image_idまたはrepository:tag>

bash
docker rmi nginx:latest

もし、削除したいイメージから作成されたコンテナが存在する場合、そのコンテナを先に削除しないとイメージは削除できません。強制的に削除したい場合は -f オプションを使いますが、あまり推奨されません。

使用されていないすべてのイメージをまとめて削除するには docker image prune を使います。

bash
docker image prune -a # -aは使用されていないすべてのイメージ

イメージの履歴を確認する (docker history)

イメージがどのように構築されてきたか(どのレイヤーが積み重なっているか)を確認するには docker history コマンドを使います。

bash
docker history ubuntu:latest

これにより、各レイヤーがどのDockerfile命令に対応しているか、そのレイヤーがいつ作成されたか、どのくらいのサイズかなどを確認できます。これは、イメージサイズが大きい原因を特定したり、ビルドプロセスを理解したりするのに役立ちます。

Docker Hubでイメージを検索する (docker search)

Docker Hubに公開されているイメージを検索するには docker search コマンドを使います。

bash
docker search ubuntu

これにより、公式イメージやコミュニティイメージなどが一覧表示されます。

6. Dockerfileを使った独自イメージの作成

公開されているイメージを使うだけでなく、自分のアプリケーションに合わせて独自のイメージを作成することが一般的です。独自のイメージを作成するには、Dockerfileというテキストファイルを使います。

Dockerfileは、イメージを構築するための手順を記述したスクリプトのようなものです。このDockerfileを元に docker build コマンドを実行することで、自動的にイメージが作成されます。

Dockerfileの基本的な書き方

Dockerfileは、FROM命令から始まり、そのイメージのベースとなるイメージを指定します。その後、RUN, COPY, WORKDIRなどの命令を使って、必要なファイルを追加したり、コマンドを実行したり、設定を行ったりしていきます。

以下は、簡単なPythonアプリケーションのイメージを作成するDockerfileの例です。

“`dockerfile

— Dockerfile —

ベースイメージを指定

Pythonの公式イメージ 3.9-slimバージョンを使用

FROM python:3.9-slim

コンテナ内の作業ディレクトリを設定

以降の命令(COPY, RUN, CMDなど)はこのディレクトリを基準に実行される

WORKDIR /app

ホストマシンからコンテナ内の作業ディレクトリにファイルをコピー

. (ホストのカレントディレクトリ) の内容を /app (コンテナ内) にコピー

COPY . /app

アプリケーションの依存関係をインストール

requirements.txt に記載されたライブラリをpipでインストール

–no-cache-dir: キャッシュを使わない (イメージサイズ削減)

-r requirements.txt: ファイルから依存関係を読み込む

RUN pip install –no-cache-dir -r requirements.txt

コンテナがリッスンするポートを指定 (ドキュメント目的)

5000番ポートでアプリケーションが動作することを明示

EXPOSE 5000

コンテナ起動時に実行されるコマンドを指定

“python” コマンドで “app.py” スクリプトを実行

CMD [“python”, “app.py”]

— End of Dockerfile —

“`

このDockerfileを使うためには、アプリケーションコード (app.pyなど) と依存関係リスト (requirements.txt) が必要です。例として以下を作成します。

app.py:

“`python

app.py

from flask import Flask

app = Flask(name)

@app.route(‘/’)
def hello_world():
return ‘Hello from Dockerized Python App!’

if name == ‘main‘:
app.run(debug=True, host=’0.0.0.0’, port=5000)
“`

requirements.txt:

Flask==2.0.2

これらの3つのファイル (Dockerfile, app.py, requirements.txt) を同じディレクトリに配置します。

主要なDockerfile命令

上記の例に出てきた命令を含め、よく使われるDockerfile命令をいくつか紹介します。

  • FROM <base_image>[:<tag>]:
    • 新しいイメージのベースとなるイメージを指定します。Dockerfileの最初の命令でなければなりません。
    • 例: FROM ubuntu, FROM nginx:latest, FROM python:3.9-slim
  • RUN <command>:
    • イメージ構築中にコンテナ内でコマンドを実行します。
    • 例えば、パッケージのインストール、ファイルの解凍、ディレクトリの作成などを行います。
    • シェルの形式 (RUN apt-get update && apt-get install -y ...) とExec形式 (RUN ["apt-get", "update"]) があります。複数のコマンドを && で繋げる場合はシェルの形式を使います。
  • CMD <command>:
    • コンテナが起動したときにデフォルトで実行されるコマンドを指定します。
    • Dockerfile内に複数記述されている場合、最後のCMDだけが有効になります。
    • docker run <image> <command>のように、docker run時にコマンドを指定すると、DockerfileのCMDは上書きされます。
    • 通常はExec形式で記述します: CMD ["python", "app.py"]
  • ENTRYPOINT <command>:
    • コンテナ起動時に必ず実行されるコマンドを指定します。
    • CMDとは異なり、docker run時に指定されたコマンドはENTRYPOINTへの引数として渡されます。
    • 例えば、ENTRYPOINT ["nginx", "-g", "daemon off;"] のような指定や、スクリプトを実行エントリーポイントとして、CMDでデフォルトの引数を指定する使い方があります。
  • COPY <src>... <dest>:
    • ホストマシン上のファイルやディレクトリを、イメージ内の指定されたパスにコピーします。
    • COPY . /app は、Dockerfileがあるディレクトリの全てのコンテンツを、イメージ内の/appディレクトリにコピーします。
  • ADD <src>... <dest>:
    • COPYと似ていますが、以下の機能も持ちます。
      • <src>にURLを指定すると、ファイルをダウンロードして<dest>に配置します。
      • <src>がtarアーカイブ形式(gzip, bzip2, xz)の場合、自動的に解凍して配置します。
    • 特殊な動作があるため、通常はよりシンプルで予測可能なCOPYを使うことが推奨されます。
  • WORKDIR <path>:
    • 以降のRUN, CMD, ENTRYPOINT, COPY, ADDなどの命令が実行される際のカレントディレクトリを設定します。
    • Dockerfile内で複数回指定できます。パスは絶対パスでも相対パスでも指定可能ですが、絶対パスが推奨されます。
  • EXPOSE <port> [<port>...]:
    • コンテナがリッスンするネットワークポートを指定します。
    • これは単なるドキュメントであり、実際にポートを公開するにはdocker run -pオプションが必要です。
  • ENV <key>=<value> ...:
    • 環境変数を設定します。
    • 例: ENV FLASK_APP=app.py, ENV PORT=5000
  • ARG <name>[=<default value>]:
    • イメージビルド時にdocker build --build-arg <name>=<value>で渡すことができる変数(ビルド引数)を定義します。
    • ENVと異なり、ビルド時のみ有効で、コンテナの実行時には環境変数として引き継がれません。
  • VOLUME <path> [<path>...]:
    • コンテナ内の特定のパスをVolumeとしてマウントすることを宣言します。
    • これは主にドキュメント目的ですが、匿名Volumeを自動作成するトリガーにもなります(詳細はストレージのセクションで後述)。

Dockerイメージのビルド (docker build)

Dockerfileがあるディレクトリで、以下のコマンドを実行してイメージをビルドします。

bash
docker build -t my-python-app:1.0 .

  • docker build: イメージをビルドするコマンド。
  • -t my-python-app:1.0: ビルドするイメージに名前とタグを付けます (repository:tag)。
  • .: ビルドコンテキストのパスを指定します。この場合、カレントディレクトリ (.) にDockerfileがあると仮定しています。ビルドコンテキストとは、DockerfileやCOPY/ADDでコピーするファイル群が含まれるディレクトリのことです。

コマンドを実行すると、DockerはDockerfileを読み込み、記述された命令を上から順番に実行していきます。各命令は新しいイメージレイヤーを作成し、それが積み重なって最終的なイメージが完成します。

ビルドが成功すると、「Successfully built 」「Successfully tagged my-python-app:1.0」のようなメッセージが表示されます。docker imagesで確認すると、新しく作成されたイメージが表示されているはずです。

.dockerignore ファイル

ビルドコンテキストには、イメージに含める必要のないファイル(例: ソースコード管理のメタデータ(.git), ビルド時の一時ファイル, 依存関係のキャッシュディレクトリなど)が含まれていることがあります。これらをイメージにコピーしないようにするには、.dockerignoreというファイルを作成します。

.dockerignoreファイルには、無視したいファイルやディレクトリのパターンを記述します。記述方法は.gitignoreファイルと似ています。

“`

.dockerignore

.git
venv/
pycache/
*.pyc
tmp/
“`

docker buildを実行する際、Docker Clientはビルドコンテキストディレクトリ全体をDocker Daemonに送信しますが、.dockerignoreに指定されたファイルやディレクトリは送信対象から除外されます。これにより、ビルドの高速化やイメージサイズの削減に繋がります。

レイヤーキャッシュの利用

docker build は、可能な限り前のビルドで作成されたレイヤーをキャッシュとして再利用します。Dockerfileの各命令が実行される際、Dockerはキャッシュに同じ命令で作成されたレイヤーがないかを探します。見つかればそのレイヤーを再利用し、見つからなければ新しくレイヤーを作成します。

キャッシュを効率的に利用するためには、Dockerfileの命令の順序が重要です。あまり頻繁に変更されない命令(例: ベースイメージの指定、依存関係のインストール)をDockerfileの上の方に書き、頻繁に変更される可能性のある命令(例: アプリケーションコードのコピー)を下の方に書くと、変更があった際に下の方のレイヤーだけを再ビルドすれば済むため、ビルド時間が短縮されます。

上記のPythonアプリの例では、COPY . /appRUN pip install より後に来ていますが、これはまさにキャッシュ戦略に基づいています。依存関係 (requirements.txt) はアプリケーションコード (app.py) よりも変更頻度が低いと想定し、先に依存関係のインストールを済ませることで、アプリケーションコードだけを変更した場合にpip installのレイヤーをキャッシュから再利用できるようにしています。

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

より効率的なイメージを作成するための高度なテクニックとして、マルチステージビルドがあります。

これは、1つのDockerfile内で複数のFROM命令を使用することで、ビルドの途中で一時的なコンテナ(ビルドコンテナ)を作成し、そこでビルドやコンパイル、テストなどを行い、最終的なイメージには必要な成果物だけをコピーするという手法です。

例えば、Go言語のアプリケーションをビルドする場合、コンパイルにはGoのコンパイラや依存ライブラリが必要ですが、実行にはコンパイル済みのバイナリだけがあれば十分です。

“`dockerfile

— Dockerfile (Multi-stage build example) —

ステージ1: ビルドステージ

FROM golang:1.17 as builder

WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download

COPY . .

RUN CGO_ENABLED=0 GOOS=linux go build -o myapp .

ステージ2: 最終イメージステージ

FROM alpine:latest

WORKDIR /app

ビルドステージからコンパイル済みバイナリだけをコピー

COPY –from=builder /app/myapp .

実行権限を付与

RUN chmod +x myapp

コンテナ起動時に実行するコマンド

CMD [“./myapp”]

— End of Dockerfile —

“`

この例では、最初のFROM golang:1.17 as builderで「builder」という名前のビルドステージを定義し、Goのコンパイラが含まれるイメージを使っています。ここで依存関係のダウンロードやバイナリのコンパイルを行います。

次に、2番目のFROM alpine:latestで最終イメージのステージを定義します。このステージでは、Goのコンパイラを含まない最小限のalpineイメージを使っています。そして、COPY --from=builder /app/myapp .という命令を使って、最初のビルドステージ (--from=builder) で生成されたコンパイル済みバイナリ /app/myapp だけを、最終イメージの /app ディレクトリにコピーしています。

これにより、最終的なイメージにはGoコンパイラやソースコードなどは含まれず、実行に必要なバイナリと最小限のOS環境だけが含まれるため、イメージサイズを大幅に削減できます。

7. Dockerネットワーク

コンテナはデフォルトで隔離されたネットワーク環境を持ちますが、実際のアプリケーションでは他のコンテナやホストOS、外部ネットワークと通信する必要があります。Dockerは様々なネットワーク機能を提供します。

デフォルトのネットワーク

Dockerをインストールすると、デフォルトでいくつかのネットワークが作成されます。

  • bridge:
    • デフォルトでコンテナが接続されるネットワークです。
    • ホストOS上に仮想的なブリッジネットワーク (docker0) が作成され、各コンテナにはこのネットワーク内のプライベートなIPアドレスが割り当てられます。
    • コンテナは互いにIPアドレスで通信できますが、デフォルトでは名前解決はできません(後述のユーザー定義ブリッジネットワークでは可能)。
    • ホストOSからコンテナにアクセスしたり、コンテナから外部ネットワークにアクセスしたりするには、NAT (Network Address Translation) が使用されます。docker run -pでポートマッピングを行うと、ホストOSのポートへのアクセスがNATによってコンテナに転送されます。
  • host:
    • コンテナがホストOSのネットワークスタックを直接使用します。
    • コンテナは独自のネットワークインターフェースを持たず、ホストOSのIPアドレスとポートを共有します。
    • これにより、ポートマッピングが不要になりますが、ポートの競合に注意が必要です。
    • 最もパフォーマンスが高いネットワークモードですが、隔離レベルが低いというトレードオフがあります。
  • none:
    • コンテナにネットワークインターフェースが割り当てられず、外部との通信が一切できません。
    • ネットワークアクセスが不要なバッチ処理などの特殊な用途に使用されます。

docker network ls コマンドで、存在するネットワーク一覧を確認できます。

bash
docker network ls

ユーザー定義ブリッジネットワーク

デフォルトのbridgeネットワークは便利ですが、いくつかの制限があります。特に、コンテナ名での名前解決ができないため、コンテナ同士がIPアドレスでしか通信できません。IPアドレスはコンテナの起動・停止で変わる可能性があるため、これは扱いにくいです。

そこで推奨されるのが、ユーザー定義ブリッジネットワークです。

ユーザー定義ブリッジネットワークを作成すると、以下の利点があります。

  • コンテナ名での名前解決: 同じユーザー定義ネットワーク内のコンテナは、コンテナ名をホスト名として互いに名前解決できます。これにより、IPアドレスを気にすることなく通信できます。
  • 隔離: デフォルトのbridgeネットワークとは別に、論理的に隔離されたネットワークを作成できます。特定のアプリケーション群だけをこのネットワークに接続し、他のアプリケーション群とは分離することができます。
  • 設定の容易さ: ネットワークのサブネットやIPアドレス範囲などを細かく設定できます。

ユーザー定義ブリッジネットワークを作成するには docker network create コマンドを使います。

bash
docker network create my-app-network

コンテナをこのネットワークに接続するには、docker run 時に --network オプションを指定します。

bash
docker run -d --name db --network my-app-network postgres:13
docker run -d --name app --network my-app-network -p 5000:5000 my-python-app:1.0

この例では、dbコンテナとappコンテナがmy-app-networkという同じネットワークに接続されています。

appコンテナからdbコンテナに接続したい場合、ホスト名としてdb(コンテナ名)を指定すれば名前解決され、dbコンテナのIPアドレスを取得できます。これにより、アプリケーションコード内でハードコーディングするIPアドレスを気にする必要がなくなります。

コンテナを複数のネットワークに接続する

1つのコンテナを複数のネットワークに接続することも可能です。例えば、インターネットに接続するためのネットワークと、特定のサービスとだけ通信するための隔離されたネットワークに接続するといった使い方があります。

コンテナ作成時に --network オプションを複数指定することはできません。docker network connect コマンドを使います。

bash
docker network connect <network_name> <container_idまたはname>

ネットワークの管理

  • docker network ls: ネットワーク一覧を表示
  • docker network inspect <network_name>: 特定のネットワークの詳細情報を表示(接続されているコンテナなど)
  • docker network rm <network_name>: ネットワークを削除

8. Dockerストレージ(永続化)

コンテナのファイルシステムは、イメージの読み取り専用レイヤーの上に書き込み可能なコンテナレイヤーが追加された構造になっています。しかし、このコンテナレイヤーに書き込まれたデータは、コンテナが削除されると失われてしまいます。データベースのデータやログファイルなど、永続化したいデータをコンテナのファイルシステムに直接書き込むのは適切ではありません。

永続化が必要なデータを扱うために、Dockerは以下のストレージオプションを提供します。

1. Volume (ボリューム)

  • Dockerが管理するホストOS上の特別な領域にデータが保存されます。
  • コンテナのライフサイクルから独立しており、コンテナを停止・削除してもVolumeのデータは保持されます。
  • 複数のコンテナで同じVolumeを共有できます。
  • ホストOS上の物理的なパスを意識する必要がなく、Dockerがよしなに管理してくれます。
  • 最も推奨される永続化の方法です。

Volumeの作成:

bash
docker volume create my-db-data

Volumeをコンテナにマウント:

docker run 時に -v <volume_name>:<container_path> または --mount type=volume,source=<volume_name>,target=<container_path> オプションを使います。

“`bash

短い形式

docker run -d –name db -v my-db-data:/var/lib/postgresql/data postgres:13

長い形式 (推奨)

docker run -d –name db –mount type=volume,source=my-db-data,target=/var/lib/postgresql/data postgres:13
“`

この例では、my-db-dataという名前のVolumeを、コンテナ内の/var/lib/postgresql/data(PostgreSQLがデータを保存するデフォルトパス)にマウントしています。

匿名Volume:

Volume名を指定せずに -v /path/in/container のように指定すると、名前のない匿名Volumeが自動的に作成され、マウントされます。この場合、Dockerがランダムな名前を付けて管理します。

Volumeの管理:

  • docker volume ls: Volume一覧を表示
  • docker volume inspect <volume_name>: Volumeの詳細情報を表示
  • docker volume rm <volume_name>: Volumeを削除 (使用中のVolumeは削除できない)
  • docker volume prune: 使用されていないすべてのVolumeを削除

2. Bind Mount (バインドマウント)

  • ホストOS上の任意のファイルやディレクトリを、コンテナ内の特定のパスにマウントします。
  • コンテナ内の変更はホストOS上の元のファイルやディレクトリに直接反映されます。
  • 開発中にソースコードをマウントして、コンテナの再ビルドなしに変更を反映させたり、ホストOS上の設定ファイルをコンテナに共有したりするのに便利です。
  • Volumeと異なり、ホストOSのファイルシステム構造やパスを意識する必要があります。

Bind Mountをコンテナにマウント:

docker run 時に -v <host_path>:<container_path> または --mount type=bind,source=<host_path>,target=<container_path> オプションを使います。<host_path>は絶対パスで指定する必要があります。

“`bash

短い形式

docker run -d -p 5000:5000 -v /path/to/your/app:/app my-python-app:1.0

長い形式 (推奨)

docker run -d -p 5000:5000 –mount type=bind,source=/path/to/your/app,target=/app my-python-app:1.0
“`

この例では、ホストOS上の/path/to/your/appディレクトリを、コンテナ内の/appディレクトリにマウントしています。これにより、ホストOS上のソースコードを変更すると、コンテナ内のコードも即座に更新され、アプリケーションの再起動やリロード(ホットリロード機能があれば)によって変更を確認できます。

3. tmpfs mount (一時マウント)

  • コンテナのライフサイクル中だけ、ホストOSのメモリにデータを保存します。
  • コンテナが停止するとデータは失われます。
  • セキュリティ上、コンテナレイヤーやVolumeに書き込みたくない一時的なデータ(例: 認証情報、一時ファイル)や、パフォーマンスが求められるI/O負荷の高い処理に利用されます。

tmpfs mountをコンテナにマウント:

docker run 時に --tmpfs <container_path> または --mount type=tmpfs,target=<container_path> オプションを使います。

“`bash
docker run -d –tmpfs /app/cache my-app:latest

docker run -d –mount type=tmpfs,target=/app/cache,tmpfs-size=1000000,tmpfs-mode=1777 my-app:latest # サイズやパーミッションも指定可能
“`

どのストレージを使うべきか?

  • Volume: 永続化が必要なアプリケーションデータ(データベースデータ、ログファイルなど)に最も適しています。Dockerが管理するため移植性が高く、パフォーマンスも優れています。
  • Bind Mount: 開発中にホスト上のソースコードをマウントしてホットリロードを有効にしたり、コンテナとホスト間で設定ファイルやバイナリなどを共有したりするのに適しています。
  • tmpfs mount: 機密性の高い一時データや、高いI/O性能が必要な一時ファイルに適しています。コンテナが停止するとデータは消えます。

9. Docker Composeを使った複数コンテナ管理

実際のアプリケーションは、通常、複数のサービス(Webサーバー、アプリケーションサーバー、データベース、キャッシュサーバーなど)で構成されています。これらのサービスをそれぞれ個別のコンテナとして実行し、連携させる必要があります。

これまでの知識だけでは、それぞれのコンテナを個別に docker run コマンドで起動し、ネットワークやVolumeの設定を手動で行う必要があり、非常に煩雑です。また、コンテナ間の起動順序の依存関係なども考慮する必要があります。

このような複数コンテナで構成されるアプリケーションを簡単に定義、起動、管理するためのツールがDocker Composeです。

Docker Composeとは?

Docker Composeは、YAMLファイルを使ってアプリケーションのサービス構成を定義し、その定義に基づいて複数のコンテナを一括で管理するツールです。

Docker Composeを使うことで、以下のことが可能になります。

  • アプリケーションを構成する各サービス(コンテナ)をYAMLファイルで定義する。
  • サービス間の依存関係(例: アプリケーションサーバーはデータベースより後に起動する)を指定する。
  • 各サービスが使用するDockerイメージ、ポートマッピング、Volume、ネットワークなどをまとめて設定する。
  • 定義ファイル1つで、アプリケーション全体(複数コンテナ)をまとめて起動、停止、再構築できる。

Docker Composeのインストール

Docker Desktopをインストールしていれば、通常Docker Composeも一緒にインストールされています。LinuxにDocker Engine単体でインストールした場合は、別途Docker Composeをインストールする必要があります。

インストールされているか確認します。

“`bash
docker compose version

または古いバージョンでは docker-compose version

“`

docker-compose.yml ファイルの書き方

Docker Composeの設定は、通常 docker-compose.yml という名前のYAMLファイルに記述します。

以下は、シンプルなWebアプリケーション(Flask + PostgreSQL)をDocker Composeで起動する例です。

“`yaml

docker-compose.yml

Composeファイルのバージョンを指定 (最新版の3系が推奨)

version: ‘3.8’

アプリケーションを構成する各サービスを定義

services:
# webサービス (Webアプリケーションコンテナ)
web:
# 使用するイメージ (Dockerfileを使って自分でビルドする場合)
build: . # Dockerfileがカレントディレクトリにあることを示す

# または、公開されているイメージを使う場合
# image: my-python-app:1.0

# ポートマッピング (ホストポート:コンテナポート)
ports:
  - "5000:5000"

# Volumeのマウント (ホストパスまたはVolume名:コンテナパス)
volumes:
  - .:/app # ソースコードをマウント (開発時向け)
  # または、永続化Volumeを使用する場合
  # - web_data:/app/data

# 環境変数を設定
environment:
  DATABASE_URL: postgres://user:password@db:5432/mydatabase
  FLASK_ENV: development # 開発モード有効

# 依存関係の定義
# このサービスはdbサービスが起動してから起動する
depends_on:
  - db

# デフォルトネットワークに加えて、ユーザー定義ネットワークに参加
networks:
  - app-network

# dbサービス (データベースコンテナ)
db:
# 使用するイメージ
image: postgres:13

# 環境変数 (PostgreSQLの設定)
environment:
  POSTGRES_DB: mydatabase
  POSTGRES_USER: user
  POSTGRES_PASSWORD: password

# Volumeのマウント (データベースデータを永続化)
volumes:
  - db_data:/var/lib/postgresql/data

# ユーザー定義ネットワークに参加
networks:
  - app-network

Volumeの定義 (名前付きVolume)

volumes:
db_data:
# web_data: # 必要に応じて定義

ネットワークの定義 (ユーザー定義ネットワーク)

networks:
app-network:
driver: bridge # ブリッジネットワークを使用
“`

このdocker-compose.ymlファイルでは、以下の要素を定義しています。

  • version: Composeファイルのバージョン。
  • services: アプリケーションを構成する各サービス(コンテナ)を定義するセクション。
    • web: Flaskアプリケーションのサービス。build: . でカレントディレクトリのDockerfileを使ってイメージをビルドし、そのイメージでコンテナを起動します。ポート5000をホストOSに公開し、ソースコードをBind Mountしています。depends_on: dbdbサービスに依存していることを示しています。
    • db: PostgreSQLデータベースのサービス。image: postgres:13 でDocker Hubから公式のPostgreSQL 13イメージを取得して使います。環境変数でデータベース名やユーザー、パスワードを設定し、db_dataという名前付きVolumeをマウントしてデータベースデータを永続化しています。
  • volumes: 名前付きVolumeを定義するセクション。ここで定義したVolumeを各サービスのvolumesで参照してマウントします。
  • networks: ユーザー定義ネットワークを定義するセクション。app-networkという名前のブリッジネットワークを作成し、services内のnetworksで指定することで、webdbコンテナがこのネットワークに参加できるようにしています。同じネットワーク内のコンテナは、サービス名(例: db)で名前解決して通信できます。

Docker Composeの基本的なコマンド

docker-compose.ymlファイルが置かれているディレクトリで、以下のコマンドを実行します。

  • アプリケーション全体を起動する (docker compose up)

    • docker-compose.ymlファイルを読み込み、定義されているサービスを順番に起動します。
    • 必要であれば、buildで指定されたイメージをビルドし、Volumeやネットワークを作成します。
    • -d オプションを付けると、デタッチドモード(バックグラウンド実行)で起動できます。
    • docker compose up (フォアグラウンド)
    • docker compose up -d (バックグラウンド)
  • アプリケーション全体を停止し、コンテナを削除する (docker compose down)

    • docker compose upで起動したすべてのコンテナを停止し、削除します。
    • デフォルトではネットワークも削除されますが、Volumeは削除されません。
    • Volumeも一緒に削除したい場合は --volumes オプションを付けます。
    • docker compose down
    • docker compose down --volumes
  • サービスのイメージを再構築する (docker compose build)

    • buildが指定されているサービスのイメージを再構築します。
    • docker compose build
  • 実行中のサービス一覧を表示する (docker compose ps)

    • docker-compose.ymlで定義されているサービスの現在の状態を表示します。
    • docker compose ps
  • 特定のサービス内でコマンドを実行する (docker compose exec)

    • 実行中の特定のサービスコンテナ内でコマンドを実行します。
    • docker compose exec web bash (webサービスコンテナ内でbashシェルを起動)
  • 特定のサービスのログを表示する (docker compose logs)

    • 特定のサービスまたはすべてのサービスのログを表示します。
    • docker compose logs web (webサービスコンテナのログを表示)
    • docker compose logs (すべてのサービスコンテナのログを表示)
    • -f オプションでリアルタイム追尾も可能です。

Docker Composeを使うことで、複雑な複数コンテナ構成のアプリケーションも、YAMLファイル1つで簡単に管理できるようになり、開発環境の構築やチームメンバー間での環境共有が非常に効率的になります。

10. Dockerの応用例とエコシステム

Dockerは開発環境だけでなく、様々な場面で活用されています。

  • 開発環境の標準化: チームメンバー全員が同じバージョンのOS、ミドルウェア、ライブラリで開発できるようになり、「Works on my machine」問題を解消します。新しいメンバーのオンボーディングも容易になります。
  • CI/CDパイプライン:
    • ビルド: アプリケーションのビルドやテストを、クリーンで一貫性のあるコンテナ環境で行えます。
    • テスト: テスト環境をコンテナで用意することで、隔離された環境で安定したテストを実行できます。
    • デプロイ: アプリケーションをコンテナイメージとしてパッケージングすることで、どの環境(開発、ステージング、本番)にも同じイメージをデプロイできるようになります。これにより、デプロイの信頼性が向上します。
  • マイクロサービスアーキテクチャ: アプリケーションを小さな独立したサービスに分割するマイクロサービスにおいて、各サービスをコンテナとしてデプロイ・管理することは非常に有効です。コンテナはサービス間の隔離を提供し、各サービスが異なる技術スタック(言語、フレームワーク)を使用していても容易に統合できます。
  • 本番環境での利用: 単一のDockerホストだけでなく、複数のホストにわたってコンテナをデプロイ・管理するためのオーケストレーションツールと組み合わせて、本番環境でスケーラブルかつ高可用なシステムを構築できます。代表的なツールとしては、KubernetesDocker Swarmがあります。
  • データサイエンスと機械学習: 特定のライブラリやフレームワーク(TensorFlow, PyTorchなど)がインストールされた環境をコンテナとして配布することで、環境構築の手間を省き、再現性のある実験環境を提供できます。
  • 古いアプリケーションの実行: サポート切れのOSやライブラリに依存する古いアプリケーションを、現代のOS上でコンテナとして実行できます。

Dockerを中心としたエコシステムには、Docker HubのようなRegistry、Kubernetesのようなオーケストレーター、各種クラウドサービス(AWS ECS/EKS, Google GKE, Azure AKSなど)との連携、コンテナセキュリティツールなど、様々なツールやサービスが存在し、コンテナベースのシステム開発・運用を強力にサポートしています。

11. Dockerを使う上での注意点とベストプラクティス

Dockerを効果的に、そして安全に利用するためには、いくつかの注意点とベストプラクティスがあります。

  • イメージサイズの最適化: イメージサイズが大きいと、ダウンロードやデプロイに時間がかかります。
    • 軽量なベースイメージを選択する(例: Alpine Linux, slim版の公式イメージ)。
    • 不要なファイルやパッケージはインストールしない。
    • .dockerignoreを活用する。
    • マルチステージビルドを活用し、最終イメージに必要なものだけを含める。
    • RUN命令で複数のコマンドを && で繋げ、一時ファイルを削除する(例: apt-get update && apt-get install ... && rm -rf /var/lib/apt/lists/*)。
  • セキュリティ:
    • 最小限の権限でコンテナを実行する(rootless Dockerなど)。
    • 不要なポートは公開しない。
    • 機密情報(パスワード、APIキーなど)はDockerfileや環境変数に直接書かず、Docker SecretsやKubernetes Secretsなどの秘密情報管理ツールを利用する。
    • イメージの脆弱性スキャンを行う。
    • 信頼できるソースからのイメージのみを使用する。
  • ログ管理: コンテナのログは docker logs で確認できますが、本番環境では集中ログ管理システム(Fluentd, Logstash, Splunkなど)に連携させる設定を行うのが一般的です。
  • リソース制限: コンテナが無制限にホストのリソース(CPU, メモリ)を使用しないように、docker run や Docker Compose でリソース制限を設定することを検討しましょう(--cpus, --memory オプションなど)。
  • Dockerfileのベストプラクティス:
    • 各命令は1つの責務を持つようにする。
    • 変更頻度の低い命令を先に記述し、キャッシュを有効活用する。
    • RUN命令は可能な限り結合し、レイヤー数を減らす(ただし、長すぎるとキャッシュ効率が落ちるトレードオフあり)。
    • 明確なタグ付けを行う。
    • 公式イメージや信頼できるイメージをベースにする。
    • 実行ユーザーをroot以外にする。
  • データの永続化: 永続化が必要なデータは必ずVolumeに保存する。Bind Mountは主に開発目的で使用する。
  • ネットワーク: コンテナ間の通信にはユーザー定義ネットワークを使用し、サービス名での名前解決を活用する。
  • Docker Compose: 複数コンテナで構成されるアプリケーションはDocker Composeで管理する。本番環境ではKubernetesなどのオーケストレーターへの移行を検討する。

12. まとめと次のステップ

本記事では、Dockerの基礎から応用まで、幅広く解説しました。

  • Dockerがなぜ必要とされているのか、VMとの比較でコンテナ技術の軽量性・ポータビリティを理解しました。
  • イメージ、コンテナ、Dockerfile、RegistryといったDockerの主要な概念とアーキテクチャを学びました。
  • docker run, docker ps, docker stop, docker rmといった基本的なコンテナ操作コマンドを習得しました。
  • docker pull, docker images, docker rmiといったイメージ操作コマンドを学びました。
  • Dockerfileを使って独自のイメージを作成する方法と、主要な命令、.dockerignore、マルチステージビルドといったテクニックを学びました。
  • Dockerネットワークの種類と、ユーザー定義ネットワークを使ったコンテナ間の通信方法を理解しました。
  • Volume, Bind Mount, tmpfs mountといったストレージオプションと、永続化の重要性を学びました。
  • Docker Composeを使って複数コンテナをまとめて管理する方法を学び、docker-compose.ymlの基本的な書き方とコマンドを知りました。
  • Dockerが開発、CI/CD、マイクロサービス、本番環境など、様々な場面で活用されていることを知りました。
  • イメージサイズ最適化やセキュリティなど、Dockerを利用する上での注意点とベストプラクティスを確認しました。

Dockerは、アプリケーションの開発・デプロイ・運用のワークフローを大きく変革する強力なツールです。本記事で学んだ基礎知識を元に、ぜひ実際に手を動かして様々なコンテナを試してみてください。

次のステップ

Dockerの基礎を理解したら、さらに以下のトピックを学ぶことで、より実践的なスキルを身につけることができます。

  • 高度なDockerfileのテクニック: ベストプラクティスをさらに深掘りし、より効率的で安全なイメージ作成をマスターする。
  • Docker Networkingの詳細: Overlayネットワーク、Macvlanネットワークなど、より高度なネットワーク構成について学ぶ。
  • Docker Storageの詳細: Volumeドライバーやストレージドライバーについて学ぶ。
  • 本番環境での運用: コンテナ監視、ログ収集、オートスケーリングなど、運用に関する知識を深める。
  • コンテナオーケストレーションツール: KubernetesまたはDocker Swarmを学び、複数のサーバーでコンテナを効率的に管理・運用する方法を習得する。特にKubernetesはデファクトスタンダードとなりつつあります。
  • クラウドサービスのコンテナ関連サービス: AWS ECS/EKS, Google GKE/Cloud Run, Azure AKS/ACRといった各クラウドベンダーが提供するコンテナサービスを利用してみる。
  • コンテナセキュリティ: コンテナの脆弱性、ランタイムセキュリティ、オーケストレーションレイヤーのセキュリティなどについて深く学ぶ。

Dockerの世界は非常に広く、常に進化しています。本記事が、その広大な世界への最初の扉を開く手助けとなれば幸いです。頑張ってください!


コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール