黄昏より暗きもの、血の流れより赤きもの

読者です 読者をやめる 読者になる 読者になる

黄昏より暗きもの、血の流れより赤きもの

自分の好きな事を好きなように書いて行きます。

UNIX/Linuxに慣れよう(2):Linux(apache)がメモリリークを起こして動かなくなったときに確認すべき5つの事

はじめに

本記事は以下の読者層を対象にしています。

本記事の想定する読者層

1.趣味のサーバーなど損害の無い環境にてUNIX/Linuxを運用している
2.vi/emacsを使ってテキストファイルを編集できる

実務の現場で実際にトラブルが起こった場合は、各部署の上司等の監修の下で操作を行って下さいますようお願い致します。尚、本記事の内容は各自でご確認の上、本記事を読んで実践した結果につきましては責任をおいかねますのでご了承下さい。

はじめに:現状動いているプログラムについて

今日はLinux(apache)がメモリリークを起こして動かなくなったときに確認すべき5つの事について話します。現状このようなプログラムを動かしていました。

概要:pixivのラブライブ!のキャラクタータグ毎の投稿数を収集するプログラム
1.言語はPHP、投稿数はWebスクレイピングを使って取得(GitHub参考)
2.1.で取得したものをMySQLに保存
3.1〜2の作業を書いたPHPプログラムを数種類、1時間または1日おきに実行するようにcronで設定

こんな事をしていたらある日突然メモリリークが発生してしまいました。今日はそんな時自分が対処した5つの方法について話します。

apacheメモリリークを追求するのは難しい!

apacheメモリリークが原因で動作が止まる事があります。この原因を追求するのは難しいようで、「PHP メモリリーク:ITフロギストン~技術系ブログみたいなもの」に依れば、phpに起因しているようです。

とはいえ、メモリリークの原因を探す作業は、砂漠に落とした針を探すくらいに難しいのです。メモリを解放しないだけで、エラーも返さず見た目上は正常に動作しているのですから。他にも問題が山積みだったこともあり、初めからいるメンバーたちも、わかっていても放置せざるを得なかったのでしょう。

 私も偶然の助けがなければ、メモリリークの原因を見つけることはできなかったかもしれません。あるオブジェクトをvar_dumpしてみたところ、変数の一つが「*RECURSION*」という値を吐き出していました。これは再帰を表しています。その変数を追ったところ、循環参照をしていました。PHP5.2では循環参照を起こすとメモリを解放しない問題があるのです。この部分を修正するだけで、メモリリークは起きなくなりました。(PHP メモリリーク:ITフロギストン~技術系ブログみたいなもの)

ととにかく解決の難しい問題なようです。起こったらまず心当たりのある所を洗って調べてみるしかなさそうです。以下、その時自分が行った事をまとめたので見てみて下さい。

実際に行った事

1.「apachectl stop」でapacheを止める

最初に行ったのはapachectl stop」を使ってapacheを止めた事です。何度もLinuxを再起動したのですが、何度もOUT OF MEMORYと言うだけで動きません。そこで隙を見て「apachectl stop」を入力して、原因を追求しやすい体制を取りました。

2.メモリリークを起こしそうなスクリプトを整理する

次にメモリリークを起こしそうなスクリプト等を整理しました。Webスクレイピングを行うスクリプトが数点あったので、これが原因だと思って調査に使うもののみに整理しました。

wordpressでブログをやっていたのですが、アクセス状況に依ってはデータ収集作業ができなくなるのでこれも消しました。

趣味でやっているとあれもこれも1つのサーバーでやってしまおうと思いがち。そして「このサーバーはどういう用途で使うか?」と言う部分を見直す事で、メモリリークを防ぐ事ができる事に気がつきました。

3.apacheのバッファの量を調整する

次はapacheのバッファの量や設定を調整しました。「php + apache のメモリ量をおさえる | レンタルサーバー・自宅サーバー設定・構築のヒント」を参考に
以下のように行いました。

1.「apachectl stop」でapacheを止める
2.「vi /etc/httpd/conf/httpd.conf」で、apachehttpd.confを起動
3.通常モードに入り、「/prefork」でpreforkの場所を検索
4.preforkを編集する
5.「apachectl start」でapacheを再起動する

preforkの変更例

まずpreforkとは、Unix/Linux向けのプロセスを使った並列処理を行うモジュールの事を言うようです。つまり並列処理をするプロセスの管理を見直す事で、メモリリークを防ぐ事ができるようです。以下自分がどのように編集したか軽く紹介致します。

<IfModule prefork.c>
##StartServers:起動時に生成される子サーバプロセスの数
StartServers 4
##MinSpareServers: アイドルな子サーバプロセスの最小個数
MinSpareServers 2
##MaxSpareServers: アイドルな子サーバプロセスの最大個数
MaxSpareServers 10
##ServerLimit: 設定可能なサーバプロセス数の上限
ServerLimit 128
##MaxClients: リクエストに応答するために作成される子プロセスの最大個数
MaxClients 128
##MaxRequestsPerChild: 個々の子サーバが稼働中に扱うリクエスト数の上限
MaxRequestsPerChild 30
</IfModule>

このときMaxRequestsPerChildを30にする事で、30個のリクエストを実行した後プロセスを消す事で古いプロセスを貯めずに設定しました。

現状サーバーのメモリ容量が1GBに対し空きメモリが700MB位で安定しているので、若干効果があったのかなと思います。理由も分からず他所のブログの真似したと言うのが正直な所ですが、どうもサーバーを使う用途やcron等で動かすphpスクリプトの設定をする際に、同時にapacheの設定を済ませておく必要があったようです。

4.定期的にメモリキャッシュを解放するようにcronを設定する

今度は定期的にメモリキャッシュを解放するようcronを設定しました。というのも収集したデータを取り出すのに外部から操作できない事態を防ぐために、メンテナンス作業を自動化できないか試みました。

具体的には「Linux のメモリキャッシュをクリアする:futuremix」を参考にcronを以下のように書き直しました。

00 3 * * * sync
00 3 * * * echo 3 > /proc/sys/vm/drop_caches

ちなみにechoの次の数字は/proc/sys/vm/drop_cachesに書き込む数字で、それぞれ以下の用途で用いるようです。

1. ページキャッシュ解放
2.ダーティキャッシュ、inode解放
3. ページキャッシュとダーティキャッシュ、inode解放
「Linux のメモリキャッシュをクリアする:futuremix」

尚cronを使ってメモリキャッシュを削除する時間帯についてですが、他のプロセスが動いて居ない時間帯に設定すると良いでしょう。

自分の場合毎日午前0:00と午前12:00にデータ収集を行うように設定しているため、午前3:00にキャッシュの削除を行うように設定しました。

5.今までやった事をまとめる

最後に今までやった事をまとめました。今までやった事をまとめる事で、次運用するときの計画を立てやすくなる事でしょう。例えば今日やった事をまとめると、

1.まずapacheを停止し、原因を調べる
2.原因となっているphpスクリプトを改修、又は削除、整理する
3.apachehttpd.confのprefork.cの部分を調整する
4.定期的にメモリキャッシュを削除するように設定し、メモリリークを発生させないようにする

自分の勘ですが、今後メモリリークが起こったとき上記1~3は必ず行う事になりそうです。どうもcronをセットしたらメモリの事も考えるという教訓を得た次第です。

最後に

とこんな具合にメモリリークの対策を書いてみました。メモリ不足が起こるのはPCもサーバーも一緒という月並みの事を勉強した次第です。最後に本記事が皆さんの役に立てば幸いです。

こちらも合わせて読みたい

「PHP メモリリーク:ITフロギストン~技術系ブログみたいなもの」
「php + apache のメモリ量をおさえる | レンタルサーバー・自宅サーバー設定・構築のヒント」
「Linux のメモリキャッシュをクリアする:futuremix」