alicorn 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +32 -4
- data/alicorn.gemspec +6 -4
- data/bin/alicorn +13 -0
- data/bin/{alicorn_profiler → alicorn-profiler} +0 -0
- data/example/config.yml +11 -0
- data/lib/alicorn/errors.rb +6 -0
- data/lib/alicorn/scaler.rb +7 -6
- data/lib/alicorn/version.rb +1 -1
- data/test/test_scaler.rb +8 -12
- metadata +21 -19
data/README.rdoc
CHANGED
@@ -1,8 +1,30 @@
|
|
1
|
-
=
|
1
|
+
= Alicorn: Auto-scaler for Unicorn webserver
|
2
2
|
|
3
|
-
An experimental auto-scaler for Eric Wong's {
|
3
|
+
An experimental auto-scaler for Eric Wong's {Unicorn}[http://unicorn.bogomips.org]
|
4
|
+
webserver. Designed for Unicorn webservers running with the {Raindrops}[raindrops.bogomips.org]
|
5
|
+
middleware enabled. It depends on the "active" and "queued" fields reported by
|
6
|
+
Raindrops, and so only works on Linux.
|
4
7
|
|
5
|
-
==
|
8
|
+
== Usage
|
9
|
+
|
10
|
+
Alicorn ships with two executables, alicorn and alicorn-profiler.
|
11
|
+
|
12
|
+
alicorn: this is the main command-line utility: run alicorn --help to see the
|
13
|
+
list of options and learn how to configure it.
|
14
|
+
|
15
|
+
alicorn-profiler: this is a tool to help determine useful parameters for alicorn.
|
16
|
+
it's recommended that you run alicorn in dry-run and verbose mode for awhile,
|
17
|
+
collect the resulting logs, and feed those through alicorn-profiler to get a
|
18
|
+
handle on what settings you'll need. Same story, run alicorn-profiler --help to
|
19
|
+
figure out what the options are and how you can tweak them.
|
20
|
+
|
21
|
+
== Versioning
|
22
|
+
|
23
|
+
Alicorn attempts to follow the {Semantic Versioning Specification}[semver.org].
|
24
|
+
Every release in the 0.x series should be considered a DEVELOPMENT version.
|
25
|
+
The public API should not be considered stable.
|
26
|
+
|
27
|
+
== Contributing
|
6
28
|
|
7
29
|
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
8
30
|
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
@@ -12,8 +34,14 @@ An experimental auto-scaler for Eric Wong's {unicorn}[http://unicorn.bogomips.or
|
|
12
34
|
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
13
35
|
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
14
36
|
|
37
|
+
== Credits
|
38
|
+
|
39
|
+
Alicorn is maintained by {Ben Somers}[http://github.com/bensomers] and is
|
40
|
+
funded by {Rafter}[http://www.rafter.com]. Huge thanks to Eric
|
41
|
+
Wong and the rest of the {Unicorn}[http://unicorn.bogomips.org] and
|
42
|
+
{Raindrops}[http://raindrops.bogomips.org] contributors.
|
43
|
+
|
15
44
|
== Copyright
|
16
45
|
|
17
46
|
Copyright (c) 2012 Ben Somers. See LICENSE.txt for
|
18
47
|
further details.
|
19
|
-
|
data/alicorn.gemspec
CHANGED
@@ -5,14 +5,14 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "alicorn"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.3.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Ben Somers"]
|
12
|
-
s.date = "2012-05-
|
12
|
+
s.date = "2012-05-29"
|
13
13
|
s.description = "Highly configurable dumb auto-scaler for managing unicorn web servers"
|
14
14
|
s.email = "somers.ben@gmail.com"
|
15
|
-
s.executables = ["alicorn", "
|
15
|
+
s.executables = ["alicorn", "alicorn-profiler"]
|
16
16
|
s.extra_rdoc_files = [
|
17
17
|
"LICENSE.txt",
|
18
18
|
"README.rdoc"
|
@@ -26,9 +26,11 @@ Gem::Specification.new do |s|
|
|
26
26
|
"Rakefile",
|
27
27
|
"alicorn.gemspec",
|
28
28
|
"bin/alicorn",
|
29
|
-
"bin/
|
29
|
+
"bin/alicorn-profiler",
|
30
|
+
"example/config.yml",
|
30
31
|
"lib/alicorn.rb",
|
31
32
|
"lib/alicorn/dataset.rb",
|
33
|
+
"lib/alicorn/errors.rb",
|
32
34
|
"lib/alicorn/log_parser.rb",
|
33
35
|
"lib/alicorn/profiler.rb",
|
34
36
|
"lib/alicorn/scaler.rb",
|
data/bin/alicorn
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
$:.push File.join(File.dirname(__FILE__),'..','lib')
|
3
|
+
require 'yaml'
|
3
4
|
require 'optparse'
|
4
5
|
require 'alicorn'
|
5
6
|
|
@@ -56,6 +57,10 @@ data sets. \n\nUsage: alicorn [options]"
|
|
56
57
|
options[:log_path] = v
|
57
58
|
end
|
58
59
|
|
60
|
+
opts.on("-c", "--config PATH", String, "path for stored options - write a yaml file containing all options") do |v|
|
61
|
+
options[:config_path] = v
|
62
|
+
end
|
63
|
+
|
59
64
|
opts.on("-v", "--[no-]verbose", "turn on to write profiling info") do
|
60
65
|
options[:verbose] = true
|
61
66
|
end
|
@@ -65,6 +70,14 @@ data sets. \n\nUsage: alicorn [options]"
|
|
65
70
|
end
|
66
71
|
|
67
72
|
end.parse!
|
73
|
+
|
74
|
+
if options[:config_path]
|
75
|
+
options = YAML.load_file(File.open(options[:config_path]))
|
76
|
+
# I prefer symbol keys, but nobody ever writes yamls with symbol keys, so convert them
|
77
|
+
options = options.inject({}) { |memo, pair| memo.store(pair.first.to_sym, pair.last); memo }
|
78
|
+
end
|
79
|
+
|
68
80
|
raise OptionParser::MissingArgument.new("--max-workers is a mandatory argument") unless options[:max_workers]
|
69
81
|
|
82
|
+
|
70
83
|
Alicorn::Scaler.new(options).scale
|
File without changes
|
data/example/config.yml
ADDED
data/lib/alicorn/scaler.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'curl'
|
2
2
|
require 'logger'
|
3
3
|
require 'alicorn/dataset'
|
4
|
+
require 'alicorn/errors'
|
4
5
|
|
5
6
|
module Alicorn
|
6
7
|
class Scaler
|
@@ -40,9 +41,9 @@ module Alicorn
|
|
40
41
|
sleep(signal_delay) # Make sure unicorn doesn't discard repeated signals
|
41
42
|
end
|
42
43
|
end
|
43
|
-
rescue
|
44
|
+
rescue StandardError => e
|
44
45
|
logger.error "exception occurred: #{e.class}\n\n#{e.message}"
|
45
|
-
raise e
|
46
|
+
raise e unless e.is_a?(AmbiguousMasterError) # AmbiguousMasters are fine, usually just indicate a restart
|
46
47
|
end
|
47
48
|
|
48
49
|
def auto_scale(data, worker_count)
|
@@ -106,11 +107,11 @@ module Alicorn
|
|
106
107
|
def find_master_pid(unicorns)
|
107
108
|
master_lines = unicorns.select { |line| line.match /master/ }
|
108
109
|
if master_lines.size == 0
|
109
|
-
raise "No unicorn master processes detected. You may still be starting up."
|
110
|
+
raise NoMasterError.new("No unicorn master processes detected. You may still be starting up.")
|
110
111
|
elsif master_lines.size > 1
|
111
|
-
raise "Too many unicorn master processes detected. You may be restarting, or have an app name collision: #{master_lines}"
|
112
|
+
raise AmbiguousMasterError.new("Too many unicorn master processes detected. You may be restarting, or have an app name collision: #{master_lines}")
|
112
113
|
elsif master_lines.first.match /\(old\)/
|
113
|
-
raise "Old master process detected. You may be restarting: #{master_lines.first}"
|
114
|
+
raise AmbiguousMasterError.new("Old master process detected. You may be restarting: #{master_lines.first}")
|
114
115
|
else
|
115
116
|
master_lines.first.split.first.to_i
|
116
117
|
end
|
@@ -123,7 +124,7 @@ module Alicorn
|
|
123
124
|
def find_unicorns
|
124
125
|
ptable = grep_process_list.split("\n")
|
125
126
|
unicorns = ptable.select { |line| line.match(/unicorn/) && line.match(/#{Regexp.escape(app_name)}/) }
|
126
|
-
raise "Could not find any unicorn processes" if unicorns.empty?
|
127
|
+
raise NoUnicornsError.new("Could not find any unicorn processes") if unicorns.empty?
|
127
128
|
|
128
129
|
unicorns.map(&:strip)
|
129
130
|
end
|
data/lib/alicorn/version.rb
CHANGED
data/test/test_scaler.rb
CHANGED
@@ -26,7 +26,7 @@ class TestScaler < Test::Unit::TestCase
|
|
26
26
|
end
|
27
27
|
|
28
28
|
should "raise an error" do
|
29
|
-
exception = assert_raise(
|
29
|
+
exception = assert_raise(Alicorn::NoUnicornsError) do
|
30
30
|
@scaler.scale
|
31
31
|
end
|
32
32
|
assert_equal "Could not find any unicorn processes", exception.message
|
@@ -41,7 +41,7 @@ class TestScaler < Test::Unit::TestCase
|
|
41
41
|
end
|
42
42
|
|
43
43
|
should "raise an error" do
|
44
|
-
exception = assert_raise(
|
44
|
+
exception = assert_raise(Alicorn::NoMasterError) do
|
45
45
|
@scaler.scale
|
46
46
|
end
|
47
47
|
assert_match /No unicorn master processes/, exception.message
|
@@ -55,11 +55,9 @@ class TestScaler < Test::Unit::TestCase
|
|
55
55
|
@scaler.stubs(:grep_process_list).returns(plist)
|
56
56
|
end
|
57
57
|
|
58
|
-
should "
|
59
|
-
|
60
|
-
|
61
|
-
end
|
62
|
-
assert_match /Too many unicorn master processes/, exception.message
|
58
|
+
should "die quietly" do
|
59
|
+
@scaler.expects(:auto_scale).never
|
60
|
+
@scaler.scale
|
63
61
|
end
|
64
62
|
end
|
65
63
|
|
@@ -70,11 +68,9 @@ class TestScaler < Test::Unit::TestCase
|
|
70
68
|
@scaler.stubs(:grep_process_list).returns(plist)
|
71
69
|
end
|
72
70
|
|
73
|
-
should "
|
74
|
-
|
75
|
-
|
76
|
-
end
|
77
|
-
assert_match /Old master process detected/, exception.message
|
71
|
+
should "die quietly" do
|
72
|
+
@scaler.expects(:auto_scale).never
|
73
|
+
@scaler.scale
|
78
74
|
end
|
79
75
|
end
|
80
76
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: alicorn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-05-
|
12
|
+
date: 2012-05-30 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: curb
|
16
|
-
requirement: &
|
16
|
+
requirement: &70222522833040 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 0.8.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70222522833040
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: shoulda
|
27
|
-
requirement: &
|
27
|
+
requirement: &70222522832560 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 3.0.1
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70222522832560
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rdoc
|
38
|
-
requirement: &
|
38
|
+
requirement: &70222522832080 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '3.12'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70222522832080
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: bundler
|
49
|
-
requirement: &
|
49
|
+
requirement: &70222522831600 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: 1.1.3
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70222522831600
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: jeweler
|
60
|
-
requirement: &
|
60
|
+
requirement: &70222522831120 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ~>
|
@@ -65,10 +65,10 @@ dependencies:
|
|
65
65
|
version: 1.8.3
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *70222522831120
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: mocha
|
71
|
-
requirement: &
|
71
|
+
requirement: &70222522830640 !ruby/object:Gem::Requirement
|
72
72
|
none: false
|
73
73
|
requirements:
|
74
74
|
- - ~>
|
@@ -76,10 +76,10 @@ dependencies:
|
|
76
76
|
version: 0.11.4
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
|
-
version_requirements: *
|
79
|
+
version_requirements: *70222522830640
|
80
80
|
- !ruby/object:Gem::Dependency
|
81
81
|
name: simplecov
|
82
|
-
requirement: &
|
82
|
+
requirement: &70222522830160 !ruby/object:Gem::Requirement
|
83
83
|
none: false
|
84
84
|
requirements:
|
85
85
|
- - ~>
|
@@ -87,12 +87,12 @@ dependencies:
|
|
87
87
|
version: 0.6.4
|
88
88
|
type: :development
|
89
89
|
prerelease: false
|
90
|
-
version_requirements: *
|
90
|
+
version_requirements: *70222522830160
|
91
91
|
description: Highly configurable dumb auto-scaler for managing unicorn web servers
|
92
92
|
email: somers.ben@gmail.com
|
93
93
|
executables:
|
94
94
|
- alicorn
|
95
|
-
-
|
95
|
+
- alicorn-profiler
|
96
96
|
extensions: []
|
97
97
|
extra_rdoc_files:
|
98
98
|
- LICENSE.txt
|
@@ -106,9 +106,11 @@ files:
|
|
106
106
|
- Rakefile
|
107
107
|
- alicorn.gemspec
|
108
108
|
- bin/alicorn
|
109
|
-
- bin/
|
109
|
+
- bin/alicorn-profiler
|
110
|
+
- example/config.yml
|
110
111
|
- lib/alicorn.rb
|
111
112
|
- lib/alicorn/dataset.rb
|
113
|
+
- lib/alicorn/errors.rb
|
112
114
|
- lib/alicorn/log_parser.rb
|
113
115
|
- lib/alicorn/profiler.rb
|
114
116
|
- lib/alicorn/scaler.rb
|
@@ -133,7 +135,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
133
135
|
version: '0'
|
134
136
|
segments:
|
135
137
|
- 0
|
136
|
-
hash: -
|
138
|
+
hash: -82603868672489802
|
137
139
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
138
140
|
none: false
|
139
141
|
requirements:
|