Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@blurredbits
Last active February 17, 2016 18:09
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save blurredbits/1f716615998bb44d0be3 to your computer and use it in GitHub Desktop.
Save blurredbits/1f716615998bb44d0be3 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
require 'excon'
require 'statsd-ruby'
require 'json'
module ExconConnection
class << self
def connection
Excon.new('unix:///', socket: socket_path)
end
def socket_path
unless(@socket_path)
if(File.exists?('/host/var/run/docker.sock'))
@socket_path = '/host/var/run/docker.sock'
else
@socket_path = '/var/run/docker.sock'
end
end
@socket_path
end
end
end
class Container
attr_reader :cid, :names, :stats, :prev_stats
def initialize(cid, names)
@cid = cid
@names = names
@stats = { memory_usage: 0.0, memory_limit: 0.0, network_in: 0.0, network_out: 0.0, cpu: 0.0, disk_io_read: 0.0 }
@prev_stats = { network_in: nil, network_out: nil, cpu: nil, disk_io_read: nil }
end
def get_stats!
ExconConnection.connection.request(method: :get, path: "/containers/#{self.cid}/stats", read_timeout: 10, response_block: streamer)
rescue Excon::Errors::Timeout
rescue Excon::Errors::SocketError => e
unless e.message.include?('stats gathered')
error("Invalid Stats API endpoint", "There was an error reading from the stats API.
Are you running Docker version 1.5+, and is /var/run/docker.sock readable by the user running scout?")
end
end
def calc_difference(stat)
@prev_stats[stat].nil? ? 0 : @stats[stat] - @prev_stats[stat]
end
def set_prev_stats(stat)
@prev_stats[stat] = @stats[stat]
end
private
def streamer
lambda do |chunk, remaining_bytes, total_bytes|
parse_stats(self.cid, chunk)
raise 'stats gathered'
end
end
def parse_stats(container_id, stats_string)
stats = JSON.parse(stats_string)
@stats[:memory_usage] = stats["memory_stats"]["usage"].to_f / 1024.0 / 1024.0
@stats[:memory_limit] = stats["memory_stats"]["limit"].to_f / 1024.0 / 1024.0
@stats[:network_in] = stats["network"]["rx_bytes"].to_f / 1024.0
@stats[:network_out] = stats["network"]["tx_bytes"].to_f / 1024.0
@stats[:cpu] = stats["cpu_stats"]["cpu_usage"]["total_usage"].to_f / 1024.0 / 1024.0
@stats[:disk_io_read] = stats["blkio_stats"]["io_service_bytes_recursive"].first["value"].to_f / 1024.0 / 1024.0
end
end
class ContainerMonitor
STATSD = Statsd.new('localhost', 8125)
attr_reader :containers
def initialize
@containers = []
refresh_containers
end
def send_stats
refresh_containers
containers.each do |container|
container.get_stats!
container.stats.each do |key, value|
if key == :memory_limit
STATSD.gauge("docker.#{container.names.first}.#{key}", "#{value}")
else
STATSD.increment("docker.#{container.names.first}.#{key}", container.calc_difference(key))
container.set_prev_stats(key)
end
end
end
end
private
def get_containers
response = ExconConnection.connection.request(method: :get, path: "/containers/json")
containers = JSON.parse(response.body)
end
def create_new_container(container)
Container.new(container["Id"], container["Names"].map { |name| name.gsub(/\A\//, '') })
end
def build_container_list(containers)
containers.map { |container| create_new_container(container) }
end
def check_for_new_containers(new_containers)
new_containers.each do |new_container|
unless @containers.any? { |existing_container| existing_container.cid == new_container.cid }
@containers << new_container
end
end
end
def check_for_shutdown_containers(new_containers)
@containers.each do |existing_container|
unless new_containers.any? { |new_container| new_container.cid == existing_container.cid }
@containers.delete(existing_container)
end
end
end
def refresh_containers
if @containers.empty?
@containers = build_container_list(get_containers)
else
new_containers = build_container_list(get_containers)
check_for_new_containers(new_containers)
check_for_shutdown_containers(new_containers)
end
end
end
c = ContainerMonitor.new
loop do
sleep 60
c.send_stats
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment