Slide 1

Slide 1 text

Rails&in&produc.on @just_lest

Slide 2

Slide 2 text

Development

Slide 3

Slide 3 text

Development $ rails server => Booting WEBrick => Rails 4.1.10 application starting in development on http://0.0.0.0:3000 => Run `rails server -h` for more startup options => Notice: server is listening on all interfaces (0.0.0.0). Consider using 127.0.0.1 (--binding option) => Ctrl-C to shutdown server [2015-03-21 10:29:23] INFO WEBrick 1.3.1 [2015-03-21 10:29:23] INFO ruby 2.2.1 (2015-02-26) [x86_64-darwin14] [2015-03-21 10:29:23] INFO WEBrick::HTTPServer#start: pid=42401 port=3000

Slide 4

Slide 4 text

$ rails server • h#p:/ /myapp.example.com:3000

Slide 5

Slide 5 text

$ rails server • h#p:/ /myapp.example.com:3000 • h#p:/ /myapp.example.com

Slide 6

Slide 6 text

$ rails server • h#p:/ /myapp.example.com:3000 • h#p:/ /myapp.example.com • sudo rails server?

Slide 7

Slide 7 text

Applica'on*servers Usually'don't'provide: • Effec&ve(serving(sta&c(files • Handling(slow(clients • Load(balancing

Slide 8

Slide 8 text

Web$servers +--------+ +------------+ +------------+ | Client |+--->| Web server |+--->| App server | +--------+ +------------+ +------------+

Slide 9

Slide 9 text

Web$servers • Nginx h(p:/ /nginx.org Served3or3proxied321.21%3busiest3sites1 • Others 1"h$p:/ /news.netcra1.com/archives/2015/02/24/february>2015>web>server>survey.html

Slide 10

Slide 10 text

Nginx server { listen 80; server_name myapp.example.com; root /home/deploy/myapp/public; location / { try_files $uri @app; } location @app { proxy_set_header Client-Ip $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; proxy_pass http://localhost:3000; } }

Slide 11

Slide 11 text

Nginx Load%balancing upstream myapp { server http://host1:3000; server http://host2:3000; } server { # ... location @app { # ... proxy_pass http://myapp; } }

Slide 12

Slide 12 text

Applica'on*servers WEBrick • Single(process • Single(thread • One(request(at(a(5me

Slide 13

Slide 13 text

Load%capacity Example • avg%0.2s%per%request • 5%requests%per%second

Slide 14

Slide 14 text

504$Gateway$Timeout

Slide 15

Slide 15 text

Processes • Run%N%applica,on%server%processes • Load%balance%them

Slide 16

Slide 16 text

Processes • Master/manager,process • Worker,processes

Slide 17

Slide 17 text

Threads • Single(process • Thread(pool

Slide 18

Slide 18 text

Load%capacity Example • 4#workers • avg#0.2s#per#request • (1#/#0.2)#*#4#=#20 • avg#20#requests#per#second#total

Slide 19

Slide 19 text

Applica'on*servers

Slide 20

Slide 20 text

Passenger h"ps:/ /www.phusionpassenger.com Pros • Integrated*with*web*server • Simple*setup

Slide 21

Slide 21 text

Passenger Cons • Advanced)features)only)in)Passenger)Enterprise

Slide 22

Slide 22 text

Unicorn h"p:/ /unicorn.bogomips.org Pros • Process(management • Signal(handling • Zero4down6me(restart • nginx4style(binary(upgrades(without(losing(connec6ons

Slide 23

Slide 23 text

Unicorn Cons • Doesn't)support)threads

Slide 24

Slide 24 text

Puma h"p:/ /puma.io Pros • Fast • Thread+pool • Clustered+mode • Phased+restart

Slide 25

Slide 25 text

Puma Cons • Doesn't)support)preload_app)with)phased_restart

Slide 26

Slide 26 text

Zero%down)me+deploy

Slide 27

Slide 27 text

Unicorn Zero%down)me+restart before_fork do |server, worker| old_pid = "#{server.config[:pid]}.oldbin" if old_pid != server.pid begin sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU Process.kill(sig, File.read(old_pid).to_i) rescue Errno::ENOENT, Errno::ESRCH end end end $ kill -USR2 `cat tmp/pids/unicorn.pid`

Slide 28

Slide 28 text

Puma Phased'restart bundle exec \ pumactl -S tmp/pids/puma.state phased-restart

Slide 29

Slide 29 text

Deploy'error'example git fetch origin git reset --hard origin master touch tmp/restart.txt

Slide 30

Slide 30 text

Deploy'error'example diff --git a/app/models/user.rb b/app/models/user.rb index 40bc07c..3ebc69f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,2 +1,5 @@ class User + def greeting + 'Hey!' + end end

Slide 31

Slide 31 text

Deploy'error'example diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb index 1ca0e26..2aeea96 100644 --- a/app/views/users/show.html.erb +++ b/app/views/users/show.html.erb @@ -1,4 +1,6 @@
Username
<%= @user.username %>
+
Greeting
+
<%= @user.greeting %>

Slide 32

Slide 32 text

Deploy'error'example git fetch origin git reset --hard origin master

Slide 33

Slide 33 text

Deploy'error'example ActionView::Template::Error (undefined method `greeting' for #)

Slide 34

Slide 34 text

Releases /home/deploy/myapp ├── current -> /home/deploy/myapp/releases/20150120114500/ ├── releases │ ├── 20150080072500 │ ├── 20150090083000 │ ├── 20150100093500 │ ├── 20150110104000 │ └── 20150120114500 ├── repo │ └── ├── revisions.log └── shared └──

Slide 35

Slide 35 text

Deploy'tools • capistrano h,ps:/ /github.com/capistrano/capistrano • mina h,ps:/ /github.com/mina4deploy/mina • vlad h,ps:/ /github.com/sea,lerb/vlad

Slide 36

Slide 36 text

Zero%down)me+database+migra)ons Safe: • Adding'columns • Crea2ng'tables • Crea2ng'indexes'(CREATE'INDEX'CONCURRENTLY) • Dropping'indexes

Slide 37

Slide 37 text

Removing)columns 1)#Make#the#app#ignore#the#column class User def self.columns super.reject { |c| c.name == 'full_name' } end end 2)#Remove#the#column 3)#Remove#the#code#that#ignored#the#column

Slide 38

Slide 38 text

Renaming(columns 1)#Add#the#column#and#write#to#both#columns 2)#Copy#the#old#column#values#to#the#new#column 3)#Remove#the#old#column

Slide 39

Slide 39 text

Dropping(tables 1)#Deploy#an#app#that#doesn't#use#the#table 2)#Deploy#the#migra1on#that#removes#the#table

Slide 40

Slide 40 text

Error$repor'ng • Airbrake h*ps:/ /airbrake.io • Errbit h*ps:/ /github.com/errbit/errbit • Rollbar h*ps:/ /rollbar.com/

Slide 41

Slide 41 text

Monitoring • New%Relic h+p:/ /newrelic.com • Skylight h+ps:/ /www.skylight.io • AppSignal h+ps:/ /appsignal.com

Slide 42

Slide 42 text

Logging • Logstash h)p:/ /logstash.net • Fluentd h)p:/ /www.fluentd.org

Slide 43

Slide 43 text

Elas%csearch*+* Kibana

Slide 44

Slide 44 text

Performance*metrics module MethodTracer def trace_method(method, name=nil) name ||= "#{self.name.underscore.gsub('/', '.')}.#{method}" original_method = :"_untraced_method_#{method}" return if method_defined?(original_method) alias_method original_method, method define_method method do |*args, &block| ActiveSupport::Notifications.instrument("performance.#{name}") do send(original_method, *args, &block) end end end end

Slide 45

Slide 45 text

Performance*metrics logstash = LogStashLogger.new(port: 5228) ActiveSupport::Notifications.subscribe /^performance\./ do |*args| event = ActiveSupport::Notifications::Event.new(*args) logstash.info(metric: event.name, duration: event.duration) end

Slide 46

Slide 46 text

Performance*metrics class MyClass def my_method # do something end end

Slide 47

Slide 47 text

Performance*metrics class MyClass extend MethodTracer def my_method # do something end trace_method :my_method end

Slide 48

Slide 48 text

Rails&in&produc.on • Web%servers • Applica/on%servers • Zero3down/me%deploy • Error%repor/ng • Logging%and%monitoring

Slide 49

Slide 49 text

Thanks!