弊研究室のアドベントカレンダーまさか無事に終わるとは思っていませんでした。
今回ははSplashのメモリ問題について書きます。
結論
メモリ食いすぎたら勝手に落ちるから再起動
これは何か
以前にScrapyはいいぞぉって記事を書きました。しかしScrapyには一点問題があります。それは、当然のようにJavaScriptが動作しないことです。JavaScriptを使うページをScrapyでスクレイピングするときによく使うのがSplashというツールです。
Splashは、JavaScriptをレンダリングしてくれるサーバで、WebAPIを使ってアクセスすることで指定したサイトのJavaScript実行後のソースを取得することができます。中身はPythonで書かれていて、TwistedとQT3を使っているみたいです。
問題
そんな便利なSplashですが、1つ大きな問題があります。それは、メモリをめちゃくちゃ消費することです。
下の図は、Splashのメモリ消費量のグラフです。
リクエストをすればするほど消費するメモリが増えていきます。この調子ではいくらメモリを積んでもあっという間になくなってしまいます。
Githubにも同様のIssueは立っていますが、Pythonの仕様でメモリを解放することができないみたいです。
https://github.com/scrapinghub/splash/issues/674
解決策
Pythonの問題は流石に手が打てません。もし知っている人がいたら教えてください。
上記のIssueにも書いてあるように、メモリを解放するには一回Splashを落とす以外に方法がありません。ただ、Splashを手動で落とすわけにはいかないので自動で落とす必要があります。cronとかで1分ごとに再起動みたいなスクリプトを書くのもアリですがめんどくさい。そんなときにどうすればいいか。
メモリ不足で落ちるまで待ちましょう。そして自動で再起動させましょう
あまり頭がいい解決策ではないですがこれが一番楽です。
どうするか
以下のような状況を対象にします。
- SplashはDockerで動かしてる
- docker-composeを使っている
まず、SplashのDockerコンテナが使えるメモリの上限を設定します。docker-composeはversion3からmem_limit
がなくなってしまったので2を使います。
また、落ちた時に自動で再起動するようにrestart:always
をつけてあげます。
version: "2"
services:
splash:
image: "scrapinghub/splash:3.3"
ports:
- "8050:8050"
mem_limit: 2g
restart: always
command: --disable-browser-caches --maxrss 4000
これでメモリを必要以上に食うことがなくなりました。
終わりに
途中でも言ったようにあまり頭のいい解決策ではないです。もっと賢く楽にできる方法があったら教えてください。