3-5. モニタリング - top / htop / vmstat / SLI/SLO / 監視ツール

所要時間: 40-50分(がっつりなら2セッション分) ゴール: 「サイト遅い」と言われた時に、どのレイヤを誰の責任で見るか判断できる。SLI/SLOの基礎で「壊れた」を定量化できる コミット内容: 調査フローと使ったコマンドを ~/learn/linux/day17 にコミット

この章が終わるとできること

  • top / htopCPU・メモリ・load average を読める
  • free -havailable を見て本当のメモリ余裕を判断できる
  • vmstat / iostatCPU待ち・I/O待ち を切り分けられる
  • ss -tnlp で listen ポートとプロセスを瞬時に確認できる
  • SLI / SLO / SLA の違いを答えられる
  • ゴールデンシグナル」4つ(Latency / Traffic / Errors / Saturation)を覚えている

Day 6 / Day 13 とのつながり

  • Day 6 で習った ps / top / lsof がここで 本格運用 モードに昇格
  • Day 13 (ログ管理) で集めたログが、ここで メトリクス化 される
  • Day 11 (systemd) の journalctl と組み合わせて「異常検知 → 原因特定」が30分で終わる

これができると何が嬉しいか

  • サイト遅い」の Twitter 通報を 顧客より早く 検知できる
  • 障害発生時、topvmstatiostatss5分以内に原因レイヤを特定 できる
  • Day 21(デプロイ)の最終形 ─「監視つきで本番品質」が見えてくる

大前提: 「動いている」だけのサーバーは何の価値もない

本番サーバーが「動いているかどうか」を見ていない運用は、本番運用とは呼びません。

「動いている」と「健全」の間には大きな差があります:

  • HTTPステータス200を返してはいるが、レスポンスが10秒かかる
  • メモリリークで毎時100MBずつメモリ使用量が増えている(今夜OOMで死ぬ)
  • ディスクI/Oで詰まっていてアクセスログが書けず、リクエストが捨てられている
  • 1秒に1リクエストしか来ていない(顧客が離脱している = 売上0)

これらは監視していないと気づけない。気づけた頃には Twitter で「サイト落ちてる」と言われていて、もう遅い。

このレッスンで身に着けるべきは2つ:

  1. 障害発生時の調査スキル: top / vmstat / iostat / ss を使い分け、原因をレイヤごとに切り分け
  2. 平時の監視設計: 何を計測し、どこに閾値を置き、誰に通知するか

両方をきちんと押さえると、「本番で詰まらない」レベルになります。


セッション①: コマンドで現状把握(25-30分)

0. 録画スタート

mkdir -p ~/log ~/learn/linux/day17
cd ~/learn/linux/day17
script ~/log/linux_day17.log

1. 監視の階層構造

監視の3階層

階層何を見るかツール例
インフラ層CPU/メモリ/ディスク/ネットワークtop, free, df, ss, Prometheus node_exporter
アプリケーション層レスポンスタイム、エラー率、スループットアプリログ、APM(Datadog APM, New Relic)
ビジネス層売上、新規登録数、離脱率、KPIBIツール、データウェアハウス

エンジニアが見るべきは インフラ + アプリ。ビジネス層もアプリログから派生可能だが、データ分析部署が見ることも多い。

障害調査時の動き方:

  1. ビジネス層で異常検知(売上が落ちた、登録数ゼロ)
  2. アプリ層を見る(エラーレートが上がっている? レスポンスタイムが伸びている?)
  3. インフラ層を見る(CPU spike? メモリ枯渇? ディスク埋まり?)

逆方向(インフラの異常を先に検知して、アプリへの影響を予測)も大事。

2. top vs htop vs vmstat vs iostat の使い分け

ざっくり言うと

「サイト遅い」と言われた時に最初に打つツールは症状で変わる

  • top / htop = 今この瞬間 のスナップショット(プロセス別の CPU / メモリ)
  • vmstat = 時系列の流れ(CPU・メモリ・I/O・スワップを1画面で)
  • iostat = ディスク I/O 専用(待機時間・IOPS・利用率)

まず素朴な疑問

「サーバー遅いです」と言われた時、何を打つ?

症状: 「サーバーが遅い」
   ↓
[top で見たら CPU 100%]    → どのプロセスが食ってる? → top で犯人特定
[top では正常だが応答遅い]  → I/O が詰まってる? → iostat
[時系列で悪化していく]      → スワップ発生してる? → vmstat
[プロセス見たいけど ツリー欲しい] → htop

top: 今この瞬間のプロセスランキング

top
# 上部: 全体サマリ (load average, CPU, memory)
# 下部: プロセス一覧(CPU使用率順)

イメージ: 「マラソンの今の順位表」。誰がトップを走っているか即わかる。

htop: top の見やすい版

htop
# F5 でツリー表示 / F9 で kill / マウス操作可

イメージ: 「top のリッチ UI 版」。色付き・ツリー・操作しやすい。

vmstat: 時系列の流れを追う

vmstat 5 10        # 5秒ごと10回
# procs --memory-- --swap-- ---io--- --cpu--
#  r  b  swpd free  si so   bi  bo   us sy id wa

イメージ: 「心拍計のグラフ」。瞬間値ではなく波形が見える。

5秒ごとに si/so(スワップ in/out)が出ていれば、メモリ不足が進行中。

iostat: ディスク I/O 専用

iostat -dx 5
# Device  r/s   w/s   r_await  w_await  %util
# sda     12.5  45.2   1.2      3.5      25.3

イメージ: 「ディスクの混雑メーター」%util が 80% 超なら詰まり確定。

対比表

tophtopvmstatiostat
見る粒度プロセス単位プロセス単位システム全体デバイス単位
時系列リアルタイム更新リアルタイム更新秒間隔で時系列秒間隔で時系列
ディスク I/Oざっくりざっくりあり (bi/bo)詳細
スワップ数値だけ数値だけ動き (si/so)なし
UIテキストリッチ・色付き表だけ表だけ
標準搭載全環境別途インストール全環境sysstat パッケージ
強みプロセス特定操作性時間軸ディスク詳細

切り分けフロー(実務)

「サーバー遅い」と報告
   ↓
top でプロセス一覧を見る
   ├─ CPU 100% のプロセスあり → そのプロセスを調査
   ├─ CPU は低いけど遅い → vmstat 1
   │       ├─ wa(I/O待ち)が高い → iostat -dx 1 でディスク特定
   │       └─ si/so(スワップ)が動いてる → メモリ不足
   └─ プロセス見にくい → htop でツリー表示

一番覚えやすい説明

  • 誰が CPU 食ってる?top / htop
  • 時間軸でどう変わってる?vmstat
  • ディスクが遅い?iostat
# top(標準搭載)
top
# 上部: 全体サマリ
#   load average: 0.42, 0.58, 0.51   ← 1分/5分/15分平均
#   Tasks: 234 total, 1 running, 233 sleeping
#   %Cpu(s): 5.0 us, 2.0 sy, 0.0 ni, 92.0 id, 1.0 wa, ...
# 下部: プロセス一覧(CPU使用率順)
 
# 主要な操作
# q     - 終了
# P     - CPU使用率順
# M     - メモリ使用率順
# c     - フルコマンド表示
# k     - kill(PIDを聞かれる)
# 1     - CPUコア別表示
# h     - ヘルプ
 
# htop(よりリッチなUI)
brew install htop      # Mac
# sudo apt install htop  # Linux
 
htop
# F2  - 設定
# F5  - tree表示
# F9  - kill
# F10 - 終了
# /   - 検索
# u   - ユーザーで絞り込み

load average の本当の意味(よくある誤解)

ざっくり言うと

load average: 0.42, 0.58, 0.51 というあの数字。「CPU 使用率(%)ではない」。「実行待ち + 実行中のプロセス数」の平均で、コア数と比べて初めて意味が出る。

まず普通の誤解
load average: 4.50
   ↓
「使用率 450% ?? 100% 超えてるけど?」と混乱

load averageCPU 使用率ではない

正しいモデル: スーパーのレジ
[レジ]   1台    │  並んでいる人(待機) + 今レジしている人(実行中) = load
                │
load = 1.0  →   ▒                  (1人が今レジ中。並びなし)
load = 3.0  →   ▒ ▒ ▒              (1人がレジ中、2人が並んでる)
load = 0.5  →   半分の時間しか動いてない(暇)
コア数と比較する
4コアCPU = レジ4台

load = 4.0  →  ▒▒▒▒                (4台フル稼働、待ち列なし)= ちょうど
load = 8.0  →  ▒▒▒▒ + 4人待機       = 過負荷
load = 2.0  →  4台中2台稼働          = 余裕
判定の目安
load / コア数状態
< 0.7余裕
0.7 〜 1.0注意
> 1.0高負荷、調査開始
> 2.0危険、対処必須
3つの数字の意味
load average: 0.42, 0.58, 0.51
                ↑    ↑    ↑
              1分   5分  15分
  • 1分だけ高い → 一時的なスパイク
  • 15分が高い → 構造的な問題
  • 1分 < 15分 → 改善中
  • 1分 > 15分 → 悪化中
Linuxは I/O 待ちもカウントすることに注意

CPU が暇でも、ディスク I/O 待ちプロセスが多いと load が上がる。「load 高い = CPU 高い」は嘘で、**「load 高い = システムどこかで詰まってる」**が正解。

イメージ
  • load average = 「レジに並んでいる人の数の平均」
  • コア数 = レジ台数
  • load > コア数 = 行列ができている = 過負荷
一番覚えやすい説明
  • load の数字単体は意味なし
  • load ÷ コア数 がプレッシャー指標
  • 1.0 を超えたら詰まり始め
nproc                    # Linux: コア数確認
sysctl -n hw.ncpu        # Mac: コア数確認

top の各列の意味

意味
PIDプロセスID
USER実行ユーザー
PR / NI優先度 / nice 値(後述)
VIRT仮想メモリ全体(スワップ含む、実使用ではない)
RES実メモリ使用量(これが本当のメモリ消費
SHR共有メモリ
S状態(R=実行中, S=スリープ, D=I/O待ち, Z=ゾンビ, T=停止)
%CPUCPU使用率(1コア100%、4コアなら最大400%)
%MEM物理メモリの割合
TIME+累積CPU時間
COMMANDコマンド名

見るべき値: %CPU が常に高いプロセス、%MEM がじわじわ増えるプロセス(メモリリーク疑い)、S=D が多数(I/O詰まり)

3. vmstat - 仮想メモリと全体状況

top が「今この瞬間」のスナップショットなら、vmstat「時系列の流れ」 を秒単位で追う道具です。「メモリ・CPU・I/O が今どう動いているか」を1画面で同時に見られます。

  • 何のコマンドか: vmstat 5 10 のように間隔と回数を指定して、CPU・メモリ・スワップ・I/O を時系列で出力するツール
  • いつ使うか: 「だんだん遅くなる」現象の調査、スワップ発生(si/so カラム)の有無確認、wa(I/O 待ち)が高いかの即判定
  • 解決する具体的な問題: 「スパイクなのか継続なのか」「CPU 詰まりなのか I/O 詰まりなのか」を、top より広い視野で切り分ける
# 5秒ごと、10回出力
vmstat 5 10
# procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
# r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
# 1  0      0 123456  12345 234567    0    0    50   100  500 1200  5  2 92  1  0

vmstat の重要列

意味注目ポイント
r実行待ちプロセス数コア数を超えたら CPU bound
bI/O待ちプロセス数1以上が続くなら ディスク bottleneck
swpdスワップ使用量 (KB)0であってほしい
free空きメモリ (KB)少なくても cache が多ければOK
cacheキャッシュ (KB)Linuxはキャッシュ多用、これは健全
si / soスワップ in / out継続的に発生していたら危険(メモリ不足)
bi / boブロックI/O in / out (blocks/s)ディスク負荷
us / sy / id / waCPU: ユーザー/システム/アイドル/I/O待ちwa が高い = I/O bottleneck

vmstat の使い所: top よりも時系列の傾向を見るのに向く。「今この瞬間」より「5秒ごとに何が起きているか」。

4. free / df / du - メモリとディスク

メモリとディスクは「枯渇したら即座にサービス停止」する2大リソースです。free / df / du の3点セットで、それぞれの状況を即座に把握できるようにします。

  • 何のコマンドか: free はメモリ使用量、df はファイルシステムごとの使用量、du はディレクトリごとの容量を見るコマンド
  • いつ使うか: 「メモリ足りない?」と疑った時、No space left on device エラーが出た時、ログディレクトリが肥大化していないか確認する時
  • 解決する具体的な問題: 「free カラムだけ見て枯渇判定する」誤解、「dfdu の値がズレる」謎を、available カラムと lsof | grep deleted で解消する
# メモリ使用量
free -h
#                total        used        free      shared  buff/cache   available
# Mem:           7.7Gi       2.3Gi       1.2Gi       128Mi       4.2Gi       5.0Gi
# Swap:          2.0Gi          0B       2.0Gi
 
# ディスク使用量(マウントポイント別)
df -h
df -hT                 # ファイルシステムタイプも表示
df -i                  # inode 使用量
 
# ディレクトリの容量
du -sh /var/log
du -sh /var/log/*          # 直下のディレクトリ別
du -h --max-depth=1 /var   # 1階層だけ
 
# 巨大ファイル特定
find / -size +100M -type f 2>/dev/null | xargs ls -lh | sort -k5 -hr | head

free の available を見よ

古い freefree カラムを見て「メモリ足りてない!」と判断しがち。間違い

Linux は「未使用メモリは無駄」という思想で、空きメモリをファイルキャッシュに積極的に使う。free カラムが少なくても、buff/cache が多ければ「いつでも解放できる状態」。

本当に見るべき: available カラム(Linux 3.14+)。「キャッシュを解放すれば使えるメモリ」を計算済み。

total = used + free + buff/cache
available ≒ free + 解放可能なbuff/cache

メモリ枯渇の判定:

  • available < 数百MB or swap used > 0 が続く → 危険
  • free だけ少なくて available が多い → 健全

dudf の値がずれる時

df -h /                       # 95%
du -sh /*                     # 合計しても 50% しか使ってない?

原因:

  1. 削除されたが open されているファイル: プロセスがファイルを開いたまま rm しても、ディスクは解放されない。プロセス終了まで持ち越し。lsof | grep deleted で確認
  2. マウントポイントに別ファイルシステムが重なっている: /mnt に他をマウントしていて、du には見えるが df には別カウント
  3. inode 不足: サイズに余裕があってもinode(ファイル数の上限)が枯渇。df -i で確認

「ディスク埋まったけど何を消せばいいか分からない」事案でハマるポイント。

5. iostat - ディスクI/Oの詳細

CPU でもメモリでもないのにサーバーが遅い時、犯人はだいたい ディスク I/O です。iostat でデバイスごとの IOPS・スループット・待機時間を詳細に追えます。

  • 何のコマンドか: iostat -dx で各ブロックデバイスの読み書き回数・帯域・平均待機時間・利用率を表示するツール(sysstat パッケージ)
  • いつ使うか: vmstatwa が高かった時、DB の遅延が発生した時、ログ書き込みで詰まっている疑いがある時、SSD への換装効果を測定する時
  • 解決する具体的な問題: 「ディスクが詰まっている」を %utilawait で定量化し、アプリ側修正(ログ削減・トランザクション分割)か物理対処(SSD・IOPS増強)かの判断材料にする
# Mac は sysstat 入れる必要あり
# Linux: apt install sysstat
 
# 5秒間隔、10回
iostat -dx 5 10
# Device   r/s   w/s   rkB/s   wkB/s  rrqm/s  wrqm/s  %rrqm  %wrqm  r_await  w_await  aqu-sz  rareq-sz  wareq-sz  svctm  %util
# sda     12.5  45.2  500.3   1234.5    0.0     2.3     0.0    4.8    1.2      3.5     0.1    40.0      27.3     0.5    25.3

iostat の主要列

意味注目
r/s, w/s秒間の読み書き回数 (IOPS)デバイスの性能限界が見える
rkB/s, wkB/s秒間スループット帯域 bottleneck の指標
r_await, w_await平均待機時間 (ms)数十ms超 = 詰まっている
%utilデバイス使用率80%以上が続くなら I/O bottleneck

%util が高い時の対処:

  1. プロセスを特定: iotop(Linux)、fs_usage(Mac)
  2. アプリ側でI/O削減: ロギング頻度、トランザクションサイズ、N+1クエリ
  3. 物理的対処: SSDへ、I/O帯域の高いインスタンスへ

6. ss と netstat - ネットワーク接続

「ポート使われてる」「TIME-WAIT で詰まってる」「接続が増え続ける」── ネットワーク絡みのトラブルでまず叩くのが ss です。netstat の現代版で、桁違いに速いです。

  • 何のコマンドか: ss -tnlp で TCP リスニング状態とプロセス名を、ss -tan で TCP の全状態を表示するソケット統計コマンド
  • いつ使うか: Address already in use エラーの犯人特定、TIME-WAIT が積み上がっていないかチェック、CLOSE-WAIT で接続リーク疑いの検出、外部APIへのコネクション数監視
  • 解決する具体的な問題: 「リッスンしてるはずなのに繋がらない」「ポート枯渇」「謎のコネクションリーク」を、状態別の集計で即座に切り分ける
# 現代は ss 推奨(netstat より速い・情報豊富)
 
# リスニング中のTCPポート
ss -tnlp
# State  Recv-Q  Send-Q  Local Address:Port  Peer Address:Port  Process
# LISTEN 0       128     0.0.0.0:22          0.0.0.0:*          users:(("sshd",pid=1234,fd=3))
# LISTEN 0       128     127.0.0.1:5432      0.0.0.0:*          users:(("postgres",pid=2345,fd=5))
 
# UDP も含めて全部
ss -tunlp
 
# 確立済みTCP接続
ss -tnp
 
# 特定ポートに接続している相手
ss -tnp 'sport = :443'
 
# TIME_WAIT が大量にあるか
ss -tan | awk 'NR>1 {print $1}' | sort | uniq -c
# 5 ESTAB
# 2342 TIME-WAIT   ← 大量にあると port exhaustion の懸念

ss が netstat より優れている理由

  • 速い: /proc/net/* を直接読まず、カーネルから直接取得(数万接続でも瞬時)
  • 情報豊富: BBR/CUBIC 等のTCP輻輳制御アルゴリズム表示、-o でタイマー
  • モダンな構文: -t -u -n -l -p でTCP/UDP/数値/Listen/プロセス

netstat は2010年代以降メンテされていない。net-tools パッケージごと将来削除される予定。新規で覚えるのは ss だけでOK。

ESTAB vs TIME-WAIT vs CLOSE-WAIT

  • ESTAB: 通常の接続中
  • TIME-WAIT: 自分から close したが、最後のACKを取りこぼした可能性に備えて2MSL(〜60秒)待機。短命接続が多いと溜まる
  • CLOSE-WAIT: 相手から close されたが、自分のアプリが close() を呼んでいない状態。バグの可能性(コネクションリーク)

TIME-WAIT が問題:

  • 同じ送信元ポートが TIME-WAIT 中は再利用できない
  • 短期接続を大量に張るアプリ(HTTPショート接続、外部API連打)で発生
  • 解決: コネクションプール、HTTP keepalive、net.ipv4.tcp_tw_reuse カーネルパラメータ

7. プロセス調査 - pidstat / lsof / strace

「犯人プロセスを特定したが、そいつが何をしているか分からない」── ここで使うのが pidstat / lsof / strace の3点セットです。プロセスを内部から覗き込めます。

  • 何のコマンドか: pidstat は特定プロセスのリソース消費、lsof は開いているファイル/ソケット、strace はシステムコール追跡
  • いつ使うか: ポート占有プロセスの特定(lsof -i :8080)、削除済みなのにディスク減らない原因調査(lsof | grep deleted)、ハング中プロセスの状態確認(strace -p
  • 解決する具体的な問題: 「top でプロセスが見つかっても、その先が分からない」を解消し、プロセス内部の挙動まで掘り下げて根本原因を特定する
# 特定プロセスのリソース使用量(毎秒)
pidstat -p <pid> 1
pidstat -d -p <pid> 1     # I/O のみ
 
# プロセスが開いているファイル/ソケット
lsof -p <pid>
lsof -i :8080             # 8080番ポートを使ってるプロセス
lsof | grep deleted       # 削除済みファイルを掴んでいるプロセス(ディスク解放されない原因)
 
# プロセスのシステムコールを追跡(Linux)
sudo strace -p <pid> -c   # 集計
sudo strace -p <pid> -e trace=network   # ネットワーク系のみ
 
# Mac は dtruss
sudo dtruss -p <pid>

lsof の実務ユースケース

  • 「ポート使われてる」エラー: lsof -i :8080 で犯人特定
  • 削除済みなのにディスク減らない: lsof | grep deleted でプロセス特定 → 再起動か HUP シグナル
  • 特定ファイルを誰が開いている: lsof /var/log/app.log

起動時の Address already in use を見て焦るが、lsof -i :ポート で即解決。

8. ログ集約 - journalctl

systemd 時代の Linux では、サービスごとに /var/log/ を探す代わりに journalctl 1コマンドで横断検索できます。「ログがどこにあるか問題」を一掃するツールです。

  • 何のコマンドか: journalctl は systemd-journald が一元管理するバイナリ形式ログを、サービス・時間・優先度でフィルタしながら閲覧するコマンド
  • いつ使うか: 「過去1時間の nginx エラー」を見たい時、サービスがクラッシュした時の直前ログ確認、複数サービスにまたがる障害の時系列追跡
  • 解決する具体的な問題: 「ログファイルを探し回る」「grepawk を組み合わせて時間で絞る」手間を、--since-u-p の組み合わせで一発解決する
# systemd 系の統合ログ
journalctl                       # 全部
journalctl -u nginx              # nginx サービスのみ
journalctl -u nginx -f           # tail -f みたいに追従
journalctl -u nginx --since "1 hour ago"
journalctl -u nginx --since today
journalctl -u nginx -p err       # err以上の重要度のみ
journalctl --disk-usage          # journal のサイズ確認
sudo journalctl --vacuum-time=7d # 7日より古いログを削除

journal vs /var/log の rsyslog

昔の Linux はテキストログを /var/log/ 配下に書き出していた。 systemd 登場後は journalctl(バイナリ形式、構造化ログ)が並行運用される。

どちらも参照可能だが、新しいディストリは journal がメイン:

  • /var/log/syslog, /var/log/messages: 伝統的なテキストログ
  • journalctl: systemd 標準、検索・フィルタが強力

本番運用: ローカルのログだけでは不十分。ログ集約基盤(Loki, Elasticsearch, CloudWatch Logs, Datadog Logs)に送って、複数サーバーから横断検索する。


セッション②: SLI/SLO と監視ツール(25-30分)

9. SLI / SLO / SLA - サービスの「健全さ」を定量化

コマンドで現状把握ができても、「壊れている」の定義が言葉だけだと、アラート設計も改善目標もブレます。それを数値で固定するのが SLI / SLO / SLA の枠組みです。

  • 何のコマンドか: コマンドではなく概念フレームワーク。SLI(測定値)/ SLO(社内目標)/ SLA(顧客契約)の3階層で「サービスの健全さ」を定量化する
  • いつ使うか: 監視ツール導入時に「何を測るか」を決める段階、アラート閾値を設計する時、運用チームと開発チームの会話の共通言語を作る時
  • 解決する具体的な問題: 「サイト遅い」「たまにエラー」のような主観的な議論を消し、「p99 レイテンシ < 200ms を 99.9% 維持」のような共通指標に基づく意思決定に変える

SLI / SLO / SLA の違い

ざっくり言うと

「サービスの健全さ」を定量化する3階層。混同しがちだが、見ているレイヤがそれぞれ違う。

  • SLI(Indicator)= 測ったデータそのもの(事実)
  • SLO(Objective)= 社内の目標値(約束)
  • SLA(Agreement)= 顧客との契約(罰則付き)
まず素朴な疑問

「サイトが落ちた」と言う時、どこから「落ちた」と判定する?

  • HTTP 500 が 1 回出たら?
  • 5 分間応答なしなら?
  • 99.9% を下回ったら?

これを主観で議論しないようにする枠組みが SLI/SLO/SLA。

3階層の関係
[実際のシステム]
       │ 測定
       ▼
   [SLI]            ← 観測した値(例: 過去30日の可用性 = 99.95%)
       │ 目標と比較
       ▼
   [SLO]            ← 社内目標(例: 99.9% を維持)
       │ 顧客と契約
       ▼
   [SLA]            ← 顧客契約(例: 99.5% を下回ったら返金)
数字で見る
階層意味
SLI「今月の p99 レイテンシは 180ms」観測された事実
SLO「p99 < 200ms を 99.9% の時間維持する」社内目標
SLA「p99 が 99.5% を下回ったら 10% 返金」顧客契約
大事な順序: SLA < SLO < 実運用
99.5%  ← SLA(顧客との契約、これを下回ると罰則)
   ↑
   余裕
   ↓
99.9%  ← SLO(社内目標、これを下回ったらアラート・改善)
   ↑
   余裕
   ↓
99.99% ← 実際の運用(できればここを目指す)

顧客に約束する値より、社内の目標を厳しくしないと、契約違反が頻発する。

イメージ
  • SLI = 「体温計の値」(37.8℃)
  • SLO = 「健康基準」(37.5℃ 以下を保つ)
  • SLA = 「保険契約」(38℃ を超えたら欠勤手当)
4つのゴールデンシグナル(SLI の代表例)
名前意味
レイテンシリクエストの所要時間(p50/p99)
トラフィックreq/sec
エラー率5xx の比率
飽和度(Saturation)リソース使用率(CPU/メモリ/接続プール)
一番覚えやすい説明
  • SLI = 測定結果(事実)
  • SLO = 社内目標(自分との約束)
  • SLA = 顧客契約(破ると罰金)

「SLA は SLO より緩く、SLO は実運用より緩い」が鉄則。

エラーバジェット

SLO を 99.9% とすると、許容できるエラー時間は月あたり:

  • 100% - 99.9% = 0.1%
  • 30日 × 24時間 × 60分 × 0.1% = 43分

この 43分が「エラーバジェット」

  • 月内にエラーバジェットを使い切ったら新機能リリース停止、安定化に集中
  • バジェットに余裕があれば積極的にリリース・実験

SREの考え方: 「100%稼働は不可能、コストに対して非合理。99.9%で許容して、その範囲内で攻める」。

10. アラート設計の基本

SLO が決まったら、それを**「逸脱した時に誰かに知らせる」**仕組みがアラートです。設計を間違えると「アラート疲れ」で真の異常を見逃します。

  • 何のコマンドか: 監視ツール(Prometheus Alertmanager / Datadog Monitor / CloudWatch Alarm)でアラート条件・通知先・重要度を宣言する設計手順
  • いつ使うか: 監視導入時、夜間オンコール体制を整える時、過去のアラート疲労を見直してチューニングする時、新機能リリース時の重要メトリクス監視
  • 解決する具体的な問題: 「アラートが多すぎて誰も見ない」「本当の障害を見逃す」を、ユーザー視点 SLO ベースの設計と症状ベースアラートで解消する

良いアラートの条件

Google SRE 本で挙げられている原則:

  1. 実用的: そのアラートを見てやるべきアクションが明確
  2. 緊急: 今すぐ対応すべき(朝まで待てるなら slack でOK)
  3. 根本原因ベース: 「ディスク90%」より「ユーザーがログインできない」
  4. 症状ベース: 「nginx の error.log が増えた」より「APIエラー率が3%超」
  5. 疲労を避ける: 誤検知が多いと無視されるようになる(アラート慣れ)

アンチパターン: 全部閾値アラート

CPU > 80% → アラート
メモリ > 80% → アラート
ディスク > 80% → アラート
プロセス数 > 100 → アラート

一見網羅的だが:

  • 正常時もアラートが鳴る(バッチ実行中に CPU 100% は正常)
  • 真の異常を見逃す: ユーザー影響と直結しない指標で疲労
  • 対応不可能なアラート: 「CPU高い」と言われても、何を直せばいいか分からない

正解: ユーザー視点の指標(エラー率、レイテンシ、登録失敗率)を中心に。インフラ指標は補助。

アンチパターン: ログを見ずに再起動

「サーバー重いな、とりあえず再起動」

これをやると:

  • 原因が永遠に分からない(再起動で症状が消えるから)
  • 同じ問題が繰り返し起きる(メモリリーク、ファイルディスクリプタ枯渇、ゾンビプロセス)
  • データ破損のリスク(不適切なshutdown)

正解: 再起動前にスナップショットを取る:

# メモリ・プロセス状態を記録
ps auxf > /tmp/before_restart_ps.log
ss -tan > /tmp/before_restart_ss.log
free -h > /tmp/before_restart_mem.log
# アプリのスタックトレース(Go なら)
kill -SIGQUIT <pid>     # スタックトレースが stderr に出る

アンチパターン: 監視ダッシュボードを誰も見ない

高いお金を払って Datadog/New Relic を導入したのに、誰もダッシュボードを開いていない

アラートだけ見て満足する運用は危険:

  • アラート閾値より低いところで「ジワジワ悪化」しているのを見落とす
  • 季節変動・週次パターンが見えない
  • キャパシティプランニングの判断材料が無い

対策: 朝会で5分、ダッシュボードを全員で眺める文化を作る。「グラフを見る習慣」が監視文化の本質。

11. 監視ツール概要

実際に SLI を取って可視化するには監視ツールが必要です。世にあるツールは多いですが、選び方の軸を押さえれば迷いません。

  • 何のコマンドか: コマンドではなくツール選定の指針。OSS(Prometheus + Grafana)vs SaaS(Datadog / New Relic)/ クラウド標準(CloudWatch)の3系統を、規模とコストで使い分ける
  • いつ使うか: 新規プロジェクトの監視基盤を選ぶ時、既存の SaaS から OSS へ移行(または逆)を検討する時、k8s 上でのメトリクス基盤を選定する時
  • 解決する具体的な問題: 「とりあえず Datadog」で破綻する課金、「自前 Prometheus」で人手不足、というツール選定ミスを、規模感とのマッチングで回避する

主要監視ツールの位置付け

ツールレイヤ特徴
Prometheus + Grafanaインフラ + アプリOSS、Pull型、PromQL、k8s 標準
DatadogフルSaaS、APMからログまで統合、コスト高
New RelicフルSaaS、APM強い
CloudWatchAWS リソース標準で全部見れる、検索弱め
Mackerelインフラ + アプリ国産SaaS、Push型、はてな製
ZabbixインフラOSS、古典、エンタープライズで多い
Nagiosインフラ古典中の古典、現役ではほぼ Prometheus へ
ELK / OpenSearchログ検索特化、ログ集約デファクト
LokiログPrometheus エコシステム、軽量

Prometheus の基本(最も普及)

Pull型: Prometheus サーバーが各監視対象(exporter)に対して HTTPで /metrics を取りに行く。

構成:

[Node Exporter] ─┐
[Nginx Exporter] ┤
[Postgres Exp.]  ├─→ [Prometheus] ─→ [Grafana]
[カスタムアプリ] ─┘                  └─→ [Alertmanager] → Slack/PagerDuty

アプリ側でメトリクスを公開:

// Go の例
import "github.com/prometheus/client_golang/prometheus/promhttp"
http.Handle("/metrics", promhttp.Handler())

PromQL でクエリ:

# 過去5分のリクエストレート
rate(http_requests_total[5m])
 
# エラー率(過去5分)
sum(rate(http_requests_total{status=~"5.."}[5m]))
/
sum(rate(http_requests_total[5m]))

12. 実例: 「サイトが遅い」と報告された時の調査フロー

ユーザー: 「サイト遅いんだけど」

順を追って切り分け:

# ステップ1: 本当に遅いか確認(再現性)
curl -w "@curl-format.txt" -o /dev/null -s https://app.example.com/api/health
# DNS: 0.001s, Connect: 0.020s, TTFB: 2.500s, Total: 2.510s   ← TTFB が遅い
 
# curl-format.txt の中身
cat > curl-format.txt <<'EOF'
    time_namelookup:  %{time_namelookup}s\n
       time_connect:  %{time_connect}s\n
    time_appconnect:  %{time_appconnect}s\n
   time_pretransfer:  %{time_pretransfer}s\n
      time_redirect:  %{time_redirect}s\n
 time_starttransfer:  %{time_starttransfer}s\n
                  ----------\n
         time_total:  %{time_total}s\n
EOF
 
# ステップ2: 経路のどこか確認
# 2-1. クライアント側? (ブラウザDevToolsで)
# 2-2. CDN/DNS? (別経路から curl して比較)
# 2-3. Nginx?
# 2-4. アプリ?
# 2-5. DB?
 
# ステップ3: サーバー側を見る
ssh prod-web
 
# Nginx のアクセスログから upstream_response_time を見る
tail -1000 /var/log/nginx/access.log | jq '.upstream_response_time' | sort -n | tail
# → upstream が遅い = アプリが原因
 
# ステップ4: アプリのリソースを確認
top
# プロセスが100% CPUで張り付いている?
# → アプリの計算処理 or 無限ループ
 
# vmstat で全体感
vmstat 1 5
# wa が高い → I/O bottleneck
# si/so が動いている → メモリ不足でスワップ
 
# ステップ5: 詳しく追う
# 5-1. ロードが高い場合
ps auxf | head -20
 
# 5-2. I/O が遅い場合
iostat -dx 1 5
 
# 5-3. DBが疑わしい場合
# サイズの大きいクエリ、スロークエリログを確認
# PostgreSQL なら: SELECT * FROM pg_stat_activity WHERE state = 'active';
 
# 5-4. 外部APIが遅いか
ss -tn '( dport = :443 or dport = :80 )' | head
 
# ステップ6: 修正 or 一時対応
# 緊急なら: アプリ再起動(ただしスナップショット取ってから)
# 根本対応: コードレビュー、クエリ最適化、リソース増強

「遅い」のレイヤ別チェックリスト

レイヤ観点コマンド/ツール
DNS名前解決遅延dig, nslookup
TCP接続RTTping, mtr, traceroute
TLS証明書、ハンドシェイクopenssl s_client -connect
CDNヒット率CDNダッシュボード、curl -I でヘッダ
LB / Nginxキュー、upstream時間access.log
アプリエラー、計算、外部APIAPM、ログ
DBクエリ時間、ロック、接続数pg_stat_activity、スロークエリログ
キャッシュヒット率、エビクションRedis INFO
ディスクI/Oiostatiostat -dx
ネットワークパケロス、輻輳ss, tcpdump

13. 監視を構築する最小構成(学習用)

# Mac上で Prometheus + Grafana を Docker で起動
# (docker compose = 複数コンテナを1つの YAML で定義して同時起動するツール。
#  Docker / docker compose の詳細は「インフラ」章で扱う。
#  ここでは「3つのサービス(Prometheus, Grafana, node_exporter)を 1 コマンドで一括起動する道具」
#  とだけ理解すればOK。動かなくても監視の概念は後段の説明で押さえられる)
mkdir -p ~/learn/linux/day17/monitoring
cd ~/learn/linux/day17/monitoring
 
cat > docker-compose.yml <<'EOF'
version: '3'
services:
  prometheus:
    image: prom/prometheus:latest
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
 
  grafana:
    image: grafana/grafana:latest
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    volumes:
      - grafana-data:/var/lib/grafana
 
  node_exporter:
    image: prom/node-exporter:latest
    ports:
      - "9100:9100"
 
volumes:
  grafana-data:
EOF
 
cat > prometheus.yml <<'EOF'
global:
  scrape_interval: 15s
 
scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']
 
  - job_name: 'node'
    static_configs:
      - targets: ['node_exporter:9100']
EOF
 
docker compose up -d
 
# Prometheus UI: http://localhost:9090
# Grafana UI: http://localhost:3000 (admin/admin)
# Grafana で Prometheus データソース (http://prometheus:9090) を追加 → Node Exporter ダッシュボード ID 1860 をインポート

練習課題

# 1. 「重い」プロセスを意図的に作って、top で見つける
# 別ターミナルで実行
yes > /dev/null &
PID=$!
 
# このターミナルで観察
top -p $PID
 
# kill する
kill $PID
 
# 2. メモリ食いプロセスを作る
# (python3 -c "..." は引数の文字列を Python ソースとして実行する形式。
#  ここでは 500MB の空白文字列を作って input() で入力待ちさせ、メモリ使用を観察する用途。
#  Python が無ければ代わりに `tail -f /dev/zero` などでも CPU/メモリの動きが見える)
python3 -c "x = ' ' * (500 * 1024 * 1024); input()" &   # 500MB確保
free -h
ps aux | sort -k4 -rn | head
 
# 3. ディスク使用量を調査
df -h
du -sh ~/*  | sort -h | tail
 
# 4. ネットワーク接続を見る
ss -tnlp
 
# 5. nginx を起動(前回の設定使う)→ ベンチマーク → ログから upstream_response_time を集計
nginx -c /full/path/to/nginx_local.conf
ab -n 1000 -c 10 http://localhost:8090/   # apache benchmark
# tail access.log で結果確認

締め: git で証跡を残す

exit
 
cd ~/learn/linux/day17
git init
git add monitoring/
git commit -m "feat(linux): モニタリング基礎・Prometheus+Grafana のローカル構成"

チェックリスト

  • load average の正しい意味とコア数との関係を説明できる
  • top で CPU/メモリ 順にソートできる
  • free -havailable を見る癖がついた
  • dfdu の値がずれる理由を3つ挙げられる
  • ss -tnlp で listen ポートとプロセスを確認できる
  • vmstat の si/so が継続的に出ている時の意味を説明できる
  • SLI/SLO/SLA の違いを即答できる
  • 「ゴールデンシグナル」4つを挙げられる
  • 「サイト遅い」と言われた時の調査ステップを5つ以上書ける

詰まった時のチートシート

やりたいことコマンド
CPU/メモリ 俯瞰top / htop
メモリ確認free -h
ディスク使用量df -h
大容量ディレクトリdu -sh /* | sort -h | tail
巨大ファイル探索find / -size +100M -type f 2>/dev/null
ディスクI/Oiostat -dx 1 5
ネットワーク接続ss -tnlp
プロセスのファイルlsof -p <pid>
ポート使用プロセスlsof -i :8080
削除済みファイル参照lsof | grep deleted
systemd ログjournalctl -u <service> -f
プロセス強制終了kill -9 <pid>
プロセスにstacktrace要求(Go)kill -SIGQUIT <pid>
並列プロセス殺害pkill -f <pattern>
ベンチマークab -n 1000 -c 10 <url> / wrk

「実務OK」基準

  • 「サイトが遅い」と聞いた時に、どのレイヤを見るか即決できる
  • top の数字(load, %CPU, %MEM, S列)を読める
  • free の available を見て本当のメモリ余裕を判断できる
  • ss でリッスン中サービスを瞬時に把握できる
  • SLI/SLO の設計を1サービス分書ける: 何を測り、目標値はいくつか
  • アラート設計の良い/悪いを判断できる: ユーザー視点 vs インフラ視点
  • 再起動前にスナップショットを取る癖がある

さらに深掘るなら

  • 書籍: 『入門 監視』(オライリー) - 監視の哲学を学ぶ必読書
  • 書籍: 『SRE サイトリライアビリティエンジニアリング』(オライリー) - Google SRE の原典、SLO設計の本家
  • 書籍: 『Linuxカーネルの仕組み』(技術評論社) - top の各指標がカーネル内部でどう生成されるか
  • 公式: https://prometheus.io/docs/ - Prometheus とPromQLの本家
  • 公式: https://grafana.com/docs/ - Grafana
  • Brendan Gregg のブログ: https://www.brendangregg.com/ - Linuxパフォーマンスの神

アンチパターン / 初心者やらかし事例

NG 1: load average を CPU 使用率と混同

load average: 4.0, 4.5, 3.8   # 「CPU 400%だ!」と慌てる

→ 4 コアなら load 4.0 は 「ちょうどフル」、8 コアなら半分。nproc でコア数を確認した上で判断。

NG 2: freeused だけ見て「メモリ満杯」と判断

              total        used        free      shared  buff/cache   available
Mem:           16Gi       14Gi       300Mi       100Mi       1.5Gi       1.8Gi

→ Linux はキャッシュにメモリを積極使用する。available が本当の余裕

NG 3: df は満杯なのに du の合計が合わない

→ 削除されたが プロセスが掴んでいるファイル がスペースを占有している。 → 対策: lsof | grep deleted で犯人特定 → プロセス再起動。

NG 4: メトリクスのアラートを「インフラ視点」だけで作る

CPU > 80% → アラート   # 顧客は遅さを感じてないのに夜中に呼び出される

→ 対策: ユーザー視点(Latency / Error Rate) を主軸に SLO を引き、SLI で計測。CPU/Memory はその下の調査用。


自己評価チェックリスト

  • load averagenproc の値と比較する習慣がついた
  • free -havailable を見るクセが付いた
  • ss -tnlp で listen ポートを瞬時に確認できる
  • SLI / SLO / SLA の3用語を区別できる
  • 「ゴールデンシグナル」4つを言える
  • 「サイト遅い」と聞いて、調査ステップを5個以上即答できる

次のレッスン: Day 20 (Final) - デプロイ

最終章は デプロイ ─ Level 1〜Level 3 の全部を統合する総合プロジェクト。

rsync / SSH script / GitHub Actions / systemd、ブルー/グリーン、ローリング、graceful shutdown、ロールバック、DB マイグレーション、シークレット管理まで。今日身につけた監視と組み合わせて、「本物のデプロイパイプライン」を組む。

Day 20: デプロイ