Top/EBUG勉強会/20141115_httpdをスケールさせる

OpenBSDでhttpdをスケールさせる

川俣 吉広
EBUG勉強会 @ 新潟, 2014/11/15

発端

以前この勉強会で紹介したウェブ/メルマガのサーバが、アクセス過多によりトップページが見れなくなっているという報告を受ける。

応急対応

/var/www/conf/httpd.conf

 # It is intended mainly as a brake to keep a runaway server from taking
 # the system with it as it spirals down...
 #
-MaxClients 256
+MaxClients 192

プロセス数の上限で制限がかかっていると仮定し、httpdのプロセス数を減らすことで 子プロセス(*.cgi)の数を増やせるようにした。

→ 30分ほどで不具合は解消

対策

ProcessModel.jpg

状況をモデリングする

→httpd.confのMaxClients、 login.confの maxprocを幾つにすればよいか?

#img(): File not found:
0005mem.gif

設定値の算定


バッファキャッシュ3GBのうち、2GBを取り崩してhttpd用に割り当てるとすると
2048 / 4 = 512

とりあえず、MaxClientを512、ワーストケースとして *.cgiが同数起動されると仮定して、wwwのログインクラスのmaxprocを1024として 動作をみる。

実施と検証

httpd.conf、MaxClientsの設定

MaxClientを512にして実行

# /usr/sbin/httpd -u -DSSL
WARNING: MaxClients of 512 exceeds compile time limit of 256 servers,
 lowering MaxClients to 256.  To increase, please see the
 HARD_SERVER_LIMIT define in src/include/httpd.h.
#

httpd.confの設定値にかかわらず、MaxClientsは256を超えないように ハードコーディングされている。(Apache 2.xでは、ServerLimitディレクティブで 設定可)

ということで

/* Limit on the total --- clients will be locked out if more servers than
 * this are needed.  It is intended solely to keep the server from crashing
 * when things get out of hand.
 *
 * We keep a hard maximum number of servers, for two reasons --- first off,
 * in case something goes seriously wrong, we want to stop the fork bomb
 * short of actually crashing the machine we're running on by filling some
 * kernel table.  Secondly, it keeps the size of the scoreboard file small
 * enough that we can read the whole thing without worrying too much about
 * the overhead.
 */
#ifndef HARD_SERVER_LIMIT
#define HARD_SERVER_LIMIT 256
#endif

この部分変更してコンパイル

# cd /usr/src/usr.sbin/httpd
# make -f Makefile.bsdwrapper obj
# make -f Makefile.bsdwrapper clean
# CFLAGS="-DHARD_SERVER_LIMIT=4096" make -f Makefile.bsdwrapper
     :
     :
# make -f Makefile.bsdwrapper install
# /usr/sbin/httpd -V
Server version: Apache/1.3.29 (Unix)
Server's Module Magic Number: 19990320:15
Server compiled with....
(略)
-D DYNAMIC_MODULE_LIMIT=64
-D HARD_SERVER_LIMIT=4096
-D HTTPD_ROOT="/var/www"

login.confのmaxproc設定

loginclass.jpg
# www - resource definition for web processes
#
www:\
       :maxproc-max=1024:\
       :maxproc-cur=1024:\
       :tc=default:\

この部分の課題 ... setuidした子プロセスについてはlogin classはどうなる(右図)?

(11/21, 後日検証した結果を追記)

#img(): File not found:

運用の監視

SAG(Server Activity Grapher)にプロセス数の項目を組み込んで監視

0005procs.gif
#
#  t0005  -  executing every 5 minutes
#
#  $Id: t0005,v 1.2 2004/05/17 01:21:52 cvs Exp $

(echo =dt $DAYTIME
 echo =mem
 cat /proc/meminfo
 echo =end
 echo =procs `sysctl -n kern.nprocs; pgrep httpd | wc -l`
 echo =sensors `sysctl -n hw.sensors.cpu0.temp0`
 ) >> var/rl0005

この状況で様子を見る。

#img(): File not found:

さらに...

将来的にさらにプロセス数の増加が必要な場合を想定し、どの程度まで増やせるかやってみる。

このとき、syglog (/var/log/messages) に出力されることを発見。

Nov 14 17:56:37 mediakaw /bsd: process: table is full

カーネル内を探す;

# grep -rs ': table is full' /usr/src/sys
Binary file /usr/src/sys/arch/i386/compile/GENERIC/bsd matches
Binary file /usr/src/sys/arch/i386/compile/GENERIC/subr_prf.o matches
/usr/src/sys/kern/subr_prf.c:   log(LOG_ERR, "%s: table is full\n", tab);
#

/usr/src/sys/kern/subr_prf.c内のtablefull関数が実行されている;

# grep -rs 'tablefull.*process' /usr/src/sys
/usr/src/sys/kern/kern_fork.c:                          tablefull("process");

kern_fork.cの該当部分

    if ((flags & FORK_THREAD) == 0) {
            if ((nprocesses >= maxprocess - 5 && uid != 0) ||
                nprocesses >= maxprocess) {
                    static struct timeval lasttfm;

                    if (ratecheck(&lasttfm, &fork_tfmrate))
                            tablefull("process");
                    nthreads--;
                    return (EAGAIN);
            }
            nprocesses++;

maxprocessをどこで初期化しているか?

# grep -rs 'maxprocess.*=' /usr/src/sys
/usr/src/sys/conf/param.c:int   maxprocess = NPROCESS;

NPROCESSは?

# grep -rs '#.*define.*NPROCESS' /usr/src/sys
/usr/src/sys/conf/param.c:#define       NPROCESS (30 + 16 * MAXUSERS)

ということで

11/21追記: login.confでのmaxproc設定関連について

前項「実施と検証」/「login.confのmaxproc設定」について示された疑問を実際に設定を行うことで調査した。

調査方法

調査1: login.confの記述方法について

以下のケースAでは、ログインクラス class1 でログインした場合、設定値は幾つになるか。
さらに、ケースBではどうか。

ケースA

class0:\
    :maxproc-cur=100:\
    :maxproc-max=200:

class1:\
    :maxproc=150:\
    :tc=class0:

ケースB

class0:\
    :maxproc=300:

class1:\
    :maxproc-cur=150:\
    :maxproc-max=250:\
    :tc=class0:

結果:ケースA、ケースBのどちらでも、class1のログインクラスではclass1自身で書いた値が有効となり、class0で書いたエントリは無効となる。

調査2:

以下の設定で、user1 (ログインクラス:class1)からuser2 (ログインクラス:class2) にsuidした場合、maxprocはどうなるか?

class0:\
    :maxproc=200:

class1:\
    :maxproc=225:

結果:class1の設定値に関係なく、class2の値が新規に設定される。

調査3:

ulimitの挙動について

:maxproc-cur=A:\
:maxproc-max=B:

上記のように記述した場合、ログイン直後の最大プロセス数はAに設定されている。 シェルのビルトイン・コマンドulimit -pで変更する場合、 プロセス数を増加する方向には「Bを超えない範囲、かつ一回だけ」変更できる。
プロセス数を減少させる方向には何回でも可能。
(以上の挙動は使用するシェルに依存する可能性あり)

また、

:maxproc=C:

と記述した場合は、

:maxproc-cur=C:\
:maxproc-max=C:

と書いた場合と等価であり、上記のルールに従う。


Top/EBUG勉強会/20141115_httpdをスケールさせる

Front page   New Page list Search Recent changes   Help   RSS of recent changes