過去何度かトライしてきたシステム監視サービスにようやく見通しが立った。インハウスで構築できる状況には、おそらくあと5年ほど要するだろう。
要するにDRAM管理の弱いツールをコンテナで安定動作させる方法がない、ということが分かった。この結論に至るまで、多様なツールをセットアップした。
とくにデータストアについては、PostgreSQLなどのRDBMSやValkeyなどの素朴なKVSだけが例外的にメモリ管理が行き届いている状況で、ほかのミドルウェアは全滅と言える。監視システムはそれぞれ独自のデータストアに依存しており、ベアメタルのハード環境が必須だ。
DRAMが高止まりしている
この先もっとも簡潔なブレークスルーのシナリオは、DRAMの価格が10分の1に下落することだ。サーバーのメモリ容量が10倍になれば、相当多くのツールが概ね安定動作するだろう。
DRAMのコモディティ化はすでに起きていてもおかしくない展開ではあったが、明らかに10年ほど前に止まった。たしかに半導体微細化が限界にぶち当たった影響は大きいのだろう。チップ当たりの容量密度が10倍になるような技術進化を達成できていない。
ただ、技術進歩が完全に止まったとしてもエクスペリエンス・カーブのような量産効果まで市場に届いて来ないのは異常に見える。
近年進展している世界経済のブロック化や戦時雰囲気のような産業政策の影響が大きいのではないか。そう考えるとDRAM価格はこの先10年も高止まりしたままの展開が十分考えられる。
コンテナOSが仮想メモリを制約している
ハード自体が高価であることに加えて、コンテナ化により仮想メモリの機能が退化していることも逆風に拍車をかけている。
LinuxなどのUnix OSはスワップというDRAM容量を補完する機能を備えているのだが、kubernetesなどのコンテナOSは長らくスワップを実装できずに来ている。
スワップは、SSDなどのディスクをメモリ領域として転用することで擬似的にDRAM容量を拡張する。
当然ながらディスクは遅いので、スワップをDRAMと同じものとして扱ってしまうと顕著に性能劣化する。これはスラッシングと呼ばれている。
kubernetesは現状スワップを本来のDRAMと区別して扱う機能がないため、機能ごとdisableにしている。よって実メモリ以上のDRAMを使う状況になるとOOMKillerによってアプリが丸ごと異常終了するより他ない。
この状況は良いと考えられているわけではないが、v1.30現在では設計が議論中であり、実現までまだ時間がかかる。
ハードの停滞を総合すると、DRAMの希少性は過去より相対的に増している。
ソフトウェアがRAMを浪費している
ハード・OSの制約上、安定動作には、各アプリケーションが消費RAMに上限を設定し、確保した容量を効率的に利用する必要がある。
見通しを良くするために結論めいたことを述べるなら、RAM管理の設計を放棄しているアプリケーションは資源枯渇に陥りやすい。
データストアを実際に運用すると、直感に反する挙動から理解できる。
システム監視ツールの主要機能は、監視データをデータストアに蓄積する機能と、目的に応じて解析する機能に分かれる。
前半のデータ蓄積段階のツールを構築したところでOOMKillerが動作する様子を眺めていて、背後にある根本的な欠陥を想像できた。
本来、収集フェーズの処理は、監視データをデータストアにフィットしたフォーマットに変形するだけであり、ディスクに書き込んだ時点で受信データは不要化するはずだ。
単体のデータサイズはせいぜい数キロバイトとすると、一時的に1000件滞留したとしても数メガバイトのRAM消費に収まるべきだろう。
数ギガバイトのRAMを食いつぶしている状況は何かが異常だし、稼働開始から時間が経つにつれてRAM消費量が単調に増大していく傾向も妥当ではない。
RAMの仕様効率について言語ライブラリは何も言えない
現時点で各種データストアを検証した範囲では、go言語で書かれたプロダクトには良いものがない。goの各種ツールを試す以前にはJavaプロダクトも検証したが、基本的な挙動は同様だった。実データに比してRAMを消費し過ぎだし、フットプリントの肥大化を抑止できない。
goは、JavaやPython、Rubyといったオブジェクト指向言語とくらべて、RAM上のデータ構造が簡素な分フットプリントが小さい印象があるのだが、どうやら大規模なキャッシュを扱うアプリケーションは相対的にメリットが打ち消されているようだ。
おそらく、RAMの使用効率は各アプリケーション特有のアルゴリズムに合わせて設計しなければ制御しようがない、ということなのだと思う。
C言語やRustはメモリ操作を強く意識させる言語であるため、アプリケーション開発者がRAM効率を設計の際に考慮していることが多く、他の言語はそうではないように見える。
CからJavaなどの言語が派生した際に、RAM管理を開発言語に任せてアプリケーションはメモリ操作に関与しない設計が増えた。しかし、けっきょく言語ランタイムではデータの必要性を断定できずRAMを浪費せざるを得ない。
たとえばLRUのような汎用アルゴリズムはあるが、優先度のヒントは決まっても、具体的にどの時点まで古いデータを削除すべきかという課題に一般解はないのだろう。
言語ランタイムまかせのRAM管理の致命的な点は、動作継続を保証する方法がないことだ。ハードのRAMが増えれば安定度は増すが、相対的にトラブルを抑制しているだけで制御できてはいない。
また、RAM消費量が増えたときにアプリケーションの高速化に役立っていない可能性も高い。先ほどの監視ツールは、すでに死んだデータを不発弾のように抱え込んでいる。
ソフトウェア品質が第一
RAMのトラブルは様々なレイヤの課題の集積ではあるが、そのうち最重要の課題はソフトウェア品質だ。ハードが進化しても本質的な問題は残る。
動作環境が混合ワークロードであることを考慮すると、各ソフトウェアは与えられた環境に合わせてRAMフットプリントを伸縮できる必要がある。
言語ランタイムの進化に期待するのは、ここ20年ほどの停滞を見るに難がある。個別アプリケーションの優劣がより重要と言える。
初回の選定段階で判断することは難しいのだが、これまでのところRAMに関するチューニング設定を持たないソフトウェアは品質が悪い。そして残念ながら、チューニング設定を持つソフトウェアなら品質が良いとも言えない。
現時点では、カテゴリによっては安定動作するアプリケーションが存在しない可能性もある。その場合には、みずから開発する必要があるだろう。
長い目で見ればRAM管理能力に欠けるソフトウェアは生き残らないはずだ。