SSブログ

apacheのチューニングの基本 [Linux(Apache)]

 apacheのチューニングは、サイトの特性によっていろいろ変わってくるので、一概に「これがいいよ!」という設定値が存在しない。まあ、「ab」を使って値を注意深く分析しなさい。という一文で終わってしまいがちだね。(笑)

 ただ、そうはいっても「基本的にはこうしたほうが良い結果が得られることが多いよ」というポイントも存在するので、それらを紹介しておく。

原則1:ログの出力処理は「重い」ということを忘れない
 apacheが絶えず行う処理の中で、「重い」処理の部類に入るのが、実は「ログの出力」である。これ、結構忘れがちになってしまうが、
  ・あとでアクセス解析とかしたいから
  ・セキュリティ上チェックを欠かせないから
  ・よく判らないけどとりあえずログとっとけ
 みたいな理由で、大きなログを取得して(そしてそのまま放置されて)いることが少なくない。確かに、ログが無きゃ無いであとで困ったことになることもあるから、保険の意味もこめてログを取得することは実際問題として重要な意味を持つ。が、ログにも「取り方」ってものがあるんですよ。(笑)

 ログは、
  1-1.必要な分だけログを取る
  1-2.後で細工できる部分はあとでやる
  1-3.どうしても必要なログは可能なら高速に書き込める領域で

 という方針を堅持したい。

 1-1.必要な分だけログを取る
 例えば、「ナントカ.html」とか「ナントカ,cgi」とか「ナントカ.php」とか、そういうリソースへのアクセスについてだけログを取得するようにし、画像ファイルへのアクセスはログを取らない…という方法を考えてみる。
 画像への直リンクをされても一向に構わないよという場合はそれでも必要十分であることが少なくない。
 例えば、どこかの企業サイト等を想定してみよう。
 アクセス解析という観点でログを採取するなら、「どのページをみたか」「どこから来たか」というような情報がほしいはずであって、個々の画像ファイルについてはあまり意に介さないことがほとんどではないだろうか。たまに、何か勘違いしたようなサイトなどでは、1つのhtmlページに数十・数百の画像ファイルが貼り付けてあることもしばしばあるが、それを全部ログ出力していたら、書き込みにどんだけサーバ負荷を奪われるのか想像に難くない。
 また、セキュリティの観点でログを採取するとしても、画像単体をクラックしにくるよりは、何がしかのCGIとかSSIとかhtmlに埋め込まれたフォームとかが対象にされることだろうから、画像ファイルへのログは採取する必要性に乏しいといえる。よって、画像ファイルへのアクセスについてはログ出力をあきらめてもさしたる問題にならないかもしれない。

 このような割りきりが可能であれば、さっそくhttpd.confに以下のような設定を追加するとよいかもしれない。
SetEnvIf Request_URI "\.(gif|jpe?g|png)" nolog
CustomLog "logs/access_log" ????? env!=nolog

 「?????」の部分は、LogFormatデレクティブでログの書式を定義している際に付与した名前を入れる。
 リクエストされたファイルの拡張子が「gif」とか「jpg」とかであった場合、環境変数「nolog」が定義される。これが定義された場合はログを出力しないように振舞う定義を行っている。


 1-2.後で細工できる部分は後でやる
 要するに、「DNS逆引きはやるな」ということ。(笑)
 具体的には
UseCanonicalName Off
HostNameLookups Off

 を入れとけってことね。
 というのも、デフォルトのままではログファイルにアクセス元のアドレスを出力する際に、IPアドレスではなくDNSを逆引きした結果の「名前」が入るようになっている。ということは、ログを1件1件出力するたびにDNS検索が走ることとなるので、余計な処理・トラフィックがログの1件ごとに発生することとなる。
 どうせ、後からログをチェックするなら、チェックするタイミングでnslookupをすれば済むだけの話なので、ログには生のIPアドレスを書くだけにしておけ!

 …ということ。


 1-3.どうしても必要なログは可能なら高速に書き込める領域で
 ログの出力…つまり、ファイルへの書き込みは「重たい」部類の処理に入ることはすでに説明した。「1-1.」でそもそも出力する対象とすべきログの量を削減すべきことを説明したが、それでも一般に公開しているようなサーバは結構な量のログを出力することとなるだろう。
 そのような場合はなるべく高速に書き込める領域へのログ保存を行うことを推奨する。

 具体的には
 可能性1:シリコンディスク(RAMディスク等)への保存
 可能性2:RAID0(RAID10でもいいし)領域への保存
 可能性3:別コントローラ・別ディスク領域への保存
 可能性4:同一コントローラ・別ディスク領域への保存
 可能性5:せめて別パーティションへの保存

 ということであり、
 禁止事項:NFSやCIFS領域への保存
 できれば避けたい事項:OSと同じ領域への保存

 ということになろう。

 可能性1について
 別に、高価なシリコンディスクオプションを買って来いといっているわけではない。
 いまどきのPC・サーバならメモリがとても安く手に入ることだろうし、最初から潤沢なメモリを搭載しているかもしれない。とすると、Linuxインストール時に標準的に確保される領域「tmpfs」を使うという手がある。
 これは仮想RAMディスクではあり、サーバに搭載されているメモリ量の最大で1/2までをストレージ領域として用いることができる。標準状態では/dev/shmとかにマウントされているので、これを適切な場所にマウントしてそこにapacheのログを吐かせれば、うそみたいにログ出力処理が軽くなる。
 まあ、その分メモリを食いつぶすことになるので、これを使ったばかりにswapを触るようになってしまっては本末転倒もはなはだしいが。

 可能性2について
 RAMディスクがダメなら、RAID0でディスクに対するスループットを上げる方法も考えられる。
 最近はハードディスクもアホみたいに安くなってきており、小さめのディスクを数本用意してストライピングを構成すれば、圧倒的な書き込み性能を確保することだって可能だ。
 さらに、Webサーバをロードバランサの下に複数台数展開しているような商用環境等では、1Uサーバでもディスクを2本とか入れてRAID1(ミラーリング)でOS領域を確保しているというところもあるかもしれない。そんな場合は、【思い切って】その2本のディスクをRAID1でなくRAID0にしてしまおう。

 「はぁ!?ディスク飛んだらどうすんの!?」

 という懸念もあるかもしれない。が、よくよく考えてほしい。なんのためにロードバランサがあって、何のためにWebサーバが複数いるのかということを。Webサーバが1台くらい飛んでも大丈夫なように、ロードバランサがあり、Webサーバの複数展開をしているはずではないか?
 それなら、その過剰な冗長性を一部解除して、スループット上昇に転用してもよいのではないだろうか。

 なお、OS領域もRAID0におかれるようになると、サーバ全体の動作がほんのりと機敏になるので個人的にはお勧めしたいのだけども。(笑)
 まあ、サーバが1台しかなくて、ディスクが飛んだら困る…みたいな場合はさすがにお勧めしないけども。

 可能性3~5と、「できれば避けたい事項」について
 RAID0が無理なら、せめてOSの領域とはまったく別の世界にログをおいてね!ということ。

 SCSI(SAS)などを使用しているなら、SCSIコントローラから別の「バス」に置いたディスクにログを保管したい。SATAなどではせめて別のディスクに保管したい。というのも、OSが入っているディスクはたとえ何もしていなくてもある程度のIOが日常的に発生してしまうものである。だからそこへさらにログ出力のためのIOを上乗せするようでは、ログ出力のための性能劣化がより明らかに出てしまうこととなるだろう。
 だから、よくを言えば「別のバス・別のディスク」だが、それが無理なら「別のディスク」に保存したい。
 また、1Uサーバでディスク1本しか入らないの!  みたいな残念なケースであれば、せめて/bootや/以外にログ専用のパーティションを作成してそこに記録するようにしたい。

 「禁止事項」について
 これはもう説明不要だろう。たまにあるのだが、「複数のWebサーバからログを集めてくるのが面倒くさいので、NFSの領域にログを吐き出せば…」というアイディアを本当にやっちゃた例が。(大笑)
 あー。きっとこれやった人は「ナイスなアイディアじゃん!」とか思ったんだろーなー。(笑)

 NFSとかCIFSとかやってる領域へのログ保存はご法度。
 ローテート済みのログならいざ知らず、今まさに出力中というようなログは絶対に置いちゃダメ。絶対。

原則2:絶対にswapしない!させない!頼らない!
 「swap3ない運動」を徹底しましょう。(笑)

 swapを触っていいことなんて何もありません。このためには、
 可能性1:必要のないモジュールは読み込まない・組み込まない
 可能性2:プロセス起動本数の調整は細やかに
 可能性3:メモリ増設・サーバ増設も否定しないで
 可能性4:サーバアプリケーション開発スタッフの「手抜き」に目を光らせよう

 可能性1について
 DSOでモジュールを組み込む場合、全く必要の無いモジュールもバンバン読み込んでしまうことがある。例えば、「BASIC認証なんか使わない」のに、認証系のモジュールを組み込んでいる場合とか。「WebDAVなんか使わない」のに、WebDAVのモジュールを組み込んでいる場合とか。
 httpd.confにLodeModuleディレクティブを大量に記述していると、httpdプロセスが消費するメモリ量がどんどん増加することになるので、「必要の無いものは読み込まない」という意識を徹底したほうが良い。

 必要なモジュールが少なければ、
  利点1:httpdが消費するメモリ量が少なくて済むので、その分プロセスを大量に起動できる(≒より多くのリクエストに対応できる)ようになる
  利点2:読み込むモジュール・確保するメモリが少なくなるので、そのためにかかる時間(オーバーヘッド)が短くて済む

 ということに。

 可能性2について
 プロセス数をてきとーにバーンと確保してしまうと、プロセスが大量に起動してメモリを食いつぶしてしまいパフォーマンスダウンを招くだけでなく、逆に「メモリが有り余ってるのにプロセス数が足りず、処理待ちになってしまうリクエストが発生する」なんてこともある。

 また、アイドル状態(リクエスト待ち状態)のプロセスについては「最大」と「最小」と両方設定するディレクティブがあるが、それぞれ適切な数値に設定しなければ、プロセスの上げ下げにCPU時間を消耗することもあれば、プロセス最大数の設定値にかかってリクエスト待ちになっちゃったり、swap触ったりすることも発生してしまう。
 この設定値として適切な値を探し出すツールとして「ab」を使うことになるのだが、基本的なセオリーとして

  アプローチ1:プロセスの最大数はhttpdやCGI(PHPなど)が必要するメモリ量との相談ということになるが、サーバオペレーションのために必要なメモリもある程度は残しておくこと
  アプローチ2:MinSpareServersはあまり大きすぎる値にしない。apacheはhttpdの数がこの設定値を下回ると、「使わないかもしれない」httpdであってもわざわざ起動して待機状態に置こうとするので、この設定値は小さいほうがよい。
  アプローチ3:MaxSpareServersは逆に、小さすぎる値にしないこと。メモリに余裕があるなら、むしろある程度大きくしておき、プロセスの上げ下げを抑止したほうが全体のスループット確保には有利に働くことが多い。(絶対そうかと聞かれると、NOというケースもあるんだけどね)
  アプローチ4:MaxRequiestPerChildを使うなら、ある程度大きな数値にすべき。デフォルト値では10000という大きな数値に設定されている。たまに「200」とか「500」とかに絞る人を見かけるが、小さな画像をいっぱい張っているようなサイトではプロセスの上げ下げが頻発することとなるので、小さな数値にすることは全く推奨できない。ただし、「メモリリークを起こしまくるような素敵なCGI」を動かしているような場合は、小さな数値にすることが良いとされている。もっとも、値を小さくする前にその「メモリリーク」というバグを直せ!というのがスジであると思うのだが。(大笑)
  アプローチ5:mem_preforkでなく、mpm_workerを使うようにできないか検討すべき。いちいちプロセスを起動するよりは、LWPの方が処理としては軽い。しかし、「残念なCGI」の場合にはスレッドセーフでないことが多いので、mem_preforkを使ってくれという要求が出てくることもあろう。CGIとか使わないサーバであれば、mpm_preforkを使う理由は全く無いだろう。

 可能性3について
 そりゃ、メモリもサーバも潤沢にあればよいにこしたことは無いよね。(笑)

 可能性4について
 サーバ・ネットワークエンジニアが、つめの先に火をともすようなカツカツのチューニングをしても、一部の(…いや、もっと多いかもしれない)アプリケーション開発エンジニアがリソースを湯水のように使ってわれわれの苦労を台無しにしてくれるなんてことも少なくない。(笑)
 かつて、財務大臣を務めた「塩爺」こと塩川正十郎氏が「母屋でおかゆをすすっているときに、離れですき焼きを食べている」なんて発言をしたことがあるが、まさにそんな心境が理解できる気がするよね。(笑)

 申し訳ないが、こういう「リソース」という観点から見た場合、アプリ開発エンジニアは無能な連中が多い(無能というか無関心というか無理解というか…)ので、放置しておくとブックブクのメタボリックCGIが平然とリリースされたりするので、注意が必要だ。

 だいたい、連中はメモリリークなんて「あって当たり前」だと思っているんだからもう…(ブツクサ

 そんな訳で、「貴重なメモリ資源」を食いつぶすダメアプリには目を光らせておきましょう。

原則3:意外と重たい「FollowSymLinks」とさらに重たい「SymLinksIfOwnerMatch」
 Optionsディレクティブに、FollowSymLinksやSymLinksIfOwnerMatchを記述(allも含めて)している場合、それを削る方向で考えよう。

 ディレクトリの構成上、例えば画像ファイルとかを置いているディレクトリをシンボリックリンクであっちこっち使いまわしてみたりすることもある。が、それは良くないので避ける方向で考えるべき。
 というのも、例えば
DocumentRoot  /var/www/http
<Directory />
  Options FollowSymLinks
</Directory>

 なんてやって、http://servre/foo/bar/index.html とかリクエストが来た日には、
  ・/
  ・/var
  ・/var/www
  ・/var/www/http
  ・/var/www/http/foo
  ・/var/www/http/foo/bar
  ・/var/www/http/foo/bar/index.html
 ↑これら全部が、シンボリックリンクかどうか全てチェックされた上で、さらにシンボリックリンクの状態を確認しようとしてしまう。つまりそれだけ無駄なシステムコールがバシバシ発生してしまうということになる。さらに悪いことに、このシステムコールは【必ずディスクアクセスを発生させる】ということ。メモリ上にキャッシュされた情報を使わずにチェックを必ずディスクにしにいくということなので、手軽に使えるからといってシンボリックリンクを多様すると、パフォーマンス的に痛い目を見ることになる。
 また、実際にシンボリックリンクを使っていなくても、Optionsにこれらの設定を有効化しただけでこれらの処理が行われてしまうので、

  「シンボリックリンクは使わない」

 という方針を真っ先に固めておくべき。
 なお、「SymLinksIfOwnerMatch」はさらに重たい処理を発生させてしまうのでさらに使うべきではない。

原則4:「.htaccess」はなるべく使わない
 「.htaccess」を多様したサイト構築をしている、アレな人を時折見かける。認証のために必要かもしれないだろうし、PHP等に何かパラメータを渡したいということもあるかもしれない。

 が、それは「使いたいディレクトリ」だけで「.htaccess」を許可(具体的にはAllowOverrideを許可)するようにすべきで、用の無いところでは使わないようにすべき。

 というのも、「AllowOverride」で何らかの情報を上書きするように設定されているディレクトリの下では、「.htaccess」というファイルがあるかどうか必ずチェックするようになる。よって、「.htaccess」が必要なくて設置もしてないディレクトリでは、必ず無駄なファイルアクセスが発生することとなる。これはありとあらゆるファイルアクセスでも発生するので、結構馬鹿にならないのである。
 また、「.htaccess」が必要ないとわかっていても、実際にファイルチェックを行わないと各ディレクティブの設定や変更が完結しないということになるので、サーバからのレスポンスがそれだけ遅延することとなるのである。これはもったいない。

 そんな訳で、「AllowOverride」は必要のある場所だけで、許可するようにDirectoryディレクティブ等で設定すべき。間違っても、ドキュメントルートからいきなり許可とか絶対やらないこと。(笑)

原則5:「DirectoryIndex」ディレクティブにワイルドカードが使わない(絶対)、必要のあるものだけ書く(なるべく)
 地味に効いてくるのが、この「DirectoryIndex」ディレクティブ。

 このディレクティブに、用も無いのにたくさんのファイル名を列挙したがる人がいる。例えば…
DirectoryIndex index.cgi index.php index.pl index.shtml index.html

 こんな状態。で、http://server/ とかリクエストされて、そこには「index.html」しか無かったりすると…

  ・index.cgiがあるかな…?   無いや。
  ・index.phpがあるかな…?   無いや。
  ・index.plがあるかな…?   無いや。
  ・index.shtmlがあるかな…?   無いや。
  ・index.htmlがあるかな…?   あ、有った。よし。これを使おう!

 ということになる。
 「このページをブックマークしてね!」みたいなことをやってて、そこがこんな状態だと、「http://server/」あるいは「http://server」でリクエストしてきた人が現れるたびに毎回こんな状態になってしまうのである。

 必要の無い、あるいは最初から無いと判っているファイル名は記述しないこと。これを徹底するように。


 まとめてしまうと、
  ・面倒くさがらないこと
  ・注意深く観察すること
  ・アプリ開発スタッフは信用しないこと(笑)
 という3点につきますな。(笑)ま、3点目は30%くらい冗談だけどね。(笑)

nice!(0)  コメント(2)  トラックバック(0) 
共通テーマ:パソコン・インターネット

nice! 0

コメント 2

NO NAME

>「複数のWebサーバからログを集めてくるのが面倒くさいので、NFSの領域にログを吐き出せば…」というアイディアを本当にやっちゃた例が。(大笑)
 あー。きっとこれやった人は「ナイスなアイディアじゃん!」とか思ったんだろーなー。(笑)
→単純に私もそう思いました。いけない理由はなんでしょうか?同時に同じファイルを開くからLockが掛かるからでしょうか?
by NO NAME (2010-11-25 11:30) 

ぴろりん

 NFSの領域にログを直接吐き出さない理由は、ロックの問題もありますが、なんと言っても書き込み速度の遅さが理由の筆頭に挙げられます。
 NFSのマウントオプションをasyncにしたとしても、apacheは特にログを大量に吐き出す傾向にあるものですから、NFSサーバ側のキャッシュ領域が結構簡単に埋まってしまうということが挙げられます。となると、Webサーバが1台とか2台とか3台とかならまだしも、10台…20台…30台…なんてことになってくると、NFSサーバ側のログ記録がそのうち追いつかなくなってWebサーバ全てが「NFSサーバ待ち」という状態に陥る恐れが生じます。
 よって、apacheのログをNFSの領域に直接吐き出すのは避けたほうが良いのです。

 まあ、ネットワークの構成とかにもよりますが、apacheが激しくリクエストを処理しているところにNFSでさらにネットワークの帯域を占領~なんてことになったら…

 なお、それでもど~してもNFSの領域にログを直接吐き出したい場合は、ログファイルの名前をWebサーバごとに変えるとか、サブディレクトリを分けるとかいうことくらいはしましょう。全く同一のログファイルに複数のWebサーバからログを記録しようとするとファイルの中身が大変なことになりますので。(笑)
by ぴろりん (2010-12-10 14:57) 

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

※ブログオーナーが承認したコメントのみ表示されます。

トラックバック 0

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。