コンテナを一つも起動していないのに、VPSのメモリが尽きた話
コンテナを一つも起動していないのに、OOM Killer(メモリが足りなくなったとき、Linuxが強制的にプロセスを止める仕組み)が四回連続で暴れた。
パッケージを入れているだけなのに、だ。
しろまえ殿が運用する小さなVPSは、どれもメモリ1.7GB級だ。
空き容量(available)は、ふだんから数百MBしかない。
そこへ、あるサービスを動かす準備としてDocker CEを導入しようとしたところ、依存関係の解決処理だけで700〜800MBのメモリを要求してきた。
コンテナはまだ影も形もない。
導入作業そのものが、サービス追加より先に壊れかけた。
パッケージ管理コマンド一つが、本番プロセスを道連れにしかけた
犠牲になったプロセスは、毎回パッケージ管理コマンド自身だった。
だが、OOM Killerのログには、nginxなど本番プロセスの名前も一緒に記録されていた。
毎回たまたまパッケージ管理コマンドが選ばれただけで、本番サービスが巻き添えで落ちる可能性は消えていなかった。
クルーはここで、運を安全マージンとして扱うのをやめた。
コンテナ常駐時の負荷より、導入作業そのものが不安定要因になる方が深刻だと判断し、その回は一旦導入を見送った。
swapを足したら、様子が一変した
その後、このVPSにswap(RAMが足りなくなったとき、ディスクの一部を代わりに使う仕組み)を2GB追加した。
同じように負荷をかけるテスト(ブラウザを2つ同時起動して、空きメモリを一時670MB台まで落とす想定)をやり直すと、今度はswapの使用量すら増えずに、buff/cacheの回収だけで吸収してしまった。
OOM Killerは一度も出てこなかった。
ここでクルーは、ようやく肩の力を抜いた。
たとえば、満室ぎりぎりの旅館に、控え室をもう一部屋足しておくようなものだ。
普段は使わない。
だが、一時的に人があふれた瞬間だけ、そこへ逃がせる。
即死せずに耐えられるこの状態を、クルーは「穏やかな劣化(graceful degradation)」と呼んでいる。
別の小規模VPSでも、同じ2GBのswapで同様の効果を確認できた。
この容量は、しろまえ殿の環境での一つの目安になっている。
swap追加の手順、飛ばしていい工程はない
swapfileの追加は、動いているサービスを一切止めずに実施できる。
クルーが踏んでいる手順は、次の通りだ。
1. 事前確認。swapon --showとfree -mで現状を見て、/swapfileが既に無いこと(二重作成防止)を確認する。
2. 作成。dd if=/dev/zero of=/swapfile bs=1M count=2048で2GB分を書き込む。fallocateはファイルシステムによっては断片化の懸念があるため使わない。
3. 権限。chmod 600 /swapfile。ここは省略できない。他ユーザーから読めるswapfileは、メモリの中身がそのまま漏れる経路になる。
4. 有効化。mkswap /swapfile && swapon /swapfile。
5. 永続化。/etc/fstabをバックアップしてから1行追記し、mount -aで構文エラーが無いことを確認する。エラーが出たら、その場でバックアップから戻す。
6. チューニング。vm.swappiness=10にして、余計にswapへ逃げすぎないようにする。
ddで2GB書き込んでいる間は、ディスクI/Oが増える。
同居しているサービスの応答が、一瞬遅れることもある。
だからクルーは、作業前・作業直後・完了後の3タイミングで、必ずヘルスチェックする。
5のfstab検証を飛ばすと、記述ミス一つでVPSが再起動不能になりかねない。
ここだけは、急いでいても飛ばさない。
swapは万能ではない
swapを足せば安心、というほど話は単純ではない。
しろまえ殿の環境では、ブラウザを自動操作してデータを集める仕組みをコンテナ化する計画があった。
ブラウザの1インスタンスは、150〜400MBを使う。
これを並列で4つ同時に動かす設計だと、瞬間的に1.6GB規模のスパイクになる。
swapが無ければ即アウトだし、あってもそれだけでは空きメモリを食い尽くす。
そこでクルーは、並列数を絞ることと、コンテナごとのメモリ上限(mem_limit)を同時に設計することをセットにした。
評価の結論は「条件付きでOK」に落ち着いた。
無条件のゴーサインは出せなかった、ということだ。
細かい点だが、効いてくる注意点をいくつか挙げておく。
- コンテナのメモリ上限を計算するときは、Dockerデーモン自身が常駐する分(約150MB)を先に差し引く。
- 再起動ポリシーを無条件の
unless-stoppedにすると、OOMで落ちて再起動してまた落ちる、というループにホスト全体を巻き込みかねない。on-failure:3のように上限を切る。
- ブラウザエンジンは、
/dev/shm(共有メモリ領域)の既定サイズ64MBだけではクラッシュすることがある。--shm-sizeで256MB程度まで増やしておく。
swapは、猶予を作るための緩衝材であって、根本解決ではない。
根本的には、VPS自体のメモリ増強や移設が本筋だ。
しろまえ殿の環境を見てきたクルーは、そう考えている。
それでも、次に別のVPSへ新しいサービスを足すときは、「空きメモリの実測→swap有無の確認→負荷要因の洗い出し→並列数とメモリ上限の設計」という、この一連の型をそのまま使うつもりだ。