DB の保持

気を付けないと、todo リストはコンテナを起動するたび、きれいに消去されます。どうしてでしょうか? コンテナがどのように動作しているのか、深掘りしましょう。

コンテナのファイルシステム

コンテナの実行時、イメージの様々なレイヤーを、コンテナのファイルシステムに使います。また、各コンテナでは、ファイルを作成、更新、削除するための「 スクラッチ領域(scratch space) 」もコンテナ自身が持ちます。たとえ同じイメージを使っていたとしても、(コンテナのファイルシステムに対する)あらゆる変更は、他のコンテナからは見えません。

実行して確認

この処理を見るために、2つのコンテナを起動し、それぞれにファイルを作成します。一方のコンテナで作成されたファイルは、もう一方のコンテナからは見えないと分かるでしょう。

  1. ubuntu コンテナを起動し、そこに /data.txt という名前のファイルを作成し、1から10000までのランダムな数を入れます。

    $ docker run -d ubuntu bash -c "shuf -i 1-10000 -n 1 -o /data.txt && tail -f /dev/null"
    

    このコマンドに興味があれば説明します。これは bash シェルを開始し、2つのコマンド(これが && を使った理由)を実行しました。前半部分はランダムな数を選び、それをファイル ./data.txt に書き出します。後半部分はコンテナを実行し続けるため、単にファイルを見ているだけです。

  1. コンテナ内に exec すると、出力を確認できます。そのためには、ダッシュボードを開き、実行している ubuntu イメージのコンテナにある、1番目のアクション( CLI と表示)をクリックします。

    Todo List Manager のスクリーンショット

    そうすると、ターミナルが開き、ubuntu コンテナ内で実行中のシェルが見えます。 /data.txt ファイルの内容を見るには、以下のコマンドを実行します。その後、このターミナルは以後使いませんので、閉じます。

    $ cat /data.txt
    

    docker exec コマンドを使う方が好きでしたら、同じようにできます。そのためにはコンテナ ID の確認が必要です。それから、以下のコマンドでファイル内容を表示します。

    $ docker exec <container-id> cat /data.txt
    

    ランダムな数が見えるでしょう!

  1. 次に、他の ubuntu コンテナ(同じイメージ)を起動しても、同じファイルは見えないでしょう。

    $ docker run -it ubuntu ls /
    

    見てください! そこに data.txt はありません! その理由とは、書き出したのは、1つめのコンテナのスクラッチ領域だけだからです。

  1. 次に進むため、 docker rm -f <コンテナID> コマンドを使って、1つめのコンテナを削除します。

コンテナの ボリューム(volume)

これまで試したように、各コンテナは、イメージの定義からコンテナが起動するのが分かりました。コンテナはファイルの作成、更新、削除ができますが、コンテナを削除すると、それらの変更は消失します。また、コンテナに対する全ての変更とは、 隔離された(isolated) 対象のコンテナに対してのみです。ですが、 ボリューム(volume) を使えば、これら全てを変えられます。

ボリューム は、コンテナ内で指定したファイルシステムのパスを、ホストマシン上へと接続できる機能を備えています。コンテナ内にディレクトリをマウントすると、ディレクトリに対する変更は、ホストマシン上からも見えます。コンテナを再起動する場合にも、同じディレクトリをマウントしていれば、再起動後も同じファイルが見えます。

ボリュームは主に2種類あります。ゆくゆくは両方を使いますが、まずは 名前付きボリューム (named volume) から始めましょう。

todo データの保持

デフォルトでは、todo アプリが自身のデータを保存するのは、コンテナ用ファイルシステム内で /etc/todo/todo.db にある SQLite Databese の中です。SQLite に不慣れでも、心配は要りません! これはシンプルなリレーショナル データベースで、1つのファイル内に全てのデータを保存します。大きくスケールするアプリケーションには最良ではありませんが、小さなデモには効果的です。これを他のデータベースエンジンに切り替える方法は、後ほどお伝えします。

データベースがたった1つのファイルのため、ホスト上のファイルを次のコンテナで利用できるようにするだけで、データベースを保持できるため、最後に中断したところから続けられるでしょう。ボリュームを作成し、データを保管するディレクトリに 取り付ける(attach) と(よく マウントする(mounting) と言います)、データを 保持(persist) できます。つまり、私たちのコンテナが書き出す todo.db ファイルは、ホスト上のボリュームに置いておけば、保持できます。

先述の通り、ここでは 名前付きボリューム(named volume) を使おうとしています。名前付きボリュームとは、単なるデータの 入れ物(bucket) と考えてください。Docker がディスク上で物理的な場所を確保するので、必要なのはボリュームの名前を覚えておくだけです。ボリュームを使うたびに、Docker は正しいデータの提供を確認します。

  1. docker volume create コマンドを使ってボリュームを作成します。

    $ docker volume create todo-db
    
  1. ダッシュボードで(あるいは docker rm -f <id> )、もう一度 todo アプリのコンテナを停止および削除します。このコンテナでは、まだ 存続するボリューム(persistent volume) を使っていないからです。

  1. todo アプリのコンテナを起動しますが、ボリュームのマウントを指定する -v フラグを追加します。ここでは名前付きボリュームを使い、 /etc/todos にマウントします。そうすると、このパスに作成された全てのファイルを保存します。

    $ docker run -dp 3000:3000 -v todo-db:/etc/todos getting-started
    
  1. コンテナが起動したら、アプリを開き、todo リストに新しいアイテムを追加します。

Todo リストにアイテムを追加
  1. todo アプリ用のコンテナを停止・削除します。コンテナの ID をダッシュボードか docker ps コマンドで調べ、 docker rm -f <id> で削除します。

  1. 先ほどと同じコマンドを使い、新しいコンテナを起動します。

  1. アプリを開きます。そうすると、まだリストにアイテムが残っているのが見えるでしょう!

  1. リストの挙動を確認できれば、次へ進むためにコンテナを削除します。

できました! これでデータを保持する方法を学びました。

注釈

名前付きボリューム(named volume)バインド マウント(bind mount) (この後すぐ説明します)は、 Docker engine のインストールが、デフォルトでサポートしている2つの主なタイプです。他にも NFS、SFTP、NetApp 等々のサポートといった、多くのボリューム ドライバ プラグインがあります。これは Swarm や Kubernetes 等のクラスタ環境で、複数のホスト上にコンテナを実行しようとするのであれば、とても重要になります。

ボリュームを深掘り

多くの人々が頻繁に「名前付きボリュームを使うと、私のデータを Docker が"実際に"保存するのはどこですか?」と尋ねます。知りたければ docker volume inspect コマンドが使えます。

$ docker volume inspect todo-db
[
    {
        "CreatedAt": "2019-09-26T02:18:36Z",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/todo-db/_data",
        "Name": "todo-db",
        "Options": {},
        "Scope": "local"
    }
]

この MountPoint こそが、ディスク上でデータを保管している本当の場所です。ほとんどのマシンでは、このディレクトにホスト上からアクセスするには root 権限が必要でしょう。ですが、そこにデータがあるのです!

注釈

Docker Desktop 上で直接ボリュームのデータにアクセスするには

Docker Desktop を実行中に、Docker コマンドが実際に動くのは、マシン上の小さな仮想マシン内です。マウントポイントのディレクトリ内で、実際の内容を見たい場合は、何よりもまず仮想マシン内に入る必要があります。

まとめ

ここまで、アプリケーションを再起動しても(テータを)保持できる機能を確認しました! これでアプリケーションを投資家に披露できますので、私たちのビジョンを把握してもらえるよう望みます。

ところで一方、初期の頃から変更を加えるたびに、何度も何度も毎回イメージの再構築をしています。これを改善したいと思いますよね? バインド マウントの使用(先ほど簡単に触れました)こそが良い方法です。詳しく見ていきましょう!