73
A Scalable Rails app Deployed in 60 seconds A love affair with Rails & Heroku. 1

A Scalable Rails App Deployed in 60 Seconds

Embed Size (px)

DESCRIPTION

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

Citation preview

Page 1: A Scalable Rails App Deployed in 60 Seconds

A Scalable Rails app Deployed in 60

secondsA love affair with Rails & Heroku.

1

Page 2: A Scalable Rails App Deployed in 60 Seconds

Ben Scheirman

• Director of Development at ChaiONE

• Microsoft MVP, ASP Insider

• Certified ScrumMaster

• Former .NET Ninja, now Rails/iPhone aficionado

2

Page 3: A Scalable Rails App Deployed in 60 Seconds

Ben Scheirman

• ...not affiliated with Heroku

3

Page 4: A Scalable Rails App Deployed in 60 Seconds

So you have a fantastic app?

4

Page 5: A Scalable Rails App Deployed in 60 Seconds

Now deploy it...

5

Page 6: A Scalable Rails App Deployed in 60 Seconds

Shared HostingVirtual Dedicated

Dedicated6

Page 7: A Scalable Rails App Deployed in 60 Seconds

Shared Hosting

7

Page 8: A Scalable Rails App Deployed in 60 Seconds

Shared Hosting

7

Page 9: A Scalable Rails App Deployed in 60 Seconds

Shared Hosting

7

Page 10: A Scalable Rails App Deployed in 60 Seconds

Shared Hosting

7

Page 11: A Scalable Rails App Deployed in 60 Seconds

Shared Hosting

7

Page 12: A Scalable Rails App Deployed in 60 Seconds

Shared Hosting

7

Page 13: A Scalable Rails App Deployed in 60 Seconds

Virtual Dedicated

8

Page 14: A Scalable Rails App Deployed in 60 Seconds

Virtual Dedicated

8

Page 15: A Scalable Rails App Deployed in 60 Seconds

Dedicated

9

Page 16: A Scalable Rails App Deployed in 60 Seconds

Scale

10

Page 17: A Scalable Rails App Deployed in 60 Seconds

11

Page 18: A Scalable Rails App Deployed in 60 Seconds

x 1,000?11

Page 19: A Scalable Rails App Deployed in 60 Seconds

12

Page 20: A Scalable Rails App Deployed in 60 Seconds

x 10,000?12

Page 21: A Scalable Rails App Deployed in 60 Seconds

writes

reads

13

Page 22: A Scalable Rails App Deployed in 60 Seconds

x 1,000,000?

writes

reads

13

Page 23: A Scalable Rails App Deployed in 60 Seconds

14

Page 24: A Scalable Rails App Deployed in 60 Seconds

ScaleOn-Demand

15

Page 25: A Scalable Rails App Deployed in 60 Seconds

0

100

200

300

400

12 AM 3AM 6AM 9AM 12PM 3PM 6PM 9PM 12AM

# Users by time of day

16

Page 26: A Scalable Rails App Deployed in 60 Seconds

0

100

200

300

400

12 AM 3AM 6AM 9AM 12PM 3PM 6PM 9PM 12AM

# Users by time of day

16

Page 27: A Scalable Rails App Deployed in 60 Seconds

0

100

200

300

400

12 AM 3AM 6AM 9AM 12PM 3PM 6PM 9PM 12AM

# Users by time of day

Only need massive scale from 9am - 9pm

16

Page 28: A Scalable Rails App Deployed in 60 Seconds

0

225

450

675

900

Mar 1 Mar 15 Apr 1 Apr 15 May 1 May 15 Jun 1 Jun 2

Expected Users For PR Campaign

17

Page 29: A Scalable Rails App Deployed in 60 Seconds

0

225

450

675

900

Mar 1 Mar 15 Apr 1 Apr 15 May 1 May 15 Jun 1 Jun 2

Expected Users For PR Campaign

17

Page 30: A Scalable Rails App Deployed in 60 Seconds

Wouldn’t this be magical?

SCALE

18

Page 31: A Scalable Rails App Deployed in 60 Seconds

Forget about servers19

Page 32: A Scalable Rails App Deployed in 60 Seconds

Awesome Shredder Art by Dan Barret

20

Page 33: A Scalable Rails App Deployed in 60 Seconds

Awesome Shredder Art by Dan Barret

gem install heroku

20

Page 34: A Scalable Rails App Deployed in 60 Seconds

• Platform as a service for Rack-based apps

• Scale easily

•Great workflow

• Free to start / Friendly Pricing

• Complete command-line API

21

Page 35: A Scalable Rails App Deployed in 60 Seconds

git initgit add .git commit -m “My First Commit”heroku creategit push heroku master

Deploy a rails app in 5 steps

Creating morning-summer-18..... doneCreated http://morning-summer-18.heroku.com/ | [email protected]:morning-summer-18.git

22

Page 36: A Scalable Rails App Deployed in 60 Seconds

git initgit add .git commit -m “My First Commit”heroku creategit push heroku master

Deploy a rails app in 5 steps

LET THAT SOAK IN...

Creating morning-summer-18..... doneCreated http://morning-summer-18.heroku.com/ | [email protected]:morning-summer-18.git

22

Page 37: A Scalable Rails App Deployed in 60 Seconds

Using git for deployment is f***ing GENIUS

23

Page 38: A Scalable Rails App Deployed in 60 Seconds

• Amazon Web Services

• Caching w/ Varnish

• Debian Linux

• Erlang Routing Mesh

• Nginx

• PostgreSQL

Technology

24

Page 39: A Scalable Rails App Deployed in 60 Seconds

25

Page 40: A Scalable Rails App Deployed in 60 Seconds

How Heroku Scales

26

Page 41: A Scalable Rails App Deployed in 60 Seconds

How Heroku Scalesor WTF is a Dyno?

26

Page 42: A Scalable Rails App Deployed in 60 Seconds

How Heroku Scalesor WTF is a Dyno?

26

Page 43: A Scalable Rails App Deployed in 60 Seconds

How Heroku Scalesor WTF is a Dyno?

26

Page 44: A Scalable Rails App Deployed in 60 Seconds

How Heroku Scales

27

Page 45: A Scalable Rails App Deployed in 60 Seconds

How Heroku Scales

27

Page 46: A Scalable Rails App Deployed in 60 Seconds

How Heroku Scales

27

Page 47: A Scalable Rails App Deployed in 60 Seconds

• Increasing response times

• “Backlog Too Deep” Error

When to add dynos

28

Page 48: A Scalable Rails App Deployed in 60 Seconds

• New Relic RPM

• Apache Bench

Always Measure

29

Page 49: A Scalable Rails App Deployed in 60 Seconds

• Multiple Heroku apps for Test, Staging, Production

• Learn the awesome-ness of Taps

• Heroku logs

• Heroku Console

• Useful addons: New Relic, Exceptional

Heroku Tips

30

Page 50: A Scalable Rails App Deployed in 60 Seconds

Backups

31

Page 51: A Scalable Rails App Deployed in 60 Seconds

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

Page 52: A Scalable Rails App Deployed in 60 Seconds

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

Page 53: A Scalable Rails App Deployed in 60 Seconds

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

Page 54: A Scalable Rails App Deployed in 60 Seconds

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

Page 55: A Scalable Rails App Deployed in 60 Seconds

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

Page 56: A Scalable Rails App Deployed in 60 Seconds

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

Page 57: A Scalable Rails App Deployed in 60 Seconds

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

Page 58: A Scalable Rails App Deployed in 60 Seconds

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

Page 59: A Scalable Rails App Deployed in 60 Seconds

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

Page 60: A Scalable Rails App Deployed in 60 Seconds

Delayed Jobs

41

Page 61: A Scalable Rails App Deployed in 60 Seconds

Delayed Jobs

def send_bulk_emails

BulkEmailSender.send_a_tonflash[:notice] => "Ok we sent them"

end

41

Page 62: A Scalable Rails App Deployed in 60 Seconds

Delayed Jobs

def send_bulk_emails

BulkEmailSender.send_a_tonflash[:notice] => "Ok we sent them"

end

def send_bulk_emails

BulkEmailSender.send_later(:send_a_ton)flash[:notice] => "Ok we sent them"

end

41

Page 63: A Scalable Rails App Deployed in 60 Seconds

Delayed Jobs

42

Page 64: A Scalable Rails App Deployed in 60 Seconds

Delayed Jobs

> gem install delayed_job> rails g delayed_job > rake db:migrate

42

Page 65: A Scalable Rails App Deployed in 60 Seconds

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

Page 66: A Scalable Rails App Deployed in 60 Seconds

Delayed Jobs

43

Page 67: A Scalable Rails App Deployed in 60 Seconds

Delayed Jobs

$0.05 per hour / worker(even when it's not processing Jobs)

43

Page 69: A Scalable Rails App Deployed in 60 Seconds

Delayed JobsPedro's Fork / auto-scale branch

44

Page 70: A Scalable Rails App Deployed in 60 Seconds

Delayed JobsPedro's Fork / auto-scale branch

//lib/delayed/job.rbdef after_createManager.scale_up if self.class.auto_scale && Manager.qty == 0

end

//lib/delayed/worker.rbManager.scale_down if count.zero? && Job.auto_scale && Job.count == 0

44

Page 71: A Scalable Rails App Deployed in 60 Seconds

• Read-only file system

• some gems don’t work

• File uploads?

• Temp Directory

• SSL, memcached, Full-text-search expensive

• Bound-by Amazon’s SLA

Gotchas

45

Page 72: A Scalable Rails App Deployed in 60 Seconds

contact = {

! :blog => http://flux88.com,!! :twitter => @subdigital,!! :email => [email protected]

}

46

Page 73: A Scalable Rails App Deployed in 60 Seconds

47