[nginx]ifを使って本番と開発で同じconfigを使う


tkp_TfLc34T.png

上の図のようにnginxの設定を本番用と開発用のサイトがバーチャルホストを使い同じサーバーに乗っていて両者はDocumentRootなど一部の設定が違うのでconfigを別々に定義しているという状況です。

しかし本番用と開発用の設定はほとんどが同じため別々に用意するのはちょっと煩雑です。そこでnginxのif文を使って違う部分だけを切り替えようというお話です。

nginxのif文

nginxの設定ではif文を使う事ができます。しかし、ちょっと注意すべき点がいくつかあります。

特にLocationの中などで使えないのは地味に痛い制約ですが、実は変数をうまく使う事で以外と何とかなります。

変数

nginxでは変数という仕組みもあり、こちはらディレクティブに関係なく使えます。

set $docroot "/var/www/mysite/htdocs"
root $docroot

上の例ではdocrootという変数に"/var/www/mysite/htdocs"を代入した上でドキュメントルートを変数を使って設定しています。

また、いくつか予約された変数があり、そこにはサーバーの環境変数などの情報が含まれます。

設定の差を確認

今回の例では本番・開発の間で下記の差以外は全て同じ設定になっています。

DocumentRoot

それではifと変数を使ってドキュメントルートを本番・開発で切り替わるように設定してみましょう。今回はバーチャルホストでの設定なので接続して来たクライアントのリクエストヘッダにあるHostパラメータを使って切り替えます。

nginxでは$server_nameがHostヘッダに該当するのでそれを使ってifで分岐させます。

set $docroot  "/var/www/mainsite/htdocs";
if ($server_name = "devsite.XXXXX.xxx"){
	set $docroot /var/www/devsite/htdocs";
}
root   $docroot;

nginxにはelseがないので最初にデフォルト値として$docrootを定義し、その後$server_nameが開発サイトだったら開発用の$docrootを上書きします。

PHPのオプション

先程も言ったようにif文はLocationで使えません。つまりこういう事はできない訳です。

location ~ \.php$ {
   include snippets/fastcgi-php.conf;
   fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;

   # 本番・開発によって切り替える
   set $fcgi_param  "(略)";
   if ($server_name = "devsite.XXXXX.xxx"){  # <<<<< エラーになる
      set $fcgi_param "(略)";
   }

   # PHP_VALUEを設定する
   fastcgi_param  PHP_VALUE $fcgi_param;
 }

そこで、if文をlocationの外に出してやります。これなら問題はないです。

 # 本番・開発によって切り替える
 set $fcgi_param  "(本番向け設定)";
 if ($server_name = "devsite.XXXXX.xxx"){
    set $fcgi_param "(開発向け設定)";
 }
 location ~ \.php$ {
   (略) 
   # PHP_VALUEを設定する
   fastcgi_param  PHP_VALUE $fcgi_param;
 }

PHP_VALUEが一部だけ違う場合

先程の例ではPHP_VALUEがまるごと本番と開発で違っていましたが、ほんの一部だけ違う場合は下記のようにすると便利です。nginxでは変数内に変数を使う事が可能です。

 # 本番・開発によって切り替える
 set $fcgi_param  "(共通設定)";
 if ($server_name = "devsite.XXXXX.xxx"){
    set $fcgi_param "$fcgi_param (開発向け設定)";
 }
 if ($server_name = "main.XXXXX.xxx"){
    set $fcgi_param "$fcgi_param (本番向け設定)";
 }
 location ~ \.php$ {
   (略) 
   # PHP_VALUEを設定する
   fastcgi_param  PHP_VALUE $fcgi_param;
 }

実際の設定例

以上の事を踏まえて最終的に下記のようなconfigが出来上がりました。

# ドキュメントルート。開発と本番で単純にすり替える
set $docroot  "/var/www/mainsite/htdocs";
if ($server_name = "devsite.XXXXX.xxx"){
	set $docroot /var/www/devsite/htdocs";
}
root   $docroot;

#PHP_VALUEの設定
#まず本番・開発で同じパラメータを設定する
set $fcgi_param  "post_max_size=10M
                             memory_limit=30M";

# 本番・開発で違う部分をif文を使って分岐した上で加える
if ($server_name = "devsite.XXXXX.xxx"){
    set $fcgi_param "$fcgi_param
                               open_basedir=/var/www/devsite";
}
if ($server_name = "main.XXXXX.xxx"){
    set $fcgi_param "$fcgi_param
                               open_basedir=/var/www/mainsite";
}

# PHPを動作させる部分。location内でifは使えないが変数は使える
location ~ \.php$ {
	(略)
	fastcgi_param  PHP_VALUE $fcgi_param;
}

注意点としてはPHP_VALUEの設定は1つの項目ごとに必ず改行を入れないと行けない点です。

# これは機能しない
set $fcgi_param "$fcgi_param open_basedir=/var/www/mainsite";

#改行を入れれば問題ない
set $fcgi_param "$fcgi_param
open_basedir=/var/www/mainsite";

# 改行した後スペースを入れるのは問題ないらしい(機能した)
set $fcgi_param "$fcgi_param
                                 open_basedir=/var/www/mainsite";

スペースが入る文には問題がないようですが、改行は必須なようです。

あとがき

このようにif文を駆使する事でconfigを統一する事ができました。今回はDocumentRootとPHP_VALUEだけでしたが、他にも色んな応用方法がありそうです。