capify-ec2 1.3.7 → 1.4.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- data/Changelog.md +7 -0
- data/LICENSE +1 -1
- data/capify-ec2.gemspec +3 -3
- data/lib/capify-ec2.rb +45 -1
- data/lib/capify-ec2/capistrano.rb +111 -10
- data/lib/capify-ec2/version.rb +1 -1
- data/readme.md +340 -92
- metadata +82 -63
data/Changelog.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
## 1.4.0.pre1 (Feb 15, 2013)
|
2
|
+
|
3
|
+
Features:
|
4
|
+
|
5
|
+
- New rolling deployment mode, allows you to deploy to your instances in serial, rather than in parallel, with an option to perform a healthcheck before proceeding to the next instance. For more information on this feature, check out the [documentation](readme.md#rolling-deployments).
|
6
|
+
- The documentation has been rewritten to make it clearer how to use Capify-EC2 and what the available options are.
|
7
|
+
|
1
8
|
## 1.3.7 (Jan 20, 2013)
|
2
9
|
|
3
10
|
- Make the behaviour for passing hash/filename consistent
|
data/LICENSE
CHANGED
data/capify-ec2.gemspec
CHANGED
@@ -7,10 +7,10 @@ Gem::Specification.new do |s|
|
|
7
7
|
s.version = Capify::Ec2::VERSION
|
8
8
|
s.platform = Gem::Platform::RUBY
|
9
9
|
s.authors = ["Noah Cantor", "Siddharth Dawara"]
|
10
|
-
s.email = ["
|
10
|
+
s.email = ["capify-ec2@forwardtechnology.co.uk"]
|
11
11
|
s.homepage = "http://github.com/forward/capify-ec2"
|
12
|
-
s.summary = %q{
|
13
|
-
s.description = %q{
|
12
|
+
s.summary = %q{Capify-EC2 is used to generate Capistrano namespaces and tasks from Amazon EC2 instance tags, dynamically building the list of servers to be deployed to.}
|
13
|
+
s.description = %q{Capify-EC2 is used to generate Capistrano namespaces and tasks from Amazon EC2 instance tags, dynamically building the list of servers to be deployed to.}
|
14
14
|
|
15
15
|
s.rubyforge_project = "capify-ec2"
|
16
16
|
|
data/lib/capify-ec2.rb
CHANGED
@@ -57,7 +57,7 @@ class CapifyEc2
|
|
57
57
|
|
58
58
|
# Title row.
|
59
59
|
puts sprintf "%-3s %s %s %s %s %s %s %s",
|
60
|
-
'',
|
60
|
+
'Num' .bold,
|
61
61
|
'Name' .ljust( column_widths[:name] ).bold,
|
62
62
|
'ID' .ljust( 10 ).bold,
|
63
63
|
'Type' .ljust( column_widths[:type] ).bold,
|
@@ -165,4 +165,48 @@ class CapifyEc2
|
|
165
165
|
STDERR.puts "#{instance.name}: tests timed out after #{time_elapsed} seconds."
|
166
166
|
end
|
167
167
|
end
|
168
|
+
|
169
|
+
def instance_health_by_url(dns, port, path, expected_response, options = {})
|
170
|
+
protocol = options[:https] ? 'https://' : 'http://'
|
171
|
+
uri = URI("#{protocol}#{dns}:#{port}#{path}")
|
172
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
173
|
+
|
174
|
+
result = nil
|
175
|
+
|
176
|
+
begin
|
177
|
+
Timeout::timeout(options[:timeout]) do
|
178
|
+
begin
|
179
|
+
result = http.get(uri.path)
|
180
|
+
raise "Server responded with '#{result.code}: #{result.body}', expected '#{expected_response}'" unless result.body == expected_response
|
181
|
+
rescue => e
|
182
|
+
puts "[Capify-EC2] Unexpected response: #{e}..."
|
183
|
+
sleep 1
|
184
|
+
retry
|
185
|
+
end
|
186
|
+
end
|
187
|
+
rescue Timeout::Error => e
|
188
|
+
end
|
189
|
+
result ? result.body == expected_response : false
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def instance_dns_with_name_tag(dns)
|
194
|
+
name_tag = ''
|
195
|
+
current_node = capify_ec2.desired_instances.select { |instance| instance.dns_name == dns }
|
196
|
+
name_tag = current_node.first.tags['Name'] unless current_node.empty?
|
197
|
+
"#{dns} (#{name_tag})"
|
198
|
+
end
|
199
|
+
|
200
|
+
def format_rolling_deploy_results(all_servers, results)
|
201
|
+
puts '[Capify-EC2] None.' unless results.any?
|
202
|
+
results.each {|server| puts "[Capify-EC2] #{instance_dns_with_name_tag(server)} with #{all_servers[server].count >1 ? 'roles' : 'role'} '#{all_servers[server].join(', ')}'."}
|
203
|
+
end
|
204
|
+
|
205
|
+
class CapifyEC2RollingDeployError < Exception
|
206
|
+
attr_reader :dns
|
207
|
+
|
208
|
+
def initialize(msg, dns)
|
209
|
+
super(msg)
|
210
|
+
@dns = dns
|
211
|
+
end
|
168
212
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), '../capify-ec2')
|
2
2
|
require 'colored'
|
3
|
+
require 'pp'
|
3
4
|
|
4
5
|
Capistrano::Configuration.instance(:must_exist).load do
|
5
6
|
def capify_ec2
|
@@ -34,24 +35,121 @@ Capistrano::Configuration.instance(:must_exist).load do
|
|
34
35
|
task :server_names do
|
35
36
|
puts capify_ec2.server_names.sort
|
36
37
|
end
|
37
|
-
|
38
|
+
|
38
39
|
desc "Allows ssh to instance by id. cap ssh <INSTANCE NAME>"
|
39
40
|
task :ssh do
|
40
41
|
server = variables[:logger].instance_variable_get("@options")[:actions][1]
|
41
42
|
instance = numeric?(server) ? capify_ec2.desired_instances[server.to_i] : capify_ec2.get_instance_by_name(server)
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
|
44
|
+
if instance and instance.contact_point then
|
45
|
+
port = ssh_options[:port] || 22
|
46
|
+
command = "ssh -p #{port} #{user}@#{instance.contact_point}"
|
47
|
+
puts "Running `#{command}`"
|
48
|
+
exec(command)
|
49
|
+
else
|
50
|
+
puts "[Capify-EC2] Error: You did not specify the instance number, which can be found via the 'ec2:status' command as follows:".bold.red
|
51
|
+
top.ec2.status
|
52
|
+
end
|
46
53
|
end
|
47
54
|
end
|
48
|
-
|
55
|
+
|
49
56
|
namespace :deploy do
|
50
57
|
before "deploy", "ec2:deregister_instance"
|
51
58
|
after "deploy", "ec2:register_instance"
|
52
59
|
after "deploy:rollback", "ec2:register_instance"
|
53
60
|
end
|
54
61
|
|
62
|
+
desc "Deploy to servers one at a time."
|
63
|
+
task :rolling_deploy do
|
64
|
+
puts "[Capify-EC2] Performing rolling deployment..."
|
65
|
+
|
66
|
+
all_servers = {}
|
67
|
+
all_roles = {}
|
68
|
+
|
69
|
+
roles.each do |role|
|
70
|
+
role[1].servers.each do |s|
|
71
|
+
all_servers[ s.host.to_s ] ||= []
|
72
|
+
all_servers[ s.host.to_s ] << role[0]
|
73
|
+
all_roles[ role[0] ] = {:options => {:healthcheck => s.options[:healthcheck] ||= nil}} unless all_roles[ role[0] ]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
successful_deploys = []
|
78
|
+
failed_deploys = []
|
79
|
+
|
80
|
+
begin
|
81
|
+
all_servers.each do |server_dns,server_roles|
|
82
|
+
|
83
|
+
roles.clear
|
84
|
+
|
85
|
+
server_roles.each do |a_role|
|
86
|
+
#TODO: Look at defining any options associated to the specific role, maybe through calling 'ec2_role'.
|
87
|
+
role a_role, server_dns
|
88
|
+
end
|
89
|
+
|
90
|
+
puts "[Capify-EC2]"
|
91
|
+
puts "[Capify-EC2] Beginning deployment to #{instance_dns_with_name_tag(server_dns)} with #{server_roles.count > 1 ? 'roles' : 'role'} '#{server_roles.join(', ')}'...".bold
|
92
|
+
|
93
|
+
# Call the standard 'cap deploy' task with our redefined role containing a single server.
|
94
|
+
top.deploy.default
|
95
|
+
|
96
|
+
server_roles.each do |a_role|
|
97
|
+
|
98
|
+
# If a healthcheck is defined for this role, run it.
|
99
|
+
if all_roles[a_role][:options][:healthcheck]
|
100
|
+
options = {}
|
101
|
+
options[:https] = all_roles[a_role][:options][:healthcheck][:https] ||= false
|
102
|
+
options[:timeout] = all_roles[a_role][:options][:healthcheck][:timeout] ||= 60
|
103
|
+
puts "[Capify-EC2] Starting healthcheck for role '#{a_role}'..."
|
104
|
+
healthcheck = capify_ec2.instance_health_by_url( server_dns,
|
105
|
+
all_roles[a_role][:options][:healthcheck][:port],
|
106
|
+
all_roles[a_role][:options][:healthcheck][:path],
|
107
|
+
all_roles[a_role][:options][:healthcheck][:result],
|
108
|
+
options )
|
109
|
+
if healthcheck
|
110
|
+
puts "[Capify-EC2] Healthcheck for role '#{a_role}' successful.".green.bold
|
111
|
+
else
|
112
|
+
puts "[Capify-EC2] Healthcheck for role '#{a_role}' failed!".red.bold
|
113
|
+
raise CapifyEC2RollingDeployError.new("Healthcheck timeout exceeded", server_dns)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
puts "[Capify-EC2] Deployment successful to #{instance_dns_with_name_tag(server_dns)}.".green.bold
|
120
|
+
successful_deploys << server_dns
|
121
|
+
|
122
|
+
end
|
123
|
+
rescue CapifyEC2RollingDeployError => e
|
124
|
+
failed_deploys << e.dns
|
125
|
+
puts "[Capify-EC2]"
|
126
|
+
puts "[Capify-EC2] Deployment aborted due to error: #{e}!".red.bold
|
127
|
+
|
128
|
+
rescue => e
|
129
|
+
failed_deploys << roles.values.first.servers.first.host
|
130
|
+
puts "[Capify-EC2]"
|
131
|
+
puts "[Capify-EC2] Deployment aborted due to error: #{e}!".red.bold
|
132
|
+
|
133
|
+
else
|
134
|
+
puts "[Capify-EC2]"
|
135
|
+
puts "[Capify-EC2] Rolling deployment completed.".green.bold
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
puts "[Capify-EC2]"
|
140
|
+
puts "[Capify-EC2] Successful servers:"
|
141
|
+
format_rolling_deploy_results( all_servers, successful_deploys )
|
142
|
+
|
143
|
+
puts "[Capify-EC2]"
|
144
|
+
puts "[Capify-EC2] Failed servers:"
|
145
|
+
format_rolling_deploy_results( all_servers, failed_deploys )
|
146
|
+
|
147
|
+
puts "[Capify-EC2]"
|
148
|
+
puts "[Capify-EC2] Pending servers:"
|
149
|
+
pending_deploys = (all_servers.keys - successful_deploys) - failed_deploys
|
150
|
+
format_rolling_deploy_results( all_servers, pending_deploys )
|
151
|
+
end
|
152
|
+
|
55
153
|
def ec2_roles(*roles)
|
56
154
|
server_name = variables[:logger].instance_variable_get("@options")[:actions].first unless variables[:logger].instance_variable_get("@options")[:actions][1].nil?
|
57
155
|
|
@@ -130,16 +228,19 @@ Capistrano::Configuration.instance(:must_exist).load do
|
|
130
228
|
def define_role(role, instance)
|
131
229
|
options = role[:options] || {}
|
132
230
|
variables = role[:variables] || {}
|
133
|
-
|
231
|
+
|
134
232
|
cap_options = options.inject({}) do |cap_options, (key, value)|
|
135
233
|
cap_options[key] = true if value.to_s == instance.name
|
136
234
|
cap_options
|
137
235
|
end
|
138
|
-
|
236
|
+
|
139
237
|
ec2_options = instance.tags["Options"] || ""
|
140
238
|
ec2_options.split(%r{,\s*}).compact.each { |ec2_option| cap_options[ec2_option.to_sym] = true }
|
141
|
-
|
142
|
-
variables.each
|
239
|
+
|
240
|
+
variables.each do |key, value|
|
241
|
+
set key, value
|
242
|
+
cap_options[key] = value unless cap_options.has_key? key
|
243
|
+
end
|
143
244
|
|
144
245
|
role role[:name].to_sym, instance.contact_point, cap_options
|
145
246
|
end
|
data/lib/capify-ec2/version.rb
CHANGED
data/readme.md
CHANGED
@@ -1,194 +1,442 @@
|
|
1
|
-
Capify
|
2
|
-
====================================================
|
1
|
+
## Capify-EC2
|
3
2
|
|
4
|
-
|
3
|
+
Capify-EC2 is used to generate Capistrano namespaces and tasks from Amazon EC2 instance tags, dynamically building the list of servers to be deployed to.
|
5
4
|
|
6
|
-
eg: If you have three servers on amazon's ec2.
|
7
5
|
|
8
|
-
|
9
|
-
server-2 Tag: Roles => "db"
|
10
|
-
server-3 Tag: Roles => "web,db, app"
|
11
|
-
|
12
|
-
Installing
|
6
|
+
### Installation
|
13
7
|
|
14
8
|
gem install capify-ec2
|
15
9
|
|
16
|
-
|
10
|
+
or add the gem to your project's Gemfile.
|
11
|
+
|
12
|
+
You will need to create a YML configuration file at 'config/ec2.yml' that looks like the following:
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
:aws_access_key_id: "YOUR ACCESS KEY"
|
16
|
+
:aws_secret_access_key: "YOUR SECRET"
|
17
|
+
:aws_params:
|
18
|
+
:region: 'eu-west-1'
|
19
|
+
:load_balanced: true
|
20
|
+
:project_tag: "YOUR APP NAME"
|
21
|
+
```
|
22
|
+
|
23
|
+
Finally, add the gem to your 'deploy.rb':
|
17
24
|
|
18
25
|
```ruby
|
19
26
|
require "capify-ec2/capistrano"
|
27
|
+
```
|
28
|
+
|
29
|
+
|
30
|
+
|
31
|
+
#### Configuration
|
32
|
+
|
33
|
+
Note: 'aws_access_key_id', 'aws_secret_access_key', and 'region' are required. Other settings are optional.
|
34
|
+
|
35
|
+
* :project_tag
|
36
|
+
|
37
|
+
If this is defined, Capify-EC2 will only create namespaces and tasks for the EC2 instances that have a matching 'Project' tag. By default, all instances available to the configured AWS access key will be used.
|
38
|
+
|
39
|
+
It is possible to include multiple projects simultaneously by using the :project_tags parameter, like so:
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
:project_tags:
|
43
|
+
- "YOUR APP NAME"
|
44
|
+
- "YOUR OTHER APP NAME"
|
45
|
+
```
|
46
|
+
|
47
|
+
* :load_balanced
|
48
|
+
|
49
|
+
When ':load_balanced' is set to 'true', Capify-EC2 uses pre and post-deploy hooks to deregister the instance from an associated Elastic Load Balancer, perform the actual deploy, then finally reregister with the ELB and validated the instance health.
|
50
|
+
Note: This options only applies to deployments made to an individual instance, using the command 'cap INSTANCE_NAME_HERE deploy' - it doesn't apply to roles.
|
51
|
+
|
52
|
+
|
53
|
+
|
54
|
+
#### EC2 Tags
|
55
|
+
|
56
|
+
You will need to create instance tags using the AWS Management Console or API, to further configure Capify-EC2. The following tags are used:
|
57
|
+
|
58
|
+
* Tag 'Project'
|
59
|
+
|
60
|
+
Used with the ':project_tag' option in 'config/ec2.yml' to limit Capify-EC2's functionality to a subset of your instances.
|
61
|
+
|
62
|
+
* Tag 'Roles'
|
63
|
+
|
64
|
+
A comma seperated list of roles that will be converted into Capistrano namespaces, for example 'app,workers', 'app,varnish,workers', 'db' and so on.
|
65
|
+
|
66
|
+
* Tag 'Options'
|
67
|
+
|
68
|
+
A comma seperated list of options which will be defined as 'true' for that instance. See the 'Options' section below for more information on their use.
|
69
|
+
task :bob, :only => {:option_nmame => true}
|
70
|
+
on_no_matching_servers => :continue
|
71
|
+
"one of those ten is cron" etc.
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
### Usage
|
76
|
+
|
77
|
+
In our examples, imagine that you have three servers on EC2 defined as follows:
|
78
|
+
|
79
|
+
server-1 Tags: Name => "server-1", Roles => "web", Options => "cron,resque"
|
80
|
+
server-2 Tags: Name => "server-2", Roles => "db"
|
81
|
+
server-3 Tags: Name => "server-3", Roles => "web,db,app"
|
82
|
+
|
83
|
+
#### Single Roles
|
84
|
+
|
85
|
+
You need to add a call to 'ec2_roles' in your 'deploy.rb', like so:
|
86
|
+
|
87
|
+
```ruby
|
20
88
|
ec2_roles :web
|
21
89
|
```
|
22
90
|
|
23
|
-
|
91
|
+
This will generate the following tasks:
|
24
92
|
|
25
93
|
```ruby
|
26
94
|
task :server-1 do
|
27
|
-
role :web,
|
95
|
+
role :web, SERVER-1_EC2_PUBLIC_DNS_HERE, :cron=>true, :resque=>true
|
28
96
|
end
|
29
97
|
|
30
98
|
task :server-3 do
|
31
|
-
role :web,
|
99
|
+
role :web, SERVER-3_EC2_PUBLIC_DNS_HERE
|
32
100
|
end
|
33
101
|
|
34
102
|
task :web do
|
35
|
-
role :web,
|
36
|
-
role :web,
|
103
|
+
role :web, SERVER-1_EC2_PUBLIC_DNS_HERE, :cron=>true, :resque=>true
|
104
|
+
role :web, SERVER-3_EC2_PUBLIC_DNS_HERE
|
37
105
|
end
|
38
106
|
```
|
39
107
|
|
40
|
-
|
108
|
+
Note that there are no tasks created for 'server-2', as it does not have the role 'web'. If we were to change the 'ec2_roles' definition in your 'deploy.rb' to the following:
|
41
109
|
|
42
110
|
```ruby
|
43
|
-
require "capify-ec2/capistrano"
|
44
111
|
ec2_roles :db
|
45
112
|
```
|
46
113
|
|
47
|
-
|
114
|
+
Then we will instead see the following tasks generated:
|
48
115
|
|
49
116
|
```ruby
|
50
117
|
task :server-2 do
|
51
|
-
role :db,
|
118
|
+
role :db, SERVER-2_EC2_PUBLIC_DNS_HERE
|
52
119
|
end
|
53
120
|
|
54
121
|
task :server-3 do
|
55
|
-
role :db,
|
122
|
+
role :db, SERVER-3_EC2_PUBLIC_DNS_HERE
|
56
123
|
end
|
57
124
|
|
58
125
|
task :db do
|
59
|
-
role :db,
|
60
|
-
role :db,
|
126
|
+
role :db, SERVER-2_EC2_PUBLIC_DNS_HERE
|
127
|
+
role :db, SERVER-3_EC2_PUBLIC_DNS_HERE
|
61
128
|
end
|
62
129
|
```
|
63
130
|
|
64
|
-
|
131
|
+
|
132
|
+
#### Multiple Roles
|
133
|
+
|
134
|
+
If you want to create tasks for servers using multiple roles, you can call 'ec2_roles' multiple times in your 'deploy.rb' as follows:
|
65
135
|
|
66
136
|
```ruby
|
67
|
-
|
137
|
+
ec2_roles :web
|
138
|
+
ec2_roles :db
|
68
139
|
```
|
69
140
|
|
70
|
-
|
141
|
+
Which would generate the following tasks:
|
71
142
|
|
72
|
-
|
143
|
+
```ruby
|
144
|
+
task :server-1 do
|
145
|
+
role :web, SERVER-1_EC2_PUBLIC_DNS_HERE, :cron=>true, :resque=>true
|
146
|
+
end
|
147
|
+
|
148
|
+
task :server-2 do
|
149
|
+
role :db, SERVER-2_EC2_PUBLIC_DNS_HERE
|
150
|
+
end
|
151
|
+
|
152
|
+
task :server-3 do
|
153
|
+
role :web, SERVER-3_EC2_PUBLIC_DNS_HERE
|
154
|
+
role :db, SERVER-3_EC2_PUBLIC_DNS_HERE
|
155
|
+
end
|
156
|
+
|
157
|
+
task :web do
|
158
|
+
role :web, SERVER-1_EC2_PUBLIC_DNS_HERE
|
159
|
+
role :web, SERVER-3_EC2_PUBLIC_DNS_HERE
|
160
|
+
end
|
161
|
+
|
162
|
+
task :db do
|
163
|
+
role :db, SERVER-2_EC2_PUBLIC_DNS_HERE, :cron=>true, :resque=>true
|
164
|
+
role :db, SERVER-3_EC2_PUBLIC_DNS_HERE
|
165
|
+
end
|
166
|
+
```
|
167
|
+
|
168
|
+
|
169
|
+
|
170
|
+
#### Role Variables
|
171
|
+
|
172
|
+
You can define custom variables which will be set as standard Capistrano variables within the scope of the role you define them one, for example:
|
173
|
+
|
174
|
+
```ruby
|
175
|
+
ec2_roles {:name=>"web", :variables => {:rails_env => 'staging'}}
|
176
|
+
```
|
177
|
+
|
178
|
+
In this case, instances that that are tagged with the 'web' role will have the custom variable 'rails_env' available to them in any tasks they use. The following tasks would be generated:
|
73
179
|
|
74
180
|
```ruby
|
75
|
-
|
181
|
+
task :server-1 do
|
182
|
+
role :web, SERVER-1_EC2_PUBLIC_DNS_HERE, :cron=>true, :resque=>true, :rails_env=>'staging'
|
183
|
+
end
|
184
|
+
|
185
|
+
task :server-3 do
|
186
|
+
role :web, SERVER-3_EC2_PUBLIC_DNS_HERE
|
187
|
+
end
|
188
|
+
|
189
|
+
task :web do
|
190
|
+
role :web, SERVER-1_EC2_PUBLIC_DNS_HERE, :cron=>true, :resque=>true, :rails_env=>'staging'
|
191
|
+
role :web, SERVER-3_EC2_PUBLIC_DNS_HERE, :rails_env=>'staging'
|
192
|
+
end
|
76
193
|
```
|
77
194
|
|
78
|
-
will register server-1 to be used by elb-1
|
79
195
|
|
80
|
-
|
196
|
+
|
197
|
+
#### Options
|
198
|
+
|
199
|
+
##### Via EC2 Tags
|
200
|
+
|
201
|
+
As mentioned in the 'EC2 Tags' section, creating an 'Options' tag on your EC2 instances will define those options as 'true' for the associated instance. This allows you to refine your capistrano tasks.
|
202
|
+
For example, if we had the following group of instances in EC2:
|
203
|
+
|
204
|
+
server-A Tags: Name => "server-A", Roles => "web"
|
205
|
+
server-B Tags: Name => "server-B", Roles => "web"
|
206
|
+
server-C Tags: Name => "server-C", Roles => "web", Options => "worker"
|
207
|
+
|
208
|
+
You could then create a task in your 'deploy.rb' that will only be executed on the worker machine, like so:
|
81
209
|
|
82
210
|
```ruby
|
83
|
-
|
211
|
+
task :reload_workers => :web, :only=>{:worker} do
|
212
|
+
# Do something to a server with cron on it
|
213
|
+
end
|
84
214
|
```
|
85
215
|
|
86
|
-
|
87
|
-
registered against.
|
216
|
+
##### Via Role Definitions
|
88
217
|
|
89
|
-
|
218
|
+
As well as defining Options at an instance level via EC2 tags, you can define an Option in your 'deploy.rb' at the same time as defining the role, as follows:
|
90
219
|
|
91
220
|
```ruby
|
92
|
-
|
221
|
+
ec2_roles {:name=>"web", :options=>{:worker=>"server-C"}}
|
93
222
|
```
|
94
223
|
|
95
|
-
|
96
|
-
|
224
|
+
In this case, you set the value of ':worker' equal to the instance name you want to be a worker.
|
225
|
+
|
97
226
|
|
98
|
-
|
227
|
+
|
228
|
+
#### Deploying
|
229
|
+
|
230
|
+
Once you have defined the various roles used by your application, you can deploy to it as you normally would a namespace, for example if you define the following in your 'deploy.rb':
|
99
231
|
|
100
232
|
```ruby
|
101
|
-
|
233
|
+
ec2_roles :web
|
234
|
+
ec2_roles :app
|
102
235
|
```
|
103
236
|
|
104
|
-
|
105
|
-
The # argument is the index of the server to ssh into. Use the 'ec2:status'
|
106
|
-
command to see the list of servers with their indices.
|
237
|
+
You can deploy to just the 'web' instances like so:
|
107
238
|
|
108
|
-
|
109
|
-
|
239
|
+
```ruby
|
240
|
+
cap web deploy
|
241
|
+
```
|
110
242
|
|
111
|
-
|
112
|
-
Use with caution! This does not work with autoscaling.
|
243
|
+
If you've defined multiple roles, you can deploy to them all by chaining the tasks, like so:
|
113
244
|
|
114
245
|
```ruby
|
115
|
-
|
246
|
+
cap web app deploy
|
116
247
|
```
|
117
248
|
|
118
|
-
|
249
|
+
|
250
|
+
|
251
|
+
##### Default Deploys
|
252
|
+
|
253
|
+
You can set a role as the default so that it will be included when you run 'cap deploy' without specifying any roles, for example in your 'deploy.rb':
|
119
254
|
|
120
255
|
```ruby
|
121
|
-
|
122
|
-
|
123
|
-
end
|
256
|
+
ec2_roles {:name=>"web", :options{:default=>true}
|
257
|
+
```
|
124
258
|
|
125
|
-
|
126
|
-
role :web, {server-1 public dns fetched from Amazon}
|
127
|
-
end
|
259
|
+
Then run:
|
128
260
|
|
129
|
-
|
130
|
-
|
131
|
-
role :web, {server-3 public dns fetched from Amazon}
|
132
|
-
end
|
261
|
+
```ruby
|
262
|
+
cap deploy
|
133
263
|
```
|
134
264
|
|
135
|
-
|
265
|
+
You can set multiple roles as defaults, so they are all included when you run 'cap deploy', like so:
|
136
266
|
|
137
267
|
```ruby
|
138
|
-
|
139
|
-
|
140
|
-
|
268
|
+
ec2_roles {:name=>"web", :options{:default=>true}
|
269
|
+
ec2_roles {:name=>"db", :options{:default=>true}
|
270
|
+
```
|
271
|
+
|
272
|
+
|
273
|
+
|
274
|
+
#### Rolling Deployments
|
275
|
+
|
276
|
+
This feature allows you to deploy your code to instances one at a time, rather than simultaneously. This becomes useful for more complex applications that may take longer to startup after a deployment. Capistrano will perform a full deploy (including any custom hooks) against a single instance, optionally perform a HTTP healthcheck against the instance, then proceed to the next instance if deployment was successful.
|
141
277
|
|
142
|
-
|
278
|
+
##### Usage
|
279
|
+
|
280
|
+
To use the rolling deployment feature without a healthcheck, simple run your deployments with the following command:
|
281
|
+
|
282
|
+
```ruby
|
283
|
+
cap rolling_deploy
|
143
284
|
```
|
144
285
|
|
145
|
-
|
146
|
-
Multiple roles can be defaults so:
|
286
|
+
You can restrict the scope of the rolling deploy by targetting one or more roles like so:
|
147
287
|
|
148
288
|
```ruby
|
149
|
-
|
150
|
-
ec2_roles :name=>:app, :options=>{ :default => true }
|
289
|
+
cap web rolling_deploy
|
151
290
|
```
|
152
291
|
|
153
|
-
|
292
|
+
```ruby
|
293
|
+
cap web db rolling_deploy
|
294
|
+
```
|
154
295
|
|
155
|
-
|
156
|
-
====================================================
|
296
|
+
##### Usage with Healthchecks
|
157
297
|
|
158
|
-
|
159
|
-
The yml file needs to look something like this:
|
298
|
+
When defining a role with the 'ec2_role' command, if you configure a healthcheck for that role as follows, it will automatically be used during the rolling deployment:
|
160
299
|
|
161
300
|
```ruby
|
162
|
-
:
|
163
|
-
:
|
164
|
-
:
|
165
|
-
|
166
|
-
:
|
167
|
-
:
|
301
|
+
ec2_roles { :name => "web",
|
302
|
+
:variables => {
|
303
|
+
:healthcheck => {
|
304
|
+
:path => '/status',
|
305
|
+
:port => 80,
|
306
|
+
:result => 'OK'
|
307
|
+
}
|
308
|
+
}
|
168
309
|
```
|
169
|
-
aws_access_key_id, aws_secret_access_key, and region are required. Other settings are optional.
|
170
310
|
|
171
|
-
|
172
|
-
hooks to deregister the instance, reregister it, and validate its
|
173
|
-
health.
|
174
|
-
:load_balanced only works for individual instances, not for roles.
|
311
|
+
In this example, the following URL would be generated:
|
175
312
|
|
176
|
-
|
177
|
-
|
178
|
-
|
313
|
+
```
|
314
|
+
http://EC2_INSTANCE_PUBLIC_DNS_HERE:80/status
|
315
|
+
```
|
316
|
+
|
317
|
+
And the contents of the page at that URL must match 'OK' for the healthcheck to pass. If unsuccessful, the healthcheck is repeated every second, until a timeout of 60 seconds is reached, at which point the rolling deployment is aborted, and a progress summary displayed.
|
318
|
+
|
319
|
+
The default timeout is 60 seconds, which can be overridden by setting ':timeout' to a custom value in seconds. The protocol used defaults to 'http://', however you can switch to 'https://' by setting ':https' equal to 'true'. For example:
|
320
|
+
|
321
|
+
```ruby
|
322
|
+
ec2_roles { :name => "web",
|
323
|
+
:variables => {
|
324
|
+
:healthcheck => {
|
325
|
+
:path => '/status',
|
326
|
+
:port => 80,
|
327
|
+
:result => 'OK',
|
328
|
+
:https => true,
|
329
|
+
:timeout => 10
|
330
|
+
}
|
331
|
+
}
|
332
|
+
```
|
333
|
+
|
334
|
+
Sets a 10 second timeout, and performs the health check over HTTPS.
|
335
|
+
|
336
|
+
#### Managing Load Balancers
|
337
|
+
|
338
|
+
You can use the following commands to deregister and reregister instances in an Elastic Load Balancer.
|
339
|
+
|
340
|
+
```ruby
|
341
|
+
cap SERVER_NAME_HERE ec2:deregister_instance
|
342
|
+
```
|
179
343
|
|
180
344
|
```ruby
|
181
|
-
:
|
182
|
-
- "YOUR APP NAME"
|
183
|
-
- "YOUR OTHER APP NAME"
|
345
|
+
cap SERVER_NAME_HERE ec2:register_instance -s loadbalancer=ELB_NAME_HERE
|
184
346
|
```
|
185
347
|
|
186
|
-
|
348
|
+
You need to specify the ELB when reregistering an instance, but not when deregistering. This can also be done automatically using the ':load_balanced' setting (see the 'Configuration' section above).
|
349
|
+
|
350
|
+
|
351
|
+
|
352
|
+
#### Viewing All Instances
|
353
|
+
|
354
|
+
The following command will generate a listing of all instances that match your configuration (projects and roles), along with their associated details:
|
355
|
+
|
356
|
+
```ruby
|
357
|
+
cap ec2:status
|
358
|
+
```
|
359
|
+
|
360
|
+
|
361
|
+
|
362
|
+
#### Connecting to an Instance via SSH
|
363
|
+
|
364
|
+
Using the 'cap ec2:ssh' command, you can quickly connect to a specific instance, by checking the listing from 'ec2:status' and using the instance number as a parameter, for example:
|
365
|
+
|
366
|
+
```ruby
|
367
|
+
cap ec2:ssh 1
|
368
|
+
```
|
369
|
+
|
370
|
+
will attempt to connect to instance number 1 (as shown in 'ec2:status'), using the public DNS address provided by AWS.
|
371
|
+
|
372
|
+
|
373
|
+
|
374
|
+
#### Other Commands
|
375
|
+
|
376
|
+
Running the following command:
|
377
|
+
|
378
|
+
```ruby
|
379
|
+
cap ec2:date
|
380
|
+
```
|
381
|
+
|
382
|
+
Will execute the 'date' command on all instances that match your configuration (projects and roles). You can limit this further by using a role, for example:
|
383
|
+
|
384
|
+
```ruby
|
385
|
+
cap web ec2:date
|
386
|
+
```
|
387
|
+
|
388
|
+
Will restrict the 'date' command so it is only run on instances that are tagged with the 'web' role. You can chain many roles together to increase the scope of the command:
|
389
|
+
|
390
|
+
```ruby
|
391
|
+
cap web db ec2:date
|
392
|
+
```
|
393
|
+
|
394
|
+
##### Cap Invoke
|
395
|
+
|
396
|
+
You can use the standard Capistrano 'invoke' task to run an arbitrary command on your instances, for example:
|
397
|
+
|
398
|
+
```ruby
|
399
|
+
cap COMMAND='uptime' invoke
|
400
|
+
```
|
401
|
+
|
402
|
+
Will run the 'uptime' command on all instances that match your configuration (projects and roles). As with the 'ec2:date' command, you can further limit this by using a role, like so:
|
403
|
+
|
404
|
+
```ruby
|
405
|
+
cap web COMMAND='uptime' invoke
|
406
|
+
```
|
407
|
+
|
408
|
+
You can also chain many roles together to increase the scope of the command:
|
409
|
+
|
410
|
+
```ruby
|
411
|
+
cap web db COMMAND='uptime' invoke
|
412
|
+
```
|
413
|
+
|
414
|
+
##### Cap Shell
|
415
|
+
|
416
|
+
You can use the standard Capistrano 'shell' task to open an interactive terminal session with your instances, for example:
|
417
|
+
|
418
|
+
```ruby
|
419
|
+
cap shell
|
420
|
+
```
|
421
|
+
|
422
|
+
Will open an interactive terminal on all instances that match your configuration (projects and roles). You can of course limit the scope of the shell to a certain role or roles like so:
|
423
|
+
|
424
|
+
```ruby
|
425
|
+
cap web shell
|
426
|
+
```
|
427
|
+
|
428
|
+
```ruby
|
429
|
+
cap web db shell
|
430
|
+
```
|
431
|
+
|
432
|
+
|
433
|
+
|
434
|
+
### Development
|
187
435
|
|
188
436
|
Source hosted at [GitHub](http://github.com/forward/capify-ec2).
|
189
437
|
Report Issues/Feature requests on [GitHub Issues](http://github.com/forward/capify-ec2/issues).
|
190
438
|
|
191
|
-
|
439
|
+
#### Note on Patches/Pull Requests
|
192
440
|
|
193
441
|
* Fork the project.
|
194
442
|
* Make your feature addition or bug fix.
|
@@ -198,6 +446,6 @@ Report Issues/Feature requests on [GitHub Issues](http://github.com/forward/capi
|
|
198
446
|
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
199
447
|
* Send me a pull request. Bonus points for topic branches.
|
200
448
|
|
201
|
-
|
449
|
+
### Copyright
|
202
450
|
|
203
|
-
Copyright (c) 2012 Forward. See [LICENSE](https://github.com/forward/capify-ec2/blob/master/LICENSE) for details.
|
451
|
+
Copyright (c) 2011, 2012, 2013 Forward. See [LICENSE](https://github.com/forward/capify-ec2/blob/master/LICENSE) for details.
|
metadata
CHANGED
@@ -1,73 +1,80 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: capify-ec2
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease:
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: -2106362774
|
5
|
+
prerelease: 6
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 4
|
9
|
+
- 0
|
10
|
+
- pre
|
11
|
+
- 1
|
12
|
+
version: 1.4.0.pre1
|
6
13
|
platform: ruby
|
7
|
-
authors:
|
14
|
+
authors:
|
8
15
|
- Noah Cantor
|
9
16
|
- Siddharth Dawara
|
10
17
|
autorequire:
|
11
18
|
bindir: bin
|
12
19
|
cert_chain: []
|
13
|
-
|
14
|
-
|
15
|
-
|
20
|
+
|
21
|
+
date: 2013-02-15 00:00:00 Z
|
22
|
+
dependencies:
|
23
|
+
- !ruby/object:Gem::Dependency
|
16
24
|
name: fog
|
17
|
-
requirement: !ruby/object:Gem::Requirement
|
18
|
-
none: false
|
19
|
-
requirements:
|
20
|
-
- - '='
|
21
|
-
- !ruby/object:Gem::Version
|
22
|
-
version: 1.3.1
|
23
|
-
type: :runtime
|
24
25
|
prerelease: false
|
25
|
-
|
26
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
26
27
|
none: false
|
27
|
-
requirements:
|
28
|
-
- -
|
29
|
-
- !ruby/object:Gem::Version
|
28
|
+
requirements:
|
29
|
+
- - "="
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
hash: 25
|
32
|
+
segments:
|
33
|
+
- 1
|
34
|
+
- 3
|
35
|
+
- 1
|
30
36
|
version: 1.3.1
|
31
|
-
- !ruby/object:Gem::Dependency
|
32
|
-
name: colored
|
33
|
-
requirement: !ruby/object:Gem::Requirement
|
34
|
-
none: false
|
35
|
-
requirements:
|
36
|
-
- - '='
|
37
|
-
- !ruby/object:Gem::Version
|
38
|
-
version: '1.2'
|
39
37
|
type: :runtime
|
38
|
+
version_requirements: *id001
|
39
|
+
- !ruby/object:Gem::Dependency
|
40
|
+
name: colored
|
40
41
|
prerelease: false
|
41
|
-
|
42
|
-
none: false
|
43
|
-
requirements:
|
44
|
-
- - '='
|
45
|
-
- !ruby/object:Gem::Version
|
46
|
-
version: '1.2'
|
47
|
-
- !ruby/object:Gem::Dependency
|
48
|
-
name: capistrano
|
49
|
-
requirement: !ruby/object:Gem::Requirement
|
42
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
50
43
|
none: false
|
51
|
-
requirements:
|
52
|
-
- -
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
|
44
|
+
requirements:
|
45
|
+
- - "="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
hash: 11
|
48
|
+
segments:
|
49
|
+
- 1
|
50
|
+
- 2
|
51
|
+
version: "1.2"
|
55
52
|
type: :runtime
|
53
|
+
version_requirements: *id002
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: capistrano
|
56
56
|
prerelease: false
|
57
|
-
|
57
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
58
58
|
none: false
|
59
|
-
requirements:
|
60
|
-
- -
|
61
|
-
- !ruby/object:Gem::Version
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
hash: 3
|
63
|
+
segments:
|
64
|
+
- 0
|
65
|
+
version: "0"
|
66
|
+
type: :runtime
|
67
|
+
version_requirements: *id003
|
68
|
+
description: Capify-EC2 is used to generate Capistrano namespaces and tasks from Amazon EC2 instance tags, dynamically building the list of servers to be deployed to.
|
69
|
+
email:
|
70
|
+
- capify-ec2@forwardtechnology.co.uk
|
67
71
|
executables: []
|
72
|
+
|
68
73
|
extensions: []
|
74
|
+
|
69
75
|
extra_rdoc_files: []
|
70
|
-
|
76
|
+
|
77
|
+
files:
|
71
78
|
- .gitignore
|
72
79
|
- Changelog.md
|
73
80
|
- Gemfile
|
@@ -81,26 +88,38 @@ files:
|
|
81
88
|
- readme.md
|
82
89
|
homepage: http://github.com/forward/capify-ec2
|
83
90
|
licenses: []
|
91
|
+
|
84
92
|
post_install_message:
|
85
93
|
rdoc_options: []
|
86
|
-
|
94
|
+
|
95
|
+
require_paths:
|
87
96
|
- lib
|
88
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
97
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
89
98
|
none: false
|
90
|
-
requirements:
|
91
|
-
- -
|
92
|
-
- !ruby/object:Gem::Version
|
93
|
-
|
94
|
-
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
hash: 3
|
103
|
+
segments:
|
104
|
+
- 0
|
105
|
+
version: "0"
|
106
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
107
|
none: false
|
96
|
-
requirements:
|
97
|
-
- -
|
98
|
-
- !ruby/object:Gem::Version
|
99
|
-
|
108
|
+
requirements:
|
109
|
+
- - ">"
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
hash: 25
|
112
|
+
segments:
|
113
|
+
- 1
|
114
|
+
- 3
|
115
|
+
- 1
|
116
|
+
version: 1.3.1
|
100
117
|
requirements: []
|
118
|
+
|
101
119
|
rubyforge_project: capify-ec2
|
102
|
-
rubygems_version: 1.8.
|
120
|
+
rubygems_version: 1.8.15
|
103
121
|
signing_key:
|
104
122
|
specification_version: 3
|
105
|
-
summary:
|
123
|
+
summary: Capify-EC2 is used to generate Capistrano namespaces and tasks from Amazon EC2 instance tags, dynamically building the list of servers to be deployed to.
|
106
124
|
test_files: []
|
125
|
+
|