前回、独自ドメインとローカルのWebアプリをつなげるプロキシサーバをnginxとDcokerで構築するでnginxを利用して独自ドメインでコンテナに接続する方法について紹介しました。
前回は接続対象のアプリケーションが1つだけでしたが、今回はサブドメインを利用して複数のアプリケーションに対応する方法について紹介します。
なお、localhost:3000
でWebアプリケーションが、localhost:3001
でAPIサーバが起動している前提とします。
今回は例としてhttp://web.local.example-dev.com
にWebアプリケーション、http://api.local.example-dev.com
でAPIサーバに接続することをゴールとします。
目次
下準備
ローカル環境の/etc/hostsを編集する
localhost
は127.0.0.1
を指すホスト名です。
任意のドメインでローカル環境を参照するには/etc/hosts
で127.0.0.1
にドメインをマッピングします。
/etc/hosts
# 以下を追加する
127.0.0.1 web.local.example-dev.com
127.0.0.1 api.local.example-dev.com
WebアプリをRailsで実装している場合: ドメインによるアクセスをアプリケーション側で許可する
Rails 6以降1、DNSリバインディング攻撃からの保護のためデフォルトでは.localhost
、0.0.0.0
、::
からのアクセスのみ許可されています。
ですので、設定ファイルにconfig.hosts << "web.local.example-dev.com;"
やconfig.hosts.clear
を追加してドメイン名でコンテナへアクセスできるようにします。
設定をしないと[ActionDispatch::HostAuthorization::DefaultResponseApp] Blocked host: web.local.example-dev.com;
というエラーが発生します。
詳細解説はlocalhost以外のホスト名でローカル環境のRailsに接続する方法で紹介しています。
docker-compose.ymlの作成
nginxコンテナを起動するdocker-compose.yml
を作成します。
docker-compose.yml
version: '3'
services:
nginx:
image: nginx:1.21
ports:
- '80:80'
command: [nginx-debug, '-g', 'daemon off;']
volumes:
- ./nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf
volumes
で指定した設定ファイルについて補足説明をします。
nginxの設定ファイルは/etc/nginx/nginx.conf
です。設定ファイルにはinclude /etc/nginx/conf.d/*.conf;
という記述があり、/etc/nginx/conf.d/
配下の.conf
という拡張子の設定が読み込まれます。
今回はnginxの/etc/nginx/conf.d/
ディレクトリとローカルディレクトリをバインドマウントさせて設定ファイルを配置します。
nginxの設定ファイルの修正
今回は以下の2つの方法について紹介します。
- 各ドメインに応じたserverブロックを用意する
- サブドメインに応じて動的にプロキシ先を変更する
方法1: 各ドメインに応じたserverブロックを用意する
server_name
ディレクティブにドメイン名(サブドメインを含む)を記述し、各ドメインに応じたserver
ブロックを用意する方法です。
nginxの設定ファイルは以下のようになります。
nginx/conf.d/default.conf
### web.local.example-dev.comに関するリクエストの処理
server {
listen 80;
### web.local.example-dev.comのリクエストを受け取る
server_name web.local.example-dev.com;
### "/"にアクセスがあったときの処理
location / {
### リクエストヘッダに$hostをセット
# $host: Hostリクエストヘッダがあればそのサーバ名、なければプライマリサーバ名(server_name)
proxy_set_header Host $host;
### ローカル環境(host.docker.internal)の3000番ポートへプロキシする
proxy_pass http://host.docker.internal:3000;
}
}
### api.local.example-dev.comに関するリクエストの処理
server {
listen 80;
### api.local.example-dev.comのリクエストを受け取る
server_name api.local.example-dev.com;
### "/"にアクセスがあったときの処理
location / {
### リクエストヘッダに$hostをセット
# $host: Hostリクエストヘッダがあればそのサーバ名、なければプライマリサーバ名(server_name)
proxy_set_header Host $host;
### ローカル環境(host.docker.internal)の3001番ポートへプロキシする
proxy_pass http://host.docker.internal:3001;
}
}
たとえば、web.local.example-dev.com
からのリクエストはserver_name web.local.example-dev.com;
のserverブロックで受け取られ、host.docker.internal
を利用してコンテナからローカル環境の3000番ポートへプロキシされます。
ローカル環境の3000番ポートではWebアプリケーションが起動しているため、web.local.example-dev.com
はローカル環境のWebアプリケーションを指すことになります。
host.docker.internal
はDocker Desktopで提供されている、コンテナからローカル環境を参照する際のホスト名です。
方法2: サブドメインに応じて動的にプロキシ先を変更する
さきほど紹介した『方法1』をよりスマートにした方法です。
サブドメインとポート番号の対応をmap
ディレクティブを利用してマッピングし、プロキシするローカル環境のURLを動的に変更する方法です。
server_name
ディレクティブは共通するドメイン部分のみ記述します。
nginxの設定ファイルは以下のようになります。
nginx/conf.d/default.conf
### $http_host(リクエストヘッダのHTTP_HOSTの値)の評価に応じて$port変数の値を変更する
map $http_host $port {
### サブドメインの文字列を評価して$portに値をセット
# ~: 大文字・小文字を区別する正規表現
# デフォルト値を用意したい場合は『default xxx』を定義
~web 3000;
~api 3001;
}
server {
listen 80;
server_name ~local.example-dev.com;
### "/"にアクセスがあったときの処理
location / {
### コンテナのDNSサーバ
resolver 127.0.0.11;
### リクエストヘッダに$hostをセット
# $host: Hostリクエストヘッダがあればそのサーバ名、なければプライマリサーバ名(server_name)
proxy_set_header Host $host;
### ローカル環境(host.docker.internal)へリクエスト。ポート番号はサブドメインに応じて動的に変更する
proxy_pass "http://host.docker.internal:$port";
}
}
上記の設定ファイルによってweb.local.example-dev.com
からのリクエストはhttp://host.docker.internal:3000
に、api.local.example-dev.com
からのリクエストはhttp://host.docker.internal:3001
にプロキシされます。
参考:『resolver 127.0.0.11;』の役割と必要性について
プロキシ先を動的に変更する場合はresolver
ディレクティブでDNSサーバ(ネームサーバ)を指定する必要があります。
resolver
ディレクティブの記述がないと名前解決ができないためno resolver defined to resolve host.docker.internal
というエラーが発生してしまいます。
127.0.0.11
はコンテナで利用されているDNSサーバのIPアドレスです。IPアドレスはdig
コマンドで確認できます。
[docker]
$ dig localhost;
(略)
;; SERVER: 127.0.0.11#53(127.0.0.11)
(略)
参考: マッピング情報を別ファイルに切り出す方法
nginxはinclude
を利用することで外部ファイルの設定を読み込めます。これを活用することでマッピング情報を切り出せます。
nginx/conf.d/default.conf
map $http_host $port {
include /etc/nginx/conf.d/domain_list;
}
server {
listen 80;
server_name ~local.example-dev.com;
location / {
resolver 127.0.0.11;
proxy_set_header Host $host;
proxy_pass "http://host.docker.internal:$port";
}
}
nginx/conf.d/domain_list
~web 3000;
~api 3001;
ファイルごとにバインドマウントしている場合はdocker-compose.yml
も修正します。
docker-compose.yml
version: '3'
services:
nginx:
(略)
volumes:
- ./nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf
+ - ./nginx/conf.d/domain_list:/etc/nginx/conf.d/domain_list
さいごに
Twitter(@nishina555)やってます。フォローしてもらえるとうれしいです!