小メモリの 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(加工・転送)
ログメッセージと症状の対応表
| コンポーネント | 代表的なログ(例) | 何が起きているか | 影響 |
|---|---|---|---|
| rsyslog | action 'ACTION-omfile': suspended (queue to disk/blocked) | ファイル出力の I/O が詰まり、アクションが休止 | メッセージがアクションキューに滞留しメモリ増加 |
| journald | Journal has been rotated due to memory pressure | journald バッファが逼迫しフラッシュ/ローテート | 一時的な CPU/I/O スパイク |
| kernel | Out of memory: Killed process <pid> (vector) | OOM Killer 発動。大きな RSS のプロセスが標的 | Vector 停止→ログパイプライン中断 |
| HAProxy | Server ... timed out / 502/504 | CPU クレジット枯渇や iowait 増加, キュー滞留 | レイテンシ増大・接続切断・SLA 悪化 |
結論(先にやることの優先順位)
- スワップ 1~2GB を追加(緊急避難用。「落ちるより遅いほうがマシ」の原則)
- rsyslog と Vector のバッファ/キューを縮小し、フル時は「ブロック」に変更(無制限膨張を禁止)
- HAProxy ログをリモートへ逃がす(ローカル I/O 競合を抑制)
- バースト VM の前提を理解し、サイズアップ(B ⇒ D/F 系へ)
- メトリクス監視を整備(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 <pid> 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://<proxy>/
# syslog スパム(検証用)
logger "load test $(seq 1 1000000)" </code></pre>
</li>
<li><strong>メトリクス採取</strong>
<pre><code>vmstat 1 > /tmp/vmstat.log
iostat -xz 1 > /tmp/iostat.log
pidstat -r 1 > /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)
- Swap の有無・容量を確認(なければ即時 1〜2GB 追加)。
- journald の RuntimeMaxUse 設定・RateLimit を導入。
- rsyslog の各アクションに小さなキュー上限+満杯時 block を設定。
- Vector の buffer を disk+when_full=block に統一。
- HAProxy のログをリモート化(もしくは専用ボリュームへ)。
- Azure Monitor で CPU クレジット・ディスク待ち・メモリ・スワップ・ログレートを可視化。
- ピーク負荷を再現し、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=10 | 2〜4GB / swappiness=10 | 4GB+ / swappiness=10 |
| rsyslog queue.size | 2,000〜5,000 | 10,000 前後 | 20,000 以上も可(用途次第) |
| Vector buffer.max_size | 256MiB | 512MiB | 1GiB |
| journald RuntimeMaxUse | 128MiB | 256MiB | 512MiB |
| HAProxy ログ | なるべくリモート | リモート推奨 | リモート or 専用ディスク |
エンジニア向けまとめ
本件の本質は「入口より出口が遅いとキューが膨らむ」ことです。B2ts_v2 の 2GB では複数のバッファが同時に膨らむだけで OOM 圏内に入ります。対策はシンプルで、(1) OOM を防ぐ保険(スワップ)、(2) 無制限バッファの排除(上限+満杯時ブロック)、(3) ログの外出し、(4) バースト VM を常時処理に使わない、の 4 点です。これらを施し、負荷テストで実データを見て、足りなければ素直にサイズアップする──それが最小コストで安定させる王道です。

コメント