何をするか?
複数の Web アプリを 1 つの VPS(レンタルサーバー)でホスティングする場合、一般的にはリバースプロキシ(nginx など)を立てて、各 Web アプリのバックエンドに繋ぐことになります。
例えば、nginx のバーチャルホスト機能を使って、app1.example.com
というアドレスと app2.example.com
というアドレスでアクセスされたときに、それぞれ別の Web アプリサーバー(バックエンド)に処理を振り分けます。
ここでは、リバースプロキシと 2 つの Web アプリを別々の Docker Compose で立ち上げて連携する方法を説明します。
1 セットの Web アプリであれば、1 つの Docker Compose 内に関連するコンテナをすべて含めてしまうのが楽ですが、ここでは、独立した 2 つの Web アプリを 1 つの物理サーバー (VPS) 上で運用することを考えているので、別々の Docker Compose に分けています。 もちろん、各アプリが使用する DB コンテナなどは、それぞれの Docker Compose 内にある想定ですが、上記の図では省略しています。
ここで紹介しているソースコードは GitHub に置いてあります。
テスト用の事前準備(hosts ファイル)
今回使用する nginx の設定ファイルでは、app1.example.com
と app2.example.com
という仮のドメインを使用していますが、本来は実在するドメインでなければいけません。
この設定のままテストしたい場合は、OS の hosts
ファイルに次のようなエントリを追加して、ローカルホスト (127.0.0.1) を指すように設定しておきます。
# For development
127.0.0.1 app1.example.com
127.0.0.1 app2.example.com
例えば、macOS の場合は、次のように hosts
ファイルを開いて編集します。
これで、Web ブラウザで http://app1.example.com/
や http://app2.example.com/
にアクセスしたときに、http://localhost/
にアクセスしたのと同じように振る舞うようになります。
ブリッジネットワークの作成
Docker Compose で起動したコンテナ群は、デフォルトで {ディレクトリ名}_default
という名前のブリッジネットワークに接続されますが、ここでは reverse-proxy-network
という名前のネットワークを生成して、そこにリバースプロキシと連携するすべてのコンテナを接続するようにします。
ネットワーク名は Docker ホスト内で一意になっている必要がありますが、1 つの Docker ホスト (= VPS) 内に複数のリバースプロキシを立てることはあまりないでしょうし、この名前 (reverse-proxy-network
) で大丈夫だと思います。
Web アプリ用の Docker Compose
2 つの Web アプリ用の docker-compose.yml
ファイルと、テスト用の index.html
ファイルを、次のようなディレクトリ構造で用意します。
- webapp1/
+-- docker-compose.yml
+-- public/index.html (I am webapp1 と表示するだけ)
- webapp2/
+-- docker-compose.yml
+-- public/index.html (I am webapp2 と表示するだけ)
下記は、1 つ目の Web アプリの docker-compose.yml
ファイルの設定例です。
2 つ目の Web アプリはほぼ同様の内容なので省略します(エイリアス名の app1-container
というところが app2-container
に変わるだけです)。
ポイントは、外部で定義したネットワーク reverse-proxy-network
に、nginx コンテナを接続しているところです。
external: true
を指定するのを忘れると、この Docker Compose 内に閉じたネットワーク (webapp1_reverse-proxy-network
) が生成されてしまうので注意してください。
あと、バックエンド DB などのコンテナに接続できるようにするために、自動生成される default
ネットワークにも繋いでおきます(実際のネットワーク名は webapp1_default
などになります)。
さらに、リバースプロキシ側からこの nginx サーバーを参照しやすいように、コンテナのエイリアス名 app1-container
を設定しています。
これを設定しなくても、webapp1-app-1
のようなディレクトリ名から自動生成されたコンテナ名でアクセスすることはできますが、ちょっと分かりにくいので、エイリアス名を付けておくことをおすすめします。
エイリアス名は、ネットワーク内で一意になっていれば十分です。
サービスのプロパティで container_name: app1-container
のようにすると、コンテナ名自体を変更することができますが、コンテナ名は Docker ホスト内で一意でないといけないので、エイリアス名を使った方がよいでしょう。
ちなみに、この nginx サーバーは、同一ネットワーク内のリバースプロキシからのみ接続できれば良いので、Docker ホスト (VPS) 側へのポート公開設定は必要ありません。
リバースプロキシ用の Docker Compose
リバースプロキシ用の Docker Compose は次のようなディレクトリ構成で作成します。
- reverse-proxy/
+-- docker-compose.yml
+-- conf.d/example.com.conf (nginx の設定ファイル)
リバースプロキシの Compose ファイルでも、Web アプリ側と同じ reverse-proxy-network
に接続するように設定します。
こちらは特にコンテナのエイリアス名などは設定する必要はありませんが、インターネット経由でアクセスできるように、Docker ホストのポートフォワード設定 (80:80
) をしておく必要があります。
nginx の設定ファイルは、バインドマウントで下記のファイルを使用するように設定しています。
この nginx のバーチャルホスト設定により、インターネット側からのアクセスが次のように各 Web アプリのコンテナに転送されるようになります。
http://app1.example.com/
でのアクセス →http://app1-container/
へ転送http://app2.example.com/
でのアクセス →http://app2-container/
へ転送
Docker Compose の起動
2 つの Web アプリと、リバースプロキシの Docker Compose を次のように起動します。
リバースプロキシ (nginx) の設定内で、Web アプリのコンテナ(エイリアス名)を参照しているため、上記のような順番で起動しなければいけないことに注意してください。
これで、Web ブラウザから http://app1.example.com/
というアドレスでアクセスしたときは I am webapp1
、http://app2.example.com/
というアドレスでアクセスしたときは I am webapp2
と表示されるはずです。
できたー ٩(๑❛ᴗ❛๑)۶ わーぃ
テストが終わって、Docker Compose をすべて停止したいときは、起動時と同様に各ディレクトリ内で docker compose down
していくか、次のように -p
オプションでプロジェクト名を指定して停止します。
おつかれ様でしたー。
(おまけ)コンテナ名ではなくポート番号で連携させる
上記の説明では、リバースプロキシとする nginx と、2 つの Web サーバーをコンテナ名で連携させましたが、Web サーバー側のポートを公開すれば、ポート番号で連携させることも可能です。 各 Web サーバーを単独で立ち上げてアクセスするケースがある場合は、こちらの方が都合がよいかもしれません。
例えば、2 つの Web サーバーをそれぞれ 8001
番、8002
番ポートで公開すれば、リバースプロキシからは次のようなアドレスでアクセスできます(参考: Docker コンテナからホスト側のサーバーにアクセスする (host.docker.internal))。
host.docker.internal:8001
host.docker.internal:8002
この場合、リバースプロキシの設定は次のようになります。
各コンテナ用の Compose ファイルは次のようになります。
8001:80
と指定すると、iptables 設定に穴が開いてインターネット上にポート 8001 番が公開されてしまうことに注意してください。
内部的な設定の優先度の問題で、Ubuntu のファイアウォール (ufw) などで公開していないポートに関してもアクセス可能になってしまいます。
確実にホスト内からのアクセスに限定するには、ports
フィールドの指定で、127.0.0.1:8001:80
のようにループバックアドレスを合わせて指定します。