Ruby on Kubernetes: Newrelic

– Report each environment as separate app
– Report namespace so we can track down where misbehaving pods live

    # config/application.rb (not in initializers)
    if ENV['POD_NAMESPACE'] # on kubernetes
      # report each env into a separate project
      NewRelic::Agent::Configuration::Manager.class_eval do
        undef app_names
        def app_names
          name = ENV.fetch('NEW_RELIC_APP_NAME')
          name += " #{Rails.env}" unless Rails.env.production?
          [name]
        end
      end

      # report namespace so we can track down where a troublesome pod lives
      # newrelic_rpm 3.18.0+ has a NEW_RELIC_PROCESS_HOST_DISPLAY_NAME setting which is supposed to do that, but did not work.
      class << NewRelic::Agent::Hostname
        def get
          "#{ENV.fetch('POD_NAME')}.#{ENV.fetch('POD_NAMESPACE')}"
        end
      end
    end

Ruby on Kubernetes: Airbrake Exception Reporting

For simple apps reporting exceptions is simple, but not obvious … have a snippet 🙂

– report context so it’s easy to find out where a bug happened
– do not report when user opened a session manually (tty)
– do not report regular system exit

# report any errors to airbrake, kubernetes takes care of restarting
require 'airbrake-ruby'
Airbrake.configure do |config|
  config.project_id = 1234567
  config.project_key = ENV.fetch('AIRBRAKE_API_KEY')
  config.environment = ENV.fetch('RAILS_ENV')
  config.ignore_environments = [:test, :development]
  config.logger = Logger.new(STDOUT)
end
at_exit do
  context = ['POD_NAME', 'POD_NAMESPACE', 'TAG'].
    map { |k| [k.downcase, ENV[k]] }.to_h
  Airbrake.notify_sync($!, context) if $! && !$stdout.tty?
end

Ruby on Kubernetes: Memory GC OOMKilled

Ruby seems to always grow and then hit the memory limit … which triggers a SIGKILL … which means shutdown without cleanup.
This snippet helps to keep memory low and softly kills the process when memory gets to close to the limit.

# run GC periodically to reduce memory and report to airbrake if we run out (instead of kubernetes silently failing)
Thread.new do
  loop do
    sleep 60
    GC.start
    used = Integer(File.read('/sys/fs/cgroup/memory/memory.usage_in_bytes')) / 1024 / 1024
    max = Integer(`cat /sys/fs/cgroup/memory/memory.stat | grep hierarchical_memory_limit`.split.last) / 1024 / 1024
    puts "Ram: #{used}M / #{max}M"
    raise "Out of memory #{used}/#{max}" if used + 5 >= max
  end
end

A fun little tool that might help too: preoomkiller

WebBrick startup is slow if your machine name looks like a domain

Socket.gethostbyname is usually fast if your local machine has a normal name, because it crashes early, but if you have a name that looks like a real domain things take 5s.

Internally webbrick/config.rb does:

ruby -r socket -e 'Socket.gethostbyname(Socket.gethostname)'

Which is slow … wait for https://bugs.ruby-lang.org/issues/13007 to resolve … or rename your localhost to something that does not look like a domain to ruby.

Ruby Code Duplication Detection with Flay

Flay is terribly useful, but has terribly usability …

If the repo was not such a mess I’d make PRs to fix it, but tests are not even runnable and PRs to make the Readme readabe got rejected … so I’m not going to bother … a small excerpt from Samson
More config options can be found on it’s homepage
Just wanted to share this useful rake task which found a bunch of duplication and already prevented me from adding new duplication twice 🙂

desc "Analyze for code duplication (large, identical syntax trees) with fuzzy matching."
task :flay do
  require 'flay' # do not require in production
  files = Dir["{config,lib,app/**/*.{rb,erb}"]
  files -= [
    # Things you want to ignore
  ]
  flay = Flay.run([*files, '--mass', '25']) # mass threshold is shown mass / occurrences
  abort "Code duplication found" if flay.report.any?
end