これは旧eviry tech blogから移行した記事です。
早々にブログ記事の更新が厳しくなってきたので、
過去に社内ドキュメントにまとめたものを加筆再利用で公開してみようという試み。
これは案外うまくいった解決事例であり、実際にはこううまくいくことばかりではない。
調査当時、顕著なメモリ消費が見られたもの
- ロールAのEC2インスタンス (調査の発端)
- 追加で確認できたロールBのEC2インスタンス (2017.06判明)
- guzzle の特定バージョンに由来することが判明
- 当時のPHP AWS-SDKが利用していた
- ↓あとでここに画像が来ます
参考URL
- アプリケーション内でhttpsによる外部APIを叩いているサーバのメモリ使用量が増加し続ける件について - s_tajima:TechBlog
- slab肥大化とdentry_cacheに辿り着くまでの話 - Qiita
- aws sdkを使っているとslabキャッシュが増大化していく - Qiita
- サーバーのメモリが少しずつ圧迫される原因と対策を調べてみた - Qiita
- negative dentry と tmpfs で negative dentry がキャッシュされない理由について調べた - hibomaのはてなダイアリー
- httpsでリクエストをするとslab cacheが増えて解放されない - Qiita
調査の進め方
- Zabbixによるメモリ量の定期モニタリング
- 1によりメモリ消費量が起動時間に比例して進むことを確認
- 2から「当該インスタンスにおいて定期実行される何かしらの処理に起因する可能性」と推察
- 当該インスタンスはロールAに属する、Cronによる定期実行バッチサーバであった
- Cron実行ジョブの洗い出し、特に毎分実行されるもの
- 5と合わせて、バッチは過去よりPHPで実装されているため「PHP メモリリーク」などで検索をかける
- 6の成果が芳しくなかったため、ライブラリに依存する問題ではないかとさらに仮説を立てる
- 毎分使用メモリ量が増えていくため、 Cronが第一容疑者であることは間違いない という確信めいたものがあった
- 7より利用しているPHPライブラリを確認
- 8から主要なライブラリであったAWSのPHP-SDKをまず疑う
- 検索により参考URLをもりもり引き当てる
原因
- CentOS6(正確にはRHELクローン)由来のcURLでは、クライアントとしてHTTPS通信を行う際には、NSSライブラリを参照する
- NSSライブラリは 実行時にファイルシステム性能チェックを行う1
- 2の時、 存在しないファイル名を意図的に大量に呼び出す。これが良くない。
- 3の時、 negative dentry cache がたまる2
- 4の時、 /proc/meminfo における Slab にカウントされる3
- 5のままユーザ使用可能メモリ量は減った状態を維持し解消されない
具体的な問題点
定期実行でメモリリーク誘発
- 当時利用していたAWS-SDK(php) の guzzle が実行されるタイミング
- guzzle が cURL に依存している
- Yum由来の cURL が NSSライブラリに依存している
- /tmp にtmpファイルの作成/削除等を繰り返しても同じ現象になるとのこと
- /dev/shm など tmpfs 上でやるとよいそうだ
暫定的な解決法
# 例) rootで実行 echo $(date ; grep Slab /proc/meminfo) # 実行前 sync ; sync ; sync ; echo 3 > /proc/sys/vm/drop_caches # クリア echo $(date ; grep Slab /proc/meminfo) # 実行後
恒久的な解決法
- PHP AWS-SDK のアップデート
- または cURL で OpenSSLを利用するようリコンパイル