From 03b50438d73dbe6076a5a4179e336afaef2b28c2 Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 13 Feb 2015 17:53:38 +1100 Subject: [PATCH] Added container for monitoring memory and cpu on all docker containers --- image/monitor/Dockerfile | 13 +++++ image/monitor/src/monitor.rb | 98 ++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 image/monitor/Dockerfile create mode 100644 image/monitor/src/monitor.rb diff --git a/image/monitor/Dockerfile b/image/monitor/Dockerfile new file mode 100644 index 0000000..794d9fc --- /dev/null +++ b/image/monitor/Dockerfile @@ -0,0 +1,13 @@ +# Used to gather information about CPU and memory +# reporting it back to statsd + +# samsaffron/discourse_monitor +# version 0.0.2 + +FROM samsaffron/discourse_base:1.0.7 +MAINTAINER Sam Saffron "https://twitter.com/samsaffron" + +ADD src/monitor.rb src/monitor.rb +RUN gem install statsd-ruby docker-api + +CMD ruby src/monitor.rb diff --git a/image/monitor/src/monitor.rb b/image/monitor/src/monitor.rb new file mode 100644 index 0000000..9cdf131 --- /dev/null +++ b/image/monitor/src/monitor.rb @@ -0,0 +1,98 @@ +require 'statsd-ruby' +require 'docker' + +$statsd = Statsd.new '10.0.0.1', 8125 + +module Docker + class CloseConnectionError < StandardError; end + class Container + def name + info["Names"].first[1..-1] + end + + def stats + path = path_for(:stats) + + result = nil + + streamer = lambda do |chunk, remaining, total| + result ||= chunk + raise CloseConnectionError if result + end + options = { response_block: streamer }.merge(connection.options) + + Excon.get(connection.url + path[1..-1], options) rescue CloseConnectionError + + Docker::Util.parse_json(result) + end + end +end + +def median(array) + sorted = array.sort + len = sorted.length + return ((sorted[(len - 1) / 2] + sorted[len / 2]) / 2.0).to_i +end + + +def analyze_container(container) + + data = container.exec(["ps", "-eo", "rss,args"])[0].join("\n").split("\n") + unicorns = data.grep(/unicorn/).map(&:to_i) + sidekiqs = data.grep(/sidekiq/).map(&:to_i) + + result = {} + + if unicorns.length > 0 + result["unicorn.max_rss"] = unicorns.max + result["unicorn.median_rss"] = median(unicorns) + end + + if sidekiqs.length > 0 + result["sidekiq.max_rss"] = sidekiqs.max + result["sidekiq.median_rss"] = median(sidekiqs) + end + result["total_mem_usage"] = container.stats["memory_stats"]["usage"] + + @prev_stats ||= {} + prev_stats = @prev_stats[container.name] + @prev_stats[container.name] = stats = container.stats + + if prev_stats + cpu_delta = stats["cpu_stats"]["system_cpu_usage"] - prev_stats["cpu_stats"]["system_cpu_usage"] + app_cpu_delta = stats["cpu_stats"]["cpu_usage"]["total_usage"] - prev_stats["cpu_stats"]["cpu_usage"]["total_usage"] + + result["cpu_usage"] = (app_cpu_delta.to_f / cpu_delta.to_f) * stats["cpu_stats"]["cpu_usage"]["percpu_usage"].length * 100.0 + end + + result + +end + +def containers + Docker::Container.all +end + +hostname = Docker.info["Name"] + +STDERR.puts "#{Time.now} Starting Monitor" + +while true + + begin + containers.each do |c| + + analyze_container(c).each do |k, v| + if v && v > 0 + $statsd.gauge "#{hostname}.#{c.name}.#{k}", v + end + end + end + rescue => e + STDERR.puts e + STDERR.puts e.backtrace + end + + sleep 60 +end + -- 2.25.1