Docker Compose Watchでファイルを同期する
Dockerを使用してポータブルで再現性のある開発環境を作成する際、コードベースに変更を加えた際に、毎回再ビルドすることなく、コンテナ内で即座に効果を発揮させる方法を見つける必要があります。可能な解決策は、ホストディレクトリをコンテナ内に直接マウントすることですが、これはコンテナの隔離性とポータビリティを破ることになり、ホストのディレクトリ構造に依存することになります。この問題を解決するために、docker-compose watchを使用できます。
このチュートリアルでは、docker-compose watchを使用して、ホストファイルをコンテナ内で同期させる方法を学びます。
このチュートリアルでは学びます:
- ボリュームとバインドマウントの違いは何ですか?
- ホストシステムとコンテナ間でファイルを同期させるためにdocker-compose watchを使用する方法
カテゴリ | 要件、規約、または使用されるソフトウェアのバージョン |
---|---|
システム | 分布に依存しない |
ソフトウェア | docker-compose |
他 | Dockerおよびdocker-composeに精通していること |
慣習 | &8211; 指定されたLinuxコマンドは、rootユーザーとして直接実行するか、sudo コマンドを使用して、root権限で実行する必要があります。$&8211; 指定されたLinuxコマンドは、通常の非特権ユーザーとして実行する必要があります。 |
Dockerボリュームとバインドマウント
コンテナ内のデータは永続的ではありません。これは、コンテナが破壊されると、その中のすべてのデータが失われることを意味します。Dockerを使用してデータの永続性を実現するには、基本的に2つの方法があります。ボリュームを使用するか、バインドマウントを使用するかのいずれかです。それぞれの解決策には利点と欠点がありますので、要約しましょう。
Dockerボリューム
匿名ボリュームと名前付きボリュームを区別することができます。彼らの動作はほぼ同じですが、今すぐ見る一つのケースを除いています。
匿名またはランダムに名付けられたボリュームは、通常、DockerfileでVOLUME命令が使用されたとき、またはdocker volume create
コマンドがボリューム名を引数として提供せずに呼び出されたときに作成されます。一方、名前付きボリュームは、その名の通り、明示的に名前を割り当てるボリュームです。
以下の例では、公式の「httpd」イメージに基づいてコンテナを実行し、データを永続化するために名前付きボリューム(「httpd_data」と呼びます)を作成することを指定します。&47;usr&47;local&47;apache2&47;htdocs&47;
コンテナディレクトリにデータを保存します。
docker run -v httpd_data:/usr/local/apache2/htdocs httpd
コンテナが起動されると、ボリュームが空であれば、コンテナのターゲットディレクトリ(この場合は&47;usr&47;local&47;apache2&47;htdocs
)に存在するデータがボリューム内にコピーされます。ボリュームが空でない場合は、コンテナ内のデータがターゲットディレクトリの内容を隠蔽します。これは、mnt
コマンドを使用して既存のディレクトリ内にファイルシステムをマウントする場合と同様です。
通常、コンテナが削除されると、それによって使用されていたボリュームは保持されます。一つの例外は、docker run
コマンドを--rm
オプションで実行してコンテナが作成される場合で、これによりコンテナが存在する際に自動的に削除されます。この特別な場合、名前付きボリュームとは異なり、文脈的に作成された匿名ボリュームは、コンテナと一緒に自動的に削除されます。
ボリュームは、アプリケーションをDockerで配布する際にデータの永続性を達成するための一般的に好まれる解決策ですが、Docker自体によって作成および管理されます。しかし、開発中は理想的な解決策ではなく、コードベースに変更を加えると、それをコンテナ内に反映させるためにはコンテナの再構築が必要になります。
バインドマウント
バインドマウントはしばしば推奨されませんが、上記の問題に対する可能な解決策を示しています。バインドマウントを使用することで、ホストディレクトリをコンテナ内で直接アクセス可能にできます。以下の例では、ホストのsrc</code>ディレクトリを、/usr/local/apache2/htdocs</code>ディレクトリにバインドマウントしています。
sudo docker run -v $(pwd)/src:/usr/local/apache2/htdocs httpd
この戦略は、コンテナ内でコードの変更を即座に反映させる方法を提供する利点がありましたが、主に二つの欠点があります。
- それはコンテナの隔離とポータビリティを破壊します(コンテナはホストのディレクトリ構造に依存するようになります)
- 特にホストシステムでSELinuxのような追加のセキュリティ対策が使用される場合、権限の管理がより困難になります。
典型的な例を挙げると、ホストシステム上に、ユーザーとそのユーザーグループが所有し、他のユーザーが書き込みできないsrc
ディレクトリにソースファイルが保存されているとします。このディレクトリをコンテナ内にバインドマウントする場合、対応するサービスがホストマシン上のユーザーと同じUIDで実行されることを確認する必要があります。そうでないと、ディレクトリ内にファイルを作成したり削除したりすることができません。もしそのサービスがrootとして実行されると、ディレクトリ内にファイルを作成することができますが、ホストマシン上の特権のないユーザーではそれらを変更することができません。これは、UIDがコンテナ内でシフトされるため、rootless dockerを使用する場合にはさらに厄介になることがあります。
docker-compose watchを使用する
Docker-composeのwatchはDockerエンジンの機能ではないため、厳密に言えばデータの永続性を確保したり、ホストシステムとコンテナ間でデータを共有する新しい方法ではありません:これは、マルチコンテナアプリケーションを簡単に実行するために使用されるツールであるdocker-composeのバージョン2.22から利用可能な機能です。
docker-compose watchを使用することで、コンテナの隔離を維持しながら、コードベースに加えた変更がコンテナ内に即座に反映されるのを見ることができます。これは、ホスト上の指定されたパスを監視して変更を検出することで実現されます。例えば、ファイルに変更が見つかると、そのファイルは自動的かつ透過的にコンテナ内に同期されます。
例を見てみましょう。ホストのsrc
ディレクトリ内に「index.html」ファイルがあるとします。ファイルの内容は以下の通りです:
<h1>Hello world!</h1>
コンテナが作成される際に、src</code>ディレクトリの内容をターゲットディレクトリにコピーするためには、実行したいサービス(この場合はhttpd)の元のDockerfileを拡張する必要があります。
FROM httpd:latest
COPY src/ /usr/local/apache2/htdocs
では、同じディレクトリで、コンテナのビルドに関する指示と、src
ディレクトリの内容の変更を監視するためのdocker-composeファイルを作成しましょう。
services:
httpd:
build:
dockerfile: Dockerfile
ports:
- 8080:80
develop:
watch:
- action: sync
path: ./src
target: /usr/local/apache2/htdocs
私たちは、docker-composeファイル内のwatch
属性を介して「監視」指示を提供します。ここでは、action
(この場合はsync)、変更を監視するdocker-composeファイルに対する相対的なpath
(./src)、および変更がコンテナ内に反映される場所を制御するtarget
を指定します。さらに、ignore
フィールドを使用して、特定のファイルを監視から除外することができます:除外パターンはpath
に対して相対的と見なされます。たとえば、./src/node-modules
ディレクトリを除外したい場合、次のように記述します。
services:
httpd:
build:
dockerfile: Dockerfile
ports:
- 127.0.0.1:8080:80
develop:
watch:
- action: sync
path: ./src
target: /usr/local/apache2/htdocs
ignore:
- node_modules/
では、docker-compose upコマンドを実行できます。ファイル監視を有効にするために、--watch
オプションを使用します。
docker-compose up --watch
カスタムイメージが構築され、コンテナが起動したら、ログを確認して監視が確立されていることを確認できます。
STEP 1/3: FROM httpd:latest
STEP 2/3: COPY src/ /usr/local/apache2/htdocs
--> a03c7ea1c705
STEP 3/3: LABEL "com.docker.compose.image.builder"="classic"
COMMIT docker.io/library/test-httpd
--> 01c475f85dc8
Successfully tagged docker.io/library/test-httpd:latest
01c475f85dc840ceb98e9257142257c27037fe8c4df31db423c90bf6430e0eb6
Successfully built 01c475f85dc8
Successfully tagged test-httpd
[+] Running 2/1
✔ Network test_default Created 0.0s
✔ Container test-httpd-1 Created 0.1s
⦿ Watch enabled
Attaching to httpd-1
httpd-1 | AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 10.89.4.2. Set the 'ServerName' directive globally to suppress this message
httpd-1 | AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 10.89.4.2. Set the 'ServerName' directive globally to suppress this message
httpd-1 | [Fri Feb 28 14:15:07.472766 2025] [mpm_event:notice] [pid 1:tid 1] AH00489: Apache/2.4.63 (Unix) configured -- resuming normal operations
httpd-1 | [Fri Feb 28 14:15:07.473037 2025] [core:notice] [pid 1:tid 1] AH00094: Command line: 'httpd -D FOREGROUND'
ホストのポート8080をコンテナ内のポート80にバウンドさせたので、http://localhost:8080
に移動することで、ソースディレクトリ内のindex.htmlファイルの内容を視覚化できるはずです。
では、index.htmlファイルの内容を次のように変更しましょう:
<h1>Hello linuxconfig.org!</h1>
変更を保存するとすぐに、ログを読み取ることでファイルが同期されたことを確認できます。
⦿ Syncing service "httpd" after 1 changes were detected
実際、http://localhost:8080に移動すると、再びコンテナ内に変更が反映されているのを見ることができるはずです。
docker-compose watchが機能するためには、コンテナ内のUSER</code>がtarget</code>ディレクトリに書き込むことができなければならないのは自明です。
docker-composeファイルの内容から推測できるように、「sync」は利用可能な監視アクションの唯一のものではありません。他のものはsync+restart
とrebuild
です。前者が使用されると、ホスト上の指定されたファイルの変更がコンテナ内のターゲットディレクトリに同期され、さらにサービスコンテナが再起動されます。これは、サービスの再起動が反映される必要がある設定ファイルを同期する際に便利です。一方、rebuildアクションが使用されると、指定されたファイルの変更がイメージの再構築と実行中のサービスコンテナの置き換えを引き起こします。
結論
このチュートリアルでは、docker-compose watchを使用して、コンテナ内でホストファイルを自動的に同期させる方法を学びました。docker-compose watchはすべての状況でバインドマウントの代替とすることを目的としているわけではありませんが、開発中には理想的なソリューションであり、コードベースに加えた変更を即座にコンテナ内に反映させることができ、コンテナの隔離性とポータビリティを損なう必要がありません。