Dockerコンテナグレートジャーニー
Dockerコンテナを0から理解する旅、Dockerコンテナ・グレートジャーニー第3回です。(本記事だけ読んでも大丈夫です)
今回はDockerコンテナを運用するうえで超重要な『ストレージ』について、イメージと実践を交えて解説します。
Dockerコンテナの場合、適当に使っているといきなりデータが消し飛ぶこともあるので、Docker初心者の方も、深く知らずに使っている方も、この機会にDockerストレージについて学んでいきましょう。
対象
- Dockerコンテナのストレージについてよくわからない人
- Dockerコンテナの揮発性について知らない人
- bindマウントとvolumeマウントの違いがわからない人
旅路(インデックス)
長いので記事を分割しています。
- そもそも仮想化とは? 仮想化ではないシステムとは?
- Dockerコマンド基礎(環境構築~ubuntu/httpdコンテナでのコマンド実行)
- Dockerのストレージについて【⇦本記事】
- Dockerのネットワークについて
- Docker-compose でコンテナをまとめる
- Docker image と Dockerfile
- AWS ECS で Dockerコンテナを走らせる(Comming soon)
【主題】dockerのストレージについて
dockerコンテナ内で保存/編集したデータは揮発性!
「Dockerコンテナのデータが消える」と言いましたが、それはどのようなことを言っているのでしょうか?
その理由を1枚の図にまとめるとこんな感じです。
- コンテナは、一度削除(rm)すると復元できない
DockerにはWindowsの「ごみばこ」のような機能はありません。 - docker run などで次に作ったコンテナは、以前作ったものとは完全に別物
新しいコンテナ2を立ち上げても、それは最初のコンテナとは全く別のもので、前回の変更を引き継ぎません。 - Dockerコンテナで行った編集は、Imageには反映されない
コンテナにいくら編集を施しても、その設計図たるImageには変更が反映されません。コンテナは形ある実体、対してImageとは設計図のようなものだからです。(オブジェクト指向で言うなら「クラスとインスタンス」と同じ関係性です)
こういった理由で、コンテナを一度破棄すると編集データが消えてしまい、復元する手段が無いのです。
ちょっとしたタスクに使うコンテナなら別に問題ないですが、例えばMySQLコンテナを立ち上げて重要データを保存していると、そのコンテナが落ちた時にデータが全て消えるのです。それはまずいですよね。
とはいえ実感がわかない方もいると思いますし、まずは実際にデータが消えるのかを実践してみましょう。
初めての方はぜひ実際に手を動かして試してみてください。
そんなことは知ってるよという方は、次章の「ストレージを永続化する方法」までスキップしてください。
1. Ubuntu内のデータを編集する
Powershellを立ち上げて、Dockerコマンドでubuntuコンテナを起動、vim(エディタ)を使ってみます。
# Ubuntuコンテナを作成(ローカルにイメージがなければダウンロードが発生する)
PS C:\Users\******> docker container run -it --name ubuntu22 ubuntu:22.04
root@acc351c001c4:/#
# vim でテキストファイルを編集...
root@acc351c001c4:/# vim usr/src/test.txt
bash: vim: command not found
デフォルトのubuntuにはvimはインストールされていないので使えません。インストールする必要がありますね。下記のコマンドを打ちこんでvimをインストールしましょう。
apt-get update
apt-get install vim
インストール出来たら、今度こそ編集していきます。
vim usr/src/test.txt
するとエディタが立ち上がります。好きな文字を入力して保存してください。
(以下Linux詳しくない方向けの説明)
-
i
を押すと編集モードになります。 - 好きな文字列を入力します
-
Esc
を押して編集モードを抜けます。 -
:wq
を入力してからEnter を押して、編集を保存して閉じます。 - 確認のため、
cat usr/src/test.txt
を実行します。 - 変更が確認出来たら
:q
と入力してEnter。保存せずに終了できます。
2. コンテナを破棄する
「Ctrl+P -> CtrlQ」でコンテナを抜けて、コンテナを破棄しましょう。
docker container stop ubuntu22
docker container rm ubuntu22
一応docker container ls -a
でコンテナが完全に消えていることを確認しておきましょう。
3. もう一度コンテナを作る
# Ubuntuコンテナを再作成
PS C:\Users\******> docker container run -it --name ubuntu22 ubuntu:22.04
root@acc351c001c4:/#
# vim でテキストファイルを編集
root@acc351c001c4:/# vim usr/src/test.txt
bash: vim: command not found
おや、先ほどダウンロードしたはずのvimが無くなっていますね。
念のため作成したファイルも確認してみましょう。
root@be2bf6e9fd1f:/# ls usr/src/
root@be2bf6e9fd1f:/#
予想通り、ファイルが消えてしまいましたね。
4. 結果
これでコンテナを破棄した後に編集データが残らないことがわかりました。
ですが本当にデータが保存できないと不便ですよね。コンテナが落ちることはよくありますし、データを誤って消してしまうこともあるでしょう。
Webサービスを作っていて、不具合のたびにコンテナ内の全データが消し飛んでは大変です。
というわけで、この後からはDockerコンテナのストレージを永続化する方法を解説します。
ストレージを永続化する方法
Dockerでストレージを永続化する方法は、大きく分けて2つあります。
- ホストOS側のストレージを共有する『bindマウント』
- Dockerが管理する領域を共有する『volumeマウント』
bindマウント
bindマウントは、ホストOSとストレージを共有する方式です。
【メリット】
- 設定ファイルの受け渡し
- ホストOSからファイルを操作できる
⇒デバッグに使いやすい
実践
ホストPC側でテスト用フォルダを作成して移動します。その下にbind_strage
のフォルダを作りましょう。
qiita_test/
|- bind_strage/
|- <空>
先ほど作ったコンテナは一度破棄し、新しく作り直します。
docker container run -it --name ubuntu_bind --mount type=bind,src=C:/Users/******/qiita_test/bind_strage,target=/usr/src ubuntu:22.04
# ※***の部分には絶対パスを補完してください
これでbindマウントは完了です。
注意1)LinuxやMacであればsrc=$(pwd)/bind_strage
で絶対パスが指定できるのですが、Windowsを使っている場合は戻り値のパス区切り文字が/
ではなく\
になります。dockerでのパス指定は/
にしなければならないので、フルパスを手動で入力する必要があります。
注意2)type=bind ~ target=/usr/src
の中で空白が入ってしまうとエラーになるので注意してください。
コマンド解説
前回の記事で基本的なコマンドである
docker container run -it --name ubuntu_bind ubuntu:22.04
の動作については説明していました。このコマンドのオプションがそれぞれ何をしているか分からない場合は、前回の記事を参考にしてください。
今回追加された部分はこちらです。
--mount type=bind,src=C:/Users/******/qiita_test/bind_strage,target=/usr/src
上のコマンドが--mount
オプションの1セットです。分解して考えると以下のようになります。
-
--mount
特定のストレージの上に乗る、つまりそこを使えるようにするコマンドです。type, src, target という引数がありますが、これらがセットになっているコマンドです。- type
マウントの種類を、type=bind
,type=volume
のように指定できます。今回はbindマウントを指定しています。 - src
ホストOS側のパスを指定します。bindマウントはホストPCとコンテナ内でストレージを共有しますが、共有するフォルダを指定できます。 - target
src と逆に、コンテナ側のパスを指定します。
- type
ホストOS側でファイルを編集する
コマンドの概要は理解したので、さっそく、実践していきます。
先ほどのコマンドでUbuntuに入れていたら、ls /usr/src
コマンドでマウントしたフォルダの中身確認してみます。いじっていなければ空のはずです。
次にホストOS側のマウントしたbind_mount/
フォルダ配下にtest.txt
を作成して「test test test desu」と書いてみます。。
そうしたらコンテナ内でもう一度lsコマンドを実行してみます。ファイルができていますね。
root@7a2481b2f930:/# ls /usr/src
test.txt
ついでにcatコマンドも。
root@7a2481b2f930:/# cat /usr/src/test.txt
test test test desu!
というわけで、うまく書き換えができましたね。
ストレージが共有されている様子が見られたかと思います。
コンテナ内でファイルを書き換える
今度は逆に、Ubuntuコンテナ上でファイルを書き換えてみましょう。またvimをインストールするのも面倒なので下記コマンドでファイルを更新します。
echo "File editted from docker containercat" > /usr/src/test.txt
これでファイルが書き換わったはずです。
Windows上でも確認してみましょう。
ファイルが書き換わっていることが確認できましたね。
コンテナを破棄する
最後に、コンテナを破棄してもファイルが残るか確認してみます。「Ctrl+P -> Ctrl+Q」でコンテナを抜けて、以下のコマンドを実行しましょう。
docker container stop ubuntu22
docker container rm ubuntu22
docker container ls -a
これでコンテナが破棄されました。ではWindows上で、ファイルがそのまま残っていることを確認しましょう。(下はテキストファイルを開きなおした図)
ということで、ホストOSとフォルダを同期させることで、ストレージを永続化することができました。
以上がbindマウントの説明です。
volumeマウント
volumeマウントは、Dockerが管理する共有ストレージを使用する方法です。
【メリット】
- フォルダの位置を気にする必要がない
- Docker上で管理できる
- 汎用的で使いやすい
bindマウントとは違い、ホストOSから直接動作させることはできません。しかしホストでの不用意な変更で問題が発生する場合もあるので、「ファイルをホストOS上で編集したい!」という要件が無ければ、基本的にはvolumeマウントを推奨します。
実践
コンテナを作る前に、Docker内にvolumeを作成する必要があります。以下のコマンドを打ちこんでみてください。
docker volume create test_volume
docker volume ls
これでDockerコンテナ内にVolumeが作成されました。
次に、ストレージにマウントするコンテナを新しく作りましょう。以下のコマンドで作成可能です
docker container run -it --name ubuntu_volume --mount type=volume,src=test_volume,target=/usr/src ubuntu:22.04
これでvolumeマウントができています。
コマンド解説
bindマウントとほぼ同じですが、type=volume
とsrc=test_volume
となっている部分が違います。
-
--mount
bindマウントの時と同じです。type, src, target
という引数がセットになっているコマンドです。- type
マウントの種類を指定します。今回はtype=volume
を指定します。 - src
bindマウントの時は『ホストOSのパス』を指定していました。volumeマウントの場合は『volumeの名前』を指定します。 - target
ここはbindマウントの時と同じで、マウントに利用するコンテナ上のフォルダを指定します。コンテナからは、このフォルダにアクセスすることでvolumeを使えます。
- type
ファイルを編集してみる
volumeマウントの場合はホストOSから直接操作できないので、ubuntuコンテナ上で直接操作します。
以下、コマンドと結果をまとめて記載します。
root@ecc5f85afb6b:/# ls usr/src/
root@ecc5f85afb6b:/# echo "volume test desu!" > usr/src/vol.txt
root@ecc5f85afb6b:/# cat usr/src/vol.txt
volume test desu!
ということで、vol.txtが作成できました。
それではコンテナを破棄して作り直しましょう。現在はubuntuのメインプロセスにいるので、exit
コマンドでコンテナごと終了させます。
root@ecc5f85afb6b:/# exit
exit
PS C:\Users\******> docker container rm ubuntu_volume
ubuntu_volume
これでコンテナが削除完了しました。
さっそくコンテナを再作成します。コマンドは先ほどと同じでも大丈夫です。一応コンテナ名だけ変えておきましょう。以下のコマンドを打ちこんでみてください。
docker container run -it --name ubuntu_volume2 --mount type=volume,src=test_volume,target=/usr/src ubuntu:22.04
ファイルを確認したところ、結果は以下のようになりました。
root@ad60cce2b37f:/# ls usr/src/
vol.txt
root@ad60cce2b37f:/# cat usr/src/vol.txt
volume test desu!
今回は先ほど作成したvol.txt
が消えていません。ストレージの永続化に成功しました!
volumeを削除する場合は、マウントしているコンテナをすべて破棄してから以下のコマンドを実行すればOKです。
docker volume rm test_volume
以上で今回のメインの解説は終了です。 dockerコンテナのストレージの独自性と、ストレージを永続化する方法について、イメージ付きの理解が深まっていれば幸いです。
その他ストレージ関連のTips
メインの解説には載せきれなかった小話です。
-v
コマンドを用いたマウント
今回はストレージのマウントに--mount
オプションを使用しました。しかしDockerコマンドを知っている人の中で「-vじゃないの?」と感じた方もいるかもしれません。
それも間違いではなく、-v
オプションを使用してストレージをマウントすることも可能です。
今回使ったコマンドを-v
オプションで書き直すと、以下のようになります。
bindマウント
# --mount を使う場合
docker container run -it --name ubuntu_bind --mount type=bind,src=C:/Users/******/qiita_test/bind_strage,target=/usr/src ubuntu:22.04
# -v を使う場合
docker container run -it --name ubuntu_bind --mount type=bind,C:/Users/******/qiita_test/bind_strage:/usr/src ubuntu:22.04
volumeマウント
# --mount を使う場合
docker container run -it --name ubuntu_volume --mount type=volume,src=test_volume,target=/usr/src ubuntu:22.04
# -v を使う場合
docker container run -it --name ubuntu_volume -v test_volume:/usr/src ubuntu:22.04
基本的に-v
オプションを使う方が短くなるのですが、いかんせんわかりにくいです。ぱっと見ただけではbindマウントなのかvolumeマウントなのかが判断しにくいです。
また、-v
だとvolumeの名前を間違えたときに新規で作られてしまいます。例えば、test_volume
のストレージにマウントしたかったのにtest_voluem
とタイプミスをしてしまった場合、既存のvolumeをマウントせずに新しいvolumeが作成されるので、意図しない結果になります。
このような理由から、公式サイトでも--mount
の使用が推奨されています。ただ、-v
コマンドを用いたソースや記事は存在していると思うので、頭の片隅に置いておくと混乱しないかと思います。
tmpfs
本記事ではbindマウントとvolumeマウントの2つのマウント方法を紹介しましたが、実は3つ目のマウントタイプがあったりします。
ここでは実践ではなく、イメージ図を用いての紹介を行います。
基本的にDockerコンテナでデータを保存した場合、PCのストレージ領域のどこかにデータが保存されます。しかし、tmpfs のマウントではデータはメモリ上に保存され(キャッシュのような形式)、高速な読み書きが可能になります。
通常のデータ保存、bindマウント、volumeマウント、tmpfs の違いを表にしてみました。
通常のデータ保存 | Bindマウント | Volumeマウント | tmpfs | |
---|---|---|---|---|
コンテナ停止時のデータ | 消えない | 消えない | 消えない | 消える |
コンテナ破棄時のデータ | 消える | 消えない | 消えない | 消える |
データ保存先 | コンテナのファイルシステム | ホストの指定ディレクトリ | Dockerが管理するディレクトリ | メモリ(RAM) |
読込速度 | 中速 | 中速 | 中速 | 高速 |
管理者 | Docker | ホストユーザー | Docker | Docker |
最後に
以上でDockerのストレージに関する解説は終了です。
Dockerのストレージに関する知識は、なんとなくのイメージででも持っておくと今後の設計や実装に役に立つでしょう。
ストレージの特性を理解したうえで、コンテナを使いこなしてください!
参考
公式ドキュメント
この書籍で勉強しました。少し古いですが、かなりわかりやすいです。