Class: FlynnAutoScale::Scaler

Inherits:
Rails::Engine
  • Object
show all
Defined in:
lib/flynn_auto_scale.rb

Instance Method Summary collapse

Instance Method Details

#auto_scale(process = 'web') ⇒ Object

START Auto Scaling Section



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/flynn_auto_scale.rb', line 75

def auto_scale(process='web')
  # This method will utilize the current RAM usage and some ENV variables to make a scaling decision.
  if !ENV['FLYNN_AUTO_SCALE'].present?
    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."
    return
  end
  
  current_ram_use = (OS.rss_bytes / 1024).to_i
  max_ram_use = ENV['FLYNN_AUTO_SCALE_RAM'].present? ? ENV['FLYNN_AUTO_SCALE_RAM'].present?.to_i : 256
  
  if current_ram_use > max_ram_use
    # Consider scaling upwards.
    scale_up(process)
  elsif current_ram_use < (max_ram_use * 0.1).to_i
    # This is a hardcoded attempt at a "downscale".
    # We will attempt to shrink the instance count if using less than 10% of the max ram.
    scale_down(process)
  else
    # There is no need to execute a scale up / down event.
    return
  end
end

#can_scale_time(process = 'web') ⇒ Object

START Maintenance Section



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/flynn_auto_scale.rb', line 100

def can_scale_time(process='web')
  # Checks if scaling is appropriate or if it's limited by a time minimum.
  last_scale = FlynnAutoScale::ScalingEvent.where(process_type: process).last
  scaling_restriction = ENV['FLYNN_AUTO_SCALE_COOLDOWN'].present?
  
  if scaling_restriction
    if !last_scale
      # If this is the first time this process is scaled, just ignore the limit and push an initial scaling event into the DB.
      last_scale = FlynnAutoScale::ScalingEvent.create(process_type: process, event_type: 'initial', instances: 1)
      true
    else
      # Calculate if we have breached the cooldown.
      current_time = DateTime.now
      last_time = last_scale.created_at
      diff = (current_time.to_f - last_time.to_f).to_i
      
      # Scaling operations should occur if the cooldown has elapsed.
      if diff > ENV['FLYNN_AUTO_SCALE_COOLDOWN'].to_i
        true
      else
        false
      end
    end
  else
    # No limit, just scale.
    true
  end
end

#check_installObject

START Setup Section



7
8
9
10
11
12
13
14
15
# File 'lib/flynn_auto_scale.rb', line 7

def check_install
  # Check for existence of Flynn inside container.
  output = `flynn`
  if output.include? "usage: flynn"
    true
  else
    false
  end
end

#connect_clusterObject



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/flynn_auto_scale.rb', line 28

def connect_cluster
  # Connect to the cluster and set as default.
  if !install
    # Warn the user something has gone terribly wrong.
    Logger.warn "Flynn Auto-Scale: There was an issue installing / verifying the Flynn installation. Auto Scaling functions will not work."
    false
  else
    # The bare minimum needed to connect to a cluster and manual scaling
    vars = [
    ENV['FLYNN_SETUP_CLUSTER_PIN'],
    ENV['FLYNN_SETUP_CLUSTER_NAME'],
    ENV['FLYNN_SETUP_CLUSTER_CONTROLLER_DOMAIN'],
    ENV['FLYNN_SETUP_CLUSTER_CONTROLLER_KEY'],
    ENV['FLYNN_SETUP_CLUSTER_APP_NAME']]
    
    # Verify the existance of all of the required variables.
    missing_vars = false
    vars.each do |var|
      if !var.present?
        Logger.warn "Flynn Auto-Scale: One of the environment variables required for connecting to the cluster is missing. Initialization will stop immediately."
        missing_vars = true
      end
    end
    
    # Abort if there are missing variables.
    if missing_vars
      return false
    end
    
    # Connect to the cluster.
    # Warning: this method of adding / defaulting the cluster is susceptible to Command Injection.
    # Do not pass params from the internet into any of this (you were warned!)
    # Source: http://brakemanscanner.org/docs/warning_types/command_injection/
    `flynn cluster add -p #{vars[0]} #{vars[1]} #{vars[2]} #{vars[3]}`
    
    output = `flynn cluster default #{vars[4]}`
    if !output.include? "is now the default cluster."
      Logger.warn "Flynn Auto-Scale: There was an issue connecting to the cluster. Please provide this output in a Github Issue: #{output}"
      false
    else
      true
    end
  end
end

#installObject



17
18
19
20
21
22
23
24
25
26
# File 'lib/flynn_auto_scale.rb', line 17

def install
  # Install the Flynn CLI if it doesn't exist.
  if !check_install
    Logger.warn "Flynn Auto-Scale: Installing the Flynn CLI. This will take some time. This installer only works under Linux / Mac OS X."
    `L=/usr/local/bin/flynn && curl -sSL -A "\`uname -sp\`" https://dl.flynn.io/cli | zcat >$L && chmod +x $L`
    check_install
  else
    true
  end
end

#scale(process = 'web', instances = 1, event_type = 'manual') ⇒ Object

The foundation of the auto / manual scaling system. You should not be calling this method unless you know what you are doing.



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/flynn_auto_scale.rb', line 154

def scale(process='web', instances=1, event_type='manual')
  if event_type == 'manual' && !ENV['FLYNN_LIMIT_INSTANCES_MANUAL_MODE'].present?
    # If the instances are not limited in manual mode, just let it rip (doesn't wait for confirmation)
    FlynnAutoScale::ScalingEvent.create(process_type: process, event_type: event_type, instances: instances)
    `flynn -a #{ENV['FLYNN_SETUP_CLUSTER_APP_NAME']} scale #{process}=#{instances} -n`
  else
    # This section is active in auto mode and in manual mode with limitations.
    if ENV['FLYNN_MIN_INSTANCES'].present? && ENV['FLYNN_MAX_INSTANCES'].present? 
      if instances < ENV['FLYNN_MIN_INSTANCES'].to_i
        Logger.warn "Flynn Auto-Scale: A scale event was stopped due to being below the minimum instance count"
        return
      end
      
      # Do the min and max instance checks
        
      if instances > ENV['FLYNN_MAX_INSTANCES'].to_i
        Logger.warn "Flynn Auto-Scale: A scale event was stopped due to being above the maximum instance count"
        return
      end
    elsif ENV['FLYNN_MIN_INSTANCES'].present?
      # Do the min instance check
      if instances < ENV['FLYNN_MIN_INSTANCES'].to_i
        Logger.warn "Flynn Auto-Scale: A scale event was stopped due to being below the minimum instance count"
        return
      end
    elsif ENV['FLYNN_MAX_INSTANCES'].present?
      # Do the max instance check
      if instances > ENV['FLYNN_MAX_INSTANCES'].to_i
        Logger.warn "Flynn Auto-Scale: A scale event was stopped due to being above the maximum instance count"
        return
      end
    elsif instances < 1 || instances > 2
      Logger.warn "Flynn Auto-Scale: A scale event was stopped due to exceeding the default values"
      return
    end
    # Okay, we checked, it's a good scaling event, hold on to your butts.
    FlynnAutoScale::ScalingEvent.create(process_type: process, event_type: event_type, instances: instances)
    `flynn -a #{ENV['FLYNN_SETUP_CLUSTER_APP_NAME']} scale #{process}=#{instances} -n`
  end
end

#scale_down(process = 'web') ⇒ Object



137
138
139
140
141
142
143
144
# File 'lib/flynn_auto_scale.rb', line 137

def scale_down(process='web')
  # This method will scale the process count down. It assumes it was called from an "AUTO_SCALE" setup.
  if can_scale_time
    scale(process, FlynnAutoScale::ScalingEvent.where(process_type: process).last.instances - 1, 'scale_down')
  else
    Logger.warn "Flynn Auto-Scale: A scale_down event was aborted due to a time limitation."
  end
end

#scale_manual(process = 'web', instances = 1) ⇒ Object



146
147
148
149
150
# File 'lib/flynn_auto_scale.rb', line 146

def scale_manual(process='web', instances=1)
  # This method will just scale the process. 
  # This method will respect very little restrictions, make sure you know what this does.
  scale(process, instances)
end

#scale_up(process = 'web') ⇒ Object



128
129
130
131
132
133
134
135
# File 'lib/flynn_auto_scale.rb', line 128

def scale_up(process='web')
  # This method will scale the process count up. It assumes it was called from an "AUTO_SCALE" setup.
  if can_scale_time
    scale(process, FlynnAutoScale::ScalingEvent.where(process_type: process).last.instances + 1, 'scale_up')
  else
    Logger.warn "Flynn Auto-Scale: A scale_up event was aborted due to a time limitation."
  end
end