eviry tech & service blog

「株式会社エビリー」の社員ブログです。弊社では、クラウド型動画配信サービス「millvi」、ソーシャル動画データ及び分析サービス「kamui tracker」、YouTube総合メディア「かむなび」を開発・提供しています。https://eviry.com/

cURLによるメモリリークとその調査の記録

これは旧eviry tech blogから移行した記事です。

早々にブログ記事の更新が厳しくなってきたので、
過去に社内ドキュメントにまとめたものを加筆再利用で公開してみようという試み。
これは案外うまくいった解決事例であり、実際にはこううまくいくことばかりではない。

調査当時、顕著なメモリ消費が見られたもの

  • ロールAのEC2インスタンス (調査の発端)
  • 追加で確認できたロールBのEC2インスタンス (2017.06判明)
  • guzzle の特定バージョンに由来することが判明
    • 当時のPHP AWS-SDKが利用していた
  • ↓あとでここに画像が来ます

参考URL

調査の進め方

  1. Zabbixによるメモリ量の定期モニタリング
  2. 1によりメモリ消費量が起動時間に比例して進むことを確認
  3. 2から「当該インスタンスにおいて定期実行される何かしらの処理に起因する可能性」と推察
  4. 当該インスタンスはロールAに属する、Cronによる定期実行バッチサーバであった
  5. Cron実行ジョブの洗い出し、特に毎分実行されるもの
  6. 5と合わせて、バッチは過去よりPHPで実装されているため「PHP メモリリーク」などで検索をかける
  7. 6の成果が芳しくなかったため、ライブラリに依存する問題ではないかとさらに仮説を立てる
    • 毎分使用メモリ量が増えていくため、 Cronが第一容疑者であることは間違いない という確信めいたものがあった
  8. 7より利用しているPHPライブラリを確認
  9. 8から主要なライブラリであったAWSのPHP-SDKをまず疑う
  10. 検索により参考URLをもりもり引き当てる

原因

  1. CentOS6(正確にはRHELクローン)由来のcURLでは、クライアントとしてHTTPS通信を行う際には、NSSライブラリを参照する
  2. NSSライブラリは 実行時にファイルシステム性能チェックを行う1
  3. 2の時、 存在しないファイル名を意図的に大量に呼び出す。これが良くない。
  4. 3の時、 negative dentry cache がたまる2
  5. 4の時、 /proc/meminfo における Slab にカウントされる3
  6. 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を利用するようリコンパイル

  1. キャッシュを利用するか否かの判断材料としているらしい

  2. dentryにある 存在しないinode のキャッシュ

  3. Slabの中で dentry という項目がある。slabtop で確認できる。