プロセス監視(ps, top, htop)
プロセスの状態を把握することは、トラブルシューティングの第一歩。
81 分で読めます40,396 文字
プロセス監視(ps, top, htop)
プロセスの状態を把握することは、トラブルシューティングの第一歩。 サーバーの負荷分析、メモリリークの検出、異常プロセスの特定 — すべてはプロセス監視から始まる。
この章で学ぶこと
- ps でプロセス一覧を取得・フィルタリングできる
- top / htop でリアルタイム監視ができる
- プロセスの状態・リソース使用量を読み解ける
- pgrep / pstree でプロセスの検索・構造把握ができる
- /proc ファイルシステムからプロセス情報を取得できる
- 監視スクリプトを作成して自動化できる
前提知識
このガイドを読む前に、以下の知識があると理解が深まります:
- 基本的なプログラミングの知識
- 関連する基礎概念の理解
1. ps — プロセスのスナップショット
1.1 基本的な使い方
# ps はプロセスの「ある瞬間」のスナップショットを取得するコマンド
# BSD形式とUNIX(System V)形式の2種類の書式がある
# BSD形式(ダッシュなし)
ps aux # 全プロセス表示
ps axjf # ツリー表示(プロセス階層)
# UNIX形式(ダッシュあり)
ps -ef # 全プロセス表示
ps -eF # 拡張フォーマットで全プロセス
# 違い:
# ps aux → USER, PID, %CPU, %MEM, VSZ, RSS, TTY, STAT, START, TIME, COMMAND
# ps -ef → UID, PID, PPID, C, STIME, TTY, TIME, CMD
# 自分のプロセスのみ
ps u # 現在のユーザーの端末に関連するプロセス
ps ux # 現在のユーザーの全プロセス
# 特定ユーザーのプロセス
ps -u gaku # ユーザー gaku のプロセス
ps -u root # root のプロセス
ps -U gaku # 実UID で検索
ps -u gaku -f # フルフォーマット1.2 出力列の詳細解説
# ps aux の出力例:
# USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
# root 1 0.0 0.1 169344 13256 ? Ss Jan01 0:15 /sbin/init
# gaku 1234 5.2 2.3 524288 37120 pts/0 Sl+ 14:30 0:42 node server.js
# 各列の詳細:
# USER: プロセスの実効ユーザー
# PID: プロセスID(一意の識別子)
# %CPU: CPU使用率(プロセスのライフタイムにおける平均)
# %MEM: 物理メモリ使用率
# VSZ: 仮想メモリサイズ(KB)— プロセスがアクセスできる全メモリ空間
# RSS: 常駐セットサイズ(KB)— 実際に物理メモリに存在するサイズ
# TTY: 制御端末(? = デーモン、pts/N = 疑似端末)
# STAT: プロセス状態(後述の詳細参照)
# START: 開始時刻(24時間以内は時刻、それ以外は日付)
# TIME: 累積CPU時間(プロセスが実際にCPUを使用した合計時間)
# COMMAND: 実行コマンド
# VSZ と RSS の違い:
# VSZ(Virtual Size): mmap されたファイル、共有ライブラリ、未使用の割当メモリも含む
# RSS(Resident Set Size): 実際に物理メモリに存在するページのサイズ
# 通常 VSZ >> RSS(VSZが大きくても実害は少ないことが多い)
# RSS が大きいプロセスが実際のメモリ消費者
# ps -ef の出力例:
# UID PID PPID C STIME TTY TIME CMD
# root 1 0 0 Jan01 ? 00:00:15 /sbin/init
# PPID: 親プロセスID(このプロセスを生成したプロセス)
# C: CPU利用率(短期間の数値)
# STIME: 開始時刻1.3 STAT(プロセス状態)の完全ガイド
# STAT フィールドは1文字の基本状態 + 追加フラグで構成される
# === 基本状態(1文字目) ===
# R: Running — 実行中またはCPU実行キューに入っている
# S: Sleeping — 割り込み可能なスリープ(I/O完了やシグナルを待機)
# D: Disk sleep — 割り込み不可のスリープ(I/O待ち)
# → kill -9 でも終了できない! ディスクやNFSの問題が原因
# Z: Zombie — 終了済みだが親がwait()していない
# → 親プロセスのバグが原因。親を終了させれば消える
# T: Stopped — シグナルで停止(SIGSTOP/SIGTSTP)
# t: Tracing — デバッガ(strace等)によるトレース中
# I: Idle — カーネルのアイドルスレッド(Linux 4.14+)
# X: Dead — 表示されることはない(終了処理中の一瞬)
# === 追加フラグ(2文字目以降) ===
# s: セッションリーダー(ログインシェルなど)
# l: マルチスレッド
# +: フォアグラウンドプロセスグループのメンバー
# <: 高優先度(nice値が負)
# N: 低優先度(nice値が正)
# L: メモリ内にロックされたページがある
# W: スワップアウトされている(Linux 2.6以降では使われない)
# よく見るSTATの組み合わせと意味:
# Ss → スリープ中のセッションリーダー(sshd, init など)
# Ssl → スリープ中のセッションリーダー、マルチスレッド(systemd など)
# R+ → 実行中のフォアグラウンドプロセス
# S+ → スリープ中のフォアグラウンドプロセス(vim, less など)
# Sl → スリープ中のマルチスレッドプロセス(Java, Node.js など)
# S< → 高優先度でスリープ中
# SN → 低優先度でスリープ中
# Z+ → ゾンビ状態のフォアグラウンドプロセス
# D+ → I/O待ちのフォアグラウンドプロセス
# STATで状態を絞り込む
ps aux | awk '$8 ~ /Z/' # ゾンビプロセスのみ
ps aux | awk '$8 ~ /D/' # I/O待ちプロセスのみ(ディスク問題の兆候)
ps aux | awk '$8 ~ /R/' # 実行中のプロセスのみ
ps aux | awk '$8 ~ /T/' # 停止中のプロセスのみ1.4 カスタム出力(-o / --format)
# 特定の列だけ表示(-o / --format)
ps -eo pid,ppid,user,%cpu,%mem,stat,cmd --sort=-%cpu | head -20
# よく使うカスタムフォーマット
ps -eo pid,ppid,user,%cpu,%mem,rss,vsz,stat,etime,cmd --sort=-%mem | head -20
# 利用可能なフォーマットキーワード(主要なもの)
# pid プロセスID
# ppid 親プロセスID
# pgid プロセスグループID
# sid セッションID
# uid ユーザーID
# user ユーザー名
# gid グループID
# group グループ名
# %cpu CPU使用率
# %mem メモリ使用率
# rss 常駐メモリサイズ(KB)
# vsz 仮想メモリサイズ(KB)
# sz 物理ページ数
# stat プロセス状態
# state プロセス状態(1文字)
# pri 優先度
# ni nice値
# tty 制御端末
# time 累積CPU時間
# etime 経過時間(プロセス起動からの時間)
# etimes 経過時間(秒数)
# cmd コマンド(引数なし)
# args コマンド(引数付き)
# comm コマンド名のみ
# wchan カーネル関数名(待機中の場所)
# lstart 起動時刻(詳細形式)
# nlwp スレッド数
# 特定プロセスの詳細
ps -p 1234 -o pid,ppid,%cpu,%mem,rss,etime,lstart,cmd
# etime: 経過時間(DD-HH:MM:SS形式)
# lstart: 起動時刻(Wed Jan 15 14:30:00 2024形式)
# カスタムヘッダー
ps -eo pid=PID,user=USER,%cpu=CPU,%mem=MEM,cmd=COMMAND --sort=-%cpu | head -10
# ヘッダーなし
ps -eo pid,%cpu,%mem,cmd --sort=-%cpu --no-headers | head -10
# スレッド表示
ps -eLo pid,tid,user,%cpu,%mem,cmd | head -20
# -L: スレッドを個別行で表示
# tid: スレッドID
# プロセスのnice値とスケジューリング情報
ps -eo pid,ni,pri,cls,cmd --sort=-ni | head -20
# ni: nice値(-20〜19、低いほど高優先度)
# pri: カーネル内部優先度
# cls: スケジューリングクラス(TS=タイムシェア、FF=FIFO、RR=ラウンドロビン)1.5 ソートオプション
# ソート指定(--sort)
ps aux --sort=-%cpu # CPU使用率の高い順(降順)
ps aux --sort=-%mem # メモリ使用率の高い順
ps aux --sort=-rss # 常駐メモリの大きい順
ps aux --sort=-vsz # 仮想メモリの大きい順
ps aux --sort=start_time # 起動時刻の古い順
ps aux --sort=-start_time # 起動時刻の新しい順
ps aux --sort=pid # PID順(昇順)
ps aux --sort=-etime # 経過時間の長い順
# 複数キーでのソート
ps aux --sort=-%cpu,-%mem # CPU順、同値ならメモリ順
# 特定のコマンドでフィルタ
ps -C nginx # コマンド名で検索
ps -C nginx -o pid,%cpu,%mem,cmd # フォーマット指定付き
ps -C nginx,node,python -o pid,cmd # 複数コマンド名1.6 パイプとの組み合わせ
# nginx プロセスの検索(grep パターン)
ps aux | grep nginx | grep -v grep
# grep -v grep: grep 自身を除外
# pgrep の方がスマート(推奨)
pgrep -la nginx # PID + コマンドライン全体
pgrep -l nginx # PID + プロセス名
pgrep -u root -l # rootのプロセス一覧
pgrep -c nginx # プロセス数のカウント
pgrep -f "node.*server" # コマンドライン全体で正規表現マッチ
pgrep -P 1234 # 親PID 1234 の子プロセス
pgrep -n nginx # 最新のnginxプロセスのPID
pgrep -o nginx # 最古のnginxプロセスのPID
pgrep -x nginx # 完全一致(部分一致を防ぐ)
# pidof(完全一致のPID取得)
pidof nginx # 全プロセスのPID(スペース区切り)
pidof -s nginx # 1つだけ取得
# プロセスの親子関係
pstree -p # PID付きツリー
pstree -p 1234 # 特定プロセスの子孫
pstree -u # UID変更を表示
pstree -a # コマンドライン引数も表示
pstree -h # カレントプロセスをハイライト
pstree -H 1234 # 指定PIDをハイライト
pstree -s 1234 # 指定PIDの祖先を表示
pstree -c # 同一プロセスを折りたたまない
pstree -g # プロセスグループIDを表示
# 実用パターン: 特定サービスのプロセスツリー
pstree -p $(pgrep -o nginx) # nginx のマスタープロセスからのツリー2. top — リアルタイム監視
2.1 画面構成の詳細解説
top # 基本起動
# top 画面の構成(5つのセクション)
# ┌──────────────────────────────────────────────────────────┐
# │ top - 14:30:00 up 30 days, 2:15, 3 users, load avg: ... │ ← (1) サマリ行
# │ Tasks: 256 total, 1 running, 254 sleeping, 1 stopped │ ← (2) タスク行
# │ %Cpu(s): 3.2 us, 1.1 sy, 0.0 ni, 95.5 id, 0.1 wa... │ ← (3) CPU行
# │ MiB Mem: 16384.0 total, 8192.0 free, 4096.0 used.. │ ← (4) メモリ行
# │ MiB Swap: 8192.0 total, 8192.0 free, 0.0 used.. │ ← (5) スワップ行
# ├──────────────────────────────────────────────────────────┤
# │ PID USER PR NI VIRT RES SHR S %CPU %MEM.. │ ← プロセス一覧
# └──────────────────────────────────────────────────────────┘2.2 サマリ行の読み方
(1) サマリ行:
top - 14:30:00 up 30 days, 2:15, 3 users, load average: 1.50, 2.00, 1.80
↑ ↑ ↑ ↑ ↑ ↑ ↑
現在時刻 稼働時間 ユーザ数 1分平均 5分平均 15分平均
(2) タスク行:
Tasks: 256 total, 1 running, 254 sleeping, 0 stopped, 1 zombie
↑ ↑ ↑ ↑ ↑
総数 実行中 スリープ中 停止中 ゾンビ
※ zombie > 0 の場合は親プロセスの問題を調査すべき
(3) CPU行:
%Cpu(s): 3.2 us, 1.1 sy, 0.0 ni, 95.5 id, 0.1 wa, 0.0 hi, 0.1 si, 0.0 st
↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
user system nice idle iowait hw-irq sw-irq steal
各値の意味:
us (user): ユーザー空間のプロセス(nice値変更なし)
sy (system): カーネル空間の処理
ni (nice): nice値を変更したユーザープロセス
id (idle): アイドル(何もしていない)
wa (iowait): I/O 完了待ち ← ディスクボトルネックの指標
hi (hardware): ハードウェア割り込み処理
si (software): ソフトウェア割り込み処理(ネットワーク処理など)
st (steal): 仮想化環境でホストに奪われた時間 ← クラウドで重要
注目すべきパターン:
wa が高い → ディスクI/O がボトルネック(SSD化、I/Oスケジューラ調整を検討)
sy が高い → カーネル処理が多い(コンテキストスイッチ過多、システムコール多発)
st が高い → VM のCPU リソース不足(インスタンスタイプの変更を検討)
us が高い → アプリケーションがCPUを消費(プロファイリングで原因特定)
(4) メモリ行:
MiB Mem: 16384.0 total, 2048.0 free, 8192.0 used, 6144.0 buff/cache
↑ ↑ ↑
完全に空き プロセス使用 バッファ/キャッシュ
※ Linux はメモリをキャッシュに積極活用するため、free が少なくても問題ない
※ 実際の空き = free + buff/cache の大部分
※ 「avail Mem」(利用可能メモリ)がより正確な空き容量
(5) スワップ行:
MiB Swap: 8192.0 total, 8192.0 free, 0.0 used. 10240.0 avail Mem
↑ ↑
スワップ使用量 利用可能メモリ
※ Swap used > 0 が継続的に増加 → メモリ不足の兆候
※ avail Mem が物理メモリの10%以下 → メモリ増設を検討
2.3 プロセス一覧の列
# プロセス一覧の各列:
# PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
# 1234 gaku 20 0 524288 37120 15360 S 5.2 2.3 0:42.50 node
# PID: プロセスID
# USER: 所有者
# PR: カーネル内部優先度(rt = リアルタイム)
# NI: nice値(-20〜19)
# VIRT: 仮想メモリ(VSZ相当)
# RES: 常駐メモリ(RSS相当)
# SHR: 共有メモリ(ライブラリなど)
# S: 状態(R/S/D/Z/T)
# %CPU: CPU使用率(直近の更新間隔での値)
# %MEM: 物理メモリ使用率
# TIME+: 累積CPU時間(1/100秒単位)
# COMMAND: コマンド名
# top と ps の %CPU の違い:
# top: 直近の更新間隔(デフォルト3秒)における瞬間的なCPU使用率
# ps: プロセスのライフタイム全体での平均CPU使用率
# → top の方がリアルタイムな負荷を反映する2.4 top の対話的操作キー
# top 実行中に使えるキー(覚えるべきもの)
# === ソート ===
# P: CPU使用率順でソート(デフォルト)
# M: メモリ使用率順でソート
# T: 累積CPU時間順でソート
# N: PID順でソート
# R: 現在のソートを逆順に
# === 表示切替 ===
# 1: CPU をコア別に表示/集約表示(マルチコアの確認に必須)
# c: コマンド名 / フルコマンドライン切替
# H: スレッド表示ON/OFF
# V: ツリー表示(プロセスの親子関係)
# e: メモリ単位切替(KB→MB→GB→TB→PB)
# E: サマリ行のメモリ単位切替
# m: メモリ行の表示形式切替(数値/バー表示)
# t: タスク/CPU行の表示形式切替
# l: ロードアベレージ行のON/OFF
# 0: ゼロ値の表示/非表示
# === フィルタリング ===
# u: ユーザーでフィルタ(ユーザー名を入力)
# o: フィルタ条件追加(例: %CPU>10, COMMAND=nginx)
# O: フィルタ条件追加(大文字小文字区別なし)
# =: フィルタをクリア
# === アクション ===
# k: プロセスを kill(PIDとシグナルを入力)
# r: プロセスの nice 値を変更(renice)
# d: 更新間隔を変更(秒数を入力)
# s: 更新間隔を変更(同上)
# === 設定 ===
# f: 表示列の選択・順序変更
# W: 現在の設定を ~/.toprc に保存
# q: 終了
# フィルタの例:
# o を押して以下を入力:
# %CPU>5.0 → CPU 5%以上のプロセスのみ
# COMMAND=java → java を含むプロセスのみ
# %MEM>10.0 → メモリ 10%以上
# USER=gaku → ユーザー gaku のみ
# !COMMAND=kworker → kworker を除外2.5 top のコマンドラインオプション
# バッチモード(スクリプト用)
top -bn1 # 1回だけ出力して終了
top -bn1 | head -20 # 上位20行のみ
top -bn1 -o %MEM | head -20 # メモリ順で1回出力
# 更新間隔の指定
top -d 1 # 1秒間隔で更新
top -d 0.5 # 0.5秒間隔
# 特定ユーザーのプロセスのみ
top -u gaku
top -u root
# 特定PIDのみ監視
top -p 1234 # 1つのプロセス
top -p 1234,5678,9012 # 複数プロセス
# スレッド表示
top -H # スレッドを個別に表示
top -H -p 1234 # 特定プロセスのスレッドを監視
# セキュア(安全)モード
top -s # kill, renice などの操作を無効化
# バッチモードの活用例
# CPUトップ10を記録
top -bn1 -o %CPU | head -17 > /tmp/cpu_snapshot_$(date +%H%M%S).txt
# 5秒間隔で10回記録(50秒分の推移)
top -bn10 -d 5 -o %CPU | head -17 > /tmp/cpu_trend.txt
# 特定プロセスのCPU使用率の推移を記録
while true; do
echo "$(date +%H:%M:%S) $(top -bn1 -p 1234 | tail -1 | awk '{print $9, $10}')"
sleep 5
done >> /tmp/process_cpu_trend.log2.6 load average の深い理解
load average: 1.50, 2.00, 1.80
↑ ↑ ↑
1分 5分 15分
意味: 「実行中(R) + 実行待ち(R in queue) + I/O待ち(D)のプロセスの指数移動平均」
重要: Linux の load average は I/O 待ち(D状態)のプロセスも含む
→ 他のUNIX(FreeBSD等)とは異なる
→ ディスクI/Oが多い環境ではCPUに余裕があっても load が高くなる
判断基準(CPUコア数との比較):
コア数の確認:
nproc # コア数
lscpu | grep "^CPU(s):" # 詳細
cat /proc/cpuinfo | grep processor | wc -l # 論理コア数
4コアマシンの場合:
load avg < 4.0 → 正常(余裕あり)
load avg ≈ 4.0 → フル稼働(ギリギリ)
load avg > 4.0 → 過負荷(CPUキューに待ちが発生)
load avg > 8.0 → 深刻な過負荷(応答遅延の可能性)
load avg > 16.0 → 危機的状況(サービス影響あり)
load avg の傾向分析:
1分 > 5分 > 15分 → 負荷が上昇中(要注意)
1分 < 5分 < 15分 → 負荷が下降中(改善傾向)
1分 ≈ 5分 ≈ 15分 → 安定状態
load が高い場合の調査手順:
1. top の CPU行(us, sy, wa, st)を確認
- wa が高い → ディスクI/Oが原因
- us が高い → アプリケーションが原因
- sy が高い → カーネル処理が原因
- st が高い → 仮想化リソース不足
2. ps aux --sort=-%cpu | head -10 でCPU消費プロセスを特定
3. iostat -x 1 でディスクI/Oを確認
4. vmstat 1 でシステム全体の状態を確認
3. htop — モダンなプロセスモニタ
3.1 インストールと起動
# インストール
# macOS:
brew install htop
# Ubuntu/Debian:
sudo apt install htop
# CentOS/RHEL:
sudo yum install htop
# または
sudo dnf install htop
# Arch Linux:
sudo pacman -S htop
# 基本起動
htop3.2 画面構成
# htop の画面構成
# ┌──────────────────────────────────────────────┐
# │ CPU[|||||||||||| 35%] Tasks: 142, 1 run│ ← CPUメーター
# │ CPU[||| 12%] Load: 1.50 2.00 │ ← マルチコア個別表示
# │ CPU[|||||| 25%] Uptime: 30 days │
# │ CPU[| 5%] │
# │ Mem[||||||||||||||| 4.2G/16.0G] │ ← メモリメーター
# │ Swp[ 0K/8.0G] │ ← スワップメーター
# ├──────────────────────────────────────────────┤
# │ PID USER PRI NI VIRT RES SHR S CPU% │ ← プロセス一覧
# │ 1234 gaku 20 0 512M 36M 15M S 5.2 │ カラー表示
# │ 5678 root 20 0 256M 18M 12M S 2.1 │ ツリー表示対応
# │ ... │ マウス操作対応
# ├──────────────────────────────────────────────┤
# │ F1Help F2Setup F3Search F4Filter F5Tree F6So │ ← ファンクションキー
# │ F7Nice- F8Nice+ F9Kill F10Quit │
# └──────────────────────────────────────────────┘
# CPUメーターの色の意味(デフォルト)
# 緑色: ユーザープロセス(通常の負荷)
# 赤色: カーネルプロセス(システム負荷)
# 青色: 低優先度プロセス(nice値が高い)
# 水色: 仮想化スチール時間
# 黄色: I/O待ち時間
# メモリメーターの色の意味
# 緑色: 使用中メモリ
# 青色: バッファ
# 黄色: キャッシュ3.3 htop の操作キー
# === 基本操作 ===
# F1 / h: ヘルプ画面
# F2 / S: 設定画面(メーター、カラー、表示列などを変更)
# F3 / /: インクリメンタル検索
# F4 / \: フィルタ(表示するプロセスを文字列でフィルタ)
# F5 / t: ツリー表示ON/OFF
# F6 / >: ソート列の選択
# F7 / ]: nice値を下げる(優先度を上げる)
# F8 / [: nice値を上げる(優先度を下げる)
# F9 / k: シグナル送信(プロセスをkill)
# F10 / q: 終了
# === 表示操作 ===
# Space: プロセスをマーク(複数選択)
# c: マークしたプロセスにタグ付け
# U: 全マーク解除
# u: ユーザーでフィルタ
# H: ユーザースレッドの表示/非表示
# K: カーネルスレッドの表示/非表示
# p: プロセスのフルパス表示
# m: メモリソートの切替
# T: CPU時間ソート
# === 高度な操作 ===
# l: プロセスが開いているファイル一覧(lsof)
# s: プロセスのシステムコール追跡(strace)
# e: プロセスの環境変数表示
# w: プロセスを /proc/PID/wchan で確認
# i: プロセスのI/O情報
# M: ライブラリマッピング表示(メモリマップ)
# === 検索とフィルタの違い ===
# F3 (検索): プロセス名でカーソルを移動(次を検索: F3 を再度押す)
# F4 (フィルタ): マッチするプロセスのみ表示(他は非表示)
# → フィルタの方が見やすい3.4 htop のコマンドラインオプション
# ユーザーフィルタ
htop -u gaku # ユーザー gaku のプロセスのみ
# 特定PIDのみ
htop -p 1234,5678 # 指定PIDのみ表示
# ツリーモードで起動
htop -t # ツリー表示をデフォルトに
# 更新間隔(単位: 1/10秒)
htop -d 10 # 1秒間隔(10 × 0.1秒)
htop -d 50 # 5秒間隔
# ソート列を指定して起動
htop --sort-key=PERCENT_CPU # CPU使用率順
htop --sort-key=PERCENT_MEM # メモリ使用率順
htop --sort-key=M_RESIDENT # RSS順
# モノクロ表示
htop -C # カラーなし
# 遅延カウントを指定
htop --delay=20 # 2秒間隔3.5 htop の設定(F2)
# F2 で設定画面を開く
# Meters(メーター設定)
# ヘッダー部分に表示するメーターを追加・削除・配置変更
# 追加可能なメーター:
# - CPU使用率(全体/個別)
# - メモリ使用率
# - スワップ使用率
# - タスク数
# - ロードアベレージ
# - 稼働時間
# - バッテリー
# - ホスト名
# - クロック
# - ディスクI/O
# - ネットワークI/O
# Display options(表示オプション)
# - ツリー表示
# - シャドウ表示
# - カウント表示
# - プロセスパスの表示方法
# Colors(カラースキーム)
# - デフォルト
# - モノクロ
# - ブラック・オン・ホワイト
# - Light Terminal
# - MC
# Columns(表示列設定)
# プロセス一覧に表示する列を追加・削除・順序変更
# 設定は ~/.config/htop/htoprc に保存される4. その他の監視ツール
4.1 glances — 統合システムモニタ
# インストール
pip install glances
# または
brew install glances # macOS
sudo apt install glances # Ubuntu
# 基本起動
glances
# glances の特徴:
# - CPU + メモリ + ディスク + ネットワーク + プロセスを一画面で表示
# - アラート機能(閾値超過で色が変わる)
# - Web UI モード
# - API モード(RESTful API でデータ取得)
# - CSV/JSON エクスポート
# Web UI モード(リモートからブラウザで監視)
glances -w # http://localhost:61208 で接続
glances -w --bind 0.0.0.0 # 全インターフェースでリッスン
# クライアント/サーバーモード
glances -s # サーバーとして起動
glances -c server-ip # クライアントとして接続
# CSV エクスポート
glances --export csv --export-csv-file /tmp/glances.csv
# JSON エクスポート
glances --stdout cpu.total,mem.percent,load4.2 btop — 美しいリソースモニタ
# インストール
brew install btop # macOS
sudo apt install btop # Ubuntu 22.04+
sudo snap install btop # Snap
# 基本起動
btop
# btop の特徴:
# - 美しいグラフィカル表示(CPU/メモリ/ネットワーク/ディスクのグラフ)
# - マウス操作対応
# - テーマ変更可能
# - プロセスのフィルタリング/ソート
# - 設定ファイル: ~/.config/btop/btop.conf4.3 /proc ファイルシステム(Linux)
# /proc はカーネルが提供する仮想ファイルシステム
# プロセスとシステムの詳細情報にアクセスできる
# === プロセス別情報(/proc/PID/) ===
# プロセスの詳細情報
cat /proc/1234/status
# Name: nginx
# State: S (sleeping)
# Tgid: 1234
# Pid: 1234
# PPid: 1
# VmPeak: 524288 kB ← 仮想メモリのピーク値
# VmSize: 524288 kB ← 現在の仮想メモリサイズ
# VmRSS: 37120 kB ← 常駐メモリサイズ
# VmSwap: 0 kB ← スワップされたサイズ
# Threads: 4 ← スレッド数
# コマンドライン
cat /proc/1234/cmdline | tr '\0' ' '
# /usr/sbin/nginx -g daemon off;
# 環境変数
cat /proc/1234/environ | tr '\0' '\n'
# HOME=/root
# PATH=/usr/local/sbin:/usr/local/bin:...
# ファイルディスクリプタ一覧
ls -l /proc/1234/fd
# lrwx------ 1 root root 64 ... 0 -> /dev/null
# l-wx------ 1 root root 64 ... 1 -> /var/log/nginx/access.log
# l-wx------ 1 root root 64 ... 2 -> /var/log/nginx/error.log
# lrwx------ 1 root root 64 ... 3 -> socket:[12345]
# ファイルディスクリプタ数
ls /proc/1234/fd | wc -l
# メモリマップ
cat /proc/1234/maps | head -20
# 各行: 開始-終了 パーミッション オフセット デバイス inode パス名
# プロセスのリソース制限
cat /proc/1234/limits
# Max open files 65536 65536 files
# プロセスのI/O統計
cat /proc/1234/io
# rchar: 読み取りバイト数
# wchar: 書き込みバイト数
# read_bytes: 実際のディスク読み取り
# write_bytes: 実際のディスク書き込み
# プロセスの cgroup 情報
cat /proc/1234/cgroup
# === システム全体の情報 ===
cat /proc/loadavg # ロードアベレージ
cat /proc/meminfo # メモリ情報の詳細
cat /proc/cpuinfo # CPU情報
cat /proc/uptime # 稼働時間(秒)
cat /proc/version # カーネルバージョン
cat /proc/stat # CPU統計
cat /proc/diskstats # ディスクI/O統計
cat /proc/net/dev # ネットワークI/O統計4.4 lsof — 開いているファイル/ソケット
# lsof (List Open Files): プロセスが開いているファイルを一覧表示
# Linuxでは「すべてがファイル」→ ソケット、パイプも対象
# 特定PIDが開いているファイル
lsof -p 1234
# ポートを使っているプロセス
lsof -i :8080 # ポート8080
lsof -i :80,443 # 複数ポート
lsof -i TCP:3000 # TCPのポート3000
lsof -i UDP:53 # UDPのポート53
lsof -i TCP # 全TCP接続
lsof -i -P -n # 名前解決しない(高速)
# ユーザーが開いているファイル
lsof -u gaku
lsof -u gaku -c python # ユーザー + コマンド名
# ディレクトリ内のファイルを開いているプロセス
lsof +D /var/log # /var/log 内のファイル
lsof +d /var/log # /var/log 直下のみ(再帰なし)
# 削除されたがまだ開いているファイル(ディスク解放されない原因)
lsof +L1
# 解決策: プロセスを再起動するか、/proc/PID/fd/N を truncate
# ネットワーク接続の確認
lsof -i -P -n | grep LISTEN # リッスンしているポート一覧
lsof -i -P -n | grep ESTABLISHED # 確立済み接続一覧
# 特定ファイルを開いているプロセス
lsof /var/log/syslog # 特定ファイル
# NFS のロック問題調査
lsof -N # NFSファイルを開いているプロセス4.5 vmstat / iostat / mpstat
# vmstat — 仮想メモリ統計(システム全体の概要)
vmstat 1 5 # 1秒間隔で5回表示
# 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 8192000 256000 4096000 0 0 10 20 500 800 3 1 95 1 0
# r: 実行待ちプロセス数(CPUコア数以上なら過負荷)
# b: I/O待ちプロセス数(D状態)
# si/so: スワップイン/スワップアウト(0以外が継続ならメモリ不足)
# bi/bo: ディスクI/O(ブロック/秒)
# in: 割り込み数/秒
# cs: コンテキストスイッチ数/秒
# iostat — ディスクI/O統計
iostat -x 1 5 # 拡張統計、1秒間隔で5回
# Device r/s w/s rkB/s wkB/s await %util
# sda 50.0 30.0 2000.0 1500.0 5.00 40.0
# %util: ディスク使用率(100%に近いとボトルネック)
# await: 平均I/O待ち時間(ミリ秒)
# mpstat — CPU別統計
mpstat -P ALL 1 5 # 全CPU、1秒間隔で5回
# 特定のCPUコアだけに負荷が偏っていないか確認5. 実践パターン
5.1 CPUボトルネックの調査
# ステップ1: 全体像の把握
top -bn1 | head -5
# load average と CPU行を確認
# ステップ2: CPU消費プロセスの特定
ps aux --sort=-%cpu | head -10
# または
top -bn1 -o %CPU | head -15
# ステップ3: 特定プロセスの詳細確認
ps -p 1234 -o pid,ppid,%cpu,%mem,etime,ni,stat,cmd
# etime: どのくらい前から動いているか
# ni: nice値(優先度)
# ステップ4: スレッドレベルの確認
ps -p 1234 -L -o pid,tid,%cpu,cmd | sort -k3 -rn | head -10
# どのスレッドがCPUを消費しているか
# ステップ5: strace でシステムコールを確認(上級)
strace -p 1234 -c -T # システムコールの統計
strace -p 1234 -e trace=write # write システムコールのみ5.2 メモリリークの調査
# パターン1: メモリ使用量の継続監視
watch -n 5 'ps -p 1234 -o pid,rss,vsz,%mem,etime'
# 5秒ごとにメモリ使用量を表示
# RSS が時間とともに増加し続ける → メモリリークの疑い
# パターン2: メモリ使用量の記録
while true; do
echo "$(date +%Y-%m-%d_%H:%M:%S) $(ps -p 1234 -o rss=,vsz=,%mem= --no-headers)"
sleep 60
done >> /tmp/memory_monitor_1234.log
# パターン3: メモリ上位プロセスの定期記録
while true; do
echo "=== $(date) ==="
ps aux --sort=-rss | head -11
echo ""
sleep 300
done >> /tmp/memory_top_processes.log
# パターン4: システム全体のメモリ使用量の推移
while true; do
echo "$(date +%H:%M:%S) $(free -m | awk '/Mem:/ {printf "used:%sMB free:%sMB avail:%sMB", $3, $4, $7}')"
sleep 10
done >> /tmp/system_memory.log
# パターン5: /proc からの詳細メモリ情報
cat /proc/1234/status | grep -E "^Vm|^Rss|^Threads"
cat /proc/1234/smaps_rollup # メモリマッピングのサマリ
# パターン6: pmap でメモリマップを確認
pmap -x 1234 | tail -5 # プロセスのメモリマップ
pmap -x 1234 | sort -k3 -rn | head -10 # サイズ順5.3 ゾンビプロセスの対処
# ゾンビプロセスの発見
ps aux | awk '$8 ~ /Z/ {print}'
# または
ps -eo pid,ppid,stat,cmd | grep -E "Z"
# ゾンビの数を確認
ps aux | awk '$8 ~ /Z/' | wc -l
# ゾンビの親プロセスを特定
ps -eo pid,ppid,stat,cmd | awk '$3 ~ /Z/ {print "Zombie PID:", $1, "Parent PID:", $2}'
# 親プロセスの情報を確認
ps -p <親PID> -o pid,cmd,stat
# 対処法1: 親プロセスに SIGCHLD を送る
kill -SIGCHLD <親PID>
# 対処法2: 親プロセスを終了する
kill <親PID>
# 親が終了すると、ゾンビは init(PID 1) に引き取られて自動的にクリーンアップ
# 対処法3: 大量のゾンビの場合
# ゾンビの親PIDを集計
ps -eo ppid,stat | grep Z | awk '{print $1}' | sort | uniq -c | sort -rn
# 注意: ゾンビプロセスは kill -9 では消えない(既に死んでいる)
# ゾンビはプロセステーブルのエントリのみ消費(CPU/メモリはほぼゼロ)
# 少数のゾンビは問題ないが、大量に増え続ける場合は親プロセスのバグ5.4 ポート使用状況の調査
# ポートを使用しているプロセスを特定(複数の方法)
# lsof(macOS/Linux 共通)
lsof -i :3000 # ポート3000
lsof -i TCP:3000 -P -n # TCP限定、名前解決なし
# ss(Linux、netstat より高速)
ss -tlnp | grep 3000 # TCPリッスン
ss -tunlp # TCP/UDP リッスン全一覧
# -t: TCP -u: UDP -l: LISTEN -n: 数値表示 -p: プロセス表示
# netstat(古い方法、レガシー環境用)
netstat -tlnp | grep 3000
# fuser(ポートを使っているプロセスを直接特定)
fuser 3000/tcp # ポート3000/TCPのPID
fuser -v 3000/tcp # 詳細表示
fuser -k 3000/tcp # ポート3000/TCPを使っているプロセスをkill5.5 包括的なシステム診断スクリプト
#!/bin/bash
# system_health_check.sh - システムヘルスチェックスクリプト
echo "=============================================="
echo "システムヘルスチェック: $(date)"
echo "ホスト名: $(hostname)"
echo "=============================================="
echo ""
echo "--- ロードアベレージ ---"
uptime
CORES=$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 1)
echo "CPUコア数: $CORES"
LOAD1=$(cat /proc/loadavg 2>/dev/null | awk '{print $1}' || uptime | awk -F'[,:]' '{print $(NF-2)}' | tr -d ' ')
echo "Load/Core比: $(echo "$LOAD1 / $CORES" | bc -l 2>/dev/null | head -c 5)"
echo ""
echo "--- メモリ使用量 ---"
free -m 2>/dev/null || vm_stat 2>/dev/null
echo ""
echo "--- ディスク使用量 ---"
df -h | grep -vE "tmpfs|devtmpfs|overlay"
echo ""
echo "--- CPU消費プロセス Top5 ---"
ps aux --sort=-%cpu | head -6 2>/dev/null || ps aux | sort -k3 -rn | head -6
echo ""
echo "--- メモリ消費プロセス Top5 ---"
ps aux --sort=-rss | head -6 2>/dev/null || ps aux | sort -k6 -rn | head -6
echo ""
echo "--- ゾンビプロセス ---"
ZOMBIES=$(ps aux 2>/dev/null | awk '$8 ~ /Z/' | wc -l)
echo "ゾンビ数: $ZOMBIES"
if [ "$ZOMBIES" -gt 0 ]; then
ps aux | awk '$8 ~ /Z/ {print}'
fi
echo ""
echo "--- D状態(I/O待ち)プロセス ---"
DSTATE=$(ps aux 2>/dev/null | awk '$8 ~ /D/' | wc -l)
echo "D状態プロセス数: $DSTATE"
if [ "$DSTATE" -gt 0 ]; then
ps aux | awk '$8 ~ /D/ {print}'
fi
echo ""
echo "--- リッスンポート ---"
ss -tlnp 2>/dev/null | head -20 || lsof -i -P -n 2>/dev/null | grep LISTEN | head -20
echo ""
echo "--- ESTABLISHED接続数 ---"
ss -tn state established 2>/dev/null | wc -l || netstat -tn 2>/dev/null | grep ESTABLISHED | wc -l
echo ""
echo "--- ディスクI/O ---"
iostat -x 1 1 2>/dev/null | tail -10
echo ""
echo "--- 最近のOOMキル ---"
dmesg 2>/dev/null | grep -i "out of memory\|oom" | tail -5
echo ""
echo "=============================================="
echo "チェック完了"
echo "=============================================="5.6 プロセスリソース監視スクリプト
#!/bin/bash
# process_monitor.sh - 特定プロセスの継続監視
# 使い方: ./process_monitor.sh <PID> [間隔秒] [出力ファイル]
PID="${1:?使い方: $0 <PID> [間隔秒] [出力ファイル]}"
INTERVAL="${2:-10}"
OUTPUT="${3:-/tmp/process_monitor_${PID}.csv}"
if ! kill -0 "$PID" 2>/dev/null; then
echo "エラー: PID $PID が存在しません" >&2
exit 1
fi
PROCESS_NAME=$(ps -p "$PID" -o comm= 2>/dev/null)
echo "監視開始: PID=$PID ($PROCESS_NAME), 間隔=${INTERVAL}秒"
echo "出力ファイル: $OUTPUT"
echo "Ctrl+C で停止"
echo ""
# CSV ヘッダー
echo "timestamp,pid,cpu_pct,mem_pct,rss_kb,vsz_kb,threads,fd_count,state" > "$OUTPUT"
trap 'echo ""; echo "監視終了: $(wc -l < "$OUTPUT") レコード記録"; exit 0' INT TERM
while kill -0 "$PID" 2>/dev/null; do
TIMESTAMP=$(date +%Y-%m-%d_%H:%M:%S)
# ps からプロセス情報を取得
PS_DATA=$(ps -p "$PID" -o %cpu=,%mem=,rss=,vsz=,nlwp=,stat= --no-headers 2>/dev/null)
if [ -z "$PS_DATA" ]; then
echo "プロセス $PID が終了しました"
break
fi
CPU=$(echo "$PS_DATA" | awk '{print $1}')
MEM=$(echo "$PS_DATA" | awk '{print $2}')
RSS=$(echo "$PS_DATA" | awk '{print $3}')
VSZ=$(echo "$PS_DATA" | awk '{print $4}')
THREADS=$(echo "$PS_DATA" | awk '{print $5}')
STATE=$(echo "$PS_DATA" | awk '{print $6}')
# ファイルディスクリプタ数
FD_COUNT=$(ls /proc/"$PID"/fd 2>/dev/null | wc -l)
echo "$TIMESTAMP,$PID,$CPU,$MEM,$RSS,$VSZ,$THREADS,$FD_COUNT,$STATE" >> "$OUTPUT"
# 画面にもサマリを表示
printf "\r%s CPU:%s%% MEM:%s%% RSS:%sKB Threads:%s FDs:%s State:%s" \
"$TIMESTAMP" "$CPU" "$MEM" "$RSS" "$THREADS" "$FD_COUNT" "$STATE"
sleep "$INTERVAL"
done
echo ""
echo "監視終了: $(wc -l < "$OUTPUT") レコード → $OUTPUT"5.7 アラート付き監視スクリプト
#!/bin/bash
# alert_monitor.sh - 閾値超過時にアラートを出す監視
# 使い方: ./alert_monitor.sh
CPU_THRESHOLD=80 # CPU使用率の閾値(%)
MEM_THRESHOLD=90 # メモリ使用率の閾値(%)
LOAD_THRESHOLD_RATIO=2 # load average / コア数の閾値
CHECK_INTERVAL=30 # チェック間隔(秒)
LOG_FILE="/tmp/alert_monitor.log"
CORES=$(nproc 2>/dev/null || echo 4)
LOAD_THRESHOLD=$(echo "$CORES * $LOAD_THRESHOLD_RATIO" | bc)
log_alert() {
local msg="[$(date '+%Y-%m-%d %H:%M:%S')] ALERT: $1"
echo "$msg" | tee -a "$LOG_FILE"
}
log_info() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] INFO: $1" >> "$LOG_FILE"
}
echo "監視開始(CPU>${CPU_THRESHOLD}%, MEM>${MEM_THRESHOLD}%, Load>${LOAD_THRESHOLD})"
echo "ログファイル: $LOG_FILE"
while true; do
# CPU チェック
CPU_HOGGERS=$(ps aux --sort=-%cpu --no-headers | awk -v thresh="$CPU_THRESHOLD" '$3 > thresh {print $2, $3"%", $11}')
if [ -n "$CPU_HOGGERS" ]; then
log_alert "CPU閾値超過:"
echo "$CPU_HOGGERS" | while read -r line; do
log_alert " $line"
done
fi
# メモリチェック
MEM_USED_PCT=$(free 2>/dev/null | awk '/Mem:/ {printf "%.0f", $3/$2*100}')
if [ -n "$MEM_USED_PCT" ] && [ "$MEM_USED_PCT" -gt "$MEM_THRESHOLD" ]; then
log_alert "メモリ使用率: ${MEM_USED_PCT}%"
ps aux --sort=-rss --no-headers | head -5 | while read -r line; do
log_alert " Top RSS: $(echo "$line" | awk '{print $2, $6"KB", $11}')"
done
fi
# Load Average チェック
LOAD1=$(cat /proc/loadavg 2>/dev/null | awk '{print $1}')
if [ -n "$LOAD1" ]; then
OVER=$(echo "$LOAD1 > $LOAD_THRESHOLD" | bc -l 2>/dev/null)
if [ "$OVER" = "1" ]; then
log_alert "Load Average: $LOAD1(閾値: $LOAD_THRESHOLD)"
fi
fi
# ゾンビチェック
ZOMBIE_COUNT=$(ps aux 2>/dev/null | awk '$8 ~ /Z/' | wc -l)
if [ "$ZOMBIE_COUNT" -gt 5 ]; then
log_alert "ゾンビプロセス: ${ZOMBIE_COUNT}個"
fi
log_info "チェック完了 (CPU:OK MEM:${MEM_USED_PCT:-?}% Load:${LOAD1:-?})"
sleep "$CHECK_INTERVAL"
done6. コマンド比較表
| 機能 | ps | top | htop |
|---|---|---|---|
| 更新 | スナップショット | リアルタイム | リアルタイム |
| 表示形式 | テキスト | TUI | カラーTUI |
| ソート | --sort オプション | 対話キー | 対話キー/マウス |
| フィルタ | grep/awk | u/o キー | F4キー |
| ツリー表示 | axjf / f | V キー | F5キー |
| kill | 別コマンド | k キー | F9キー |
| カスタマイズ | -o オプション | f キー | F2設定画面 |
| スクリプト用 | 最適 | -bn1 で可 | 不向き |
| マウス操作 | なし | なし | 対応 |
| スレッド | -L / -T | H キー | H キー |
| インストール | 標準搭載 | 標準搭載 | 追加インストール |
使い分けガイド:
スクリプト・自動化 → ps(出力が安定、パイプに適する)
対話的なトラブルシュート → htop(最も使いやすい)
htop がない環境 → top(どこにでもある)
特定プロセスの詳細調査 → ps -p PID -o ...
サーバーの定期監視 → top -bn1(cron + ログ記録)
実践演習
演習1: 基本的な実装
以下の要件を満たすコードを実装してください。
要件:
- 入力データの検証を行うこと
- エラーハンドリングを適切に実装すること
- テストコードも作成すること
# 演習1: 基本実装のテンプレート
class Exercise1:
"""基本的な実装パターンの演習"""
def __init__(self):
self.data = []
def validate_input(self, value):
"""入力値の検証"""
if value is None:
raise ValueError("入力値がNoneです")
return True
def process(self, value):
"""データ処理のメインロジック"""
self.validate_input(value)
self.data.append(value)
return self.data
def get_results(self):
"""処理結果の取得"""
return {
'count': len(self.data),
'data': self.data
}
# テスト
def test_exercise1():
ex = Exercise1()
assert ex.process(1) == [1]
assert ex.process(2) == [1, 2]
assert ex.get_results()['count'] == 2
try:
ex.process(None)
assert False, "例外が発生するべき"
except ValueError:
pass
print("全テスト合格!")
test_exercise1()演習2: 応用パターン
基本実装を拡張して、以下の機能を追加してください。
# 演習2: 応用パターン
from typing import List, Dict, Optional
from datetime import datetime
class AdvancedExercise:
"""応用パターンの演習"""
def __init__(self, max_size: int = 100):
self._items: List[Dict] = []
self._max_size = max_size
self._created_at = datetime.now()
def add(self, key: str, value: any) -> bool:
"""アイテムの追加(サイズ制限付き)"""
if len(self._items) >= self._max_size:
return False
self._items.append({
'key': key,
'value': value,
'timestamp': datetime.now().isoformat()
})
return True
def find(self, key: str) -> Optional[Dict]:
"""キーによる検索"""
for item in reversed(self._items):
if item['key'] == key:
return item
return None
def remove(self, key: str) -> bool:
"""キーによる削除"""
for i, item in enumerate(self._items):
if item['key'] == key:
self._items.pop(i)
return True
return False
def stats(self) -> Dict:
"""統計情報"""
return {
'total_items': len(self._items),
'max_size': self._max_size,
'usage_percent': len(self._items) / self._max_size * 100,
'uptime': str(datetime.now() - self._created_at)
}
# テスト
def test_advanced():
ex = AdvancedExercise(max_size=3)
assert ex.add("a", 1) == True
assert ex.add("b", 2) == True
assert ex.add("c", 3) == True
assert ex.add("d", 4) == False # サイズ制限
assert ex.find("b")['value'] == 2
assert ex.remove("b") == True
assert ex.find("b") is None
stats = ex.stats()
assert stats['total_items'] == 2
print("応用テスト全合格!")
test_advanced()演習3: パフォーマンス最適化
以下のコードのパフォーマンスを改善してください。
# 演習3: パフォーマンス最適化
import time
from functools import lru_cache
# 最適化前(O(n^2))
def slow_search(data: list, target: int) -> int:
"""非効率な検索"""
for i in range(len(data)):
for j in range(i + 1, len(data)):
if data[i] + data[j] == target:
return (i, j)
return (-1, -1)
# 最適化後(O(n))
def fast_search(data: list, target: int) -> tuple:
"""ハッシュマップを使った効率的な検索"""
seen = {}
for i, num in enumerate(data):
complement = target - num
if complement in seen:
return (seen[complement], i)
seen[num] = i
return (-1, -1)
# ベンチマーク
def benchmark():
import random
data = list(range(5000))
random.shuffle(data)
target = data[100] + data[4000]
start = time.time()
result1 = slow_search(data, target)
slow_time = time.time() - start
start = time.time()
result2 = fast_search(data, target)
fast_time = time.time() - start
print(f"非効率版: {slow_time:.4f}秒")
print(f"効率版: {fast_time:.6f}秒")
print(f"高速化率: {slow_time/fast_time:.0f}倍")
benchmark()ポイント:
- アルゴリズムの計算量を意識する
- 適切なデータ構造を選択する
- ベンチマークで効果を測定する
トラブルシューティング
よくあるエラーと解決策
| エラー | 原因 | 解決策 |
|---|---|---|
| 初期化エラー | 設定ファイルの不備 | 設定ファイルのパスと形式を確認 |
| タイムアウト | ネットワーク遅延/リソース不足 | タイムアウト値の調整、リトライ処理の追加 |
| メモリ不足 | データ量の増大 | バッチ処理の導入、ページネーションの実装 |
| 権限エラー | アクセス権限の不足 | 実行ユーザーの権限確認、設定の見直し |
| データ不整合 | 並行処理の競合 | ロック機構の導入、トランザクション管理 |
デバッグの手順
- エラーメッセージの確認: スタックトレースを読み、発生箇所を特定する
- 再現手順の確立: 最小限のコードでエラーを再現する
- 仮説の立案: 考えられる原因をリストアップする
- 段階的な検証: ログ出力やデバッガを使って仮説を検証する
- 修正と回帰テスト: 修正後、関連する箇所のテストも実行する
# デバッグ用ユーティリティ
import logging
import traceback
from functools import wraps
# ロガーの設定
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s [%(levelname)s] %(name)s: %(message)s'
)
logger = logging.getLogger(__name__)
def debug_decorator(func):
"""関数の入出力をログ出力するデコレータ"""
@wraps(func)
def wrapper(*args, **kwargs):
logger.debug(f"呼び出し: {func.__name__}(args={args}, kwargs={kwargs})")
try:
result = func(*args, **kwargs)
logger.debug(f"戻り値: {func.__name__} -> {result}")
return result
except Exception as e:
logger.error(f"例外発生: {func.__name__}: {e}")
logger.error(traceback.format_exc())
raise
return wrapper
@debug_decorator
def process_data(items):
"""データ処理(デバッグ対象)"""
if not items:
raise ValueError("空のデータ")
return [item * 2 for item in items]パフォーマンス問題の診断
パフォーマンス問題が発生した場合の診断手順:
- ボトルネックの特定: プロファイリングツールで計測
- メモリ使用量の確認: メモリリークの有無をチェック
- I/O待ちの確認: ディスクやネットワークI/Oの状況を確認
- 同時接続数の確認: コネクションプールの状態を確認
| 問題の種類 | 診断ツール | 対策 |
|---|---|---|
| CPU負荷 | cProfile, py-spy | アルゴリズム改善、並列化 |
| メモリリーク | tracemalloc, objgraph | 参照の適切な解放 |
| I/Oボトルネック | strace, iostat | 非同期I/O、キャッシュ |
| DB遅延 | EXPLAIN, slow query log | インデックス、クエリ最適化 |
設計判断ガイド
選択基準マトリクス
技術選択を行う際の判断基準を以下にまとめます。
| 判断基準 | 重視する場合 | 妥協できる場合 |
|---|---|---|
| パフォーマンス | リアルタイム処理、大規模データ | 管理画面、バッチ処理 |
| 保守性 | 長期運用、チーム開発 | プロトタイプ、短期プロジェクト |
| スケーラビリティ | 成長が見込まれるサービス | 社内ツール、固定ユーザー |
| セキュリティ | 個人情報、金融データ | 公開データ、社内利用 |
| 開発速度 | MVP、市場投入スピード | 品質重視、ミッションクリティカル |
アーキテクチャパターンの選択
| アーキテクチャ選択フロー |
|---|
| ① チーム規模は? |
| ├─ 小規模(1-5人)→ モノリス |
| └─ 大規模(10人+)→ ②へ |
| ② デプロイ頻度は? |
| ├─ 週1回以下 → モノリス + モジュール分割 |
| └─ 毎日/複数回 → ③へ |
| ③ チーム間の独立性は? |
| ├─ 高い → マイクロサービス |
| └─ 中程度 → モジュラーモノリス |
トレードオフの分析
技術的な判断には必ずトレードオフが伴います。以下の観点で分析を行いましょう:
1. 短期 vs 長期のコスト
- 短期的に速い方法が長期的には技術的負債になることがある
- 逆に、過剰な設計は短期的なコストが高く、プロジェクトの遅延を招く
2. 一貫性 vs 柔軟性
- 統一された技術スタックは学習コストが低い
- 多様な技術の採用は適材適所が可能だが、運用コストが増加
3. 抽象化のレベル
- 高い抽象化は再利用性が高いが、デバッグが困難になる場合がある
- 低い抽象化は直感的だが、コードの重複が発生しやすい
# 設計判断の記録テンプレート
class ArchitectureDecisionRecord:
"""ADR (Architecture Decision Record) の作成"""
def __init__(self, title: str):
self.title = title
self.context = ""
self.decision = ""
self.consequences = []
self.alternatives = []
def set_context(self, context: str):
"""背景と課題の記述"""
self.context = context
return self
def set_decision(self, decision: str):
"""決定内容の記述"""
self.decision = decision
return self
def add_consequence(self, consequence: str, positive: bool = True):
"""結果の追加"""
self.consequences.append({
'description': consequence,
'type': 'positive' if positive else 'negative'
})
return self
def add_alternative(self, name: str, reason_rejected: str):
"""却下した代替案の追加"""
self.alternatives.append({
'name': name,
'reason_rejected': reason_rejected
})
return self
def to_markdown(self) -> str:
"""Markdown形式で出力"""
md = f"# ADR: {self.title}\n\n"
md += f"## 背景\n{self.context}\n\n"
md += f"## 決定\n{self.decision}\n\n"
md += "## 結果\n"
for c in self.consequences:
icon = "✅" if c['type'] == 'positive' else "⚠️"
md += f"- {icon} {c['description']}\n"
md += "\n## 却下した代替案\n"
for a in self.alternatives:
md += f"- **{a['name']}**: {a['reason_rejected']}\n"
return mdFAQ
Q1: このトピックを学ぶ上で最も重要なポイントは何ですか?
実践的な経験を積むことが最も重要です。理論だけでなく、実際にコードを書いて動作を確認することで理解が深まります。
Q2: 初心者がよく陥る間違いは何ですか?
基礎を飛ばして応用に進むことです。このガイドで説明している基本概念をしっかり理解してから、次のステップに進むことをお勧めします。
Q3: 実務ではどのように活用されていますか?
このトピックの知識は、日常的な開発業務で頻繁に活用されます。特にコードレビューやアーキテクチャ設計の際に重要になります。
まとめ
| コマンド | 用途 | よく使うオプション |
|---|---|---|
| ps aux | プロセス一覧(スナップショット) | --sort, -o, -p, -u, -C |
| ps aux --sort=-%cpu | CPU順でソート | head -N で上位N件 |
| pgrep -la name | プロセス名で検索 | -f(全コマンドライン), -c(カウント) |
| pstree -p | プロセスツリー | -s(祖先表示), -a(引数表示) |
| top | リアルタイム監視(標準) | -bn1(バッチ), -u(ユーザー), -p(PID) |
| htop | リアルタイム監視(高機能) | -t(ツリー), -u(ユーザー), -p(PID) |
| lsof -i :port | ポート使用プロセス特定 | -P -n(高速化) |
| vmstat 1 | システム全体の統計 | r, b 列に注目 |
調査フローチャート
サーバーが遅い
├→ uptime: load average を確認
│ ├→ load < コア数 → CPUは余裕あり → アプリ・ネットワーク調査
│ └→ load > コア数 → CPUボトルネック
│ ├→ top: us が高い → ps --sort=-%cpu → アプリ最適化
│ ├→ top: wa が高い → iostat -x → ディスクI/O改善
│ ├→ top: sy が高い → vmstat → コンテキストスイッチ多発
│ └→ top: st が高い → VM リソース増強
├→ free -m: メモリ確認
│ ├→ avail > 10% → メモリは余裕あり
│ └→ avail < 10% → メモリ不足
│ └→ ps --sort=-rss → メモリ消費プロセス特定
└→ df -h: ディスク確認
└→ 使用率 > 90% → 不要ファイル削除 or ディスク拡張
次に読むべきガイド
参考文献
- Barrett, D. "Efficient Linux at the Command Line." Ch.8, O'Reilly, 2022.
- Gregg, B. "Systems Performance: Enterprise and the Cloud." 2nd Ed, Addison-Wesley, 2020.
- Evi Nemeth et al. "UNIX and Linux System Administration Handbook." 5th Ed, Addison-Wesley, 2017.
- "proc(5) — Linux manual page." man7.org.
- "htop — an interactive process viewer." htop.dev.