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

Building Serverless Ruby Bots @ Ruby Conf 2018

DamirSvrtan
November 15, 2018

Building Serverless Ruby Bots @ Ruby 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.

DamirSvrtan

November 15, 2018
Tweet

More Decks by DamirSvrtan

Other Decks in Technology

Transcript

  1. BUILDING
    Serverless RUBY
    BOTS

    View full-size slide

  2. # Gemfile
    gem 'httparty'
    gem 'oga'
    gem 'actionmailer'
    gem 'rake'
    gem 'whenever'

    View full-size slide

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

    View full-size slide

  4. TRAVELING Ruby
    MRuby
    JRuby

    View full-size slide

  5. TRAVELING RUBY

    View full-size slide

  6. SHRINK THE CODE
    BASE

    View full-size slide

  7. GEMFILE
    gem 'httparty'
    gem 'actionmailer'
    gem 'whenever'
    gem 'rake'
    gem 'oga'

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  10. USE LAMBDA SCHEDULER
    gem 'httparty'
    gem 'actionmailer'
    gem 'whenever'
    gem 'rake'
    gem 'oga'

    View full-size slide


  11. ...
    13.02.2017.


    ...
    13.02.2017.


    ...
    14.02.2017.

    View full-size slide

  12. REGEX IT IS.

    View full-size slide

  13. RUBY BOT CODE

    View full-size slide

  14. # 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

    View full-size slide

  15. 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 ",
    to: RECIPIENT,
    subject: 'New Apartments found!',
    html: "Check new apartments"
    )
    end
    end

    View full-size slide

  16. SEARCH_URL = 'http://iapi.njuskalo.hr?sort=new&categoryId=10920&locationId=2619'
    MailgunEmailer.call if NewApartments.new(SEARCH_URL).any?

    View full-size slide

  17. IMPLEMENTING THE BOT
    WITH Traveling Ruby
    PACKAGED MRI RUNTIMES

    View full-size slide

  18. DOWNLOAD THE RUNTIMES
    OS X FOR OUR DEV MACHINE
    LINUX X86_64 COMPATIBLE BINARY FOR AWS LAMBDA

    View full-size slide

  19. 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

    View full-size slide

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

    View full-size slide

  21. traveling-ruby-bot
    - ruby-linux-x86_64
    - bin/
    - ruby
    - ...
    - ...
    - ruby-os-x
    - bin/
    - ruby
    - ...
    - ...

    View full-size slide

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

    View full-size slide

  23. USUAL RUBY EXECUTION
    ruby main.rb

    View full-size slide

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

    View full-size slide

  25. UPLOADING THE BOT TO AWS
    Lambda

    View full-size slide

  26. // 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(); });
    }

    View full-size slide

  27. zip ruby-bot.zip ruby-linux-x86_64 main.rb lambda-function-wrapper.js

    View full-size slide

  28. 1. CREATE A FUNCTION

    View full-size slide

  29. 2. CREATE A TRIGGER

    View full-size slide

  30. 3. CREATE A RULE FOR THE TRIGGER

    View full-size slide

  31. 4. UPLOAD THE FUNCTION CODE

    View full-size slide

  32. 5. SET A HANDLER

    View full-size slide

  33. IMPLEMENTING THE BOT
    WITH mruby

    View full-size slide

  34. 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.

    View full-size slide

  35. BUILDING DIRECTLY FROM MRUBY SOURCE

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  38. mruby-bot
    - bin/
    - mruby
    - mirb
    - build_config.rb
    - minirake

    View full-size slide

  39. > echo "print 'hello world'" >> hello_world.rb

    View full-size slide

  40. INVOKE WITH MRUBY
    bin/mruby hello_world.rb

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  43. EXECUTE THE
    CODE
    bin/mruby main.rb

    View full-size slide

  44. CROSS COMPILATION

    View full-size slide

  45. IMPLEMENTING THE BOT
    WITH JRUBY

    View full-size slide

  46. AWS LAMBDA JRUBY

    View full-size slide

  47. jruby-bot
    - lib/
    - jruby.jar
    - src/
    - main/
    - resources/
    - main.rb
    - gradlew

    View full-size slide

  48. PASTE CODE
    mv main.rb src/main/resources/main.rb

    View full-size slide

  49. BUILD THE PROJECT
    ./gradlew build

    View full-size slide

  50. UPLOAD THE BOT CODE

    View full-size slide

  51. METRICS & NUMBERS

    View full-size slide

  52. CODE SIZE
    Traveling Ruby 6.7 MB
    mruby 945 kB
    JRuby 24 MB

    View full-size slide

  53. MEMORY CONSUMPTION
    Traveling Ruby 25 MB
    mruby 40 MB
    JRuby 150 MB

    View full-size slide

  54. EXECUTION TIME (COLD START)
    Traveling Ruby ~3900 ms
    mruby ~3900 ms
    JRuby ~25000 ms

    View full-size slide

  55. EXECUTION TIME (WARMED UP)
    Traveling Ruby ~3300 ms
    mruby ~3300 ms
    JRuby ~1200 ms

    View full-size slide

  56. AWS LAMBDA PRICING

    View full-size slide

  57. 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)

    View full-size slide

  58. FREE TIER
    1M REQUESTS & 400,000 GB-SECONDS PER MONTH

    View full-size slide

  59. 1M TIMES PER MONTH
    ROUGLY EVERY 3 SECONDS

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  63. CHOOSING THE RIGHT Ruby
    FOR THE JOB

    View full-size slide

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

    View full-size slide

  65. MRuby
    LOW MEMORY CONSUMPTION /
    LIGHTWEIGHT
    LIBRARY SUPPORT / CROSS COMPILATION

    View full-size slide

  66. JRuby
    SIMPLE PACKAGING* / FAST EXECUTION
    HIGH MEMORY CONSUMPTION / JAVA
    ERRORS

    View full-size slide

  67. Alternatives

    View full-size slide

  68. RUBY Packer
    RUBY Snap

    View full-size slide

  69. Azure FUNCTIONS
    Google Cloud FUNCTIONS

    View full-size slide

  70. Lots of OPTIONS

    View full-size slide

  71. Hacky OPTIONS

    View full-size slide

  72. ..A MONTH AGO..

    View full-size slide

  73. APACHE
    OpenWhisk

    View full-size slide

  74. IBM Cloud Functions

    View full-size slide

  75. IBM Cloud Functions

    View full-size slide

  76. Inline editor

    View full-size slide

  77. Serverless Framework
    SUPPORT

    View full-size slide

  78. serverless create --template openwhisk-ruby
    --path ruby_bot

    View full-size slide

  79. - ruby_bot
    - handler.rb
    - serverless.yml

    View full-size slide

  80. # serverless.yml
    service: ruby_bot
    provider:
    name: openwhisk
    runtime: ruby
    functions:
    ruby_bot:
    handler: handler.main
    plugins:
    - serverless-openwhisk

    View full-size slide

  81. serverless deploy

    View full-size slide

  82. JETS
    SERVERLESS FRAMEWORK FOR RUBY

    View full-size slide

  83. Jets.application.routes.draw do
    get 'posts', to: 'posts#index'
    end
    class PostsController < ApplicationController
    def index
    render json: { hello: 'world', action: 'index' }
    end
    end

    View full-size slide

  84. FaastRuby.io

    View full-size slide

  85. SERVERLESS-RUBY.ORG

    View full-size slide

  86. DAMIR SVRTAN

    View full-size slide

  87. tinyurl.com/netflix-ruby

    View full-size slide

  88. DAMIR SVRTAN
    SENIOR SOFTWARE ENGINEER @ Netflix
    HIT ME UP @DamirSvrtan
    TINYURL.COM/NETFLIX-RUBY

    View full-size slide