バインド マウント(bind mount) の使用

Part 5 ではデータベース内のデータを保持するため、ボリュームのマウントを使いました。アプリケーションのデータをどこかに保持する必要がある場合、ボリュームマウントは良い選択肢です。

バインド マウント(bind mount) は他のタイプのマウントであり、ホスト上のファイルシステムをコンテナ内と直接共有します。アプリケーションの動作中でも、バインド マウントを使ってソースコードをコンテナ内にマウントすると、コードの変更が見えたり反映できるようになります。つまり、アプリケーションに対する変更が、直ちに見えるでしょう。

本章で分かるのは、バインドマウントの使い方と、ファイル変更を監視してアプリケーションを自動的に再起動する nodemon と呼ばれるツールについてです。

ボリューム型の素早い比較

以下の表が示すのは、ボリュームマウントとバインドマウントの主な違いです。

名前付きボリューム(named volume)

バインド マウント(bind mount)

ホスト上の場所

Docker が選択

自分で決める

マウント例( --mount を使用)

type=volume,src=my-volume,target=/usr/local/data

type=bind,src=/path/to/data,target=/usr/local/data

コンテナの内容に新しいボリュームを加える

はい

いいえ

ボリューム ドライバのサポート

はい

いいえ

バインド マウントを試す

アプリケーション開発でバインドマウントを使う方法を学ぶ前に、どのようにしてバインドマウントが動作するのか実用的な理解をするために、簡単な実験を行えます。

  1. ターミナルを開き、ディレクトリを getting-started リポジトリの app に変更します。

  1. 以下のコマンドは、バインドマウントした ubuntu コンテナで bash を実行します。

    Mac / Linux

    $ docker run -it --mount type=bind,src="$(pwd)",target=/src ubuntu bash
    

    Windows

    PowerShell でコマンドを実行します。

    $docker run -it --mount "type=bind,src=$pwd,target=/src" ubuntu bash
    

    --mount オプションは Docker に対してバインドマウントの作成を命令します。 src で示す場所は、ホストマシン上の現在の作業ディレクトリ( getting-started/app )です。 target で示す場所は、コンテナ内に現れるディレクトリ( /src )です。

  1. コマンドの実行後、コンテナが持つファイルシステムのルートディレクトリ内で、Docker は対話式の bash セッションを開始します。

    root@ac1237fad8db:/# pwd
    /
    root@ac1237fad8db:/# ls
    bin   dev  home  media  opt   root  sbin  srv  tmp  var
    boot  etc  lib   mnt    proc  run   src   sys  usr
    
  1. src ディレクトリにディレクトリを変更します。

    ここはコンテナ起動時にマウントしたディレクトリです。このディレクトリ内容の一覧を表示したら、ホストマシン上の getting-started/app ディレクトリと同じようにファイルを表示します。

    root@ac1237fad8db:/# cd src
    root@ac1237fad8db:/src# ls
    Dockerfile  node_modules  package.json  spec  src  yarn.lock
    
  1. myfile.txt という名前の新しいファイルを作成します。

    root@ac1237fad8db:/src# touch myfile.txt
    root@ac1237fad8db:/src# ls
    Dockerfile  myfile.txt  node_modules  package.json  spec  src  yarn.lock
    
  1. ホスト上の app ディレクトリを開き、ディレクトリ内に myfile.txt があるかどうか調べます。

    ├── app/
    │ ├── Dockerfile
    │ ├── myfile.txt
    │ ├── node_modules/
    │ ├── pacakge.json
    │ ├── spec/
    │ ├── src/
    │ └── yarn.lock
    
  1. ホストから myfile.txt ファイルを削除します。

  1. コンテナ内で、再び app ディレクトリ内の内容を一覧表示します。今度はファイルが消えてしまったと分かります。

    root@ac1237fad8db:/src# ls
    Dockerfile  node_modules  package.json  spec  src  yarn.lock
    
  1. 対話型のコンテナセッションを Ctrl + D で停止します。

以上がバインドマウントの簡単な紹介のすべてです。この手順では、ホストとコンテナ間でどのようにファイルを共有しているのかと、どちらにも変更が直ちに反映されるのを示しました。これでソフトウェア開発にバインドマウントを利用できます。

コンテナのデプロイ

ローカル開発環境のセットアップで、バインドマウントの利用は一般的です。利点は、開発マシン上に全ての構築ツールや環境をインストールする必要がありません。docker run コマンドを1回実行するだけで、Docker は依存関係とツールを取得します。

開発用のコンテナでアプリを実行

以下の手順で示すのは、バインドマウントがある開発用のコンテナを実行する手順です:

バインドマウントしてコンテナを実行するには、 CLI か Docker Desktop を使えます。

CLI

  1. これまでの getting-started コンテナが動作していないのを確認します。

  1. getting-started/app ディレクトリから以下のコマンドを実行します。

    Mac / Linux

    $ docker run -dp 127.0.0.1:3000:3000 \
        -w /app --mount type=bind,src="$(pwd)",target=/app \
        node:18-alpine \
        sh -c "yarn install && yarn run dev"
    

    Windows

    このコマンドを PowerShell で実行します。

    $ docker run -dp 127.0.0.1:3000:3000 `

    -w /app --mount "type=bind,src=$pwd,target=/app" ` node:18-alpine ` sh -c "yarn install && yarn run dev"

以下はコマンドの詳細です:

  • -dp 127.0.0.1:3000:3000 … 以前と同じです。デタッチド(バックグラウンド)モードで実行し、ポート割り当てを作成します。

  • -w /app … 「 作業ディレクトリ(working directory) 」 またはカレントディレクトリを指定します。

  • --mount "type=bind,src=$pwd,target=/app" … ホスト上のカレントディレクトリを、コンテナ内の /app ディレクトリにバインドマウントします。

  • node:18-alpine … 使用するイメージです。なお、これが Dockerfile からアプリを作成するベースイメージです。

  • sh -c "yarn install && yarn run dev" … シェルを開始するのに sh を使い( alpine には bash はありません)、パッケージをインストールするため yarn install を実行し、開発用サーバを開始するために yarn run dev を実行します。 packagejson の中を見ると、 dev スクリプトが nodemon を起動しているのが分かります。

  1. docker logs <container-id> を使いログを観察できます。準備が調えば、次のような表示になるでしょう:

    $ docker logs -f <container-id>
    nodemon src/index.js
    [nodemon] 2.0.20
    [nodemon] to restart at any time, enter `rs`
    [nodemon] watching dir(s): *.*
    [nodemon] starting `node src/index.js`
    Using sqlite database at /etc/todos/todo.db
    Listening on port 3000
    

    ログの観察が終わったら、 Ctrl + C を入力して終了します。

Docker Desktop

  1. これまでの getting-started コンテナが動作していないのを確認します。

  1. バインドマウントしてイメージを起動します。

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

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

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

      Tip

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

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

    5. Optional settings を選びます。

    6. Host path (ホスト側パス)で、ホストマシン上の app ディレクトリのパスを指定します

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

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

  2. Docker Desktop を使ってコンテナのログを観察できます。

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

    2. コンテナ名を選びます。

    準備が調えば、次の様な表示になるでしょう:

    $ docker logs -f <container-id>
    nodemon src/index.js
    [nodemon] 2.0.20
    [nodemon] to restart at any time, enter `rs`
    [nodemon] watching dir(s): *.*
    [nodemon] starting `node src/index.js`
    Using sqlite database at /etc/todos/todo.db
    Listening on port 3000
    

開発用コンテナにアプリをデプロイ

ホストマシン上のアプリを更新し、コンテナ内に変更が反映されるのを確認します。

  1. src/static/js/app.js ファイル内の 109 行目の、「Add Item」ボタンをシンプルに「Add」と表示するように変えます。

    -                         {submitting ? 'Adding...' : 'Add Item'}
    +                         {submitting ? 'Adding...' : 'Add'}
    

    ファイルを保存します。

  1. ウェブブラウザでページを再読み込みしたら、ほぼ直ちに変更が反映しているのが分かるでしょう。Node サーバの再起動には、数秒かかるかもしれず、もしエラーが出てしまった場合には、数秒後に再読み込みを試してください。

    Add ボタンのラベルを更新したスクリーンショット
  1. あとは、他も自由気ままに変更します。変更後は毎回ファイルを保存しますと、 nodemon プロセスがコンテナ内のアプリを再起動します。終わったら、コンテナを停止し、以下のコマンドを使って新しいイメージを構築します。

    $ docker build -t getting-started .
    

次のステップ

現段階で、データベースを保持できるようになり、イメージを再構築しなくても、開発しているアプリを変更できるのが分かりました。

ボリュームマウントとバインドマウントに加え、より複雑かつ専門的なユースケースに対応するため、Docker は他のマウントタイプやストレージドライバをサポートします。

アプリの本番環境を準備するには、データベースを SQLite からスケール可能な何かに移行する必要があります。扱いやすさのため、関係データベースの採用にあたり、アプリケーションが MySQL を使うように切り替えます。ですが、どうやって MySQL を動かせばよいのでしょうか? どのようにしてコンテナ間でお互いが通信できるのでしょうか? これは次のセクションで学びます。