Upload
ben-scofield
View
1.035
Download
1
Tags:
Embed Size (px)
DESCRIPTION
A year ago, I was a committed VPS and dedicated-machine deployer. I thought the cloud imposed silly restrictions - how dare you take away my shell account! Whaddya mean I can't save files locally? Since then, I've had some interesting experiences. I've worked on big cloud-deployed systems, and certain large traditionally-deployed systems, and I've seen how a lot of the decisions that you're ... encouraged to make when designing an app to run in the cloud. Most interestingly, I've discovered how those same decisions can make for a much better app regardless of where it'll end up. In this talk, I'll share those architectural patterns with you, and show why they work. Hopefully, I'll convince all of you to build cloud castles -- even if you've got your foundation firmly on the ground.
Citation preview
Ben Scofield / @bscofield
and Finding Firmer FoundationsBuilding Cloud Castles
West End Ruby / 2 Feb 2011
flickr: natlockwood
WORK IN PROGRESSthis presentation is a
flickr: turtlemom_nancy
THE CLOUD
flickr: sizemoresr
IS FAR AWAY
LIMITED ACCESS
DIAGNOSIS
$ ssh [email protected] production.server #1 SMP Sat Dec 5 16:04:55 UTC 2009 i686
To access official Ubuntu documentation, please visit:http://help.ubuntu.com/Last login: Fri Jan 28 16:33:49 2011 from local.hostdeploy@production:~$ cd /var/log/apache2deploy@production:/var/log/apache2$ tail error.log[Sun Jan 23 06:25:02 2011] [notice] Apache/2.2.12 (Ubuntu) Phusion_Passenger/2.2.11... [Tue Jan 25 15:21:42 2011] [error] [client 118.129.166.97] Invalid URI in request G...[Fri Jan 28 12:01:50 2011] [error] [client 85.132.70.133] client sent HTTP/1.1 requ...[Sun Jan 30 06:25:06 2011] [notice] SIGUSR1 received. Doing graceful restart
REPAIR
$ ssh [email protected] production.server #1 SMP Sat Dec 5 16:04:55 UTC 2009 i686
To access official Ubuntu documentation, please visit:http://help.ubuntu.com/Last login: Fri Jan 28 16:33:49 2011 from local.hostdeploy@production:~$ cd /var/www/app/current/deploy@production:/var/www/app/current$ rails console productionLoading production environment (Rails 3.0.3)>> Article.count => 112>> Article.where(:problem => true).update_attributes(:problem => false)
require 'test_helper'
class ArticleTest < ActiveSupport::TestCase context 'Broken articles' do setup do 5.times.do { Factory :broken_article } end
should 'be fixable' do assert_equal 5, Article.where(:problem => true).count Article.fix_problem_articles assert_equal 0, Article.where(:problem => true).count end endend
class Article def self.fix_problem_articles where(:problem => true).update_attributes(:problem => false) endend
flickr: turtlemom_nancy
THE CLOUD
flickr: 93921318@N00
IS UNRELIABLE
FILESYSTEMS
class Comic < ActiveRecord::Base has_attached_file :cover, :styles => { :thumb => "80x120>", :medium => "300x450>" }end
$ cd public/system$ ls /covers10/ 12/ 53/ 81/$ ls /covers/10/medium/ original/ thumb/$ ls /covers/10/mediumbatman-450.png
class Comic < ActiveRecord::Base has_attached_file :cover, :storage => s3, :s3_credentials => { :access_key_id => ENV['s3_key'], :secret_access_key => ENV['s3_secret'] }, :bucket => 'comicsapp', :url => ":s3_path_url", :s3_headers => { 'Expires' => 1.year.from_now.httpdate }, :styles => { :thumb => "80x120>", :medium => "300x450>" }end
class Comic < ActiveRecord::Base has_attached_file :cover, :storage => s3, :s3_credentials => { :access_key_id => ENV['s3_key'], :secret_access_key => ENV['s3_secret'] }, :bucket => 'comicsapp', :url => ":s3_path_url", :s3_headers => { 'Expires' => 1.year.from_now.httpdate }, :styles => { :thumb => "80x120>", :medium => "300x450>" }end
module Watchtower class Application < Rails::Application # ... require 'openid/store/filesystem' config.middleware.use OmniAuth::Strategies::OpenID, OpenID::Store::Filesystem.new('/tmp') endend
module Watchtower class Application < Rails::Application # ... require 'openid/store/filesystem' config.middleware.use OmniAuth::Strategies::OpenID, OpenID::Store::Filesystem.new('./tmp') endend
module Watchtower class Application < Rails::Application # ... require 'openid/store/filesystem' config.middleware.use OmniAuth::Strategies::OpenID, OpenID::Store::Filesystem.new('./tmp') endend
flickr: turtlemom_nancy
THE CLOUD
flickr: lensonlife
IS HOSTILE
EXTERNAL SERVICES
class Searcher def self.search(term) Article.where(['content ILIKE ?', "%#{term}%"]) endend
class Searcher cattr_accessor :index
def self.index @api ||= IndexTank::Client.new(ENV['INDEXTANK_API_URL']) @index = @api.indexes 'articles' end
def self.search(term) raw = self.index.search(term, :function => 1) results = raw['results'].to_a
article_ids = results.map {|result| result['docid'] }
unsorted = Article.published.where(:id => article_ids) results.map { |result| unsorted.find {|u| u.id.to_i == result['docid'].to_i} }.compact endend
class Searcher cattr_accessor :index
def self.index @api ||= IndexTank::Client.new(ENV['INDEXTANK_API_URL']) @index = @api.indexes 'articles' end
def self.search(term) results = begin raw = self.index.search(term, :function => 1) raw['results'].to_a rescue URI::InvalidURIError # An IndexTank error occurred search_by_sql(term)['results'] end
article_ids = results.map {|result| result['docid'] }
unsorted = Article.published.where(:id => article_ids) results.map { |result| unsorted.find {|u| u.id.to_i == result['docid'].to_i} }.compact end
def self.search_by_sql(term) {'results' => Article.where(['content ILIKE ?', "%#{term}%"]). map {|a| {'docid' => a.id}}} endend
class Searcher cattr_accessor :index
def self.index @api ||= IndexTank::Client.new(ENV['INDEXTANK_API_URL']) @index = @api.indexes 'articles' end
def self.search(term) results = begin raw = self.index.search(term, :function => 1) raw['results'].to_a rescue URI::InvalidURIError # An IndexTank error occurred search_by_sql(term)['results'] end
article_ids = results.map {|result| result['docid'] }
unsorted = Article.published.where(:id => article_ids) results.map { |result| unsorted.find {|u| u.id.to_i == result['docid'].to_i} }.compact end
def self.search_by_sql(term) {'results' => Article.where(['content ILIKE ?', "%#{term}%"]). map {|a| {'docid' => a.id}}} endend
flickr: turtlemom_nancy
THE CLOUD
flickr: 3sth3r
IS RECYCLED
CACHING
class BooksController < ApplicationController caches_page :index def index @books = Book.all endend
class BooksController < ApplicationController caches_action :index def index @books = Book.all endend
module CardCatalog class Application < Rails::Application # ... ActionController::Base.cache_store = :mem_cache_store, "memcache_host" endend
class BooksController < ApplicationController caches_action :index def index @books = Book.all endend
module CardCatalog class Application < Rails::Application # ... ActionController::Base.cache_store = :mem_cache_store, "memcache_host" endend
class BooksController < ApplicationController def index response.headers['Cache-Control'] = 'public, max-age=300' @books = Book.all endend
class BooksController < ApplicationController def index response.headers['Cache-Control'] = 'public, max-age=300' @books = Book.all endend
flickr: turtlemom_nancy
THE CLOUD
flickr: dev07
IS MADE OF TINY PARTS
THINKING SMALL
App
App
App
App
App
App
App
App
App
App
App
App
App
App
App
App
App
App
$ heroku createCreating empty-sword-187....... donehttp://empty-sword-187.heroku.com/ | [email protected]:empty-sword-187.gitGit remote heroku added$ git push heroku masterCounting objects: 5, done.Delta compression using up to 4 threads.Compressing objects: 100% (3/3), done.Writing objects: 100% (3/3), 285 bytes, done.Total 3 (delta 2), reused 0 (delta 0)
-----> Heroku receiving push-----> Rails app detected-----> Detected use of caches_page Installing caches_page_via_http plugin... done-----> Detected Rails is not set to serve static_assets Installing rails3_serve_static_assets... done-----> Gemfile detected, running Bundler version 1.0.7 Unresolved dependencies detected; Installing... Using --without development:test Fetching source index for http://rubygems.org/ ...
App
App
App
App
App
App
App
App
App
App
App
App
App
App
App
App
App
App
App
App
App
App
App
App
App
App
App
App
App
App
App
App
App
App
HTTP and REST
PATTERNS and VIRTUES
SINGLE RESPONSIBILITY PRINCIPLE
HUMILITY
LAZINESS
PARANOIA
Ben Scofield / @bscofield
Thanks!
West End Ruby / 2 Feb 2011
http://spkr8.com/t/5491http://benscofield.com
http://heroku.com