Added container for monitoring memory and cpu on all docker containers
authorSam <sam.saffron@gmail.com>
Fri, 13 Feb 2015 06:53:38 +0000 (17:53 +1100)
committerSam <sam.saffron@gmail.com>
Fri, 13 Feb 2015 06:53:54 +0000 (17:53 +1100)
image/monitor/Dockerfile [new file with mode: 0644]
image/monitor/src/monitor.rb [new file with mode: 0644]

diff --git a/image/monitor/Dockerfile b/image/monitor/Dockerfile
new file mode 100644 (file)
index 0000000..794d9fc
--- /dev/null
@@ -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 (file)
index 0000000..9cdf131
--- /dev/null
@@ -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
+