DB への保存

気づかないままでいると todo リストの中身は、コンテナーを起動するたびに空っぽになってしまいます。 なぜそうなっているのでしょう? この部では、コンテナーがどうやって動作しているのかについて学んでいきます。

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

コンテナーが起動すると、イメージ内のさまざまなレイヤーがファイルシステムに利用されます。 また各コンテナーには、ファイルの生成/更新/削除を行うための「スクラッチスペース」(scratch space、一時的な領域)をもっています。 1 つのイメージを共通に利用しているからといって、1 つのコンテナー上の変更が他のコンテナーに及ぶわけではありません。

実際を確認

実際の動作を見てみるために 2 つのコンテナーを起動させます。 1 つのコンテナーではファイル生成を行います。 もう 1 つのコンテナーでは、同じファイルが存在しているかどうかを確認します。

  1. Alpine コンテナーを起動させて、その中で新たなファイルを生成します。

    $ docker run --rm alpine touch greeting.txt
    

    情報

    Any commands you specify after the image name (in this case, alpine) are executed inside the container. In this case, the command touch greeting.txt puts a file named greeting.txt on the container's filesystem.

  2. 新たな Alpine コンテナーを実行し、stat コマンドを使ってそのファイルの存在を確認します。

    $ docker run --rm alpine stat greeting.txt
    

    以下のような出力が得られるはずです。 新しいコンテナー内にはファイルが存在していないことがわかります。

    stat: can't stat 'greeting.txt': No such file or directory
    

1 つめのコンテナーにおいて生成した greeting.txt ファイルは、2 つめのコンテナーには存在しませんでした。 これは各コンテナーが持つ書き込み可能は「トップレイヤー」は、それぞれが独立しているということです。 2 つのコンテナーが同一ベースイメージから生成された同一レイヤーを持っている場合であっても、それぞれの書き込み可能レイヤーは、個々のコンテナーに固有のものであるということです。

コンテナーボリューム

上で行った実験では、起動元となるイメージ定義に基づいて、各コンテナーがその都度起動する様子を見ました。 コンテナーではファイルの生成、更新、削除を行うことができますが、コンテナーを削除すると、そういった変更はすべて失われ、コンテナーから切り離されます。 そこでボリュームを利用すると、この状況を変えることができます。

ボリューム とは、コンテナー内に特別なファイルシステムがホストシステムに向けて生成され、そこにアクセスする機能を提供するものです。 コンテナー内のあるディレクトリがマウントされていると、そのディレクトリ内で行われた変更がホストマシンからも見ることができます。 仮にコンテナーの再起動の前後で 1 つのディレクトリをマウントしておけば、同一のファイルを維持できることになります。

ボリュームには大きく 2 つの種類があります。 最終的にはその両方を利用しますが、まずはボリュームマウントから始めます。

todo データの保存

Todo アプリはデフォルトで、各種データをコンテナーのファイルシステムの /etc/todos/todo.db にある SQLite データベースに保存します。 SQLite がよくわからなくても心配無用です。 これは単純なリレーショナルデータベースであって、すべてのデータを 1 つのファイルに保存するものです。 大規模アプリケーションに対して利用するのは適切ではありませんが、ちょっとしたデモであれば十分に動作します。 この後には別のデータベースエンジンについて学んでいきます。

データベースが 1 つのファイルに収められているので、ホスト上のファイルとして保存しておけば、新たなコンテナーからも利用できます。 したがって最後に更新されたものを、その次にも使い続けられます。 ボリュームを生成して、データを保存するディレクトリに割り当てます(通常これを「マウンティングする」と言い表します)。 こうすればデータを失うことなく保存できます。 今の場合、コンテナーから todo.db ファイルへの書き込みを行っているので、ボリュームを通じてホスト上にデータが保持されることになります。

先ほど言ったように、これから扱うのはボリュームマウントです。 ボリュームマウントとは、単純にデータが入った 1 つのバケツだと思ってください。 Docker は、ボリュームによって確保される保存領域を含め、このボリュームを完全に管理します。 ボリュームという名前を覚えるだけで十分です。

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

ボリュームを生成してコンテナーを起動するのは、CLI あるいは Docker Desktop のグラフィック画面のいずれからでも行うことができます。


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

    $ docker volume create todo-db
    
  2. docker rm -f <id> を実行して todo アプリのコンテナーを再び停止させて削除します。 実行中のコンテナーは、まだ永続的なボリュームを利用していないからです。

  3. todo アプリコンテナーを起動します。 今回は --mount オプションをつけてボリュームマウントを指定します。 そこではボリューム名を指定し、コンテナー内の /etc/todos へのマウントを行います。 このパス内に生成されるファイルをすべて把握することができます。

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

    メモ

    Git Bash を利用している場合は、このコマンドにおいて異なる文法を用いる必要があります。

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

    Git Bash の文法の相違に関しては Git Bash を使った操作 を参照してください。

ボリューム生成のために以下を行います。

  1. Docker Desktop において Volumes (ボリューム) を開きます。
  2. Volumes (ボリューム) において Create (生成) をクリックします。
  3. ボリューム名として todo-db を入力し Create (生成) をクリックします。

アプリコンテナーの停止と削除は以下を行います。

  1. Docker Desktop において Containers (コンテナー) を開きます。
  2. 対象コンテナーの Actions (動作) カラムにおいて Delete (削除) を選択します。

ボリュームマウントを行った todo アプリコンテナーの起動は以下を行います。

  1. Docker Desktop の最上段の検索欄を選択します。

  2. 検索画面にて Images タブを選択します。

  3. 検索欄においてイメージ名として getting-started を入力します。

    情報

    検索フィルターを用いてイメージの絞り込みを行えば ローカルイメージ のみを表示することができます。

  4. イメージを選択して Run (実行) をクリックします。

  5. Optional settings (オプション設定) をクリックします。

  6. Host port (ホストポート) に、たとえば 3000 といったポートを入力します。

  7. Host path (ホストパス) にボリューム名 todo-db を入力します。

  8. Container path (コンテナーパス) に /etc/todos を入力します。

  9. Run (実行) をクリックします。


データの保存確認

  1. コンテナーが起動したら、アプリケーションを開いて todo リストにアイテムをいくつか追加します。

    todo リストに追加したアイテム
  2. todo アプリのコンテナーを停止して削除します。 これを行うには Docker Desktop を利用します。 または docker ps を実行して ID を確認した上で docker rm -f <id> により削除します。

  3. 以前行った手順を使って新たなコンテナーを起動します。

  4. アプリを開きます。 追加したアイテムがリストの中にあるはずです。

  5. リストの確認を終えたら、コンテナーを削除して先に進みます。

ここまでにデータの保存方法について学びました。

ボリュームの中身

多くの人からよく尋ねられます。 「ボリューム利用時に Docker はデータをどこに保存するのか?」 この答えを知りたければ docker volume inspect コマンドを実行してみてください。

$ docker volume inspect todo-db

You should see output like the following:

[
    {
        "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 アクセス権限を必要とします。

まとめ

本節ではコンテナーデータの保存方法について学びました。

関連情報

次のステップ

次は、より効果的にアプリ開発を行うためにバインドマウントの利用方法について学びます。