• 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