mdh-ec2onrails 0.9.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +274 -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