Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Metrux - Enviando métricas de sua app Ruby para...

Metrux - Enviando métricas de sua app Ruby para o InfluxDB de forma simples e prática

Ricardo Tealdi, Desenvolvedor de software, mostra a facilidade e os benefícios em se coletar métricas de uma aplicação Ruby e enviá-las para o InfluxDB utilizando a gem metrux, feita na Locaweb.

Avatar for Ricardo Tealdi

Ricardo Tealdi

August 18, 2016

Other Decks in Programming

Transcript

  1. METRUX Enviando métricas de sua app Ruby para o InfluxDB

    de forma simples e prática RICARDO TEALDI
  2. $ whoami • Dev @ hosting team • Dev desde

    2004 • desde 2007 ◦ Usabilidade / Site / Sistemas Centrais (antigo EA) / Revenda / Registro / Integração / alguns outros ◦ Criador do feed-processor / lw-auth-client / metrux • Em setembro estarei mudando para • Formado em Sistemas de Informação • Blog: blog.tealdi.com.br
  3. • Por que medir minha app? • O que é

    metrux? • Como usar? • Como visualizar? AGENDA
  4. • Entende de verdade a saúde de sua app ◦

    Identificar gargalos ◦ Entender como está a alocação de recursos ◦ Evita desenvolvimento de melhorias que trazem pouco valor • Dá suporte ao négocio na tomada de decisões • Custo relativamente baixo de implementação CUSTO / BENEFÍCIO
  5. O QUE É METRUX? Metrux é uma gem, criada aqui

    na Locaweb, que facilita e abstrai a coleta de métricas de uma app Ruby e o envio delas para o InfluxDB. code.locaweb.com.br/locawebcommon/metrux
  6. E POR QUE CRIARAM A GEM? • Precisávamos migrar nossas

    métricas de apps críticas do Leela => InfluxDB ◦ As apps usavam as gems metriks, metriks-leela e advisor ◦ As gems que existiam do InfluxDB não atendiam nosso propósito, pois teríamos que mexer muito no código das apps
  7. NÃO!!! Usamos o client oficial do influx para mandar os

    dados - influxdb-ruby • Ele tem um bom tratamento de erros e funciona de forma assíncrona, evitando que sua app trave por algum problema no envio dos dados para o influx MAS VOCÊS CRIARAM OUTRO CLIENT PRO INFLUX?
  8. SÓ QUE AINDA NÃO É OPEN-SOURCE :( • Ainda está

    no nosso gitlab e no gems.locaweb.com.br
  9. E TEM ALGUÉM USANDO ISSO AÍ? Já está rodando em

    6 apps críticas do time de hospedagem
  10. E COMO CONFIGURO? # config/metrux.yml development: app_name: "<%= Rails.application.class.parent_name %>"

    active: true influx_host: "hostname" influx_port: 80 influx_database: "database" influx_username: "user" influx_password: "secret" code.locaweb.com.br/locawebcommon/metrux/blob/master/config/metrux.sample.yml
  11. MAS EU SIGO O 12 FACTOR E USO ENV VARS!

    export METRUX_APP_NAME="Application name" export METRUX_ACTIVE=true export METRUX_INFLUX_HOST="host" export METRUX_INFLUX_PORT=80 export METRUX_INFLUX_DATABASE="database" export METRUX_INFLUX_USERNAME="username" export METRUX_INFLUX_PASSWORD="password"
  12. “ Legal! Agora que consegui configurá-lo na minha app, quero

    começar a coletar as primeiras métricas.
  13. > SELECT * FROM "application_name/meters/log_in" env hostname program_name role value

    production CPRO123 puma adm 1 production CPRO125 puma reglr 1 production CPRO124 puma reglr 1 COMANDO: METER
  14. > SELECT * FROM "application_name/meters/log_in" env hostname program_name role value

    production CPRO123 puma adm 1 production CPRO125 puma reglr 1 production CPRO124 puma reglr 1 COMANDO: METER Nome da app que foi definida nas confs
  15. > SELECT * FROM "application_name/meters/log_in" env hostname program_name role value

    production CPRO123 puma adm 1 production CPRO125 puma reglr 1 production CPRO124 puma reglr 1 COMANDO: METER Tipo da métrica
  16. > SELECT * FROM "application_name/meters/log_in" env hostname program_name role value

    production CPRO123 puma adm 1 production CPRO125 puma reglr 1 production CPRO124 puma reglr 1 COMANDO: METER Nome da métrica
  17. > SELECT * FROM "application_name/meters/log_in" env hostname program_name role value

    production CPRO123 puma adm 1 production CPRO125 puma reglr 1 production CPRO124 puma reglr 1 COMANDO: METER tags fields
  18. > SELECT * FROM "application_name/meters/log_in" env hostname program_name role value

    production CPRO123 puma adm 1 production CPRO125 puma reglr 1 production CPRO124 puma reglr 1 COMANDO: METER O metrux adiciona essas tags em todas as escritas
  19. > SELECT * FROM "application_name/meters/log_in" env hostname program_name role value

    production CPRO123 puma adm 1 production CPRO125 puma reglr 1 production CPRO124 puma reglr 1 COMANDO: METER Infos adicionais
  20. > SELECT * FROM "application_name/meters/log_in" env hostname program_name role value

    production CPRO123 puma adm 1 production CPRO125 puma reglr 1 production CPRO124 puma reglr 1 COMANDO: METER Cada vez que fizerem um login, gerará uma linha no influx. Para obter o total basta somar todos os pontos
  21. > SELECT * FROM "application_name/timers/worker" program_name queue value sidekiq high

    342 sidekiq low 1042 sidekiq high 5404 COMANDO: TIMER Tipo da métrica
  22. > SELECT * FROM "application_name/timers/worker" program_name queue value sidekiq high

    342 sidekiq low 1042 sidekiq high 5404 COMANDO: TIMER Nome da métrica
  23. > SELECT * FROM "application_name/timers/worker" program_name queue value sidekiq high

    342 sidekiq low 1042 sidekiq high 5404 COMANDO: TIMER info adicionais
  24. > SELECT * FROM "application_name/timers/worker" program_name queue value sidekiq high

    342 sidekiq low 1042 sidekiq high 5404 COMANDO: TIMER Duração em milissegundos
  25. COMANDO: NOTICE ERROR def perform(user_id) # .. rescue => exception

    Metrux.notice_error(exception, queue: queue) # .. error handling end
  26. COMANDO: NOTICE ERROR def perform(user_id) # .. rescue => exception

    Metrux.notice_error(exception, queue: queue) # .. error handling end A própria instância do erro
  27. COMANDO: NOTICE ERROR def perform(user_id) # .. rescue => exception

    Metrux.notice_error(exception, queue: queue) # .. error handling end Infos adicionais
  28. > SELECT * FROM "application_name/meters/errors" error message queue value ArgumentError

    error message1 high 1 RecordNotFound error message2 low 1 ArgumentError error message3 low 1 COMANDO: NOTICE ERROR
  29. > SELECT * FROM "application_name/meters/errors" error message queue value ArgumentError

    error message1 high 1 RecordNotFound error message2 low 1 ArgumentError error message3 low 1 COMANDO: NOTICE ERROR É um meter
  30. > SELECT * FROM "application_name/meters/errors" error message queue value ArgumentError

    error message1 high 1 RecordNotFound error message2 low 1 ArgumentError error message3 low 1 COMANDO: NOTICE ERROR Nome da métrica será sempre esse
  31. > SELECT * FROM "application_name/meters/errors" error message queue value ArgumentError

    error message1 high 1 RecordNotFound error message2 low 1 ArgumentError error message3 low 1 COMANDO: NOTICE ERROR Nome da classe da exception (como tag)
  32. > SELECT * FROM "application_name/meters/errors" error message queue value ArgumentError

    error message1 high 1 RecordNotFound error message2 low 1 ArgumentError error message3 low 1 COMANDO: NOTICE ERROR Mensagem do erro (como field, pois essa info pode não se repetir)
  33. > SELECT * FROM "application_name/meters/errors" error message queue value ArgumentError

    error message1 high 1 RecordNotFound error message2 low 1 ArgumentError error message3 low 1 COMANDO: NOTICE ERROR info adicionais
  34. > SELECT * FROM "application_name/meters/errors" error message queue value ArgumentError

    error message1 high 1 RecordNotFound error message2 low 1 ArgumentError error message3 low 1 COMANDO: NOTICE ERROR Cada erro gerará uma linha no influx. Para obter o total basta somar todos os pontos
  35. COMANDO: GAUGE def available? Metrux.gauge('availability') do # … end end

    Resultado do bloco é salvo no influx e devolvido para fora, para não influenciar no retorno do seu método
  36. “ Mas e se eu quiser coletar dados periodicamente e

    minha aplicação não tiver nada para fazer background processing?
  37. COMANDO: PERIODIC GAUGE # config/initializers/metrux.rb Metrux.periodic_gauge('users') do User.active.count end Esse

    bloco vai ser executado a cada 60 segundos e o resultado disso é salvo no Influx
  38. > SELECT * FROM "application_name/gauges/users" program_name value puma-0 10 puma-1

    10 puma-0 42 puma-1 43 COMANDO: PERIODIC GAUGE É um gauge
  39. > SELECT * FROM "application_name/gauges/users" program_name value puma-0 10 puma-1

    10 puma-0 42 puma-1 43 COMANDO: PERIODIC GAUGE Nome da métrica
  40. > SELECT * FROM "application_name/gauges/users" program_name value puma-0 10 puma-1

    10 puma-0 42 puma-1 43 COMANDO: PERIODIC GAUGE Resultado de cada execução
  41. > SELECT * FROM "application_name/gauges/users" program_name value puma-0 10 puma-1

    10 puma-0 42 puma-1 43 COMANDO: PERIODIC GAUGE Todo processo que sua app subir vai executar essa medição, ENTÃO...
  42. COMANDO: PERIODIC GAUGE • Executada em uma thread ◦ Cada

    periodic é executado sequencialmente • Tem que ser algo bem leve • Executada a cada 60 segundos (mas é configurável)
  43. “ Mas e se eu coletar algo que seja comum

    em duas apps? Preciso ficar copiando e colando código?
  44. PLUGINS class DatabaseMetruxPlugin def call Metrux.periodic_gauge('database') do pool_size = ActiveRecord::Base

    .connection_pool .connections .count { pool_size: pool_size } end end end
  45. PLUGINS class DatabaseMetruxPlugin def call Metrux.periodic_gauge('database') do pool_size = ActiveRecord::Base

    .connection_pool .connections .count { pool_size: pool_size } end end end Implementa o método call
  46. PLUGINS class DatabaseMetruxPlugin def call Metrux.periodic_gauge('database') do pool_size = ActiveRecord::Base

    .connection_pool .connections .count { pool_size: pool_size } end end end Define o que quer que seja executado
  47. • Metrux::Plugins::Thread - coleta quantidade de threads (ruby) • Metrux::Plugins::Gc

    - coleta dados do garbage collector • Metrux::Plugins::Yarv - coleta dados da RubyVM • Metrux::Plugins::Process - coleta memória consumida do processo atual PLUGINS BUILT-IN
  48. “ Mas e se eu não quiser deixar o código

    da coleta tão intrusivo no meu código?
  49. METER C/ METRUX-ADVISOR # Antes def log_in(login, password) # ...

    Metrux.meter('log_in', tags: { role: role }) end
  50. METER C/ METRUX-ADVISOR # Depois class Session extend Advisor::Metruxable measure

    :log_in, with: :counter, key: 'log_in' def log_in(login, password) # ... end
  51. METER C/ METRUX-ADVISOR # Depois class Session extend Advisor::Metruxable measure

    :log_in, with: :counter, key: 'log_in' def log_in(login, password) # ... end Extende o advice do metrux
  52. METER C/ METRUX-ADVISOR # Depois class Session extend Advisor::Metruxable measure

    :log_in, with: :counter, key: 'log_in' def log_in(login, password) # ... end Declara o método que quer medir
  53. # Depois class Session extend Advisor::Metruxable measure :log_in, with: :counter,

    key: 'log_in' def log_in(login, password) # ... end Diz qual métrica a ser coletada, nesse caso é um meter (counter) METER C/ METRUX-ADVISOR
  54. METER C/ METRUX-ADVISOR # Depois class Session extend Advisor::Metruxable measure

    :log_in, with: :counter, key: 'log_in' def log_in(login, password) # ... end Informa qual o nome da métrica
  55. METER C/ METRUX-ADVISOR # Depois class Session extend Advisor::Metruxable measure

    :log_in, with: :counter, key: 'log_in' def log_in(login, password) # ... end Implementação fica apenas com o código necessário
  56. TIMER C/ METRUX-ADVISOR # Depois class Worker extend Advisor::Metruxable measure

    :perform, with: :timer, key: 'worker' def perform(user_id) # ... end
  57. TIMER C/ METRUX-ADVISOR # Depois class Worker extend Advisor::Metruxable measure

    :perform, with: :timer, key: 'worker' def perform(user_id) # ... end Declara o método que quer medir
  58. TIMER C/ METRUX-ADVISOR # Depois class Worker extend Advisor::Metruxable measure

    :perform, with: :timer, key: 'worker' def perform(user_id) # ... end Diz qual métrica a ser coletada, nesse caso é um timer
  59. TIMER C/ METRUX-ADVISOR # Depois class Worker extend Advisor::Metruxable measure

    :perform, with: :timer, key: 'worker' def perform(user_id) # ... end Informa qual o nome da métrica
  60. TIMER C/ METRUX-ADVISOR # Depois class Worker extend Advisor::Metruxable measure

    :perform, with: :timer, key: 'worker' def perform(user_id) # ... end Implementação fica apenas com o código necessário
  61. GAUGE C/ METRUX-ADVISOR # Depois class Domain extend Advisor::Metruxable measure

    :available?, with: :gauge, key: 'availability' def available? # ... end
  62. GAUGE C/ METRUX-ADVISOR # Depois class Domain extend Advisor::Metruxable measure

    :available?, with: :gauge, key: 'availability' def available? # ... end Declara o método que quer medir
  63. GAUGE C/ METRUX-ADVISOR # Depois class Domain extend Advisor::Metruxable measure

    :available?, with: :gauge, key: 'availability' def available? # ... end Diz qual métrica a ser coletada, nesse caso é um gauge
  64. GAUGE C/ METRUX-ADVISOR # Depois class Domain extend Advisor::Metruxable measure

    :available?, with: :gauge, key: 'availability' def available? # ... end Informa qual o nome da métrica
  65. GAUGE C/ METRUX-ADVISOR # Depois class Domain extend Advisor::Metruxable measure

    :available?, with: :gauge, key: 'availability' def available? # ... end Implementação fica apenas com o código necessário