さくらのVPS 2Gプランで、自作の業務システムを3本動かしています。9日間連続稼働、ロードアベレージ0.20、メモリは7割が空いたままです。

測ってみて、想定外だったことがあります。アプリ3本が使っているのは約166MB。一方で、このサーバーでは働きようがないOSのデーモンが、合計50〜100MBを占めていました。

自分のアプリより、使う予定のない仕組みのほうが目立つ。プラン変更を考える前に、見るべき場所がありました。

測定条件

数字を出す記事なので、どういう状態で測ったかを先に書きます。

  • サーバー:さくらのVPS 2Gプラン / Ubuntu 24.04 LTS
  • 測定時刻:平日15:57、業務時間内だがアクセスはほぼない状態
  • 稼働日数:9日15時間(無停止)
  • サンプル数:1回(vmstat のみ5秒間)

したがって以下の数字はアイドルに近い状態のものです。ピーク時の値ではありません。負荷時の測定は別途行い、追記します。

動かしている3本

すべて自作の業務システムです。フロントには Caddy を置いています。

システム構成常駐メモリ(RSS)
反応計測システムFastAPI 0.141.1 / uvicorn 0.52.4 / SQLite約66MB
基幹システムPython + gunicorn(-w 1 --threads 8 -t 120約65MB
仮想待合室Go(静的リンクの単一バイナリ)/ 設定はJSON約35MB
3本合計約166MB
Caddyリバースプロキシ / HTTPS約23MB

3本とも 127.0.0.1 のポートで待ち受けていて、外部に直接は晒していません。Caddyが受けて振り分けています。

基幹システムは自社の業務用なので、フレームワークやデータ構造の詳細は伏せます。この記事で必要なのはメモリの数字だけなので、そこは支障ありません。

3本で166MBに収まっている理由

DBサーバーを1台も動かしていないからです。

systemctl list-units --type=service --state=running \
  | grep -iE "postgres|mysql|maria|redis|mongo"
# → 何も返らない

PostgreSQLもMySQLもRedisも入っていません。反応計測システムはSQLiteのファイル1つ(data/dareyomi.db)で動いています。

MySQLやPostgreSQLを1つ立てるだけで、デフォルト設定なら100〜300MBは持っていかれます。 アプリ3本の合計より重い。「2GBで足りるか」は、アプリの本数ではなくDBサーバーを立てるかどうかで決まる面が大きい、というのが実測から見えたことです。

もうひとつ効いているのが、gunicornの -w 1 --threads 8 です。ワーカーを増やすとプロセスがまるごと複製されてメモリを食うので、限られたメモリではスレッドで捌く構成が有利になります。

仮想待合室のGoバイナリが34.7MBでマルチテナントを捌いているのも、数字としては効いています。ランタイムを持たない単一バイナリなので、Pythonの2本と比べて素直に軽い。

メモリ実測

               total        used        free      shared  buff/cache   available
Mem:           1.9Gi       504Mi       362Mi       1.0Mi       1.3Gi       1.4Gi
Swap:             0B          0B          0B
 15:57:45 up 9 days, 15:58,  1 user,  load average: 0.20, 0.34, 0.17
項目意味
total1.9Gi2GBプランで実際に使える量
used504Mi使用中。全体の約1/4
available1.4Giこれから使える量。見るべきはここ
buff/cache1.3Giディスクキャッシュ。必要になれば解放される

free でつまずきやすいところ

buff/cache の1.3GiBは「奪われている」わけではありません。Linuxは空きメモリをディスクキャッシュに回し、アプリが必要とすれば解放します。無駄ではなく、働いている状態です。

見るべきは available。これが「これから何かを載せられる余地」で、今回は1.4GiB。全体の約7割が空いています。

free の数字が小さいことを理由に上位プランへ変える前に、まず available を見てください。

プロセス別の内訳

ps aux --sort=-%mem の上位を、役割ごとに分けました。

自分のアプリ:約166MB

プロセスRSS
反応計測 / uvicorn66.5MB
基幹システム / gunicorn worker38.8MB
基幹システム / gunicorn master25.9MB
仮想待合室 / Goバイナリ34.7MB

動いていて当然のもの:約74MB

プロセスRSS役割
systemd-journald24.5MBログ
caddy23.0MBWebサーバー
systemd13.5MBinit
systemd-resolved12.9MBDNS

このサーバーでは働きようがないもの

ここが今回の発見です。

プロセスRSS本来の用途
fwupd44.0MB物理デバイスのファームウェア更新
multipathd26.7MBストレージへの複数I/Oパスの管理
unattended-upgrade-shutdown22.2MB自動更新中のシャットダウン待機
udisksd13.4MBディスク・リムーバブルメディアの管理
ModemManager12.8MBモバイル通信モデムの管理

VPSに物理ファームウェアはありません。モバイル通信のモデムも刺さっていません。USBメモリを挿すこともありません。

常駐している3つ(multipathd + ModemManager + udisksd)だけで 約53MB。ここに fwupd が起動していると 約97MB になります。

自作アプリ3本が166MBなので、使う予定のない仕組みが、その3割から6割に相当する量を占めていたことになります。

multipathd は本当に何もしていないのか

「不要そう」で終わらせず、確認しました。

multipathd の役割について、Ubuntu公式ドキュメントはこう説明しています。マルチパスは「複数の物理的なSAN接続(別々のケーブル、スイッチ、コントローラを含む)を通じて、複数のI/Oパスを集約する仮想デバイスを作成する」ための機能で、冗長性やI/O分散を目的としています。

つまりストレージへの経路が複数ある環境のための仕組みです。では、このVPSに経路は何本あるのか。

$ systemctl is-active multipathd
active

$ ls /dev/mapper/
control

$ lsblk
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
sr0     11:0    1 1024M  1 rom
vda    253:0    0  100G  0 disk
├─vda1 253:1    0    1M  0 part
└─vda2 253:2    0  100G  0 part /

multipathd動いています。しかし /dev/mapper/ にあるのは control だけ ── これはdevice-mapperの制御ノードで、マルチパスの管理対象ではありません。mpath で始まるデバイスは1つもない。

lsblk を見ても、ディスクは vda が1本だけです。束ねるべき2本目の経路が、そもそも存在しない。

管理対象ゼロで常駐している、ということが確認できました。

補足:上記3コマンドは、同じさくらのVPS・Ubuntu 24.04の別の1台で実行したものです。構成は同じですが、厳密には測定対象のサーバーとは別の機体です。

止める前に、自分の環境で確認してください

「不要だから止めろ」と書くつもりはありません。 環境によって前提が変わります。上の3コマンドを自分のサーバーで叩いて、同じ結果になることを確かめてから判断してください。

sudo multipath -ll        # 何も出なければ、束ねる経路はない
systemctl status multipathd ModemManager udisks2 fwupd

止める場合も、1つずつ、間隔を空けて。本番サーバーなら、まず検証用の環境で試してください。

sudo systemctl disable --now ModemManager
# しばらく様子を見る
sudo systemctl disable --now udisks2
# しばらく様子を見る
sudo systemctl disable --now multipathd

fwupd は測定の2分前(15:55)に起動していました。常時この44MBを消費し続けているとは限りません。この記事では「観測時点で起動していた」という事実のみを書いています。

また RSS は共有ライブラリを重複して数えるため、各プロセスの単純合計は used と一致しません。プロセス間の大小を比べるための目安として見てください。

Swapが0だった ── 4台とも

SwapTotal:             0 kB
SwapFree:              0 kB

スワップ領域がありませんでした。そして、これはこのサーバーだけの話ではありません。

さくらのVPSを4台運用していますが、4台すべてがスワップ0でした。 ログイン時のバナーに出る Swap usage: 0% が、全台で並びます。

これは設定漏れではなく、提供側の仕様として説明されています。さくらのVPS公式マニュアル「swapfileの追加」には「一部、スワップパーティションを含まないイメージの提供を行っております」と明記されており、スワップファイルを後から追加する手順も公式に用意されています。

危険なのか

スワップがないということは、メモリを使い切った瞬間にOOM Killerが走り、プロセスが選ばれて強制終了されるということです。緩衝材がない。

ただし、実際に起きているかは別の話です。確認しました。

journalctl -k | grep -i "killed process"
# → 3台とも何も返らない

OOM Killerの発動履歴は、どのサーバーにもありませんでした。 メモリ使用率が26〜31%で安定している以上、当然といえば当然です。

つまり現状は「危険な構成だが、余裕があるので問題が表面化していない」状態です。ディスクは99GB中81GBが空いているので、スワップを置かない理由も特にありません。何かを追加で載せる前には作っておくべきだと考えています。

2GBで足りる人・足りない人

実測から言えることを整理します。

足りる

  • 少人数が使う業務システム。今回は3本載せてメモリ使用は約1/4
  • DBサーバーを立てない(SQLiteやファイルベースで足りる)
  • アクセス数が読める。急なスパイクがない
  • 画像・動画・PDFの重い生成処理がない
  • ワーカーを増やさず、スレッドで捌く構成にできる

足りない可能性が高い

  • 不特定多数が使う公開サービス
  • PostgreSQLやMySQLを立てる
  • サーバー上でビルドやCIを回す(一瞬でメモリが跳ねる)
  • AI系のツールを載せたい

最後の項目が、このサイトの本題です。

このサーバーは available 1.4GiB、ロードアベレージ0.20。数字の上では、まだ何かを載せる余地があります。 ただし n8n や Dify のような自動化ツールは、Docker・データベース・ワーカーがセットで付いてきます。166MBで動いている自作アプリとは要求される桁が違うはずですが、それは実際に載せてみないとわかりません。

次の記事で、実際に測ります。

まとめ

  • さくらVPS 2Gで自作システム3本:使用504MB / 空き1.4GiB / ロードアベレージ0.20、9日間無停止
  • アプリ3本の実使用は約166MB。2GBは思ったより余裕がある
  • 収まっている最大の理由はDBサーバーを立てていないこと
  • このサーバーでは働きようがないデーモンが53〜97MBを占めていた。プラン変更の前に、まずここを見る
  • multipathd は動いているが、管理対象はlsblk/dev/mapper/で確認する限りゼロ
  • freeused ではなく available を見る
  • 4台すべてスワップ0。さくらのVPSではスワップなしのイメージが提供される場合があり、公式に追加手順が用意されている
  • スワップなしだが、OOM Killerの発動履歴は3台とも無し

参考

  • さくらのVPS マニュアル「swapfileの追加」
  • Ubuntu Server Documentation「Introduction to device mapper multipathing」