Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

APIサーバ時代のnginx設定決定版

More than 3 years have passed since last update.

かどうかは別として、こんな構成もできるよ。というネタと思っていただければ。(笑)

ご注意: この構成は所属先の見解ではなく、また、実運用の実績があるものではありません。あくまでも情報の一つとしてお読みください。

php-fpm(fastcgi)によるAPIサーバを建てるにあたって、LBをフロントエンドに置いて負荷分散。
というのは当たり前すぎる構成ですね。

図にするとこんな感じ。
スクリーンショット 2017-03-26 14.31.39.png

さて、ここからが本題。
APIサーバということは、全てのリクエストはフレームワークを経由することになります。
最近のフレームワークはpublic/index.phpを単一のフロントコントローラとしているものがほとんどです。
LaravelやLumenなどがそうですね。

ということは、バックエンド側にはwebサーバは不要だと思いませんか? phpの処理系であるphp-fpmデーモンだけが立っていれば良いと思いませんか?
図にするとこんな感じ。
スクリーンショット 2017-03-26 14.31.19.png

php-fpmはTCP/IPをlistenできるので、元々こういう使い方も想定されているわけですね。

この構成だとwebサーバが1つ減るのでオーバヘッドが減ります。CPU処理コスト、メモリ消費、アクセスログのディスクI/O、(webサーバ → php-fpmの)内部接続コストがなくなります。サーバ間の役割がはっきり分離できるのもポイントだと思いませんか?

静的ファイルについてはフロントエンド側で対応するか、静的専用のwebサーバを建てるか、S3のwebホスティングを使うか、CDNを使えば十分ですね。

具体的な設定方法

nginxをフロントエンド(LB)にした場合の設定例です。
httpsでリクエストを受けてSSL terminationをした上で、複数のバックエンドにラウンドロビンでロードバランスする構成です。

php-fpm(バックエンド側)

こちらは極めて単純。
listenアドレスに0.0.0.0を指定してデーモンを再起動するだけ。

/etc/php/fpm-php7.1/fpm.d/www.conf
listen = 0.0.0.0:9000

nginx(フロントエンド側)

/etc/nginx/conf.d/frontend.conf
upstream backends {
    server [backend IP 1]:9000;
    server [backend IP 2]:9000;
}

server {
    listen 443 ssl http2;
    server_name site.localdomain;

    charset utf-8;
    root /var/www/html;

    access_log /var/log/nginx/access.log;
    error_log  /var/log/nginx/error.log;

    # SSL terminationをする場合
    ssl on;
    ssl_certificate      /path/to/cert.pem;
    ssl_certificate_key  /path/to/key.pem;

    location / {
        try_files $uri /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass   backends;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  [php-fpm側のindex.phpがあるディレクトリのpath]$fastcgi_script_name;
        include        fastcgi_params;
    }
}

ポイントは3つ。

  • バックエンド側のphp-fpmサーバを、upstreamとして登録する。
  • nginx側のrootディレクトリ(/var/www/html)に、ダミーのindex.phpファイルを作成しておく。(重用)
  • SCRIPT_FILENAME は php-fpm側のindex.phpがあるディレクトリを指定する。

ダミーのindex.phpが必要なのは、try_filesでリクエストURLが/index.phpに変換されて存在チェックが入るからです。
このチェックが通らないと、location ~ \.php$に制御が渡らず404 NotFoundとなってしまいます。

index.phpファイルは存在している事自体が重要なので、中身は空でもかまいません。

touch /var/www/html/index.php

このあたりがもう少しエレガントに設定できれば良いのですが、他の方法が思いつきませんでした。

一応手元で環境構築してテストしたところ、ちゃんとラウンドロビンされてることと、バックエンドサーバを停止したらそこへの振り分けが停止され、バックエンドサーバを再開したら再度振り分け対象になったことは確認できました。

hirohero
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away