SVX日記
2023-01-22(Sun) dockerのコンテナ内でもっとアレしたい
年末に「今年のまとめ」としてアレコレ書きたかったのだが、ほかにもヤリたいことだらけで、まとめられないまま1月も下旬になってしまった。まぁ、反省や抱負も大事だが、ヤリたいことがあるうちは、それをヤルことが優先である。反省や抱負は一段落してからにしよう。
最近は、コンテナで動かすことを前提にしたプログラムをすることが多く、ちょうど1年前くらいに作ったdockerのコンテナ内でアレしたいのツール群が大活躍中なのであるが、活躍しているからこそ、先に面倒だから実装を省いた「プロセスの起動時間やCPU時間」が見たくなってきてしまった。
# docker_ps ldap-
# INTO [ldap-alpha].
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 11:44 - 00:00:00 /sbin/init
root 26 1 0 11:44 - 00:00:00 /usr/lib/systemd/systemd-journald
root 36 1 0 11:44 - 00:00:00 /usr/lib/systemd/systemd-homed
dbus 44 1 0 11:44 - 00:00:00 /usr/bin/dbus-broker-launch --scope system --audit
dbus 53 44 0 11:44 - 00:00:00 dbus-broker --log 4 --controller 9 --machine-id a6cd51e94fd74eb98afdf11c55965591 --max-bytes 536870912 --max-fds 4096 --max-matches 16384 --audit
ldap 54 1 0 11:44 - 00:00:00 /usr/sbin/slapd -u ldap -h ldap:/// ldaps:/// ldapi:///
root 1287 0 0 -
# docker exec ldap-alpha ps -efww
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 11:44 ? 00:00:00 /sbin/init
root 26 1 0 11:44 ? 00:00:00 /usr/lib/systemd/systemd-journald
root 36 1 0 11:44 ? 00:00:00 /usr/lib/systemd/systemd-homed
dbus 44 1 0 11:44 ? 00:00:00 /usr/bin/dbus-broker-launch --scope system --audit
dbus 53 44 0 11:44 ? 00:00:00 dbus-broker --log 4 --controller 9 --machine-id a6cd51e94fd74eb98afdf11c55965591 --max-bytes 536870912 --max-fds 4096 --max-matches 16384 --audit
ldap 54 1 0 11:44 ? 00:00:00 /usr/sbin/slapd -u ldap -h ldap:/// ldaps:/// ldapi:///
root 1356 0 0 12:07 ? 00:00:00 ps -efww
#!/usr/bin/env ruby
# coding: utf-8
class String
def to_ipaddr
r = self.scan(/.{8}/).map {|h8|
h8.scan(/../).map {|hh|
hh.to_i(16)
}.reverse
}.join('.')
self.size < 9 ? r : r.gsub(/(0\.){3,}0?/, '::')
end
end
states = [
'', 'ESTABLISHED', 'SYN_SENT', 'SYN_RECV', 'FIN_WAIT1', 'FIN_WAIT2',
'TIME_WAIT', 'CLOSE', 'CLOSE_WAIT', 'LAST_ACK', 'LISTEN', 'CLOSING',
]
$0 =~ /docker_(.+)/ and subcmd = $1
ps = `docker ps`
targets = []; ps.split(/\n/)[1..-1].each {|l|
(it = l.split(/\s+/).last) =~ /#{ARGV.last}/ and targets << it
}
if(targets.size == 0)
puts('# NOT MATCH...')
elsif(targets.size == 1)
def get_ps(target)
uids = {}; progs = {}; sockets = {}
IO.popen(['docker', 'exec', target, 'ls', '-lR', '/proc'], :err => [:child, :out]) {|io|
pid = nil; io.each {|line|
if(!pid and line =~ /^d.*?(\d+)$/)
uids[$1] = line.split(/\s+/)[2]
end
if(line =~ /^\/proc\/(\d+):/)
progs[pid = $1] ||= []
end
if(line =~ / exe -> (.+)/)
progs[pid] << $1
end
if(line =~ / -> socket:\[(\d+)\]/)
sockets[$1] ||= []
sockets[$1] << pid
end
}
}
[uids, progs, sockets]
end
def get_ps2(target)
ppids = {}; cmdlines = {}
IO.popen(['docker', 'exec', target, '/bin/bash', '-c',
'ls /proc | grep -e "^[1-9]" | sed "s/\(.*\)/cat \/proc\/\\1\/status/" | sh 2>/dev/null'], :err => [:child, :out]) {|io|
pid = nil; io.each {|line|
line =~ /^Pid:\s*(\d+)/ and pid = $1
line =~ /^PPid:\s*(\d+)/ and ppids[pid] = $1
}
}
IO.popen(['docker', 'exec', target, '/bin/bash', '-c',
'ls /proc | grep -e "^[1-9]" | sed "s/\(.*\)/echo -n \\1:; cat \/proc\/\\1\/cmdline; echo/" | sh 2>/dev/null'], :err => [:child, :out]) {|io|
io.each {|line|
line =~ /^(\d+):(.*)/ and cmdlines[$1] = $2.gsub(/\u0000/, ' ')
}
}
[ppids, cmdlines]
end
def get_ps3(target)
start_times = {}; cpu_times = {}
btime = nil
IO.popen(['/bin/bash', '-c', 'cat /proc/stat'], :err => [:child, :out]) {|io|
io.each {|line|
line =~ /^btime\s+(\d+)/ and btime = $1.to_i and break
}
}
now_a = Time.now.to_a
IO.popen(['docker', 'exec', target, '/bin/bash', '-c',
'ls /proc | grep -e "^[1-9]" | sed "s/\(.*\)/cat \/proc\/\\1\/stat/" | sh 2>/dev/null'], :err => [:child, :out]) {|io|
io.each {|line|
if(line =~ /(\d+)\s+\([^)]+\)\s+(.+)/)
pid = $1
stats = $2.split(/\s/)
cpu_time = (stats[11].to_i + stats[12].to_i) / 100 # 11:utime + 12:stime
days = cpu_time / 86400
cpu_times[pid] = (days > 0 ? '%2d-' % days : '') + Time.at(cpu_time).utc.strftime('%H:%M:%S')
start_time = Time.at(btime + stats[19].to_i / 100) # 19:start_time
start_time_a = start_time.to_a
if(now_a[5] != start_time_a[5])
start_times[pid] = '%5d' % start_time.year
elsif(now_a[7] != start_time_a[7])
start_times[pid] = start_time.strftime('%b%d')
else
start_times[pid] = start_time.strftime('%H:%M')
end
end
}
}
[start_times, cpu_times]
end
puts('# INTO [%s].' % targets[0])
if(subcmd == 'exec')
# TODO
system('docker exec -it %s %s /bin/bash' % [ARGV[0..-2].join(' '), targets[0]])
elsif(subcmd == 'ps')
uids, progs, sockets = get_ps(targets[0])
ppids, cmdlines = get_ps2(targets[0])
start_times, cpu_times = get_ps3(targets[0])
puts('UID PID PPID C STIME TTY TIME CMD')
progs.keys.sort {|a, b|
a.to_i <=> b.to_i
}.each {|pid|
puts('%-8s %7d %7d %2d %5s %-8s %8s %s' % [uids[pid], pid, ppids[pid] || 0, 0, start_times[pid], '-', cpu_times[pid], cmdlines[pid]])
}
elsif(subcmd == 'netstat')
uids, progs, sockets = get_ps(targets[0])
puts('Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name')
['tcp', 'tcp6', 'udp', 'udp6'].each {|prot|
IO.popen(['docker', 'exec', targets[0], 'cat', '/proc/net/' + prot]) {|io|
io.each {|line|
ls = line.split(/[:\s]+/)
ls[1] =~ /^\d/ or next
laddr = ls[2].to_ipaddr + ':' + ls[3].to_i(16).to_s
faddr = ls[4].to_ipaddr + ':' + ls[5].to_i(16).to_s
prog = '-'; (it = sockets[ls[14]]) and prog = '%s/%s' % [it[0], progs[it[0]][0].split('/').last]
puts('%-5s %6d %6d %-23s %-23s %-11s %-s' % [prot, ls[8].to_i(16), ls[7].to_i(16), laddr, faddr, states[ls[6].to_i(16)], prog])
}
}
(it = $?.exitstatus and it == 0) or raise 'failed.'
}
else
system('docker %s %s %s' % [subcmd, ARGV[0..-2].join(' '), targets[0]])
end
else
puts('# MULTIPLE MATCH...')
puts(targets.join("\n"))
end
__END__