LoginSignup
24
9

「そのコンテナのデータ、消えるよ……」とさせないための、イメージ図+実践で理解するDockerストレージ【Dockerコンテナ・グレートジャーニー③】

Last updated at Posted at 2023-05-31

Dockerコンテナグレートジャーニー

Dockerコンテナを0から理解する旅、Dockerコンテナ・グレートジャーニー第3回です。(本記事だけ読んでも大丈夫です)
今回はDockerコンテナを運用するうえで超重要な『ストレージ』について、イメージと実践を交えて解説します。
Dockerコンテナの場合、適当に使っているといきなりデータが消し飛ぶこともあるので、Docker初心者の方も、深く知らずに使っている方も、この機会にDockerストレージについて学んでいきましょう。

対象

  • Dockerコンテナのストレージについてよくわからない人
  • Dockerコンテナの揮発性について知らない人
  • bindマウントとvolumeマウントの違いがわからない人

旅路(インデックス)

長いので記事を分割しています。

  1. そもそも仮想化とは? 仮想化ではないシステムとは?
  2. Dockerコマンド基礎(環境構築~ubuntu/httpdコンテナでのコマンド実行)
  3. Dockerのストレージについて【⇦本記事】
  4. Dockerのネットワークについて
  5. Docker-compose でコンテナをまとめる
  6. Docker image と Dockerfile
  7. AWS ECS で Dockerコンテナを走らせる(Comming soon)

【主題】dockerのストレージについて

dockerコンテナ内で保存/編集したデータは揮発性!

「Dockerコンテナのデータが消える」と言いましたが、それはどのようなことを言っているのでしょうか?
その理由を1枚の図にまとめるとこんな感じです。

image.png
消えるというのには3つの観点があり、

  1. コンテナは、一度削除(rm)すると復元できない
    DockerにはWindowsの「ごみばこ」のような機能はありません。
  2. docker run などで次に作ったコンテナは、以前作ったものとは完全に別物
    新しいコンテナ2を立ち上げても、それは最初のコンテナとは全く別のもので、前回の変更を引き継ぎません。
  3. 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詳しくない方向けの説明)

  1. iを押すと編集モードになります。
  2. 好きな文字列を入力します
  3. Escを押して編集モードを抜けます。
  4. :wqを入力してからEnter を押して、編集を保存して閉じます。
  5. 確認のため、cat usr/src/test.txtを実行します。
  6. 変更が確認出来たら:qと入力してEnter。保存せずに終了できます。

image.png
これで保存できました。

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つあります。

  1. ホストOS側のストレージを共有する『bindマウント
  2. Dockerが管理する領域を共有する『volumeマウント

image.png

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 と逆に、コンテナ側のパスを指定します。

image.png

ホストOS側でファイルを編集する

コマンドの概要は理解したので、さっそく、実践していきます。
先ほどのコマンドでUbuntuに入れていたら、ls /usr/srcコマンドでマウントしたフォルダの中身確認してみます。いじっていなければ空のはずです。
次にホストOS側のマウントしたbind_mount/フォルダ配下にtest.txtを作成して「test test test desu」と書いてみます。。
image.png

そうしたらコンテナ内でもう一度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上でも確認してみましょう。
image.png
ファイルが書き換わっていることが確認できましたね。

コンテナを破棄する

最後に、コンテナを破棄してもファイルが残るか確認してみます。「Ctrl+P -> Ctrl+Q」でコンテナを抜けて、以下のコマンドを実行しましょう。

docker container stop ubuntu22
docker container rm ubuntu22
docker container ls -a

これでコンテナが破棄されました。ではWindows上で、ファイルがそのまま残っていることを確認しましょう。(下はテキストファイルを開きなおした図)
image.png

ということで、ホストOSとフォルダを同期させることで、ストレージを永続化することができました。
以上がbindマウントの説明です。

volumeマウント

volumeマウントは、Dockerが管理する共有ストレージを使用する方法です。

【メリット】

  • フォルダの位置を気にする必要がない
  • Docker上で管理できる
  • 汎用的で使いやすい

bindマウントとは違い、ホストOSから直接動作させることはできません。しかしホストでの不用意な変更で問題が発生する場合もあるので、「ファイルをホストOS上で編集したい!」という要件が無ければ、基本的にはvolumeマウントを推奨します。

実践

コンテナを作る前に、Docker内にvolumeを作成する必要があります。以下のコマンドを打ちこんでみてください。

docker volume create test_volume
docker volume ls

これでDockerコンテナ内にVolumeが作成されました。
image.png

次に、ストレージにマウントするコンテナを新しく作りましょう。以下のコマンドで作成可能です

docker container run -it --name ubuntu_volume --mount type=volume,src=test_volume,target=/usr/src ubuntu:22.04

これでvolumeマウントができています。

コマンド解説

bindマウントとほぼ同じですが、type=volumesrc=test_volumeとなっている部分が違います。

  • --mount
    bindマウントの時と同じです。type, src, targetという引数がセットになっているコマンドです。
    • type
      マウントの種類を指定します。今回はtype=volumeを指定します。
    • src
      bindマウントの時は『ホストOSのパス』を指定していました。volumeマウントの場合は『volumeの名前』を指定します。
    • target
      ここはbindマウントの時と同じで、マウントに利用するコンテナ上のフォルダを指定します。コンテナからは、このフォルダにアクセスすることでvolumeを使えます。

ファイルを編集してみる

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 のマウントではデータはメモリ上に保存され(キャッシュのような形式)、高速な読み書きが可能になります。

image.png

通常のデータ保存、bindマウント、volumeマウント、tmpfs の違いを表にしてみました。

通常のデータ保存 Bindマウント Volumeマウント tmpfs
コンテナ停止時のデータ 消えない 消えない 消えない 消える
コンテナ破棄時のデータ 消える 消えない 消えない 消える
データ保存先 コンテナのファイルシステム ホストの指定ディレクトリ Dockerが管理するディレクトリ メモリ(RAM)
読込速度 中速 中速 中速 高速
管理者 Docker ホストユーザー Docker Docker

最後に

以上でDockerのストレージに関する解説は終了です。
Dockerのストレージに関する知識は、なんとなくのイメージででも持っておくと今後の設計や実装に役に立つでしょう。
ストレージの特性を理解したうえで、コンテナを使いこなしてください!

参考

公式ドキュメント

この書籍で勉強しました。少し古いですが、かなりわかりやすいです。

24
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
9