Your SlideShare is downloading. ×
0
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
A Scalable Rails App Deployed in 60 Seconds
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

A Scalable Rails App Deployed in 60 Seconds

4,128

Published on

I gave this talk at Lone Star Ruby Conference in Austin on 8/28/2010.

I gave this talk at Lone Star Ruby Conference in Austin on 8/28/2010.

Published in: Technology
0 Comments
6 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
4,128
On Slideshare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
81
Comments
0
Likes
6
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. A Scalable Rails app Deployed in 60 seconds 1
  • 2. Ben Scheirman • Director of Development at ChaiONE • Microsoft MVP, ASP Insider • Certified ScrumMaster • Former .NET Ninja, now Rails/iPhone aficionado 2
  • 3. Ben Scheirman • ...not affiliated with Heroku 3
  • 4. So you have a fantastic app? 4
  • 5. 5
  • 6. 6
  • 7. 7
  • 8. 7
  • 9. 7
  • 10. 7
  • 11. 7
  • 12. 7
  • 13. 8
  • 14. 8
  • 15. 9
  • 16. 10
  • 17. 11
  • 18. x 1,000? 11
  • 19. 12
  • 20. x 10,000? 12
  • 21. writes reads 13
  • 22. writes reads x 1,000,000? 13
  • 23. 14
  • 24. On-Demand 15
  • 25. # Users by time of day 400 300 200 100 0 12 AM 3AM 6AM 9AM 12PM 3PM 6PM 9PM 12AM 16
  • 26. # Users by time of day 400 300 200 100 0 12 AM 3AM 6AM 9AM 12PM 3PM 6PM 9PM 12AM 16
  • 27. # Users by time of day 400 300 200 100 0 12 AM 3AM 6AM 9AM 12PM 3PM 6PM 9PM 12AM Only need massive scale from 9am - 9pm 16
  • 28. Expected Users For PR Campaign 900 675 450 225 0 Mar 1 Mar 15 Apr 1 Apr 15 May 1 May 15 Jun 1 Jun 2 17
  • 29. Expected Users For PR Campaign 900 675 450 225 0 Mar 1 Mar 15 Apr 1 Apr 15 May 1 May 15 Jun 1 Jun 2 17
  • 30. SCALE 18
  • 31. 19
  • 32. Awesome Shredder Art by Dan Barret 20
  • 33. gem install heroku Awesome Shredder Art by Dan Barret 20
  • 34. • Platform as a service for Rack-based apps • Scale easily • Great workflow • Free to start / Friendly Pricing • Complete command-line API 21
  • 35. Deploy a rails app in 5 steps git init git add . git commit -m “My First Commit” heroku create git push heroku master Creating morning-summer-18..... done Created http://morning-summer-18.heroku.com/ | git@heroku.com:morning-summer-18.git 22
  • 36. Deploy a rails app in 5 steps git init git add . git commit -m “My First Commit” heroku create git push heroku master Creating morning-summer-18..... done Created http://morning-summer-18.heroku.com/ | git@heroku.com:morning-summer-18.git LET THAT SOAK IN... 22
  • 37. Using git for deployment is f***ing GENIUS 23
  • 38. Technology • Amazon Web Services • Caching w/ Varnish • Debian Linux • Erlang Routing Mesh • Nginx • PostgreSQL 24
  • 39. 25
  • 40. How Heroku Scales 26
  • 41. How Heroku Scales or WTF is a Dyno? 26
  • 42. How Heroku Scales or WTF is a Dyno? 26
  • 43. How Heroku Scales or WTF is a Dyno? 26
  • 44. How Heroku Scales 27
  • 45. How Heroku Scales 27
  • 46. How Heroku Scales 27
  • 47. When to add dynos • Increasing response times • “Backlog Too Deep” Error 28
  • 48. Always Measure • New Relic RPM • Apache Bench 29
  • 49. Heroku Tips • Multiple Heroku apps for Test, Staging, Production • Learn the awesome-ness of Taps • Heroku logs • Heroku Console • Useful addons: New Relic, Exceptional 30
  • 50. Backups 31
  • 51. Coping with the Free Single Bundle task :backup do bundle_name = "my-backup" heroku_app = ENV['app'] def wait_for_response_text(text) heroku_app = ENV['app'] #lame, I know begin puts "." bundles = `heroku bundles --app #{heroku_app}` end while bundles.match(text).nil? end #check for existing bundle bundles = `heroku bundles --app #{heroku_app}` unless bundles.match /has no bundles/ `heroku bundles:destroy #{bundle_name} --app #{heroku_app}` wait_for_response_text 'has no bundles' end puts "Capture bundle #{bundle_name}..." `heroku bundles:capture #{bundle_name} --app #{heroku_app}` wait_for_response_text "complete" puts "Downloading bundle..." `heroku bundles:download #{bundle_name} --app #{heroku_app}` timestamp = `date -u '+%Y-%m-%d-%H-%M'`.chomp new_path = "backups/#{heroku_app}-#{timestamp}.tar.gz" mkdir "backups" unless File.exists? "backups" `mv #{heroku_app}.tar.gz #{new_path}` puts "Bundle saved to #{new_path}" end end 32
  • 52. Coping with the Free Single Bundle task :backup do bundle_name = "my-backup" heroku_app = ENV['app'] def wait_for_response_text(text) heroku_app = ENV['app'] #lame, I know begin puts "." bundles = `heroku bundles --app #{heroku_app}` end while bundles.match(text).nil? end #check for existing bundle bundles = `heroku bundles --app #{heroku_app}` unless bundles.match /has no bundles/ `heroku bundles:destroy #{bundle_name} --app #{heroku_app}` wait_for_response_text 'has no bundles' end puts "Capture bundle #{bundle_name}..." `heroku bundles:capture #{bundle_name} --app #{heroku_app}` wait_for_response_text "complete" puts "Downloading bundle..." `heroku bundles:download #{bundle_name} --app #{heroku_app}` timestamp = `date -u '+%Y-%m-%d-%H-%M'`.chomp new_path = "backups/#{heroku_app}-#{timestamp}.tar.gz" mkdir "backups" unless File.exists? "backups" `mv #{heroku_app}.tar.gz #{new_path}` puts "Bundle saved to #{new_path}" end end 33
  • 53. Coping with the Free Single Bundle task :backup do bundle_name = "my-backup" heroku_app = ENV['app'] def wait_for_response_text(text) heroku_app = ENV['app'] #lame, I know begin puts "." bundles = `heroku bundles --app #{heroku_app}` end while bundles.match(text).nil? end #check for existing bundle bundles = `heroku bundles --app #{heroku_app}` unless bundles.match /has no bundles/ `heroku bundles:destroy #{bundle_name} --app #{heroku_app}` wait_for_response_text 'has no bundles' end puts "Capture bundle #{bundle_name}..." `heroku bundles:capture #{bundle_name} --app #{heroku_app}` wait_for_response_text "complete" puts "Downloading bundle..." `heroku bundles:download #{bundle_name} --app #{heroku_app}` timestamp = `date -u '+%Y-%m-%d-%H-%M'`.chomp new_path = "backups/#{heroku_app}-#{timestamp}.tar.gz" mkdir "backups" unless File.exists? "backups" `mv #{heroku_app}.tar.gz #{new_path}` puts "Bundle saved to #{new_path}" end end 34
  • 54. Coping with the Free Single Bundle task :backup do bundle_name = "my-backup" heroku_app = ENV['app'] def wait_for_response_text(text) heroku_app = ENV['app'] #lame, I know begin puts "." bundles = `heroku bundles --app #{heroku_app}` end while bundles.match(text).nil? end #check for existing bundle bundles = `heroku bundles --app #{heroku_app}` unless bundles.match /has no bundles/ `heroku bundles:destroy #{bundle_name} --app #{heroku_app}` wait_for_response_text 'has no bundles' end puts "Capture bundle #{bundle_name}..." `heroku bundles:capture #{bundle_name} --app #{heroku_app}` wait_for_response_text "complete" puts "Downloading bundle..." `heroku bundles:download #{bundle_name} --app #{heroku_app}` timestamp = `date -u '+%Y-%m-%d-%H-%M'`.chomp new_path = "backups/#{heroku_app}-#{timestamp}.tar.gz" mkdir "backups" unless File.exists? "backups" `mv #{heroku_app}.tar.gz #{new_path}` puts "Bundle saved to #{new_path}" end end 35
  • 55. Coping with the Free Single Bundle task :backup do bundle_name = "my-backup" heroku_app = ENV['app'] def wait_for_response_text(text) heroku_app = ENV['app'] #lame, I know begin puts "." bundles = `heroku bundles --app #{heroku_app}` end while bundles.match(text).nil? end #check for existing bundle bundles = `heroku bundles --app #{heroku_app}` unless bundles.match /has no bundles/ `heroku bundles:destroy #{bundle_name} --app #{heroku_app}` wait_for_response_text 'has no bundles' end puts "Capture bundle #{bundle_name}..." `heroku bundles:capture #{bundle_name} --app #{heroku_app}` wait_for_response_text "complete" puts "Downloading bundle..." `heroku bundles:download #{bundle_name} --app #{heroku_app}` timestamp = `date -u '+%Y-%m-%d-%H-%M'`.chomp new_path = "backups/#{heroku_app}-#{timestamp}.tar.gz" mkdir "backups" unless File.exists? "backups" `mv #{heroku_app}.tar.gz #{new_path}` puts "Bundle saved to #{new_path}" end end 36
  • 56. Coping with the Free Single Bundle task :backup do bundle_name = "my-backup" heroku_app = ENV['app'] def wait_for_response_text(text) heroku_app = ENV['app'] #lame, I know begin puts "." bundles = `heroku bundles --app #{heroku_app}` end while bundles.match(text).nil? end #check for existing bundle bundles = `heroku bundles --app #{heroku_app}` unless bundles.match /has no bundles/ `heroku bundles:destroy #{bundle_name} --app #{heroku_app}` wait_for_response_text 'has no bundles' end puts "Capture bundle #{bundle_name}..." `heroku bundles:capture #{bundle_name} --app #{heroku_app}` wait_for_response_text "complete" puts "Downloading bundle..." `heroku bundles:download #{bundle_name} --app #{heroku_app}` timestamp = `date -u '+%Y-%m-%d-%H-%M'`.chomp new_path = "backups/#{heroku_app}-#{timestamp}.tar.gz" mkdir "backups" unless File.exists? "backups" `mv #{heroku_app}.tar.gz #{new_path}` puts "Bundle saved to #{new_path}" end end 37
  • 57. Coping with the Free Single Bundle task :backup do bundle_name = "my-backup" heroku_app = ENV['app'] def wait_for_response_text(text) heroku_app = ENV['app'] #lame, I know begin puts "." bundles = `heroku bundles --app #{heroku_app}` end while bundles.match(text).nil? end #check for existing bundle bundles = `heroku bundles --app #{heroku_app}` unless bundles.match /has no bundles/ `heroku bundles:destroy #{bundle_name} --app #{heroku_app}` wait_for_response_text 'has no bundles' end puts "Capture bundle #{bundle_name}..." `heroku bundles:capture #{bundle_name} --app #{heroku_app}` wait_for_response_text "complete" puts "Downloading bundle..." `heroku bundles:download #{bundle_name} --app #{heroku_app}` timestamp = `date -u '+%Y-%m-%d-%H-%M'`.chomp new_path = "backups/#{heroku_app}-#{timestamp}.tar.gz" mkdir "backups" unless File.exists? "backups" `mv #{heroku_app}.tar.gz #{new_path}` puts "Bundle saved to #{new_path}" end end 38
  • 58. Coping with the Free Single Bundle task :backup do bundle_name = "my-backup" heroku_app = ENV['app'] def wait_for_response_text(text) heroku_app = ENV['app'] #lame, I know begin puts "." bundles = `heroku bundles --app #{heroku_app}` end while bundles.match(text).nil? end #check for existing bundle bundles = `heroku bundles --app #{heroku_app}` unless bundles.match /has no bundles/ `heroku bundles:destroy #{bundle_name} --app #{heroku_app}` wait_for_response_text 'has no bundles' end puts "Capture bundle #{bundle_name}..." `heroku bundles:capture #{bundle_name} --app #{heroku_app}` wait_for_response_text "complete" puts "Downloading bundle..." `heroku bundles:download #{bundle_name} --app #{heroku_app}` timestamp = `date -u '+%Y-%m-%d-%H-%M'`.chomp new_path = "backups/#{heroku_app}-#{timestamp}.tar.gz" mkdir "backups" unless File.exists? "backups" `mv #{heroku_app}.tar.gz #{new_path}` puts "Bundle saved to #{new_path}" end end 39
  • 59. One-button Deployments task :deploy do heroku_app = ENV['app'] heroku_config = Hash.new Rake::Task["heroku:backup"].invoke puts "Turning maintenance on..." puts "** " + `heroku maintenance:on --app #{heroku_app}` sleep 15 #maintenance mode causes your slug to be recompiled puts "Pushing changes to heroku..." puts "** " + `git push #{git_remotes[heroku_app]} master` puts "Migrating heroku databases..." puts "** " + `heroku rake db:migrate --app #{heroku_app}` puts "Setting up config variables..." heroku_config.each_pair do |k, v| puts "** " + `heroku config:add #{k.to_s.upcase}=#{v}` end puts "Turning maintenance mode off" puts "** " + `heroku maintenance:off --app #{heroku_app}` puts "DEPLOYMENT DONE!" end 40
  • 60. Delayed Jobs 41
  • 61. Delayed Jobs def send_bulk_emails BulkEmailSender.send_a_ton flash[:notice] => "Ok we sent them" end 41
  • 62. Delayed Jobs def send_bulk_emails BulkEmailSender.send_a_ton flash[:notice] => "Ok we sent them" end def send_bulk_emails BulkEmailSender.send_later(:send_a_ton) flash[:notice] => "Ok we sent them" end 41
  • 63. Delayed Jobs 42
  • 64. Delayed Jobs > gem install delayed_job > rails g delayed_job > rake db:migrate 42
  • 65. Delayed Jobs > gem install delayed_job > rails g delayed_job > rake db:migrate > heroku workers 1 simple-snow-68 now running 1 worker > heroku workers +1 simple-snow-68 now running 2 workers 42
  • 66. Delayed Jobs 43
  • 67. Delayed Jobs $0.05 per hour / worker (even when it's not processing Jobs) 43
  • 68. Delayed Jobs $0.05 per hour / worker (even when it's not processing Jobs) http://github.com/pedro/delayed_job/commit/ 09d7657e1fc7d25072e6c5e73ede20d6e1185eac http://bit.ly/9Qokff 43
  • 69. Delayed Jobs Pedro's Fork / auto-scale branch 44
  • 70. Delayed Jobs Pedro's Fork / auto-scale branch //lib/delayed/job.rb def after_create Manager.scale_up if self.class.auto_scale && Manager.qty == 0 end //lib/delayed/worker.rb Manager.scale_down if count.zero? && Job.auto_scale && Job.count == 0 44
  • 71. • Read-only file system • some gems don’t work • File uploads? • Temp Directory • SSL, memcached, Full-text-search expensive • Bound-by Amazon’s SLA 45
  • 72. contact = { ! :blog => http://flux88.com, ! ! :twitter => @subdigital, ! ! :email => ben@scheirman.com } 46
  • 73. 47

×