flynn_auto_scale 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 57177c265ec52b7370f5435554bbf8ea95389504
4
+ data.tar.gz: 5315b450481d61a0a38c82840c0f9107455c66d3
5
+ SHA512:
6
+ metadata.gz: 01a8da9664023ff21b36392cd14f21e17701f242fb6d110f0c83d4b1ffaafbdc8d3ac476b00e28aa7ebd9d40c0f59b04463d56d23cd7f22102b2cd1f18700d4c
7
+ data.tar.gz: d7a0eecf24e8e5b4a5786c06c1d0e293b8f79f7a34f2e40f86ef7cdb7b45901b6cad8c7e67b6f77e4f6d41869ee331b81e124772fed7c56a8cee74b8d4dfa5a4
@@ -0,0 +1,58 @@
1
+ Warning: This gem is in active development and may have some serious bugs.
2
+
3
+ # FlynnAutoScale
4
+ A gem that allows your Rails apps to self-scale as they need more and more resources under [Flynn](https://flynn.io/). Comes with an automated mode that can be used for "day to day" web hosting operations and a manual mode where you can control when scaling operations take place (for scripts, background jobs and anything else)
5
+
6
+ ## Installation
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'flynn_auto_scale'
11
+ ```
12
+
13
+ And then execute:
14
+ ```bash
15
+ $ bundle
16
+ ```
17
+
18
+ Make sure to migrate after installing this gem (it makes use of ActiveRecord to keep the autoscaler working)
19
+
20
+ In order to get up and running quickly, please set up the following ENV variables.
21
+ Failure to set up some of these could cause "bad things to happen", so ensure you read this section.
22
+
23
+ # These are the minimum required items for [connecting to a cluster](https://flynn.io/docs/cli#adding-clusters)
24
+ FLYNN_SETUP_CLUSTER_PIN
25
+ FLYNN_SETUP_CLUSTER_NAME
26
+ FLYNN_SETUP_CLUSTER_CONTROLLER_DOMAIN
27
+ FLYNN_SETUP_CLUSTER_CONTROLLER_KEY
28
+ FLYNN_SETUP_CLUSTER_APP_NAME - the app name to be scaled.
29
+
30
+
31
+ # These are configuration variables that are used to customize how FlynnAutoScale will handle scaling events.
32
+ FLYNN_AUTO_SCALE - If present, FlynnAutoScale will attempt auto scaling operations -> (default: do nothing)
33
+ FLYNN_AUTO_SCALE_RAM - The amount of RAM in MB you'd like FlynnAutoScale to consider scaling at -> (default: 256)
34
+ FLYNN_AUTO_SCALE_COOLDOWN - How long to wait after you have scaled (in seconds) before scaling again -> (default: 0)
35
+ FLYNN_LIMIT_INSTANCES_MANUAL_MODE - If present, whether manual mode should follow the instance limits -> (default: ignore limits)
36
+ FLYNN_MIN_INSTANCES - Minimum number of instances -> (default: 1)
37
+ FLYNN_MAX_INSTANCES - Maximum number of instances -> (default: 2)
38
+
39
+ Add the following at the top of your application_controller.rb to get started with auto scaling.
40
+
41
+ ```ruby
42
+ # This should be a single line
43
+ around_action def scale; yield; FlynnAutoScale::Scaler.auto_scale('web'); end
44
+ ```
45
+
46
+ ## Usage
47
+ For a more advanced use of the scaler, you can directly call the manual scaler yourself.
48
+ ```ruby
49
+ # You can pass the process name: 'console', 'web', 'worker'.
50
+ FlynnAutoScale::Scaler.scale_manual('web', 2)
51
+ ```
52
+ ## Contributing
53
+ It's an open source repo, fork the thing, make some changes, and I'll put you on the list of cool people making Flynn + Rails great again.
54
+
55
+ I am also looking for a partner in crime or two to turn that database of scaling changes into a nice dashboard (a la sidekiq's web_ui). I can do it, but it's gonna be super butt ugly if I do it alone.
56
+
57
+ ## License
58
+ The gem is available as open source under the CC0 terms.
@@ -0,0 +1,37 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'FlynnAutoScale'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+
21
+ load 'rails/tasks/statistics.rake'
22
+
23
+
24
+
25
+ require 'bundler/gem_tasks'
26
+
27
+ require 'rake/testtask'
28
+
29
+ Rake::TestTask.new(:test) do |t|
30
+ t.libs << 'lib'
31
+ t.libs << 'test'
32
+ t.pattern = 'test/**/*_test.rb'
33
+ t.verbose = false
34
+ end
35
+
36
+
37
+ task default: :test
@@ -0,0 +1,2 @@
1
+ //= link_directory ../javascripts/flynn_auto_scale .js
2
+ //= link_directory ../stylesheets/flynn_auto_scale .css
@@ -0,0 +1,13 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file. JavaScript code in this file should be added after the last require_* statement.
9
+ //
10
+ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require_tree .
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,5 @@
1
+ module FlynnAutoScale
2
+ class ApplicationController < ActionController::Base
3
+ protect_from_forgery with: :exception
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ module FlynnAutoScale
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module FlynnAutoScale
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module FlynnAutoScale
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: 'from@example.com'
4
+ layout 'mailer'
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module FlynnAutoScale
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ module FlynnAutoScale
2
+ class ScalingEvent < ApplicationRecord
3
+ end
4
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Flynn auto scale</title>
5
+ <%= stylesheet_link_tag "flynn_auto_scale/application", media: "all" %>
6
+ <%= javascript_include_tag "flynn_auto_scale/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
@@ -0,0 +1,3 @@
1
+ # This initializer will attempt to connect to the cluster as soon as the app is brought online.
2
+ # The internals of the gem should prevent errors but if you get an error, just comment out this line.
3
+ FlynnAutoScale::Scaler.connect_cluster
@@ -0,0 +1,2 @@
1
+ FlynnAutoScale::Engine.routes.draw do
2
+ end
@@ -0,0 +1,10 @@
1
+ class CreateFlynnAutoScaleScalingEvents < ActiveRecord::Migration[5.0]
2
+ def change
3
+ create_table :flynn_auto_scale_scaling_events do |t|
4
+ t.string :event_type
5
+ t.integer :instances
6
+
7
+ t.timestamps
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,5 @@
1
+ class AddProcessTypeToScalingEvent < ActiveRecord::Migration[5.0]
2
+ def change
3
+ add_column :scaling_events, :process_type, :string
4
+ end
5
+ end
@@ -0,0 +1,196 @@
1
+ require "flynn_auto_scale/engine"
2
+ require "os"
3
+
4
+ module FlynnAutoScale
5
+ class Scaler < Rails::Engine
6
+ # START Setup Section
7
+ def check_install
8
+ # Check for existence of Flynn inside container.
9
+ output = `flynn`
10
+ if output.include? "usage: flynn"
11
+ true
12
+ else
13
+ false
14
+ end
15
+ end
16
+
17
+ def install
18
+ # Install the Flynn CLI if it doesn't exist.
19
+ if !check_install
20
+ Logger.warn "Flynn Auto-Scale: Installing the Flynn CLI. This will take some time. This installer only works under Linux / Mac OS X."
21
+ `L=/usr/local/bin/flynn && curl -sSL -A "\`uname -sp\`" https://dl.flynn.io/cli | zcat >$L && chmod +x $L`
22
+ check_install
23
+ else
24
+ true
25
+ end
26
+ end
27
+
28
+ def connect_cluster
29
+ # Connect to the cluster and set as default.
30
+ if !install
31
+ # Warn the user something has gone terribly wrong.
32
+ Logger.warn "Flynn Auto-Scale: There was an issue installing / verifying the Flynn installation. Auto Scaling functions will not work."
33
+ false
34
+ else
35
+ # The bare minimum needed to connect to a cluster and manual scaling
36
+ vars = [
37
+ ENV['FLYNN_SETUP_CLUSTER_PIN'],
38
+ ENV['FLYNN_SETUP_CLUSTER_NAME'],
39
+ ENV['FLYNN_SETUP_CLUSTER_CONTROLLER_DOMAIN'],
40
+ ENV['FLYNN_SETUP_CLUSTER_CONTROLLER_KEY'],
41
+ ENV['FLYNN_SETUP_CLUSTER_APP_NAME']]
42
+
43
+ # Verify the existance of all of the required variables.
44
+ missing_vars = false
45
+ vars.each do |var|
46
+ if !var.present?
47
+ Logger.warn "Flynn Auto-Scale: One of the environment variables required for connecting to the cluster is missing. Initialization will stop immediately."
48
+ missing_vars = true
49
+ end
50
+ end
51
+
52
+ # Abort if there are missing variables.
53
+ if missing_vars
54
+ return false
55
+ end
56
+
57
+ # Connect to the cluster.
58
+ # Warning: this method of adding / defaulting the cluster is susceptible to Command Injection.
59
+ # Do not pass params from the internet into any of this (you were warned!)
60
+ # Source: http://brakemanscanner.org/docs/warning_types/command_injection/
61
+ `flynn cluster add -p #{vars[0]} #{vars[1]} #{vars[2]} #{vars[3]}`
62
+
63
+ output = `flynn cluster default #{vars[4]}`
64
+ if !output.include? "is now the default cluster."
65
+ Logger.warn "Flynn Auto-Scale: There was an issue connecting to the cluster. Please provide this output in a Github Issue: #{output}"
66
+ false
67
+ else
68
+ true
69
+ end
70
+ end
71
+ end
72
+ # END Setup Section
73
+
74
+ # START Auto Scaling Section
75
+ def auto_scale(process='web')
76
+ # This method will utilize the current RAM usage and some ENV variables to make a scaling decision.
77
+ if !ENV['FLYNN_AUTO_SCALE'].present?
78
+ Logger.warn "Flynn Auto-Scale: A call was made to the auto_scale method but the FLYNN_AUTO_SCALE ENV variable is not set. Nothing will be done."
79
+ return
80
+ end
81
+
82
+ current_ram_use = (OS.rss_bytes / 1024).to_i
83
+ max_ram_use = ENV['FLYNN_AUTO_SCALE_RAM'].present? ? ENV['FLYNN_AUTO_SCALE_RAM'].present?.to_i : 256
84
+
85
+ if current_ram_use > max_ram_use
86
+ # Consider scaling upwards.
87
+ scale_up(process)
88
+ elsif current_ram_use < (max_ram_use * 0.1).to_i
89
+ # This is a hardcoded attempt at a "downscale".
90
+ # We will attempt to shrink the instance count if using less than 10% of the max ram.
91
+ scale_down(process)
92
+ else
93
+ # There is no need to execute a scale up / down event.
94
+ return
95
+ end
96
+ end
97
+ # END Auto Scaling Section
98
+
99
+ # START Maintenance Section
100
+ def can_scale_time(process='web')
101
+ # Checks if scaling is appropriate or if it's limited by a time minimum.
102
+ last_scale = FlynnAutoScale::ScalingEvent.where(process_type: process).last
103
+ scaling_restriction = ENV['FLYNN_AUTO_SCALE_COOLDOWN'].present?
104
+
105
+ if scaling_restriction
106
+ if !last_scale
107
+ # If this is the first time this process is scaled, just ignore the limit and push an initial scaling event into the DB.
108
+ last_scale = FlynnAutoScale::ScalingEvent.create(process_type: process, event_type: 'initial', instances: 1)
109
+ true
110
+ else
111
+ # Calculate if we have breached the cooldown.
112
+ current_time = DateTime.now
113
+ last_time = last_scale.created_at
114
+ diff = (current_time.to_f - last_time.to_f).to_i
115
+
116
+ # Scaling operations should occur if the cooldown has elapsed.
117
+ if diff > ENV['FLYNN_AUTO_SCALE_COOLDOWN'].to_i
118
+ true
119
+ else
120
+ false
121
+ end
122
+ end
123
+ else
124
+ # No limit, just scale.
125
+ true
126
+ end
127
+ end
128
+ def scale_up(process='web')
129
+ # This method will scale the process count up. It assumes it was called from an "AUTO_SCALE" setup.
130
+ if can_scale_time
131
+ scale(process, FlynnAutoScale::ScalingEvent.where(process_type: process).last.instances + 1, 'scale_up')
132
+ else
133
+ Logger.warn "Flynn Auto-Scale: A scale_up event was aborted due to a time limitation."
134
+ end
135
+ end
136
+
137
+ def scale_down(process='web')
138
+ # This method will scale the process count down. It assumes it was called from an "AUTO_SCALE" setup.
139
+ if can_scale_time
140
+ scale(process, FlynnAutoScale::ScalingEvent.where(process_type: process).last.instances - 1, 'scale_down')
141
+ else
142
+ Logger.warn "Flynn Auto-Scale: A scale_down event was aborted due to a time limitation."
143
+ end
144
+ end
145
+
146
+ def scale_manual(process='web', instances=1)
147
+ # This method will just scale the process.
148
+ # This method will respect very little restrictions, make sure you know what this does.
149
+ scale(process, instances)
150
+ end
151
+
152
+ # The foundation of the auto / manual scaling system.
153
+ # You should not be calling this method unless you know what you are doing.
154
+ def scale(process='web', instances=1, event_type='manual')
155
+ if event_type == 'manual' && !ENV['FLYNN_LIMIT_INSTANCES_MANUAL_MODE'].present?
156
+ # If the instances are not limited in manual mode, just let it rip (doesn't wait for confirmation)
157
+ FlynnAutoScale::ScalingEvent.create(process_type: process, event_type: event_type, instances: instances)
158
+ `flynn -a #{ENV['FLYNN_SETUP_CLUSTER_APP_NAME']} scale #{process}=#{instances} -n`
159
+ else
160
+ # This section is active in auto mode and in manual mode with limitations.
161
+ if ENV['FLYNN_MIN_INSTANCES'].present? && ENV['FLYNN_MAX_INSTANCES'].present?
162
+ if instances < ENV['FLYNN_MIN_INSTANCES'].to_i
163
+ Logger.warn "Flynn Auto-Scale: A scale event was stopped due to being below the minimum instance count"
164
+ return
165
+ end
166
+
167
+ # Do the min and max instance checks
168
+
169
+ if instances > ENV['FLYNN_MAX_INSTANCES'].to_i
170
+ Logger.warn "Flynn Auto-Scale: A scale event was stopped due to being above the maximum instance count"
171
+ return
172
+ end
173
+ elsif ENV['FLYNN_MIN_INSTANCES'].present?
174
+ # Do the min instance check
175
+ if instances < ENV['FLYNN_MIN_INSTANCES'].to_i
176
+ Logger.warn "Flynn Auto-Scale: A scale event was stopped due to being below the minimum instance count"
177
+ return
178
+ end
179
+ elsif ENV['FLYNN_MAX_INSTANCES'].present?
180
+ # Do the max instance check
181
+ if instances > ENV['FLYNN_MAX_INSTANCES'].to_i
182
+ Logger.warn "Flynn Auto-Scale: A scale event was stopped due to being above the maximum instance count"
183
+ return
184
+ end
185
+ elsif instances < 1 || instances > 2
186
+ Logger.warn "Flynn Auto-Scale: A scale event was stopped due to exceeding the default values"
187
+ return
188
+ end
189
+ # Okay, we checked, it's a good scaling event, hold on to your butts.
190
+ FlynnAutoScale::ScalingEvent.create(process_type: process, event_type: event_type, instances: instances)
191
+ `flynn -a #{ENV['FLYNN_SETUP_CLUSTER_APP_NAME']} scale #{process}=#{instances} -n`
192
+ end
193
+ end
194
+ # END Maintenance Section
195
+ end
196
+ end
@@ -0,0 +1,5 @@
1
+ module FlynnAutoScale
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace FlynnAutoScale
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ module FlynnAutoScale
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :flynn_auto_scale do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: flynn_auto_scale
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Lazaro Herrera
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-05-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: os
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: sqlite3
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: A gem that allows your Rails apps to self-scale as they need more and
56
+ more resources under Flynn. Comes with an automated mode that can be used for 'day
57
+ to day' web hosting operations and a manual mode where you can control when scaling
58
+ operations take place (for scripts, background jobs and anything else)
59
+ email:
60
+ - lazherrera@gmail.com
61
+ executables: []
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - README.md
66
+ - Rakefile
67
+ - app/assets/config/flynn_auto_scale_manifest.js
68
+ - app/assets/javascripts/flynn_auto_scale/application.js
69
+ - app/assets/stylesheets/flynn_auto_scale/application.css
70
+ - app/controllers/flynn_auto_scale/application_controller.rb
71
+ - app/helpers/flynn_auto_scale/application_helper.rb
72
+ - app/jobs/flynn_auto_scale/application_job.rb
73
+ - app/mailers/flynn_auto_scale/application_mailer.rb
74
+ - app/models/flynn_auto_scale/application_record.rb
75
+ - app/models/flynn_auto_scale/scaling_event.rb
76
+ - app/views/layouts/flynn_auto_scale/application.html.erb
77
+ - config/initializers/flynn_auto_scale.rb
78
+ - config/routes.rb
79
+ - db/migrate/20170530115155_create_flynn_auto_scale_scaling_events.rb
80
+ - db/migrate/20170530121037_add_process_type_to_scaling_event.rb
81
+ - lib/flynn_auto_scale.rb
82
+ - lib/flynn_auto_scale/engine.rb
83
+ - lib/flynn_auto_scale/version.rb
84
+ - lib/tasks/flynn_auto_scale_tasks.rake
85
+ homepage: https://github.com/WriteCodeEveryday/flynn_auto_scale
86
+ licenses:
87
+ - CC0
88
+ metadata: {}
89
+ post_install_message:
90
+ rdoc_options: []
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ requirements: []
104
+ rubyforge_project:
105
+ rubygems_version: 2.6.12
106
+ signing_key:
107
+ specification_version: 4
108
+ summary: A gem that allows your Rails apps to self-scale as they need more and more
109
+ resources under Flynn. Comes with an automated mode that can be used for 'day to
110
+ day' web hosting operations and a manual mode where you can control when scaling
111
+ operations take place (for scripts, background jobs and anything else)
112
+ test_files: []