vm.overcommit_memory という設定、聞いたことありますか。
僕はまったく知りませんでした。
あるEC2上のLinuxサーバにソフトをインストールしようとしたとき、検証環境では問題なく進んだのに、本番相当の環境ではなぜか失敗する、ということがありました。
ログを見ても、最初は何を言われているのか分からず、画面を見つめながら「わけわからん……」となっていました。
最終的には、Linuxカーネルの vm.overcommit_memory というメモリ関連の設定値が原因でした。
一緒に見てくださった先輩方が切り分けてくださり、なんとか解決できました。本当に感謝です。
この記事では、そのときに学んだ vm.overcommit_memory について整理します。
まずカーネルとは何か
vm.overcommit_memory はLinuxカーネルの設定値です。
ざっくりいうと、カーネルはOSの中核で、アプリケーションとハードウェアの間にいる管理役です。
[アプリケーション]
- メモリを使いたい
- ファイルを読みたい
- 通信したい
|
v
[Linux カーネル]
- 要求を受ける
- 安全性を確認する
- CPUやメモリなどの資源を割り当てる
|
v
[CPU / メモリ / ディスク / ネットワーク]
アプリケーションが「メモリを使いたい」と言ったときに、実際にどこまで許可するかを判断するのもカーネルの仕事です。
sysctl とカーネルパラメータ
Linuxには、カーネルの動作を調整するための設定値があります。
この設定値をカーネルパラメータと呼びます。
sysctl は、そのカーネルパラメータを確認したり、変更したりするためのコマンドです。
たとえば、今回の設定値は次のように確認できます。
sysctl vm.overcommit_memory
または、次のファイルを直接見ても確認できます。
cat /proc/sys/vm/overcommit_memory
永続化する場合は、環境によって /etc/sysctl.conf や /etc/sysctl.d/*.conf に書かれていることがあります。
vm.overcommit_memory = 0
ここで大事なのは、vm.overcommit_memory はアプリケーション側の設定ではなく、Linuxカーネル側のメモリ管理に関わる設定だということです。
overcommit とは何か
overcommit は、直訳すると「過剰に約束する」みたいな意味です。
メモリの文脈では、実際の物理メモリよりも多めにメモリの予約を許す考え方です。
アプリケーションは「これくらいメモリを使うかもしれない」と先にメモリの予約をすることがあります。
でも、予約したメモリを最初から全部使うとは限りません。
予約を許しすぎると、本当に使おうとしたときに足りなくなってOOM Killerの出番になる可能性があります。
一方で、厳しすぎると、実際にはまだ使わないメモリ予約まで拒否されてしまい、アプリケーションやインストーラが動けなくなることがあります。
この「どれくらいメモリ予約を許すか」を決めるのが vm.overcommit_memory です。
vm.overcommit_memory の値
Linuxのドキュメントでは、vm.overcommit_memory には主に次の3つの値があります。
| 値 | 意味 | ざっくりした理解 |
|---|---|---|
0 |
ヒューリスティックに判断する | 明らかに無理なものは拒否するが、ある程度はカーネルが判断する |
1 |
常にovercommitを許可する | メモリ予約にはかなり寛容。ただし実際に足りなくなる可能性はある |
2 |
overcommitを抑制する | 予約できる仮想メモリ量を厳しくチェックする |
デフォルトは 0 です。
0 は、カーネルがメモリ要求を見て、明らかに危なそうなものは拒否しつつ、ある程度は判断してくれるモードです。
1 はかなり緩いです。
「とりあえず予約は許す。実際に足りなくなったらそのとき考える」に近いです。
そのため、後から本当にメモリが足りなくなると、OOM Killerによってプロセスが落とされる可能性があります。
2 はかなり厳しめです。
overcommit_ratio や overcommit_kbytes などの設定を使って、予約できる仮想メモリ量の上限を決めます。
その上限を超えるようなメモリ予約は、実際にはまだ使っていなくても拒否されることがあります。
今回起きたこと
今回、自分が遭遇したのは、ざっくりいうと次のような状況でした。
| 環境 | vm.overcommit_memory |
結果 |
|---|---|---|
| 検証環境 | 0 |
ソフトをインストールできた |
| 本番相当の環境 | 2 |
インストールに失敗した |
最初は、インストーラの問題なのか、権限なのか、ディスク容量なのか、まったく分かりませんでした。
その中で、CloudWatchで取得していたメモリメトリクスも確認しました。
メモリ使用率を見る限り、まだ余裕があるように見えました。
なので最初は「メモリ不足ではなさそうだな」と判断していました。
ただ、ここで見ていたのは、あくまでその時点で実際に使われているメモリ量になります。
今回問題になっていたのは、実際にメモリを大量に使い始めた後の話ではありません。
インストール処理の途中で、プロセスが「これくらいメモリを使うかもしれない」と仮想メモリを予約しようとした段階で、カーネルに拒否されていました。
つまり、CloudWatch上でメモリ使用量が大きく増える前に、vm.overcommit_memory の設定によってメモリ予約が通らず、処理が進めなくなっていたということです。
本番相当の環境だけ vm.overcommit_memory が 2 になっており、これがメモリ予約を厳しく見る設定です。
そのため、インストール中に必要な仮想メモリの予約が通らず、ソフトのインストールに失敗していた、という流れでした。
最終的には、関係者と確認したうえで本番相当の環境の vm.overcommit_memory を検証環境と同じ値にそろえ、インストールを進めることができました。
確認したコマンド
実際にこのあたりを見るときは、次のようなコマンドで確認できます。
sysctl vm.overcommit_memory
cat /proc/sys/vm/overcommit_memory
関連する値も見るなら、次のようなものがあります。
sysctl vm.overcommit_ratio
sysctl vm.overcommit_kbytes
cat /proc/meminfo | grep Commit
CommitLimit は、その環境でどこまでメモリ予約を許すかの目安になります。
Committed_AS は、すでに予約されているメモリ量の目安です。
vm.overcommit_memory=2 のときは、このあたりを見ると「予約が厳しく見られているのかも」と考える材料になります。
変更するときに考えたほうがよいこと
今回、自分たちは値をそろえることでインストールできました。
ただし、vm.overcommit_memory を緩めるのが常に正解、という話ではありません。
2 にしている環境には、OOMを避けたい、メモリ予約を厳密に管理したい、といった意図があるかもしれません。
なので、変更するなら少なくとも次のようなことを確認したほうがよさそうです。
| 見ること | 理由 |
|---|---|
| なぜその値になっているか | 運用上の意図があるかもしれない |
| 検証環境と本番環境で値が違う理由 | 差分が意図的か、設定漏れかを切り分ける |
| メモリとswapの状況 | 本当に余裕があるかを見る |
| インストール後に元へ戻す必要があるか | 一時的な変更で済む可能性がある |
このあたりは、自分ひとりで判断するより、サーバを管理している人やチームと確認したほうが安全だと思います。
学んだこと
今回の一件で、ソフトのインストールが失敗したときに見る場所が少し増えました。
今までは、権限、ディスク容量、ネットワーク、リポジトリあたりを見ていました。
でも、今回のようにカーネルのメモリ管理設定が効いてくることもあるのだと知りました。
- 検証環境と本番相当の環境の差分を見る
- OSやカーネルパラメータも疑う
- インストール時の「メモリ予約」にも目を向ける
- CloudWatchのメモリ使用率だけで「メモリは問題ない」と決めきらない
このあたりが大事だったのだと思います。
普段はあまり触らない設定かもしれません。
でも、サーバを運用している以上、こういうOS側のチューニング差分も無視できないんだなと感じました。
次に似たようなことが起きたら、もう少し早く気づけるようにしたいです。