大規模サイトの作り方 web server 編

前のエントリでロードバランサができましたので、次は実際にサービスを提供するウェブサーバを用意します。
apache の設定についての解説は山ほどありますのでそちらにお任せするとして、ポイントだけ書いておきたいと思います。

ウェブサーバに限りませんがサーバをたくさん並べていくと、各サーバにいちいちログインして何かするのが非常に面倒になってきて、それに伴い抜けが出る恐れが出てきます。
そんなときに ssh で直接各サーバのコマンドをたたいてあげるべく、以下のような事をしようとすると tty がないので sudo 使えないよと怒られます。

ssh host sudo /usr/bin/id -un

この場合 sudoers ファイルにある Defaults requiretty をコメントアウトしてあげれば、上記のような使い方が出来るようになります。

# Defaults requiretty

当然セキュリティポリシーによるところもあるかと思いますが……。

話をウェブサーバに戻しますが、トラフィックを考えると出力されるヘッダの量も出来る限り減らしたいところです。
php.ini で以下の指定をすると、 X-Powered-By: ヘッダを出力しなくなります。

expose_php = Off

多量のログをどう処理するかも悩ましいところですが、cronolog を使って1時間ごとに1つのファイルを作るような仕組みが良さそうです。
CentOS 5.x では以下のようにしてインストールします。
設定については後述します。

rpm -Uhv http://apt.sw.be/redhat/el5/en/x86_64/rpmforge/RPMS/rpmforge-release-0.3.6-1.el5.rf.x86_64.rpm
yum install cronolog

そしてメインの httpd.conf についてです。
どうにもまとまらないので箇条書きで。

  • ServerTokens Prod
    前述のように可能な限り出力されるヘッダを減らすため、Server 応答ヘッダを以下のように最小にします。
    Server: Apache
  • ServerSignature Off
    サーバが生成するドキュメントのフッタを抑止します。
  • Timeout 30
    ここで設定するタイムアウトは以下の通りで、初期値はかなり長いと思います。
  1. GET リクエストを受け取るのにかかる総時間
  2. POST や PUTリクエストにおいて、次の TCP パケットが届くまでの待ち時間
  3. レスポンスを返す際、TCP の ACK が帰ってくるまでの時間
  • KeepAliveTimeout 5
    ここのタイムアウトは、接続を閉じる前に待機する時間です。
    もっと短くても良いかもしれません。

以下は prefork の場合の接続数の設定です。

  • StartServers 50
    起動時の子プロセス数
  • MinSpareServers 10
    アイドル子プロセスの最小数
    これに満たない場合、毎秒1プロセスずつ起動します。
  • MaxSpareServers 50
    アイドル子プロセスの最大数
    これを越えている場合、親プロセスから kill されます。
  • ServerLimit 512
    この値を越えてMaxClients値を設定できません。
  • MaxClients 512
    最大子プロセス数(同時接続数)
  • MaxRequestsPerChild 4000
    KeepAlive が有効なのでリクエスト数ではなくコネクション数
    少なすぎる場合は頻繁に子プロセスの作り直しが行われてパフォーマンスに影響が出る恐れがあります。
    しかし0を指定して無効にしたり、むやみに大きくする事はおすすめできません。
    万一プロセスでのメモリリークがあった場合にその影響を限定的にできますし、何より子プロセスを定期的に作り直すことで親プロセスとの共有メモリを最大に出来るメリットがあります。

不要なモジュールはなるべくコメントアウトしておく方が良いでしょう。
各モジュールについて、分かる範囲ですが簡単に説明しておきます。

  • LoadModule auth_basic_module modules/mod_auth_basic.so
    BASIC 認証メソッド
  • LoadModule auth_digest_module modules/mod_auth_digest.so
    Digest 認証メソッド
  • LoadModule authn_default_module modules/mod_authn_default.so
    認証バックエンドのデフォルトモジュール(削除不可)
  • LoadModule authn_file_module modules/mod_authn_file.so
    テキストファイルを用いた認証バックエンド
  • LoadModule authn_dbm_module modules/mod_authn_dbm.so
    dbm ファイルを用いた認証バックエンド
  • LoadModule authn_alias_module modules/mod_authn_alias.so
    AuthnProviderAlias ディレクティブを提供しているモジュール
  • LoadModule authn_anon_module modules/mod_authn_anon.so
    認証で匿名を使うモジュールのようですが……
  • LoadModule authz_host_module modules/mod_authz_host.so
    ホスト名または IP アドレスでのアクセスコントロール
  • LoadModule authz_user_module modules/mod_authz_user.so
    ユーザ名によるアクセスコントロール
  • LoadModule authz_owner_module modules/mod_authz_owner.so
    ファイルの owner によるアクセスコントロール
  • LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
    グループファイルによるアクセスコントロール
    大抵の解説ページで AuthGroupFile /dev/null と記述されているので不要だと思いますが、コメントアウトすると AuthGroupFile の記述があるところでエラーになりますのでそこだけ注意が必要です。
  • LoadModule authz_default_module modules/mod_authz_default.so
    アクセスコントロールのデフォルトモジュール(削除不可)
  • LoadModule ldap_module modules/mod_ldap.so
    LDAP による認証モジュール
  • LoadModule authnz_ldap_module modules/mod_authnz_ldap.so
    LDAP によるアクセスコントロール
  • LoadModule include_module modules/mod_include.so
    SSI モジュール
  • LoadModule log_config_module modules/mod_log_config.so
    ログ機能のモジュール
  • LoadModule logio_module modules/mod_logio.so
    ログで入出力バイト数を記録するためのモジュール
  • LoadModule env_module modules/mod_env.so
    環境変数
  • LoadModule expires_module modules/mod_expires.so
    Expires/Cache-control ヘッダ
  • LoadModule deflate_module modules/mod_deflate.so
    圧縮機能
  • LoadModule headers_module modules/mod_headers.so
    HTTPヘッダ
  • LoadModule usertrack_module modules/mod_usertrack.so
    Cookie* ディレクティブを提供
  • LoadModule setenvif_module modules/mod_setenvif.so
    SetEnvIf ディレクティブ関係を提供
  • LoadModule dav_module modules/mod_dav.so
    LoadModule dav_fs_module modules/mod_dav_fs.so
    WebDAV 関係
  • LoadModule status_module modules/mod_status.so
    server-status を確認するモジュールです。
    基本的に切る方が良いと思いますが、確認のため一部サーバで有効にしておくのもありだと思います。
    設定については後述します。
  • LoadModule cache_module modules/mod_cache.so
    ドキュメントキャッシュ(RFC2616)のコアモジュール
  • LoadModule disk_cache_module modules/mod_disk_cache.so
    ドキュメントキャッシュモジュール
  • LoadModule mem_cache_module modules/mod_mem_cache.so
    ドキュメントキャッシュモジュール
  • LoadModule file_cache_module modules/mod_file_cache.so
    ファイルキャッシュモジュール(旧)
    これは今まで使ってなければ今後使われないのではないでしょうか……。
  • LoadModule cgi_module modules/mod_cgi.so
    CGI モジュール

以下、ログ関係の設定です。

ロードバランサや監視サーバの IP アドレスからログを取らないようにするため、特定の IP アドレスからのアクセスに環境変数をつけてあげます。

SetEnvIf Remote_Addr xxx.xxx.xxx.xxx nolog

画像ディレクトリのアクセスはすべてログを取らないようにするため、以下のように環境変数を設定してあげます。

SetEnvIf Request_URI "^/img/" nolog

今回の要、cronolog の設定です。
以下では月ごとにディレクトリにわけて、時間ごとのログを出力します。
また現時刻のログに対して current というシンボリックリンクを張ります。

ErrorLog "|/usr/sbin/cronolog -S \
      /var/log/www/err/current \
      /var/log/www/err/%Y%m/%d-%H.log"
LogLevel warn
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
CustomLog "|/usr/sbin/cronolog -S \
       /var/log/www/acc/current \
       /var/log/www/acc/%Y%m/%d-%H.log" combined env=!nolog

特にサービス開始直後からしばらくは、サーバステータスを確認してあげた方が良いでしょう。
以下のように IfModule で囲ってあげれば、必要な時や特定サーバだけ LoadModule で呼んであげれば有効になります。
また念のため IP address での制限をかけておきます。

<IfModule mod_status.c>
    ExtendedStatus On
    <Location /server-status>
        SetHandler server-status
        Order Allow,Deny
        Allow from yyy.yyy.yyy.yyy
    </Location>
</IfModule>

ウェブサーバとしてはこのくらいでしょうか。
実際はサーバ上で走るプログラム自身の作りが重要になってきますので、ウェブサーバで出来るチューニングは限られてくると思います。

今回の記事については以下の図書が参考になります。

UNIX

Posted by yokky