Building Serverless Ruby Bots @ Paris.rb Conf 2018

Building Serverless Ruby Bots @ Paris.rb Conf 2018

Want to build a Bot without the hassle of provisioning and managing servers? Amazon’s got a service for that and it’s called AWS Lambda - it executes your code only when needed, scales automatically and you pay only for the compute time you consume.

There’s one problem with Lambda - it doesn’t support Ruby! 
Let’s checkout multiple options on how to build a Ruby Bot and package it into an executable which you can run on any machine without the need to actually have Ruby installed.

5f81e2d2889d7642fd84c8b24db7ee17?s=128

DamirSvrtan

June 28, 2018
Tweet

Transcript

  1. BUILDING Serverless RUBY BOTS

  2. None
  3. NJUSKALO.HR

  4. None
  5. None
  6. # Gemfile gem 'httparty' gem 'oga' gem 'actionmailer' gem 'rake'

    gem 'whenever'
  7. None
  8. Serverless

  9. AWS LAMBDA

  10. LANGUAGE SUPPORT JAVA, NODE.JS, PYTHON ...

  11. 29 0CT 2015

  12. 16 JAN 2018

  13. NO RUBY? :(

  14. TRAVELING Ruby MRuby JRuby

  15. TRAVELING RUBY

  16. MRUBY

  17. JRUBY

  18. None
  19. SHRINK THE CODE BASE

  20. GEMFILE gem 'httparty' gem 'actionmailer' gem 'whenever' gem 'rake' gem

    'oga'
  21. USE NET/HTTP gem 'httparty' gem 'actionmailer' gem 'whenever' gem 'rake'

    gem 'oga'
  22. USE NET/HTTP gem 'httparty' gem 'actionmailer' gem 'whenever' gem 'rake'

    gem 'oga'
  23. USE LAMBDA SCHEDULER gem 'httparty' gem 'actionmailer' gem 'whenever' gem

    'rake' gem 'oga'
  24. None
  25. None
  26. <li> ... <time datetime="2017-02-13T21:00:00+01:00">13.02.2017.</time> </li> <li> ... <time datetime="2017-02-13T21:08:12+01:00">13.02.2017.</time> </li>

    <li> ... <time datetime="2017-02-14T23:44:18+01:00">14.02.2017.</time> </li>
  27. REGEX IT IS.

  28. RUBY BOT CODE

  29. # main.rb require 'net/http' require 'time' class NewApartments attr_reader :url

    def initialize(url) @url = url @last_scrapped_at = Time.now - 5 * 60 # 5 minutes ago. end def any? response_body .scan(/datetime\="(.*)" /) .any? { |published_at| Time.parse(published_at) > last_scrapped_at } end private def response_body Net::HTTP.get(URI(url)) end end
  30. class MailgunEmailer def self.call Net::HTTP.post_form( URI("https://api:key-#{MAILGUN_API_KEY}@api.mailgun.net/v3/#{MAILGUN_DOMAIN}/messages"), from: "Apartment Bot <postmaster@#{MAILGUN_DOMAIN}>",

    to: RECIPIENT, subject: 'New Apartments found!', html: "Check new <a href='#{SEARCH_URL}'>apartments</a>" ) end end
  31. SEARCH_URL = 'http://iapi.njuskalo.hr?sort=new&categoryId=10920&locationId=2619' MailgunEmailer.call if NewApartments.new(SEARCH_URL).any?

  32. IMPLEMENTING THE BOT WITH Traveling Ruby PACKAGED MRI RUNTIMES

  33. DOWNLOAD THE RUNTIMES OS X FOR OUR DEV MACHINE LINUX

    X86_64 COMPATIBLE BINARY FOR AWS LAMBDA
  34. curl -O https://d6r77u77i8pq3.cloudfront.net/releases/traveling-ruby-20141215-2.1.5-linux-x86_64.tar.gz curl -O https://d6r77u77i8pq3.cloudfront.net/releases/traveling-ruby-20141215-2.1.5-osx.tar.gz

  35. traveling-ruby-bot - ruby-linux-x86_64/ - ruby-os-x/

  36. traveling-ruby-bot - ruby-linux-x86_64 - bin/ - ruby - ... -

    ... - ruby-os-x - bin/ - ruby - ... - ...
  37. traveling-ruby-bot - ruby-linux-x86_64/ - ruby-os-x/ - main.rb

  38. USUAL RUBY EXECUTION ruby main.rb

  39. INVOKE WITH TRAVELING RUBY ruby-os-x/bin/ruby main.rb

  40. UPLOADING THE BOT TO AWS Lambda

  41. // lambda-function-wrapper.js var spawn = require('child_process').spawn; exports.handler = function(event, context)

    { var child = spawn('ruby-linux-x86_64/bin/ruby main.rb'); child.stdout.on('data', function (data) { console.log("stdout:\n"+data); }); child.stderr.on('data', function (data) { console.log("stderr:\n"+data); }); child.on('close', function (code) { context.done(); }); }
  42. zip ruby-bot.zip ruby-linux-x86_64 main.rb lambda-function-wrapper.js

  43. 1. CREATE A FUNCTION

  44. 2. CREATE A TRIGGER

  45. 3. CREATE A RULE FOR THE TRIGGER

  46. 4. UPLOAD THE FUNCTION CODE

  47. 5. SET A HANDLER

  48. IMPLEMENTING THE BOT WITH mruby

  49. TWO WAYS TO BUILD APPS WITH MRUBY: - DOWNLOAD MRUBY

    SOURCE DIRECTLY FROM GITHUB - MRUBY-CLI - PLATFORM FOR BUILDING NATIVE COMMAND LINE APPLICATIONS FOR LINUX, WINDOWS, AND OS X.
  50. BUILDING DIRECTLY FROM MRUBY SOURCE

  51. git clone https://github.com/mruby/mruby

  52. mruby-bot - bin - build_config.rb - minirake

  53. ./MINIRAKE

  54. mruby-bot - bin/ - mruby - mirb - build_config.rb -

    minirake
  55. > echo "print 'hello world'" >> hello_world.rb

  56. INVOKE WITH MRUBY bin/mruby hello_world.rb

  57. > bin/mruby main.rb main.rb:1: undefined method 'require' for main (NoMethodError)

  58. # build_config.rb conf.gem github: 'iij/mruby-env' conf.gem github: 'mattn/mruby-curl' conf.gem github:

    'mattn/mruby-onig-regexp'
  59. ./MINIRAKE

  60. EXECUTE THE CODE bin/mruby main.rb

  61. CROSS COMPILATION

  62. IMPLEMENTING THE BOT WITH JRUBY

  63. AWS LAMBDA JRUBY

  64. jruby-bot - lib/ - jruby.jar - src/ - main/ -

    resources/ - main.rb - gradlew
  65. PASTE CODE mv main.rb src/main/resources/main.rb

  66. BUILD THE PROJECT ./gradlew build

  67. UPLOAD THE BOT CODE

  68. METRICS & NUMBERS

  69. CODE SIZE Traveling Ruby 6.7 MB mruby 945 kB JRuby

    24 MB
  70. MEMORY CONSUMPTION Traveling Ruby 25 MB mruby 40 MB JRuby

    150 MB
  71. EXECUTION TIME (COLD START) Traveling Ruby ~3900 ms mruby ~3900

    ms JRuby ~25000 ms
  72. COLDSTARTS

  73. EXECUTION TIME (WARMED UP) Traveling Ruby ~3300 ms mruby ~3300

    ms JRuby ~1200 ms
  74. YMMV

  75. AWS LAMBDA PRICING

  76. AWS LAMBDA PRICING THE NUMBER OF REQUESTS (I.E. NUMBER OF

    TIMES OUR FUNCTION IS INVOKED) THE SUM OF DURATIONS OUR FUNCTIONS TOOK TO EXECUTE (EXPRESSED IN GB-SECONDS)
  77. FREE TIER 1M REQUESTS & 400,000 GB-SECONDS PER MONTH

  78. 1M TIMES PER MONTH ROUGLY EVERY 3 SECONDS

  79. 400,000 GB-SECONDS / MONTH MEMORY SIZE * DURATION IN SECONDS

  80. e.g. 128MB * 4s = 0.5GB-secons

  81. IF WE RAN THE BOT EVERY MINUTE 20.000 GB-SECONDS

  82. CHOOSING THE RIGHT Ruby FOR THE JOB

  83. Traveling Ruby LOW MEMORY CONSUMPTION / WELL KNOWN ENVIRONMENT SUPPORT?

  84. MRuby LOW MEMORY CONSUMPTION / LIGHTWEIGHT LIBRARY SUPPORT / CROSS

    COMPILATION
  85. JRuby SIMPLE PACKAGING* / FAST EXECUTION HIGH MEMORY CONSUMPTION /

    JAVA ERRORS
  86. Alternatives

  87. RUBY Packer RUBY Snap

  88. Azure FUNCTIONS Google Cloud FUNCTIONS

  89. None
  90. Lots of OPTIONS

  91. Hacky OPTIONS

  92. ..A MONTH AGO..

  93. APACHE OpenWhisk

  94. IBM Cloud Functions

  95. IBM Cloud Functions

  96. Inline editor

  97. Serverless Framework SUPPORT

  98. None
  99. serverless create --template openwhisk-ruby --path ruby_bot

  100. - ruby_bot - handler.rb - serverless.yml

  101. # serverless.yml service: ruby_bot provider: name: openwhisk runtime: ruby functions:

    ruby_bot: handler: handler.main plugins: - serverless-openwhisk
  102. serverless deploy

  103. JETS SERVERLESS FRAMEWORK FOR RUBY

  104. Jets.application.routes.draw do get 'posts', to: 'posts#index' end class PostsController <

    ApplicationController def index render json: { hello: 'world', action: 'index' } end end
  105. FaastRuby.io

  106. Conclusion

  107. SERVERLESS-RUBY.ORG

  108. None
  109. DAMIR SVRTAN

  110. None
  111. None
  112. tinyurl.com/netflix-ruby

  113. DAMIR SVRTAN SENIOR SOFTWARE ENGINEER @ Netflix HIT ME UP

    @DamirSvrtan TINYURL.COM/NETFLIX-RUBY