Azure B2ts_v2でHAProxyとrsyslog/VectorがOOMとログ停止を起こす原因と対策:スワップ・キュー設計・スケールアップの実践ガイド

小メモリの Azure B2ts_v2(2GB、スワップなし)で HAProxy とローカルログ基盤(rsyslog/Vector)を同居させると、「ログ停止 → メモリ逼迫 → OOM → 応答遅延/502」という悪循環に陥りがちです。本稿はその再現条件・根因・対処の優先度・具体的な設定例・検証手順までを一気通貫で解説します。

目次

B2ts_v2 で起きる現象の俯瞰

2GB RAM・スワップなし・バースト CPU の仮想マシンに、プロキシ(HAProxy)とログ収集・加工(rsyslog+Vector)を同居させる構成は、短時間のスパイクで次の症状を連鎖的に引き起こします。

  • rsyslog の omfile アクションが一時停止(ディスク書き込みのバックプレッシャ)
  • systemd-journald がメモリ圧迫を検知してキャッシュをフラッシュ
  • カーネル OOM Killer が大きなメモリを掴む Vector を強制終了
  • その間、HAProxy でキュー滞留・レイテンシ増大・502/504 の断続的発生

対象環境(例)

  • Azure VM: B2ts_v2(2 vCPU / 2GB RAM)
  • OS: Ubuntu / Debian / RHEL 系(systemd で journald 稼働)
  • プロキシ: HAProxy
  • ログ: rsyslog(ローカルファイル出力)+ Vector(加工・転送)

ログメッセージと症状の対応表

コンポーネント代表的なログ(例)何が起きているか影響
rsyslogaction 'ACTION-omfile': suspended (queue to disk/blocked)ファイル出力の I/O が詰まり、アクションが休止メッセージがアクションキューに滞留しメモリ増加
journaldJournal has been rotated due to memory pressurejournald バッファが逼迫しフラッシュ/ローテート一時的な CPU/I/O スパイク
kernelOut of memory: Killed process <pid> (vector)OOM Killer 発動。大きな RSS のプロセスが標的Vector 停止→ログパイプライン中断
HAProxyServer ... timed out / 502/504CPU クレジット枯渇や iowait 増加, キュー滞留レイテンシ増大・接続切断・SLA 悪化

結論(先にやることの優先順位)

  1. スワップ 1~2GB を追加(緊急避難用。「落ちるより遅いほうがマシ」の原則)
  2. rsyslog と Vector のバッファ/キューを縮小し、フル時は「ブロック」に変更(無制限膨張を禁止)
  3. HAProxy ログをリモートへ逃がす(ローカル I/O 競合を抑制)
  4. バースト VM の前提を理解し、サイズアップ(B ⇒ D/F 系へ)
  5. メトリクス監視を整備(RAM/Swap/iowait/CPU クレジット/ログキュー)

なぜ連鎖が起きるのか(技術的な根因)

ログは「受ける→バッファする→書く(ファイル/転送)」の段で必ず キュー を経由します。I/O が遅い、あるいは CPU がスロットリングされると「出る」速度が「入る」速度を下回り、キューはメモリを食いながら膨張します。B2ts_v2 は 2GB しかないため、数十〜数百万行のスパイクで簡単に 100〜数百 MB 級の RSS 増へ発展します。journald も rsyslog も Vector も、それぞれの内部バッファと再試行キューを持ち、同時多重で膨らむのが落とし穴です。

さらに B 系列は CPU クレジット枯渇時にスロットリングされ、「書く力」 が落ちます。I/O 渋滞が長引くと dirty ページが積み上がり、カーネルが強制フラッシュ→iowait 増→ユーザープロセスが余計に待つ、という負のスパイラルを作ります。最終的には OOM Killer が最もメモリを掴むプロセス(多くは Vector)を強制終了し、パイプラインは断裂します。

対策一覧(要約)

対策内容期待効果
より大きい VM へスケールB4ms(16GB)、D2s_v3(8GB)などへ移行RAM/CPU に余力。ログバッファや HAProxy が飽和しにくい
スワップ 1〜2GBスワップファイルを追加し swappiness を低め(例:10)瞬間的なメモリ尖りでの OOM を抑制
リモートログ転送rsyslog omfwd、HAProxy log <host>:514ローカル I/O とメモリ滞留を軽減
バッファと再試行の最適化rsyslog/Vector のキュー上限を明示。満杯時は drop ではなく blockメモリ無限増を防ぎ、長時間のブロックを回避
CPU クレジット監視「CPU Credits Remaining」を可視化スロットリングの見える化。遅延の根本を把握
総合監視RAM/Swap/iowait/ログキュー/HAProxy キューを監視早期検知→スケールまたはドレイン判断

すぐ使える設定例と手順

スワップ追加(1〜2GB、低スワップ運用)

sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

# 最低限の利用に抑える(保険として)

echo 'vm.swappiness=10' | sudo tee /etc/sysctl.d/99-swappiness.conf
sudo sysctl --system 

ポイント: スワップは「最後の砦」。常用ではなく、急峻なスパイクで落ちないための保険として使います。スワップが全くない環境での OOM は一撃死になりがちです。

rsyslog のメモリ膨張を抑える

大きすぎるアクションキューやディスクキュー(spool)は小メモリでは逆効果です。上限を明示し、満杯時は「ブロック」させて入口側の流量を自然に抑え込みます。

# /etc/rsyslog.d/20-tuning.conf (例)
# グローバルキューは最小限
$MainMsgQueueType           LinkedList
$MainMsgQueueSize           5000

# ローカルファイル書き込み(必要最小限)

module(load="omfile")
action(
type="omfile"
file="/var/log/app/app.log"

# アクションキューは小さく、満杯時はブロック

queue.type="LinkedList"
queue.size="3000"
queue.dequeueBatchSize="256"
queue.highWatermark="2500"
queue.lowWatermark="1000"
queue.workerThreads="1"
action.resumeRetryCount="-1"  # 無制限にリトライ(ただしブロック)
)

# 可能ならローカル書き込みをやめリモートへ逃がす

# action(

# type="omfwd"

# target="10.0.0.5" port="514" protocol="udp"

# queue.type="LinkedList" queue.size="3000"

# action.resumeRetryCount="-1"

# )

ポイント: diskQueue をむやみに有効化すると小メモリ環境では I/O 競合を悪化させます。まずはメモリキューを小さく、満杯時に「止める」ほうが全体最適です。

Vector のバッファリングを安全側へ

Vector の sink バッファは memory から disk に切り替え、満杯時は block にします(最新版の設定キー名は環境で確認してください)。

# /etc/vector/vector.toml (例)

[sources.app]

type = “file” include = [“/var/log/app/app.log”] ignore_older_secs = 0

[transforms.trim]

type = “remap” inputs = [“app”] source = ”’ . = del(.fields.unused) ”’

[sinks.out_file]

type = “file” inputs = [“trim”] path = “/var/log/vector/processed.log” encoding.codec = “text”

[sinks.out_file.buffer]

type = “disk” # memory → disk max_size = 268435456 # 256MiB 程度に制限 when_full = “block” # drop ではなく入口をブロック

ポイント: Vector は強力ですが、既定のメモリバッファやバッチサイズが小メモリ VM には重い場合があります。max_events や batch.max_bytes 相当の値を縮め、ソース側の read バッファも抑制します。

HAProxy のログ出力戦略

HAProxy のローカル syslog 依存をやめ、可能なら専用のリモート syslog に逃がします。併せて同時接続の上限で機械的に保護します。

# /etc/haproxy/haproxy.cfg (抜粋)
global
  maxconn 2000
  log 10.0.0.5:514 local0  # リモート syslog
  # ローカルに出すなら rate-limit を意識する
  # log 127.0.0.1 local0

defaults
mode http
option  httplog
option  dontlognull
timeout connect 5s
timeout client  30s
timeout server  30s

frontend fe_http
bind *:80
default_backend be_app

backend be_app
balance roundrobin
server app1 10.0.1.10:8080 check </code></pre>

<h3>journald の暴走防止</h3>
<pre><code># /etc/systemd/journald.conf(例)
Storage=auto
RuntimeMaxUse=128M
RateLimitIntervalSec=10s
RateLimitBurst=5000
SystemMaxFileSize=32M
</code></pre>
<p><strong>ポイント:</strong> RuntimeMaxUse を設け、スパイク時のメモリ消費を頭打ちにします。RateLimit は極端に絞りすぎると解析性が落ちるため、実データ量に合わせて調整します。</p>

<h3>カーネルの書き込みバッファを小さめに</h3>
<pre><code># /etc/sysctl.d/98-dirty.conf(例)
vm.dirty_background_ratio=5
vm.dirty_ratio=10
</code></pre>
<p><strong>ポイント:</strong> 書き込み遅延を招く大量の dirty ページ溜め込みを防ぎ、iowait のスパイクを抑えます。ディスク性能やワークロードに応じて微調整してください。</p>

<h2>スケールアップ判断の指標</h2>
<p>「チューニング+スワップ」で凌げる範囲は限定的です。次の条件に当てはまる場合は、<strong>VM サイズを上げる</strong>か<strong>ログを外出し</strong>してください。</p>
<ul>
  <li>ピーク 10 分間でログが毎秒 5,000 行以上(1 行 200 B として 1 秒あたり ~1 MB)</li>
  <li>平常時でも HAProxy のバックエンド接続が 500 以上</li>
  <li>CPU クレジットが日常的に 0 に張り付き</li>
  <li>iowait が 10% 超で張り付き</li>
</ul>

<h3>推奨サイズとねらい</h3>
<table>
  <thead>
    <tr>
      <th>VM サイズ(例)</th>
      <th>RAM</th>
      <th>ねらい</th>
      <th>向いている状況</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>B4ms</td>
      <td>16GB</td>
      <td>同系統のコストを抑えつつ余裕を確保</td>
      <td>バースト寄りのワークロードだがピークが大きい</td>
    </tr>
    <tr>
      <td>D2s_v3</td>
      <td>8GB</td>
      <td>ベース性能を底上げ、スロットリング回避</td>
      <td>常時処理が前提のプロキシ+ログ取り</td>
    </tr>
  </tbody>
</table>
<p><strong>備考:</strong> 常時処理の基盤は B 系列より D/F 系が無難です。B は「バースト向け」であり、恒常的な I/O/CPU を要するログ基盤やプロキシには不向きです。</p>

<h2>現場で役立つ「見える化」チェックリスト</h2>
<table>
  <thead>
    <tr>
      <th>観測指標</th>
      <th>取得コマンド例</th>
      <th>注目ポイント</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>メモリ/スワップ</td>
      <td><code>free -m</code> / <code>vmstat 1</code></td>
      <td>Swap 使用が右肩上がり+<em>si/so</em> が高止まりならメモリ不足</td>
    </tr>
    <tr>
      <td>iowait</td>
      <td><code>iostat -xz 1</code> / <code>sar -d 1</code></td>
      <td>await と %util が高く、書き込みが詰まっていないか</td>
    </tr>
    <tr>
      <td>プロセスメモリ</td>
      <td><code>pidstat -r -p &lt;pid&gt; 1</code></td>
      <td>Vector/rsyslog の RSS の張り付き</td>
    </tr>
    <tr>
      <td>HAProxy キュー</td>
      <td><code>echo "show info" | socat stdio /run/haproxy/admin.sock</code></td>
      <td>qcur/qmax の推移</td>
    </tr>
    <tr>
      <td>CPU クレジット</td>
      <td>Azure Monitor / CLI</td>
      <td>残量 0 付近で張り付くと全体が鈍化</td>
    </tr>
  </tbody>
</table>

<h2>テストで「再現→改善確認」までやり切る</h2>
<p>本番前にピークを模擬し、効果を数値で確認します。</p>
<ol>
  <li><strong>負荷生成</strong>
    <pre><code># HTTP リクエスト(例: wrk)
wrk -t4 -c400 -d600s http://&lt;proxy&gt;/

# syslog スパム(検証用)

logger "load test $(seq 1 1000000)" </code></pre>

  </li>
  <li><strong>メトリクス採取</strong>
    <pre><code>vmstat 1 &gt; /tmp/vmstat.log
iostat -xz 1 &gt; /tmp/iostat.log
pidstat -r 1 &gt; /tmp/pidmem.log

チューニング適用 → 再テスト(スワップ・キュー縮小・リモート転送)
指標比較:OOM 消滅、iowait 低下、HAProxy レイテンシ安定。

よくある落とし穴と回避策

  • 「スワップは悪」固定観念:確かに遅いですが、落ちるよりは遅いほうが良い。小容量+低 swappiness で保険に。
  • 「キューを大きくすれば安心」:小メモリでは逆に OOM への近道。上限を設け、満杯時は止める。
  • 「全部ローカルに書く」:プロキシとログが同じディスクを叩くと競合。リモート転送やディスク分離を検討。
  • 「B 系で常時運用」:スパイクに弱く、CPU クレジットに依存。ベース負荷があるなら D/F 系へ。

RSS を概算して容量計画する

メッセージ 1 行の平均サイズを 200 バイト、キューに溜める最大件数を 5,000 と仮定すると、200B × 5,000 ≒ 1MB です。ヘッダや内部オーバーヘッドを見込んで 10 倍の 10MB を見積もれば、rsyslog のアクション 3 本でも 30MB 程度。Vector 側で同様に 50MB 程度に収まるよう、queue.size や buffer.max_size を逆算しましょう。2GB RAM の VM で、OS や HAProxy を含む他プロセスの常用分を 1.2〜1.5GB と仮置きすると、ログ基盤が使えるのは 300〜500MB 程度が目安です。

運用の型(Runbook)

  1. Swap の有無・容量を確認(なければ即時 1〜2GB 追加)。
  2. journald の RuntimeMaxUse 設定・RateLimit を導入。
  3. rsyslog の各アクションに小さなキュー上限+満杯時 block を設定。
  4. Vector の buffer を disk+when_full=block に統一。
  5. HAProxy のログをリモート化(もしくは専用ボリュームへ)。
  6. Azure Monitor で CPU クレジット・ディスク待ち・メモリ・スワップ・ログレートを可視化。
  7. ピーク負荷を再現し、OOM/レイテンシ/エラー率が改善したら本番適用。

Azure 固有の注意点

  • CPU クレジット枯渇:B 系は残量 0 でスロットリング。ログ書き込みが細り、キューが膨らみやすい。
  • ディスク SKU と IOPS/帯域:Standard SSD と Premium SSD では書き込み特性が異なり、ログの連続書き込みで差が出ます。小容量ディスクは IOPS が低い傾向。
  • 一時ディスクの扱い:揮発性領域へのログ出力は再起動で消えるため、意図的に使う場合は転送先と再送設計を併用。

FAQ

Q. スワップを使うと遅くなりませんか?
A. なります。ただし OOM でプロセスが落ちるより、短時間の遅延のほうが可用性を保てます。容量は小さく、swappiness は低く、あくまで保険として。

Q. B2ts_v2 のまま運用できますか?
A. ログ量が少なくピーク時のみ負荷が上がる用途なら、設定次第で「落ちない」運用は可能です。とはいえ解析や転送を伴うログ基盤+プロキシの同居は、D/F 系への移行が王道です。

Q. rsyslog と Vector のどちらを優先的に絞るべき?
A. 入口側(journald→rsyslog)で止めるほうがメモリ影響が小さい傾向です。まず rsyslog の各アクションのキュー上限を明示し、Vector は disk バッファ+block で追従させます。

最終まとめ(実践チェックリスト)

  • [必須]スワップ 1〜2GB を追加、vm.swappiness=10。
  • [必須]rsyslog の各アクションに小さな queue.size と action.resumeRetryCount=-1。
  • [必須]Vector の buffer.type=disk・when_full=block・max_size を制限。
  • [推奨]HAProxy のログはリモートへ。ローカルなら専用ボリューム。
  • [推奨]journald の RuntimeMaxUse と RateLimit を設定。
  • [推奨]CPU クレジット、iowait、ログレート、HAProxy キューを可視化。
  • [判断基準]ピークが想定を超えるなら D/F 系へスケールアップ。

例:スワップ追加+設定反映のワンライナー(検証用)

sudo bash -c 'fallocate -l 2G /swapfile && chmod 600 /swapfile && mkswap /swapfile && swapon /swapfile && echo "/swapfile none swap sw 0 0" >> /etc/fstab && echo "vm.swappiness=10" > /etc/sysctl.d/99-swappiness.conf && sysctl --system'

例:rsyslog リモート転送(ローカル I/O 回避)

# /etc/rsyslog.d/30-remote.conf(例)
module(load="omfwd")
*.* action(
  type="omfwd" target="10.0.0.5" port="514" protocol="udp"
  queue.type="LinkedList" queue.size="3000"
  action.resumeRetryCount="-1"
)

例:Vector → リモート(HTTP/S)転送に切替

# /etc/vector/vector.toml(例・HTTP sink)

[sinks.http_out]

type = “http” inputs = [“trim”] uri = “https://log-endpoint.example.com/ingest” encoding.codec = “json”

[sinks.http_out.request]

retry_attempts = 5 retry_backoff_secs = 2

[sinks.http_out.buffer]

type = “disk” max_size = 268435456 when_full = “block”

本稿の要点

  • 小メモリ+スワップなしが OOM・遅延・502 の引き金。
  • バッファとキューの上限を明示し、満杯時は止める設計にする。
  • ログはできる限りリモートへ逃がし、ローカル I/O の競合を減らす。
  • スワップは保険。小容量+低 swappiness で OOM を回避。
  • バースト VM の特性を理解し、常時処理には D/F 系へ移行する。

補足:構成別の推奨パラメータ早見表

項目B2ts_v2(2GB)D2s_v3(8GB)B4ms(16GB)
スワップ1〜2GB / swappiness=102〜4GB / swappiness=104GB+ / swappiness=10
rsyslog queue.size2,000〜5,00010,000 前後20,000 以上も可(用途次第)
Vector buffer.max_size256MiB512MiB1GiB
journald RuntimeMaxUse128MiB256MiB512MiB
HAProxy ログなるべくリモートリモート推奨リモート or 専用ディスク

エンジニア向けまとめ

本件の本質は「入口より出口が遅いとキューが膨らむ」ことです。B2ts_v2 の 2GB では複数のバッファが同時に膨らむだけで OOM 圏内に入ります。対策はシンプルで、(1) OOM を防ぐ保険(スワップ)、(2) 無制限バッファの排除(上限+満杯時ブロック)、(3) ログの外出し、(4) バースト VM を常時処理に使わない、の 4 点です。これらを施し、負荷テストで実データを見て、足りなければ素直にサイズアップする──それが最小コストで安定させる王道です。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次