- Dockerコンテナの通信量を見て動いてるか確認するためのスクリプト
- LMArenaでSonnet 4とGPT-5に書かせてみた。
- 1秒間に行われた通信を1秒ごとに表示する。
- 画面のバッファリングをしてちらつき防止もしている。
irb版
ちらつかない。irbにコピペで動く。
#!/usr/bin/env ruby
require 'io/console'
prev_stats = {}
first_run = true
loop do
start_time = Time.now
output = []
# ヘッダー部分
output << "Docker Network Monitor - #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}"
output << sprintf('%-40s %12s %12s', 'Container Name', 'RX(KB/s)', 'TX(KB/s)')
output << '------------------------------------------------------------------'
# 全コンテナを取得
containers = `docker ps --format "{{.Names}}"`.strip.split("\n").sort
containers.each do |container|
next if container.empty?
# コンテナの通信量を取得
stats_raw = `docker exec #{container} cat /proc/net/dev 2>/dev/null`
next if $?.exitstatus != 0
# /proc/net/devの解析
stats = stats_raw.lines.drop(2).map do |line|
parts = line.split
[parts[1].to_i, parts[9].to_i]
end.transpose.map(&:sum)
rx, tx = stats
# 前回の値があれば差分を計算
if prev_stats[container]
prev_rx, prev_tx = prev_stats[container]
rx_diff = (rx - prev_rx) / 1024
tx_diff = (tx - prev_tx) / 1024
output << sprintf('%-40s %12d %12d', container, rx_diff, tx_diff)
end
# 今回の値を保存
prev_stats[container] = [rx, tx]
end
# ターミナル制御による上書き表示
if first_run
print "\e[2J\e[H" # 画面全体をクリア&カーソルを左上に移動
first_run = false
else
print "\e[H" # カーソルを左上に移動(クリアしない)
end
# 各行を上書き表示
output.each_with_index do |line, i|
print "\e[K#{line}\n" # 行をクリアしてから新しい内容を表示
end
print "\e[J" # カーソル位置から画面下端までクリア
# 1秒間隔調整
elapsed = Time.now - start_time
sleep_time = 1.0 - elapsed
sleep(sleep_time) if sleep_time > 0
end
zsh版
バッファリングはしてるけれどちょっとちらつく。
# 一時ファイルを作成(前回の通信量を記録するため)
tmpfile=$(mktemp)
while true; do
# 処理開始時刻を記録(正確な1秒間隔のため)
start_time=$(date +%s)
{
# ヘッダー部分の表示
echo "Docker Network Monitor - $(date '+%Y-%m-%d %H:%M:%S')"
printf '%-40s %12s %12s\n' 'Container Name' 'RX(KB/s)' 'TX(KB/s)'
printf '%s\n' '------------------------------------------------------------------'
# 実行中の全コンテナをループ処理
while read -r container; do
# コンテナ内の /proc/net/dev から通信量を取得
stats=$(docker exec "$container" cat /proc/net/dev 2>/dev/null | \
awk 'NR>2 {rx+=$2; tx+=$10} END {print rx, tx}')
if [[ -n "$stats" ]]; then
# 現在の受信量と送信量を分離
rx=$(echo "$stats" | cut -d' ' -f1)
tx=$(echo "$stats" | cut -d' ' -f2)
# 前回記録された値を取得
prev=$(grep "^$container " "$tmpfile" 2>/dev/null)
if [[ -n "$prev" ]]; then
# 前回の値を分離
prev_rx=$(echo "$prev" | cut -d' ' -f2)
prev_tx=$(echo "$prev" | cut -d' ' -f3)
# 差分を計算してKB/s単位に変換
rx_diff=$(((rx-prev_rx)/1024))
tx_diff=$(((tx-prev_tx)/1024))
# 結果を表示
printf '%-40s %12d %12d\n' "$container" $rx_diff $tx_diff
fi
# 今回の値を一時ファイルに保存(次回の差分計算用)
grep -v "^$container " "$tmpfile" > "${tmpfile}.tmp" 2>/dev/null
echo "$container $rx $tx" >> "${tmpfile}.tmp"
mv "${tmpfile}.tmp" "$tmpfile"
fi
done < <(docker ps --format "{{.Names}}") # プロセス置換でサブシェル問題を回避
} > /tmp/docker_netstat_buf # 全出力をバッファに保存
# 画面をクリアして一括表示(ちらつき防止)
clear
cat /tmp/docker_netstat_buf
# 処理時間を計算して正確に1秒間隔になるよう調整
end_time=$(date +%s)
elapsed=$((end_time - start_time))
sleep_time=$((1 - elapsed))
# 処理時間が1秒未満の場合のみsleep
if [[ $sleep_time -gt 0 ]]; then
sleep $sleep_time
fi
done