データベースの保持

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

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

コンテナの実行時、イメージの様々なレイヤーを、コンテナの ファイルシステム(filesystem) に使います。また、各コンテナでは、ファイルを作成、更新、削除するための「 スクラッチ領域(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. コンテナのターミナルにアクセスしたら、出力を確認できます。そのために CLI か Docker Desktop のグラフィカルインタフェースを使います。

    CLI

    コマンドライン上からコンテナにアクセスするには docker exec コマンドを使います。コンテナ ID の取得が必要です(取得には docker ps を使います)。Mac や Linux のターミナルで、あるいは、 Windows コマンドプロンプトや PowerShell で以下のコマンドで実行し、内容を確認します。

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

    ランダムな数字が表示されるでしょう。

    Docker Desktop

    Docker Desktop では Containers* に移動し、 **ubuntu イメージを実行しているコンテナの上にマウスカーソルを移動し、 Show container actions メニューを選びます。下に展開したメニューから Open in terminal を選びます。

    ターミナルとして開いているのは、 Ubuntu コンテナ内で実行中のシェルです。 /data.txt ファイル内の内容を確認するには、次のコマンドを実行します。後ほど終わったら、このターミナルを閉じます。

    $ cat /data.txt
    

    ランダムな数字が表示されるでしょう。

  1. 次に、他の ubuntu コンテナ(同じイメージ)を起動しても、同じファイルは見えないでしょう。Mac や Linux のターミナルで、あるいは、 Windows コマンドプロンプトや PowerShell で以下のコマンドで実行し、内容を確認します。

    $ docker run -it ubuntu ls /
    

    このコマンドの場合、コンテナ内のルートディレクトリ以下のファイルを一覧表示します。確認してください、 data.txt ファイルは一覧にありません。その理由は、ファイルを書き出したのは、1つめのコンテナのスクラッチ領域だけだからです。

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

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

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

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

ボリュームは主に2種類あります。ゆくゆくは両方を使いますが、まずはボリュームのマウントから始めましょう。

todo データを保持する

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

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

先述の通り、ここではボリュームのマウントを使おうとしています。ボリュームのマウントとは、中身が見えないデータの 入れ物(bucket) と考えてください。Docker がディスク上で物理的な場所を確保するため、必要なのはボリュームの名前を覚えておくだけです。

ボリュームの作成とコンテナの起動

CLI か Docker Desktop のグラフィカルインタフェースを使い、ボリュームの作成とコンテナの起動ができます。

CLI

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

    $ docker volume create todo-db
    
  1. todo アプリのコンテナを再び作り直します。 保存するボリューム(persistence) を使わずに起動しているため、 docker rm -f <tag> で停止と削除をします。

  1. todo アプリのコンテナを起動しますが、ボリュームのマウントを指定する --mount オプションを追加します。ボリューム名を与え、そこをコンテナ内の /etc/todos にマウントすると、そのパスに作成された全てのファイルを(ボリューム内に)確保します。Mac や Linux のターミナルで、あるいは、 Windows コマンドプロンプトや PowerShell で以下のコマンドで実行します。

$ docker run -dp 127.0.0.1:3000:3000 --mount type=volume,src=todo-db,target=/etc/todos getting-started

Docker Desktop

  1. ボリュームを作成します。

    1. Docker Desktop で Volumes を選びます。

    2. VolumesCreate を選びます。

    3. ボリューム名として todo-db を指定し、それから Create を選びます。

  2. アプリのコンテナを停止・削除します。

    1. Docker Desktop で Containers を選びます。

    2. 対象コンテナの Actions 列にある Delete を選びます。

  3. ボリュームを待つのしてアプリコンテナを起動します。

    1. Docker Desktop の一番上にある検索ボックスを選びます。

    2. 検索ウインドウで Images タブを選びます。

    3. 検索ボックスでコンテナ名を getting-started と指定します。

      Tip

      検索でフィルタを使えば local images (ローカルイメージ)のみ表示できます。

    4. 自分が作ったイメージを選び、 Run (実行)を選びます。

    5. Optional settings を選びます。

    6. Host path (ホスト側パス)で、ボリューム名 todo-db を指定します

    7. Container path (コンテナ側パス)で /etc/todos を指定します。

    8. Run (実行)を選びます。

データの保持を確認

  1. コンテナが起動したら、アプリを開き、todo リストに新しいアイテムを追加します。

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

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

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

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

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

ボリュームを深掘り

多くの人々が頻繁に尋ねるのは「ボリュームを使う時、 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 コマンドが実際に動くのは、マシン上の小さな仮想マシン内です。マウントポイントのディレクトリ内で、実際の内容を見たい場合は、何よりもまず仮想マシン内に入る必要があります。

次のステップ

ここまで、アプリケーションを再起動しても(テータを)保持できる機能を確認しました。

ところで一方、初期の頃から変更を加えるたびに、何度も何度も毎回イメージの再構築をしています。これを改善したいと思いますよね? バインド マウントの使用こそが良い方法です。