arpitjain11-ec2onrails 0.9.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (163) hide show
  1. data/CHANGELOG +180 -0
  2. data/COPYING +339 -0
  3. data/Manifest +162 -0
  4. data/README.textile +214 -0
  5. data/Rakefile +36 -0
  6. data/TODO +102 -0
  7. data/ec2onrails.gemspec +42 -0
  8. data/examples/Capfile +3 -0
  9. data/examples/deploy.rb +101 -0
  10. data/examples/s3.yml +9 -0
  11. data/lib/ec2onrails.rb +20 -0
  12. data/lib/ec2onrails/capistrano_utils.rb +43 -0
  13. data/lib/ec2onrails/recipes.rb +844 -0
  14. data/lib/ec2onrails/version.rb +31 -0
  15. data/server/build-ec2onrails.sh +44 -0
  16. data/server/files/etc/aliases +5 -0
  17. data/server/files/etc/aliases.db +0 -0
  18. data/server/files/etc/apache2/apache2.conf +295 -0
  19. data/server/files/etc/apache2/conf.d/app.proxy_cluster.conf +7 -0
  20. data/server/files/etc/apache2/conf.d/app.proxy_frontend.conf +10 -0
  21. data/server/files/etc/apache2/mods-available/proxy.conf +18 -0
  22. data/server/files/etc/apache2/sites-available/app.common +56 -0
  23. data/server/files/etc/apache2/sites-available/app.custom +0 -0
  24. data/server/files/etc/apache2/sites-available/default +14 -0
  25. data/server/files/etc/apache2/sites-available/default-ssl +19 -0
  26. data/server/files/etc/cron.d/backup_app_db_to_s3 +16 -0
  27. data/server/files/etc/cron.daily/app +9 -0
  28. data/server/files/etc/cron.daily/logrotate_post +19 -0
  29. data/server/files/etc/cron.hourly/app +10 -0
  30. data/server/files/etc/cron.monthly/app +10 -0
  31. data/server/files/etc/cron.weekly/app +10 -0
  32. data/server/files/etc/denyhosts.conf +628 -0
  33. data/server/files/etc/dpkg/dpkg.cfg +13 -0
  34. data/server/files/etc/ec2onrails/README +32 -0
  35. data/server/files/etc/ec2onrails/balancer_members +6 -0
  36. data/server/files/etc/ec2onrails/roles.yml +5 -0
  37. data/server/files/etc/environment +2 -0
  38. data/server/files/etc/god/app.god +35 -0
  39. data/server/files/etc/god/db.god +17 -0
  40. data/server/files/etc/god/examples/have_god_daemonize.god +18 -0
  41. data/server/files/etc/god/master.conf +35 -0
  42. data/server/files/etc/god/memcache.god +15 -0
  43. data/server/files/etc/god/notifications.god +14 -0
  44. data/server/files/etc/god/system.god +34 -0
  45. data/server/files/etc/god/web.god +36 -0
  46. data/server/files/etc/init.d/ec2-every-startup +29 -0
  47. data/server/files/etc/init.d/ec2-first-startup +36 -0
  48. data/server/files/etc/init.d/god +42 -0
  49. data/server/files/etc/init.d/nginx +78 -0
  50. data/server/files/etc/init.d/set_roles +3 -0
  51. data/server/files/etc/logrotate.d/apache2 +16 -0
  52. data/server/files/etc/logrotate.d/mongrel +11 -0
  53. data/server/files/etc/logrotate.d/nginx +11 -0
  54. data/server/files/etc/memcached.conf +47 -0
  55. data/server/files/etc/mongrel_cluster/app.yml +9 -0
  56. data/server/files/etc/motd.tail +13 -0
  57. data/server/files/etc/mysql/my.cnf +152 -0
  58. data/server/files/etc/nginx/nginx.conf +296 -0
  59. data/server/files/etc/postfix/main.cf +4 -0
  60. data/server/files/etc/rcS.d/S91ec2-first-startup +1 -0
  61. data/server/files/etc/rcS.d/S92ec2-every-startup +1 -0
  62. data/server/files/etc/rcS.d/S99set_roles +1 -0
  63. data/server/files/etc/ssh/sshd_config +94 -0
  64. data/server/files/etc/sudoers +1 -0
  65. data/server/files/etc/sudoers.full_access +26 -0
  66. data/server/files/etc/sudoers.restricted_access +28 -0
  67. data/server/files/etc/syslog.conf +69 -0
  68. data/server/files/usr/bin/god +26 -0
  69. data/server/files/usr/local/ec2onrails/COPYING +339 -0
  70. data/server/files/usr/local/ec2onrails/bin/archive_file.rb +44 -0
  71. data/server/files/usr/local/ec2onrails/bin/backup_app_db.rb +159 -0
  72. data/server/files/usr/local/ec2onrails/bin/ec2_meta_data.rb +80 -0
  73. data/server/files/usr/local/ec2onrails/bin/exec_runner +73 -0
  74. data/server/files/usr/local/ec2onrails/bin/init_services.rb +64 -0
  75. data/server/files/usr/local/ec2onrails/bin/optimize_mysql.rb +348 -0
  76. data/server/files/usr/local/ec2onrails/bin/rails_env +35 -0
  77. data/server/files/usr/local/ec2onrails/bin/rebundle.sh +70 -0
  78. data/server/files/usr/local/ec2onrails/bin/restore_app_db.rb +58 -0
  79. data/server/files/usr/local/ec2onrails/bin/set_rails_env +40 -0
  80. data/server/files/usr/local/ec2onrails/bin/set_roles.rb +87 -0
  81. data/server/files/usr/local/ec2onrails/bin/setup_web_proxy.rb +109 -0
  82. data/server/files/usr/local/ec2onrails/config +30 -0
  83. data/server/files/usr/local/ec2onrails/lib/aws_helper.rb +76 -0
  84. data/server/files/usr/local/ec2onrails/lib/god_helper.rb +129 -0
  85. data/server/files/usr/local/ec2onrails/lib/god_patch.rb +43 -0
  86. data/server/files/usr/local/ec2onrails/lib/mysql_helper.rb +101 -0
  87. data/server/files/usr/local/ec2onrails/lib/roles_helper.rb +151 -0
  88. data/server/files/usr/local/ec2onrails/lib/s3_helper.rb +99 -0
  89. data/server/files/usr/local/ec2onrails/lib/utils.rb +16 -0
  90. data/server/files/usr/local/ec2onrails/lib/vendor/ini.rb +268 -0
  91. data/server/files/usr/local/ec2onrails/startup-scripts/every-startup/get-hostname.sh +25 -0
  92. data/server/files/usr/local/ec2onrails/startup-scripts/first-startup/README +5 -0
  93. data/server/files/usr/local/ec2onrails/startup-scripts/first-startup/create-dirs.sh +39 -0
  94. data/server/files/usr/local/ec2onrails/startup-scripts/first-startup/generate-default-web-cert-and-key.sh +49 -0
  95. data/server/files/usr/local/ec2onrails/startup-scripts/first-startup/misc.sh +27 -0
  96. data/server/files/usr/local/ec2onrails/startup-scripts/first-startup/prepare-mysql-data-dir.sh +24 -0
  97. data/server/files/usr/local/ec2onrails/startup-scripts/first-startup/setup-credentials.sh +29 -0
  98. data/server/files/usr/local/ec2onrails/startup-scripts/first-startup/setup-file-permissions.sh +30 -0
  99. data/server/rakefile.rb +248 -0
  100. data/setup.rb +1585 -0
  101. data/test/autobench.conf +60 -0
  102. data/test/spec/lib/s3_helper_spec.rb +134 -0
  103. data/test/spec/lib/s3_old.yml +3 -0
  104. data/test/spec/test_files/test1 +0 -0
  105. data/test/spec/test_files/test2 +0 -0
  106. data/test/test_app/Capfile +3 -0
  107. data/test/test_app/README +182 -0
  108. data/test/test_app/Rakefile +10 -0
  109. data/test/test_app/app/controllers/application.rb +7 -0
  110. data/test/test_app/app/controllers/db_fast_controller.rb +6 -0
  111. data/test/test_app/app/controllers/fast_controller.rb +5 -0
  112. data/test/test_app/app/controllers/slow_controller.rb +6 -0
  113. data/test/test_app/app/controllers/very_slow_controller.rb +6 -0
  114. data/test/test_app/app/helpers/application_helper.rb +3 -0
  115. data/test/test_app/app/helpers/db_fast_helper.rb +2 -0
  116. data/test/test_app/app/helpers/fast_helper.rb +2 -0
  117. data/test/test_app/app/helpers/slow_helper.rb +2 -0
  118. data/test/test_app/app/helpers/very_slow_helper.rb +2 -0
  119. data/test/test_app/config/boot.rb +109 -0
  120. data/test/test_app/config/database.yml +19 -0
  121. data/test/test_app/config/deploy.rb +21 -0
  122. data/test/test_app/config/environment.rb +60 -0
  123. data/test/test_app/config/environments/development.rb +21 -0
  124. data/test/test_app/config/environments/production.rb +18 -0
  125. data/test/test_app/config/environments/test.rb +19 -0
  126. data/test/test_app/config/routes.rb +27 -0
  127. data/test/test_app/db/schema.rb +7 -0
  128. data/test/test_app/doc/README_FOR_APP +2 -0
  129. data/test/test_app/public/404.html +30 -0
  130. data/test/test_app/public/500.html +30 -0
  131. data/test/test_app/public/dispatch.cgi +10 -0
  132. data/test/test_app/public/dispatch.fcgi +24 -0
  133. data/test/test_app/public/dispatch.rb +10 -0
  134. data/test/test_app/public/favicon.ico +0 -0
  135. data/test/test_app/public/images/rails.png +0 -0
  136. data/test/test_app/public/javascripts/application.js +2 -0
  137. data/test/test_app/public/javascripts/controls.js +963 -0
  138. data/test/test_app/public/javascripts/dragdrop.js +972 -0
  139. data/test/test_app/public/javascripts/effects.js +1120 -0
  140. data/test/test_app/public/javascripts/prototype.js +4225 -0
  141. data/test/test_app/public/robots.txt +1 -0
  142. data/test/test_app/script/about +3 -0
  143. data/test/test_app/script/breakpointer +3 -0
  144. data/test/test_app/script/console +3 -0
  145. data/test/test_app/script/destroy +3 -0
  146. data/test/test_app/script/generate +3 -0
  147. data/test/test_app/script/performance/benchmarker +3 -0
  148. data/test/test_app/script/performance/profiler +3 -0
  149. data/test/test_app/script/performance/request +3 -0
  150. data/test/test_app/script/plugin +3 -0
  151. data/test/test_app/script/process/inspector +3 -0
  152. data/test/test_app/script/process/reaper +3 -0
  153. data/test/test_app/script/process/spawner +3 -0
  154. data/test/test_app/script/runner +3 -0
  155. data/test/test_app/script/server +3 -0
  156. data/test/test_app/test/functional/db_fast_controller_test.rb +18 -0
  157. data/test/test_app/test/functional/fast_controller_test.rb +18 -0
  158. data/test/test_app/test/functional/slow_controller_test.rb +18 -0
  159. data/test/test_app/test/functional/very_slow_controller_test.rb +18 -0
  160. data/test/test_app/test/test_helper.rb +28 -0
  161. data/test/test_ec2onrails.rb +11 -0
  162. data/test/test_helper.rb +2 -0
  163. metadata +278 -0
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/ruby
2
+
3
+ # This file is part of EC2 on Rails.
4
+ # http://rubyforge.org/projects/ec2onrails/
5
+ #
6
+ # Copyright 2007 Paul Dowman, http://pauldowman.com/
7
+ #
8
+ # EC2 on Rails is free software; you can redistribute it and/or modify
9
+ # it under the terms of the GNU General Public License as published by
10
+ # the Free Software Foundation; either version 2 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # EC2 on Rails is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
20
+
21
+ # This script archives a file to S3
22
+
23
+ require "rubygems"
24
+ require "optiflag"
25
+ require "#{File.dirname(__FILE__)}/../lib/s3_helper"
26
+ require "#{File.dirname(__FILE__)}/../lib/utils"
27
+
28
+ include FileUtils
29
+
30
+ module CommandLineArgs extend OptiFlagSet
31
+ optional_flag "bucket"
32
+ optional_flag "dir"
33
+ optional_flag "file"
34
+ and_process!
35
+ end
36
+
37
+ # include the hostname in the bucket name so test instances don't accidentally clobber real backups
38
+ bucket = ARGV.flags.bucket
39
+ dir = ARGV.flags.dir
40
+ file = ARGV.flags.file
41
+ exit unless File.exists?(file)
42
+
43
+ @s3 = Ec2onrails::S3Helper.new(bucket, dir)
44
+ @s3.store_file(file)
@@ -0,0 +1,159 @@
1
+ #!/usr/bin/ruby
2
+
3
+ # This file is part of EC2 on Rails.
4
+ # http://rubyforge.org/projects/ec2onrails/
5
+ #
6
+ # Copyright 2007 Paul Dowman, http://pauldowman.com/
7
+ #
8
+ # EC2 on Rails is free software; you can redistribute it and/or modify
9
+ # it under the terms of the GNU General Public License as published by
10
+ # the Free Software Foundation; either version 2 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # EC2 on Rails is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
20
+
21
+ #exit if the current application is not deployed, meaning there is nothing to back up!
22
+ exit unless File.exists?("/mnt/app/current")
23
+
24
+ require "rubygems"
25
+ require "optiflag"
26
+ require "fileutils"
27
+ require 'EC2'
28
+ require "#{File.dirname(__FILE__)}/../lib/mysql_helper"
29
+ require "#{File.dirname(__FILE__)}/../lib/s3_helper"
30
+ require "#{File.dirname(__FILE__)}/../lib/aws_helper"
31
+ require "#{File.dirname(__FILE__)}/../lib/roles_helper"
32
+
33
+ require "#{File.dirname(__FILE__)}/../lib/utils"
34
+
35
+ # Only run if this instance is the db_pimrary
36
+ # The original code would run on any instance that had /etc/init.d/mysql
37
+ # Which was pretty much all instances no matter what role
38
+ include Ec2onrails::RolesHelper
39
+ exit unless in_role?(:db_primary)
40
+
41
+
42
+ module CommandLineArgs extend OptiFlagSet
43
+ optional_flag "bucket"
44
+ optional_flag "dir"
45
+ optional_switch_flag "incremental"
46
+ optional_switch_flag "reset"
47
+ and_process!
48
+ end
49
+ @mysql = Ec2onrails::MysqlHelper.new
50
+
51
+
52
+ if File.exists?("/etc/mysql/conf.d/mysql-ec2-ebs.cnf")
53
+ # we have ebs enabled....
54
+
55
+ @aws = Ec2onrails::AwsHelper.new
56
+ vols = YAML::load(File.read("/etc/ec2onrails/ebs_info.yml"))
57
+ ec2 = EC2::Base.new( :access_key_id => @aws.aws_access_key, :secret_access_key => @aws.aws_secret_access_key )
58
+
59
+ #lets make sure we have space: AMAZON puts a 500 limit on the number of snapshots
60
+ snaps = ec2.describe_snapshots['DescribeSnapshotsResponse']['snapshotSet']['item'] rescue nil
61
+ if snaps && snaps.size > 450
62
+ # TODO:
63
+ # can we make this a bit smarter? With a limit of 500, that is difficult.
64
+ # possible ideas (and some commented out code below)
65
+ # * only apply cleanups to the volume_ids attached to this instance
66
+ # * keep the last week worth (at hrly snapshots), then daily for a month, then monthly
67
+ # * a sweeper task
68
+ #
69
+ # vol_ids = []
70
+ # vols.each_pair{|k,v| vol_ids << v['volume_id']}
71
+ # #lets only work on those that apply for these volumnes attached
72
+ # snaps = snaps.collect{|sn| vol_ids.index(sn['volumeId']) ? sn : nil}.compact
73
+ # # get them sorted
74
+ snaps = snaps.sort_by{|snapshot| snapshot['startTime']}.reverse
75
+ curr_batch = {}
76
+ remaining = []
77
+ snaps[200..-1].each do |sn|
78
+ next if sn.blank? || sn['status'] != 'completed'
79
+ today = Date.parse(sn['startTime']).to_s
80
+ if curr_batch[sn['volumeId']] != today
81
+ curr_batch[sn['volumeId']] = today
82
+ remaining << sn
83
+ else
84
+ ec2.delete_snapshot(:snapshot_id => sn['snapshotId'])
85
+ end
86
+ # next unless vol_ids.index(sn['volumeId'])
87
+ end
88
+ if remaining.size > 400
89
+ puts " WARNING: still contains #{remaining.size} snapshots; removing the oldest 100 to clean up space"
90
+ remaining[350..-1].each do |sn|
91
+ ec2.delete_snapshot(:snapshot_id => sn['snapshotId'])
92
+ end
93
+ end
94
+ else
95
+ puts "Could not retrieve snapshots: auto archiving cleanup will not occur" unless snaps
96
+ end
97
+
98
+ @mysql.execute do |conn|
99
+ begin
100
+ conn.query "FLUSH TABLES WITH READ LOCK;"
101
+
102
+ res = conn.query "SHOW MASTER STATUS"
103
+ logfile, position = res.fetch_row[0..1]
104
+ # puts "Snapshot occuring at: log: #{logfile}, #{position}"
105
+ vols.each_pair do |mount, ebs_info|
106
+ begin
107
+ `sudo xfs_freeze -f #{mount}`
108
+ output = ec2.create_snapshot(:volume_id => ebs_info['volume_id'])
109
+ snap_id = output['CreateSnapshotResponse']['snapshotId'] rescue nil
110
+ snap_id ||= output['snapshotId'] rescue nil #this is for the old version of the amazon-ec2
111
+ if snap_id.nil? || snap_id.empty?
112
+ puts "Snapshot for #{ebs_info['volume_id']} FAILED"
113
+ exit
114
+ end
115
+ vol_id = ebs_info['volume_id']
116
+ ensure
117
+ `sudo xfs_freeze -u #{mount}`
118
+ end
119
+ end
120
+ ensure
121
+ conn.query <<-SQL
122
+ UNLOCK TABLES;
123
+ SQL
124
+ end
125
+ end
126
+ else
127
+ #not persisted, so lets push the binary log files to s3
128
+ # include the hostname in the bucket name so test instances don't accidentally clobber real backups
129
+ bucket = ARGV.flags.bucket
130
+ dir = ARGV.flags.dir || "database"
131
+ @s3 = Ec2onrails::S3Helper.new(bucket, dir)
132
+ @temp_dir = "/mnt/tmp/ec2onrails-backup-#{@s3.bucket}-#{dir.gsub(/\//, "-")}"
133
+ if File.exists?(@temp_dir)
134
+ puts "Temp dir exists (#{@temp_dir}), aborting. Is another backup process running?"
135
+ exit
136
+ end
137
+
138
+ begin
139
+ FileUtils.mkdir_p @temp_dir
140
+ if ARGV.flags.incremental
141
+ # Incremental backup
142
+ @mysql.execute_sql "flush logs"
143
+ logs = Dir.glob("/mnt/log/mysql/mysql-bin.[0-9]*").sort
144
+ logs_to_archive = logs[0..-2] # all logs except the last
145
+ logs_to_archive.each {|log| @s3.store_file log}
146
+ @mysql.execute_sql "purge master logs to '#{File.basename(logs[-1])}'"
147
+ else
148
+ # Full backup
149
+ file = "#{@temp_dir}/dump.sql.gz"
150
+ @mysql.dump(file, ARGV.flags.reset)
151
+ @s3.store_file file
152
+ @s3.delete_files("mysql-bin")
153
+ end
154
+ ensure
155
+ FileUtils.rm_rf(@temp_dir)
156
+ end
157
+
158
+ end
159
+
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/ruby
2
+
3
+ # This file is part of EC2 on Rails.
4
+ # http://rubyforge.org/projects/ec2onrails/
5
+ #
6
+ # Copyright 2007 Paul Dowman, http://pauldowman.com/
7
+ #
8
+ # EC2 on Rails is free software; you can redistribute it and/or modify
9
+ # it under the terms of the GNU General Public License as published by
10
+ # the Free Software Foundation; either version 2 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # EC2 on Rails is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
20
+ #
21
+ #
22
+ # This file helps in setting up and retrieving ec2 meta-data
23
+ # If a key is passed in, it will only retrieve that key
24
+ # if not, then it will return all of the meta-data formatted in yaml
25
+ #
26
+
27
+ require "rubygems"
28
+ require "optiflag"
29
+ require 'yaml'
30
+ require "#{File.dirname(__FILE__)}/../lib/roles_helper"
31
+ include Ec2onrails::RolesHelper
32
+
33
+ CURL_OPTS = "-s -S -f -L --retry 7"
34
+ META_URL = "http://169.254.169.254/latest/meta-data"
35
+
36
+
37
+ module CommandLineArgs extend OptiFlagSet
38
+ optional_flag "key" do
39
+ description "The ec2 meta-data value you would like to look up"
40
+ end
41
+ and_process!
42
+ end
43
+
44
+
45
+ def process_files(files, root_url="")
46
+ output = {}
47
+ files.split.each do |file|
48
+ if file =~ /\/$/
49
+ output.merge! process_files(`curl #{CURL_OPTS} #{META_URL}/#{file}`, "#{file}")
50
+ else
51
+ if file =~ /=/
52
+ key, data = file.split('=')
53
+ else
54
+ url = "#{META_URL}/#{root_url}/#{file}"
55
+ data = `curl #{CURL_OPTS} #{url}`
56
+ raise "Failed to fetch entry #{file}: code #{$?.exitstatus} -- #{data}" unless $?.success?
57
+ end
58
+ if root_url.nil? || root_url.strip.length == 0
59
+ output[file] = data
60
+ else
61
+ output[root_url] ||= {}
62
+ output[root_url][file] = data
63
+ end
64
+ end
65
+ end
66
+ output
67
+ end
68
+
69
+
70
+
71
+ if ARGV.flags.key
72
+ puts get_metadata(ARGV.flags.key)
73
+ else
74
+
75
+ files = `curl #{CURL_OPTS} #{META_URL}/`
76
+ raise "Failed to fetch directory: code #{$?.exitstatus} -- #{files}" unless $?.success?
77
+ val = process_files(files)
78
+ puts val.to_yaml
79
+ end
80
+
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/ruby
2
+
3
+ # This file is part of EC2 on Rails.
4
+ # http://rubyforge.org/projects/ec2onrails/
5
+ #
6
+ # Copyright 2007 Paul Dowman, http://pauldowman.com/
7
+ #
8
+ # EC2 on Rails is free software; you can redistribute it and/or modify
9
+ # it under the terms of the GNU General Public License as published by
10
+ # the Free Software Foundation; either version 2 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # EC2 on Rails is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
20
+ #
21
+ # This script allows you to ONLY execute within a shell IF the role of
22
+ # the server (as defined by capistrano) matches the role specified.
23
+ # ex.
24
+ # * exec_runner -role :db -exec 'echo "hello world"'
25
+ # # will only run if the server is in the db role
26
+ # * exec_runner -role :app -exec /some/app/specific/script/to/call
27
+ #
28
+
29
+ require "rubygems"
30
+ require "optiflag"
31
+
32
+ require "fileutils"
33
+ require "#{File.dirname(__FILE__)}/../lib/roles_helper"
34
+ require "#{File.dirname(__FILE__)}/../lib/utils"
35
+
36
+ include Ec2onrails::RolesHelper
37
+
38
+ module CommandLineArgs extend OptiFlagSet
39
+ optional_flag "role" do
40
+ description "The role of this server, as defined by capistrano. ex. 'db', or 'app' If not used, will be applied to all roles"
41
+ end
42
+
43
+ optional_flag "only_env" do
44
+ description "Only apply the script if it is running within this environment"
45
+ end
46
+
47
+ flag "exec" do
48
+ description "what to run if the role of the server matches the -role passed in"
49
+ end
50
+
51
+ and_process!
52
+ end
53
+
54
+ #strip out the ':', in case the user enters ':db', or ':app'
55
+ role = ARGV.flags.role.sub(/^:/, '').to_sym
56
+ if ARGV.flags.role && !in_role?(role)
57
+ puts "This script is not being run because the server is not running under the #{role} role"
58
+ exit
59
+ end
60
+
61
+ curr_env = Ec2onrails::Utils.rails_env
62
+ if ARGV.flags.only_env && ARGV.flags.only_env.strip != curr_env
63
+ puts "This script is not being run because the server is not running under the #{curr_env} environment"
64
+ exit
65
+ end
66
+
67
+
68
+ # set the default to the current directory; makes it easier to
69
+ Dir.chdir('/mnt/app/current')
70
+
71
+ ENV['RAILS_ENV'] = curr_env
72
+ run ARGV.flags.exec
73
+
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/ruby
2
+
3
+ # This file is part of EC2 on Rails.
4
+ # http://rubyforge.org/projects/ec2onrails/
5
+ #
6
+ # Copyright 2007 Paul Dowman, http://pauldowman.com/
7
+ #
8
+ # EC2 on Rails is free software; you can redistribute it and/or modify
9
+ # it under the terms of the GNU General Public License as published by
10
+ # the Free Software Foundation; either version 2 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # EC2 on Rails is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
20
+
21
+ require "#{File.dirname(__FILE__)}/../lib/roles_helper"
22
+ include Ec2onrails::RolesHelper
23
+
24
+
25
+ #TODO:
26
+ # if not in a role, we want to make sure that the service is stopped....
27
+ # it gets a little tricky with the web_proxy, which is not enabled if it is
28
+ # not already in a web role. Leave as is, as all it does is throw an error
29
+ # until GOD is in the picture, at which case it should be easy to enable
30
+ # and let it handle it instead of the init.d script....
31
+
32
+ # memcache role:
33
+ if in_role?(:memcache)
34
+ # increase memory size, etc if no other roles exist?
35
+ start(:memcache, "memcached")
36
+ else
37
+ stop(:memcache, "memcached")
38
+ end
39
+
40
+ # db primary role:
41
+ if in_role?(:db_primary)
42
+ start(:db, "mysql", "mysqld")
43
+ else
44
+ stop(:db, "mysql", "mysqld")
45
+ end
46
+
47
+ # web role:
48
+ if in_role?(:web)
49
+ #we symlink the web_proxy we are using....
50
+ start(:web, "web_proxy", 'nginx apache')
51
+ # sleep(5)
52
+ # run("/etc/init.d/web_proxy reload")
53
+ else
54
+ #not started...
55
+ stop(:web, "web_proxy", 'nginx apache')
56
+ end
57
+
58
+
59
+ # app role:
60
+ if in_role?(:app)
61
+ start(:app, "mongrel", "mongrel_rails")
62
+ else
63
+ stop(:app, "mongrel", "mongrel_rails")
64
+ end
@@ -0,0 +1,348 @@
1
+ #!/usr/bin/ruby
2
+
3
+
4
+ ## Notes about optimizations are inline below.
5
+ # based upon recommendations listed here:
6
+ # http://www.mysqlperformanceblog.com/2006/09/29/what-to-tune-in-mysql-server-after-installation/
7
+ #
8
+ # For best effect, call this script after every other service has been started.
9
+ #
10
+
11
+ require 'fileutils'
12
+ require "/usr/local/ec2onrails/lib/roles_helper"
13
+ require "/usr/local/ec2onrails/lib/vendor/ini"
14
+ require "/usr/local/ec2onrails/lib/mysql_helper"
15
+ require "/usr/local/ec2onrails/lib/utils"
16
+
17
+ include Ec2onrails::RolesHelper
18
+
19
+ DEFAULT_CONFIG_LOC = "/etc/mysql/my.cnf"
20
+
21
+ exit unless in_role?(:db_primary)
22
+
23
+ local_roles = roles.collect{|r| r.first if r.last.include?('127.0.0.1')}.compact
24
+ only_db_role = local_roles.size < 2
25
+
26
+
27
+ #lets make a copy of the original...but do not overwrite if it already exists!
28
+ FileUtils.copy(DEFAULT_CONFIG_LOC, "#{DEFAULT_CONFIG_LOC}.pre_optimized") unless File.exists?("#{DEFAULT_CONFIG_LOC}.pre_optimized")
29
+
30
+
31
+ ##### ************************** COMPUTE METRICS ************************** #####
32
+ # define metrics used to modify my.cnf. These metrics are used as guidelines
33
+ # and are not meant to be exact measurements or limits.
34
+ #
35
+ # * how much free memory is available?
36
+ #
37
+ # * how many other roles is the db server taking on? Based upon the type
38
+ # and number of other roles, change the amout of memory we should dedicate
39
+ # to the db. This ratio is not the absolute ratio but is used as a guideline
40
+ #
41
+ # * how many cores does the slice have. Then, based upon the Engineyard
42
+ # recommendation that upto 4 mongrels generally take up one core, compute
43
+ # the number of estimated available cores
44
+ #
45
+ # * how many max connections are allowed, and how many database tables exist
46
+ ##### ************************ END COMPUTE METRICS ************************ #####
47
+
48
+ #default is one core
49
+ num_cores = `cat /proc/cpuinfo`.find_all{|o| o =~ /^\s*processor\s+/}.size rescue 1
50
+ #if also running app, just search for ruby because that will
51
+ #include mongrel but also daemons and other scripts
52
+ num_ruby_instances = local_roles.include?(:app) ? `ps ax | grep ruby | grep -v 'grep ruby'`.split("\n").size : 0
53
+ avail_cores = num_cores - num_ruby_instances/4
54
+ avail_cores = 0 if avail_cores.nil? || avail_cores < 0
55
+
56
+
57
+ mem_opt = nil
58
+ mem_opt ||= 0.15 if local_roles.include?(:app) && local_roles.include?(:web) && local_roles.include?(:memcache)
59
+ mem_opt ||= 0.25 if local_roles.include?(:app) && local_roles.include?(:memcache)
60
+ mem_opt ||= 0.35 if local_roles.include?(:app) || local_roles.include?(:memcache)
61
+ mem_opt ||= 0.50 if local_roles.include?(:web)
62
+ mem_opt ||= 0.70 #if only db, lets use a 70% ratio
63
+
64
+ orig_free_mem = (`free -m` =~ /buffers\/cache:\s+\d+\s+(\d+)/; $1).to_i rescue 1024
65
+
66
+ #TODO: take into account memcached settings if memcached is running on this server
67
+ ruby_mem_reserved = num_ruby_instances * 180
68
+ if orig_free_mem > 4098 && (orig_free_mem * mem_opt + ruby_mem_reserved) * 1.25 < orig_free_mem
69
+ mem_opt *= 1.5
70
+ end
71
+ free_mem = (orig_free_mem * mem_opt).to_i
72
+
73
+ @mysql = Ec2onrails::MysqlHelper.new
74
+
75
+ # result = sudo("god start db")
76
+ result = sudo("/etc/init.d/mysql start")
77
+ if result
78
+ puts <<-MSG
79
+ ****** WOOPS ******
80
+ mysql was not successfully started up.
81
+ Not optimizing mysql config file
82
+ MSG
83
+ exit 1
84
+ end
85
+ # sleep(10) #make sure the db has some time to start up!
86
+
87
+ num_connections = 100
88
+ mysql_cmd = %{mysql -u #{@mysql.user} -e "select @@max_connections;"}
89
+ mysql_cmd += " -p'#{@mysql.password}' " unless @mysql.password.nil?
90
+ if `#{mysql_cmd}` =~ /(\d+)/imx
91
+ num_connections = $1.to_i
92
+ end
93
+
94
+ num_tables = 100
95
+ mysql_cmd = %{mysql -u #{@mysql.user} -e "SELECT count(*) TABLES from information_schema.tables where TABLE_SCHEMA = '#{@mysql.database}';"}
96
+ mysql_cmd += " -p'#{@mysql.password}' " unless @mysql.password.nil?
97
+ if `#{mysql_cmd}` =~ /(\d+)/imx
98
+ num_tables = $1.to_i
99
+ end
100
+
101
+ # the default my.cnf is already different than the default example files... it has already
102
+ # been modified for ec2. So lets use that one rather than starting from scratch again.
103
+ # default_mysql_config = if free_mem > 2048
104
+ # "my-huge.cnf"
105
+ # elsif free_mem > 768
106
+ # "my-large.cnf"
107
+ # else
108
+ # "my-medium.cnf"
109
+ # end
110
+
111
+ puts <<-MSG
112
+
113
+ Optimizing mysql based off of these stats:
114
+ * sharing server with these roles : #{local_roles.inspect}
115
+ * num cores (est avail cores) : #{num_cores} (#{avail_cores})
116
+ * avail mem (mem for db) : #{orig_free_mem} (#{free_mem})
117
+ * num database tables : #{num_tables}
118
+ * max num conns : #{num_connections}
119
+ MSG
120
+
121
+
122
+ #lets figure out which default config file to start with:
123
+ # new_config = "/etc/mysql/#{default_mysql_config}"
124
+
125
+
126
+ ##### ******************** Modifying MYSQL config file ******************** #####
127
+ configs = Ini.load(DEFAULT_CONFIG_LOC, :comment => '#')
128
+ configs['mysqld']['key_buffer_size'] ||= configs['mysqld']['key_buffer']
129
+
130
+ modifying_keys = %w(thread_concurrency thread_cache_size query_cache_size table_cache
131
+ key_buffer_size innodb_flush_log_at_trx_commit
132
+ innodb_buffer_pool_size innodb_additional_mem_pool_size
133
+ innodb_log_buffer_size innodb_log_file_size)
134
+
135
+ original_values = modifying_keys.inject([]){|all, key| all << [key, configs['mysqld'][key.to_s]]}
136
+
137
+
138
+ ##### thread_concurrency: only turn on thread concurrency if
139
+ # there are some spare 'cores' available
140
+ if avail_cores < 2
141
+ configs['mysqld'].delete('thread_concurrency')
142
+ elsif
143
+ # Try number of CPU's*2 for thread_concurrency
144
+ configs['mysqld']['thread_concurrency'] = (avail_cores) * 2
145
+ end
146
+
147
+
148
+ #### thread_cache: we don't want threads being created on a regular basis, but
149
+ # if we don't have available cores it doesn't make much sense to cache
150
+ # to many threads
151
+ configs['mysqld']['thread_cache_size'] = if avail_cores > 6
152
+ 24
153
+ elsif avail_cores > 3
154
+ 16
155
+ else
156
+ 8
157
+ end
158
+
159
+
160
+ #### query_cache_size: Important for read-heavy db loads, but it gets expensive
161
+ # to maintain if it gets too large.
162
+ configs['mysqld']['query_cache_size'] = if free_mem > 4096
163
+ only_db_role ? 512 : 384
164
+ elsif free_mem > 2048
165
+ only_db_role ? 256 : 128
166
+ elsif free_mem > 1024
167
+ only_db_role ? 128 : 64
168
+ else
169
+ 64
170
+ end
171
+ configs['mysqld']['query_cache_size'] = "#{configs['mysqld']['query_cache_size']}M"
172
+
173
+
174
+ #### table_cache: Opening tables can be expensive, so this cache helps mitigate that.
175
+ # Each connection needs its own entry in the table cache, but this is less important for innodb
176
+ # heavy database (which most rails apps are).
177
+ # based upont he observation that a cache size of 1024 is a good size for a db with a few hundred tables
178
+ configs['mysqld']['table_cache'] = (num_connections * num_tables)/10
179
+
180
+
181
+ #### key_buffer_size: Does not need to be very large because most rails
182
+ # applications do not use MyISAM, or use if very little (usually to store
183
+ # db-based sessions). Keep space available for temp tables and other
184
+ # little mysql needs
185
+ configs['mysqld']['key_buffer_size'] = if free_mem > 4096
186
+ only_db_role ? 256 : 128
187
+ elsif free_mem > 2048
188
+ only_db_role ? 128 : 64
189
+ elsif free_mem > 1024
190
+ only_db_role ? 64 : 32
191
+ else
192
+ 16
193
+ end
194
+ configs['mysqld']['key_buffer_size'] = "#{configs['mysqld']['key_buffer_size']}M"
195
+ #can use either key_buffer or key_buffer_size.
196
+ #Since we are using key_buffer_size, lets remove the other one
197
+ configs['mysqld'].delete('key_buffer')
198
+
199
+
200
+ #### innodb_flush_log_at_trx_commit: this makes INNODB *much* faster, but
201
+ # it is not 100% ACID compliant. Instead of flushing to disk for every
202
+ # commit, this flushes to the OS file cache. That means that if MySQL
203
+ # crashes, the data will be written, but if the OS crashes, 1-2
204
+ # seconds of information could be lost
205
+ configs['mysqld']['innodb_flush_log_at_trx_commit'] = 2
206
+
207
+
208
+ # * innodb_buffer_pool_size upto 70% of memory..but if sharing and small data sizes, use less (50%?)
209
+
210
+ #### innodb_buffer_pool_size: this is where we should put most of our free memory, since
211
+ # rails apps are heavy users of innodb. Need to be careful NOT to specify TOO much memory
212
+ configs['mysqld']['innodb_buffer_pool_size'] = if free_mem > 4096
213
+ free_mem - 512
214
+ elsif free_mem > 2048
215
+ free_mem - 384
216
+ elsif free_mem > 1024
217
+ free_mem - 256
218
+ elsif free_mem > 512
219
+ free_mem - 192
220
+ elsif free_mem > 256
221
+ free_mem - 128
222
+ else
223
+ free_mem - 98
224
+ end
225
+ configs['mysqld']['innodb_buffer_pool_size'] = "#{configs['mysqld']['innodb_buffer_pool_size']}M"
226
+
227
+
228
+ #### innodb_additional_mem_pool_size: This is not really needed as most OS's do a good job
229
+ # of allocating memory.
230
+ configs['mysqld']['innodb_additional_mem_pool_size'] ||= '16M'
231
+
232
+
233
+ #### innodb_log_buffer_size: This is flushed every second anyway, so 8-16M is generally ok
234
+ configs['mysqld']['innodb_log_buffer_size'] ||= '12M'
235
+
236
+
237
+ #### innodb_log_file_size: Help with heavy writes,
238
+ # BUT if it is too large recovery times can be a lot longer
239
+ configs['mysqld']['innodb_log_file_size'] = if free_mem > 4096
240
+ 512
241
+ elsif free_mem > 2048
242
+ 256
243
+ elsif free_mem > 1024
244
+ 128
245
+ else
246
+ 64
247
+ end
248
+ configs['mysqld']['innodb_log_file_size'] = "#{configs['mysqld']['innodb_log_file_size']}M"
249
+ ##### ****************** END Modifying MYSQL config file ****************** #####
250
+
251
+
252
+
253
+ new_values = modifying_keys.inject([]){|all, key| all << [key, configs['mysqld'][key.to_s]]}
254
+ msg = "\nModified these mysqld parameters:\n"
255
+ msg += <<-MSG
256
+ mysqld key: new value (original value)
257
+ -----------------------------------------------
258
+ MSG
259
+ new_values.each_with_index do |v, i|
260
+ orig_value = original_values[i].last.nil? ? 'not set' : original_values[i].last
261
+ msg += <<-MSG
262
+ * #{v.first}: #{v.last} (#{orig_value})
263
+ MSG
264
+ end
265
+ puts msg
266
+
267
+ config_file_loc = DEFAULT_CONFIG_LOC
268
+ #We need to shut down mysql BEFORE we move the new configs over...
269
+ puts "\nCleanly stopping mysql to replace its config file."
270
+
271
+ #make sure the mysql has time to startup before we shut it down again
272
+ #TODO: can we improve this?
273
+ sleep(5)
274
+
275
+ # result = sudo("god stop db")
276
+ # not using god because it doesn't wait for the script to finish
277
+ # before returning....
278
+ # result = sudo("god stop db")
279
+ result = sudo("/etc/init.d/mysql stop")
280
+ clean_stop = true
281
+ if result
282
+ config_file_loc += ".optimized"
283
+ clean_stop = false
284
+ puts <<-MSG
285
+ ****** WOOPS ******
286
+ mysql was not successfully shut down, so we dare not
287
+ update the config file (it can cause problems with the
288
+ ib_logfile cache). We have saved the new config file at
289
+ #{config_file_loc}
290
+ in case you still want to use it in place of
291
+ #{DEFAULT_CONFIG_LOC}
292
+ MSG
293
+ else
294
+ puts <<-MSG
295
+ cleanly shutdown mysql. Replacing config file:
296
+ #{config_file_loc}
297
+ The original config file can be found here:
298
+ #{DEFAULT_CONFIG_LOC}.pre_optimized
299
+
300
+ Starting mysql...
301
+ MSG
302
+ end
303
+
304
+ configs.save(config_file_loc)
305
+
306
+ config_file = File.read(DEFAULT_CONFIG_LOC)
307
+ File.open(DEFAULT_CONFIG_LOC, 'w') do |file|
308
+ file << <<-MSG
309
+ # This file is generated by '#{__FILE__}'
310
+ # Based upon the default '#{DEFAULT_CONFIG_LOC}'
311
+ # which is now saved at '#{DEFAULT_CONFIG_LOC}.pre_optimized'
312
+
313
+ # See file for comments:
314
+ # #{__FILE__}
315
+ #
316
+ #
317
+
318
+ MSG
319
+ file << config_file
320
+ end
321
+
322
+ if clean_stop
323
+ #before we can start, we need to move the old cache files...
324
+ old_logs = []
325
+ Dir.glob("/mnt/mysql_data/ib_logfile*").each do |f|
326
+ FileUtils.mv(f, f + "_old")
327
+ old_logs << "#{f}_old"
328
+ end
329
+ puts <<-MSG
330
+ Moving the old mysql ib logfiles because we might have changed the
331
+ default logfile cache size. If mysql startups up successfully,
332
+ these files can be removed:
333
+ #{old_logs.join("\n ")}
334
+ MSG
335
+
336
+
337
+ # result = sudo("god start db")
338
+ #using init.d instead of god, because god doesn't wait for the command to finish...
339
+ #it fires it off and then checks it in x seconds
340
+ result = sudo("/etc/init.d/mysql start")
341
+ if result
342
+ puts <<-MSG
343
+ ****** WOOPS ******
344
+ mysql was not successfully started up.
345
+ Check syslog, as the culprit will be logged there.
346
+ MSG
347
+ end
348
+ end