【3-2】Docker実践入門!基本コマンドからデータ永続化・ネットワーク・連携まで
はじめに
前回はDockerの概念整理とインストールを完了し、docker run hello-worldが成功するところまで進めました。今回は日常的に使うための基本コマンド(イメージ取得、コンテナ起動・停止・削除、ログ確認、リソース監視)を実践しつつ、実運用のキモである「データの永続化(Volume)」と「コンテナ間通信(Network)」まで到達します。最後にMySQLとphpMyAdminの連携で総仕上げを行います。
1. イメージ操作: アプリの"ミールキット"を取り寄せる
まずは公式レジストリ(Docker Hub)から必要なイメージを取得し、一覧を確認します。
1.1 イメージのダウンロード(pull)
docker pullコマンドで、必要なイメージをローカルに持ってきます。
# 軽量なWebサーバーであるNginxのイメージを取得
docker pull nginx
# OSのイメージをバージョン(タグ)を指定して取得
docker pull ubuntu:22.04
# 非常に軽量なLinuxディストリビューションであるAlpineの最新版を取得
docker pull alpine:latest
補足: `pull` したイメージはどこに保存される?
docker pull でダウンロードしたイメージは、一体どこに保存されるのでしょうか?
答えは、「Dockerが動作しているマシン(ホストマシン)のローカルストレージ」です。
1. ホストマシンとは?
今回の記事の手順で言えば、「Docker Engineをインストールした Ubuntu Server 22.04」そのものを指します。このDocker本体が稼働しているOSを「ホストOS」や「ホストマシン」と呼びます。
2. 具体的な保存場所
Linux(Ubuntu)の場合、デフォルトでは以下のディレクトリに、イメージのデータがレイヤー構造として保存されます。
/var/lib/docker
このディレクトリは、Docker Engineをインストールした際に自動的に作成され、pull したイメージの他にも、作成したコンテナやボリューム(データを永続化する領域)など、Dockerが管理するあらゆるデータの実体が格納される場所です。
3. Dockerの仕組み
少し補足すると、Docker EngineはインストールされるとOSの「サービス」として登録され、OSの起動と同時に自動的に裏側で動き始めます(デーモンとして常駐します)。
私たちが docker pull や docker run といった docker コマンドを実行すると、その命令がこの裏側で動いている「Docker Engine」に送られ、Engineが実際の処理(イメージのダウンロードやコンテナの起動)を行い、結果を /var/lib/docker に書き込んでいるのです。
なお、このディレクトリを私たちが直接操作することはほとんどありません。ダウンロード済みのイメージを確認したい場合は、docker images コマンド(または docker image ls)を使用します。
補足: /var/lib/docker というパスの意味は?
docker pull したイメージの保存先が /var/lib/docker であると説明しましたが、このパス(ディレクトリ名)にはどのような意味があるのでしょうか?
これはLinuxの標準的なディレクトリ構成(FHS: Filesystem Hierarchy Standard)のルールに基づいています。
📁 /var (Variable)
「Variable(可変的な)」の略です。
システムの動作中に、内容が頻繁に変わる(増えたり、書き換わったりする)データを格納する場所です。
-
/var/log: システムの動作記録(ログ) -
/var/cache: 一時的なキャッシュデータ -
/var/lib: 各アプリケーションの状態データ
📚 /lib (Library)
「Library(ライブラリ)」の略です。
OSの動作に不可欠な「共有ライブラリ(プログラムの共通部品)」が格納される場所です。
🧩 /var/lib の意味
ここで「あれ?」と思うかもしれません。/var/lib は「可変的なライブラリ?」と。
この場合の lib は、OSの部品(/lib)という意味ではなく、「アプリケーションが使用する状態ファイル(state files)」というニュアンスで使われています。
つまり、/var/lib は「システムが動作する上で、各アプリケーションが使用する『可変的な状態(データ)』を保存しておくディレクトリ」という意味になります。
🐳 /var/lib/docker
したがって、/var/lib/docker は、
「Dockerというアプリケーションが、動作する上で必要とする『可変的な状態ファイル(ダウンロードしたイメージ、作成したコンテナなど)』を置いておく場所」
という意味になります。Linuxのルールに則った、非常に理にかなった保存場所なのです。
1.2 イメージの一覧確認(images)
ダウンロードしたイメージはdocker imagesで確認できます。
docker images
実行結果の例:
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest xxxxxxxxxxxx 2 weeks ago 142MB
ubuntu 22.04 xxxxxxxxxxxx 3 weeks ago 77.8MB
alpine latest xxxxxxxxxxxx 4 weeks ago 7.34MB
hello-world latest xxxxxxxxxxxx 5 months ago 9.14kB
Alpine Linuxの圧倒的な小ささが分かりますね。
2. コンテナのライフサイクル管理
ここからは、イメージを元にコンテナを実際に動かし、その起動から削除までを管理していきます。
2.1 バックグラウンド実行: Nginx Webサーバーの起動
Webサーバーのように裏側で動き続けてほしいアプリケーションは、-dオプションでバックグラウンド実行します。
docker run -d --name my-nginx -p 8080:80 nginx
-
-d: バックグラウンドで実行 -
--name my-nginx: 分かりやすい名前を付与 -
-p 8080:80: ホスト(例: us01)の8080番ポートからコンテナの80番へ転送
ブラウザで http://192.168.3.101:8080 にアクセスして確認してみます。
【実践トラブル①】ERR_SSL_PROTOCOL_ERRORで繋がらない?
ここで早速トラブル発生。ブラウザに「このサイトは安全に接続できません」というエラーが表示されてしまいました。
エラーコードはERR_SSL_PROTOCOL_ERROR。これは、ブラウザが暗号化されたHTTPS通信を試みている証拠です。しかし、私たちが起動したNginxコンテナは、暗号化されていないHTTP通信で待ち受けています。
これは、ブラウザがURLを自動的にhttps://に書き換えてしまったことが原因でした。
解決策として、ブラウザのプライベートウィンドウ(シークレットモード)を開き、改めてhttp://192.168.3.101:8080と手入力することで、無事にNginxの歓迎ページが表示されました。単純なミスですが、非常に良い学びとなりました。
2.2 対話モード実行: コンテナの中に入る
次に、コンテナの中に入って直接コマンドを操作してみます。-itオプションを使います。
docker run -it --name my-ubuntu ubuntu:22.04 /bin/bash
-
-it: コンテナと対話的に操作するためのオプションです。-
-i(インタラクティブ): ホストのキーボード入力をコンテナに接続します。 -
-t(TTY): コマンド入力(lsなど)を受け付けるための仮想ターミナル画面をコンテナ内に用意します。 - この2つを合わせて「コンテナの中に入って操作する」ために指定します。
-
-
/bin/bash: コンテナ内で最初に実行するコマンドです。/bin/bash(シェル)を指定することで、私たちがコマンドを入力できる状態(プロンプト)で起動します。
【実践トラブル②】コンテナ名が既に使用されている?
Conflict. The container name "/my-ubuntu" is already in use...と表示された場合、以前のコンテナが「停止」状態で残っている可能性があります。
解決手順:
-
docker ps -aで停止中も含め一覧確認 -
docker rm my-ubuntuで古いコンテナを削除 -
docker run -it ...を再実行
「停止」と「削除」は別操作である点を理解できました。
プロンプトがroot@xxxxxxxxxxxx:/#に変われば成功です。lsやcat /etc/os-releaseを試し、exitで抜けます。
2.3 状態確認・ログ・制御(ps, logs, stop/start)
コンテナの状態を確認し、制御する方法を学びます。
# 実行中のコンテナを確認
docker ps
# 停止中のコンテナも含めて確認
docker ps -a
# my-nginxのアクセスログを確認
docker logs my-nginx
# my-nginxを停止
docker stop my-nginx
# 停止したmy-nginxを再開
docker start my-nginx
2.4 後片付け(rm, rmi)
不要になったコンテナとイメージを削除します。
# 停止しているmy-ubuntuコンテナを削除
docker rm my-ubuntu
# ubuntu:22.04イメージを削除
docker rmi ubuntu:22.04
注意: 実行中もしくは依存コンテナがあるイメージは削除できません。先にコンテナをrmしましょう。
2.5 リソース監視(stats)
最後に、実行中のコンテナがどれくらいリソースを使っているか見てみましょう。
docker stats
CPUやメモリ使用状況をリアルタイム表示します。Ctrl+Cで終了します。
3. データの永続化: Docker Volumeを使いこなす
コンテナは本来、破棄と作成を繰り返す「使い捨て」の思想で作られています(これをImmutableと言います)。しかし、データベースのデータやユーザーがアップロードしたファイルなど、消えては困るデータはどうすればよいのでしょうか。その答えが「ボリューム」です。
ボリュームは、Dockerが管理する、コンテナとは独立した専用のデータ保管領域です。
3.1 ボリュームの作成とマウント
まずはボリュームを作成し、コンテナに接続(マウント)してみましょう。
# "my-data"という名前のボリュームを作成
docker volume create my-data
# ボリュームをコンテナ内の/dataディレクトリに接続して起動
docker run -it --name data-test -v my-data:/data ubuntu:22.04
-
-v my-data:/data: 「my-dataボリュームをコンテナの/dataへマウント」という意味。/dataへのI/Oはホスト側のmy-dataへ保存されます。
補足: なぜデータは消える?「書き込みレイヤー」と「ボリューム」の違い
「コンテナを削除してもデータを保存する」ためにボリュームを使いますが、もしボリュームを使わなかった場合、データはどこへ行き、なぜ消えてしまうのでしょうか。
その答えは、データが「コンテナの書き込み可能レイヤー」に保存されるためです。
1. データが消える仕組み: コンテナの「書き込み可能レイヤー」
Dockerコンテナのファイルシステムは、複数の「レイヤー(層)」が重なってできています。
-
イメージレイヤー (読み取り専用)
-
docker pullしたイメージ(例:ubuntu:22.04)のことです。 - これらの実体は
/var/lib/dockerの中(例:overlay2ディレクトリ配下)に保存されています。
-
-
書き込み可能レイヤー (コンテナ専用)
-
docker runすると、イメージレイヤーの一番上に、そのコンテナ専用の「書き込み可能な層」が新しく作られます。(これも/var/lib/docker/overlay2配下に作られます)
-
ボリュームを指定せずにコンテナ内でファイルを作成すると、そのデータはすべてこの「書き込み可能レイヤー」に保存されます。
このレイヤーはコンテナ本体と論理的に一体化しています。そのため、docker rm でコンテナを削除すると、このレイヤーも一緒に完全に削除されてしまうのです。
2. データが残る仕組み: 「ボリューム」は外部ストレージ
docker volume create my-data で作成するボリュームは、コンテナのレイヤー構造とは全く別の場所に作られる「Dockerが管理するデータ専用の保管庫」です。
-
実体: ホストマシン上(例:
/var/lib/docker/volumes/my-data/_data)
docker run -v my-data:/data ... のように指定すると、Dockerはこの「外部の保管庫」をコンテナ内の /data ディレクトリに**接続(マウント)**します。
この仕組みは、PC(コンテナ)本体のSSD(書き込み可能レイヤー)にデータを保存するのではなく、**外付けUSBドライブ(ボリューム)**をPCに接続し、そこにデータを保存するイメージに近いです。
PC本体(コンテナ)を廃棄(docker rm)しても、外付けUSBドライブ(ボリューム)は手元に残るため、データは安全に永続化されます。
3.2 データが消えないことを確認
コンテナ内でテストファイルを作成し、コンテナを削除してもデータが残ることを確認します。
# コンテナ内でファイルを作成
root@xxxxxxxx:/# echo "persistent data" > /data/test.txt
root@xxxxxxxx:/# exit
# コンテナを削除
adm-labuser@us01:~$ docker rm data-test
# 別の新しいコンテナを、同じボリュームに接続して起動
adm-labuser@us01:~$ docker run -it --name data-test2 -v my-data:/data ubuntu:22.04
# 新しいコンテナ内でファイルが残っているか確認
root@yyyyyyyy:/# cat /data/test.txt
persistent data
persistent dataと表示されました!コンテナを削除してもボリューム内のデータは残ります。実運用で必須の機能です。
4. コンテナ間の通信: Docker Networkで連携する
実際のアプリケーションは、Webサーバー、APサーバー、DBサーバーのように、複数のコンテナが連携して動作します。これらのコンテナ同士が安全に通信するために、Dockerは専用の仮想ネットワークを作成する機能を提供しています。
4.1 ネットワーク作成とコンテナ接続
# "my-network"という名前のネットワークを作成
docker network create my-network
# 2つのNginxコンテナを同じネットワークに接続して起動
docker run -d --name web1 --network my-network nginx
docker run -d --name web2 --network my-network nginx
# web1からweb2へ"名前"で疎通確認
docker exec web1 ping web2
docker execは実行中コンテナ内でコマンドを実行する命令です。このpingコマンドで、またしても学びの深いトラブルに遭遇しました。
【実践トラブル③】ping: command not found?
pingを実行するとexecutable file not foundというエラーが出ました。ネットワークの問題ではなく、Nginxコンテナの中にpingコマンド自体が存在しないのが原因でした。
これは、Dockerの公式イメージが 「軽量性」と「安全性」 のために、アプリケーションの実行に不要なツールを意図的に削ぎ落として作られているためです。汎用的なUbuntuイメージとは思想が違うのです。
解決策:
docker exec -it web1 /bin/bashでコンテナの中に入り、apt-get update && apt-get install -y iputils-pingを実行して、手動でpingコマンドをインストールしました。(apt-getを使ったのは、最小構成のイメージとの互換性を考慮してのことです。)
これにより、無事にweb2への疎通を確認できました。「Dockerイメージは最小構成である」という重要な原則を学ぶ良い機会となりました。
補足: DockerネットワークとLinux Bridgeの仕組み
なぜ docker network create を使うと、コンテナ同士が名前で通信できるのでしょうか?
これは、DockerがLinuxの「Network Namespace(ネットワーク名前空間)」と「Linux Bridge(仮想スイッチ)」という技術を使って、コンテナ専用の仮想LANを作っているからです。
1. 隔離されたネットワーク空間
docker run でコンテナを起動すると、各コンテナは「隔離された専用のネットワーク環境(Network Namespace)」を持ちます。
デフォルトでは、各コンテナは他のコンテナやホストOSとは切り離されています。
2. 仮想スイッチ (Linux Bridge) の役割
Dockerは、これらの隔離されたコンテナ同士を通信させるために、「仮想的なネットワークスイッチ」(実体はホストOS上に作られる Linux Bridge)を使います。
(A) デフォルトの仮想スイッチ (bridge)
Dockerをインストールすると、docker0 という名前で知られる「デフォルトのブリッジ」が自動的に作成されます。
-
docker runの際に--networkオプションを何も指定しないと、コンテナはこの「デフォルトブリッジ」に接続されます。 - 欠点: このネットワークに接続されたコンテナは、他のコンテナのIPアドレスを直接指定しないと通信できません。コンテナ名での通信(名前解決)が自動ではサポートされていません。
(B) ユーザー定義の仮想スイッチ (my-network)
docker network create my-network を実行すると、docker0 とは別に、my-network という名前の**新しい仮想スイッチ(Linux Bridge)**がホストOS上に作成されます。
-
docker run --network my-network ...と指定すると、コンテナ(web1やweb2)は、このmy-networkスイッチに接続されます。 - 最大の利点: このユーザー定義のネットワークには、DockerによるDNSサーバーが内蔵されています。
- これにより、同じネットワーク(
my-network)に接続されたコンテナは、IPアドレスを知らなくても、お互いを「コンテナ名」(web1,web2)で呼び出すことができるのです。
docker exec web1 ping web2 が成功したのは、web1 と web2 がDNS機能付きの my-network という同じ仮想LANに参加していたから、というわけです。
5. 実用例: MySQLとphpMyAdminの連携
最後に、学んだ知識の集大成として、データベース(MySQL)と管理ツール(phpMyAdmin)の2つのコンテナを連携させます。
# MySQLコンテナを起動。-eで初期パスワードを設定
docker run -d --name my-mysql -e MYSQL_ROOT_PASSWORD=password mysql:8.0
# phpMyAdminコンテナを起動し、MySQLコンテナにリンク。ポートを8081に公開
docker run -d --name my-phpmyadmin --link my-mysql:db -p 8081:80 phpmyadmin/phpmyadmin
-
-e: コンテナへ環境変数を渡す(ここではMySQLのrootパスワード) -
--link my-mysql:db: phpMyAdmin側からdbというホスト名でmy-mysqlへ接続可能- 注:
--linkは古い機能。実務では前述のdocker networkで同一ネットワークに接続し、ホスト名解決する方法が推奨
- 注:
ブラウザでhttp://192.168.3.101:8081にアクセスし、サーバーdb、ユーザーroot、パスワードpasswordでログインできれば成功です!
まとめ
これでコンテナを自由に操作する土台は整いました。しかし、毎回長大なdocker runコマンドをいくつも手で打つのは現実的ではありません。
次回は、複数コンテナの構成をYAMLで一元管理できるDocker Composeを扱い、手入力のdocker run地獄から脱却します。記事の中で定義、依存関係、環境変数、ボリューム/ネットワークの宣言的管理まで段階的に実践していきます!
Discussion