Is your EC2 server pool starting to look like this?
PoolParty can help you turn it into this:
Why would I need PoolParty?
Amazon’s Elastic Compute Cloud has made it cheap and straightforward to run your own server pool, but there are various issues with volatility, lack of load balancing between instances, no persistent instance storage (currently in private beta). Also there is no mechanism for scaling your instances dynamically (ie, adding or removing instances as your load changes).
That is all taken care of by PoolParty, a recently open sourced Ruby gem and the brainchild of fellow CitrusByte developer Ari Lerner. PoolParty takes care of the following:
Automatic scaling based on demand and load
Starting/stopping instances
Self-healing
Provisioning and bootstrapping initial software
Setting up S3Fuse
Load balancing with HAProxy
Built-in monitoring
Plug-in architecture for extendability / customization
How?
PoolParty is based on established open source technologies that have been widely used in production. It takes your EC2 server pool, and invites S3Fuse, monit, and members of the High Availability Linux Project . Now you don’t just have a pool. You have a rocking pool party. All you have to do is sign those invites! (In other words: just provide a config file and you’re done).
More Information
You can check it out at the official PoolParty website. Tutorials, walk-throughs and articles are coming soon.
If you are interested in active development, check out the source at github. Active discussion is on the PoolParty Google Group
Here are the slides from the talk Ari gave at RailsConf sessions (also available here):
Today, we released a new gem to make integrating off-site processing easy. With this gem and utilizing Amazon’s EC2 cloud and S3 storage back-end, you can easily process media files in a non-blocking, threaded way and do it all inline with the rest of your application.
First download the gem and all it’s dependencies by typing:
sudo gem install processor_pool -y
This gem is built on top of Sinatra. So, if you are familiar with the Sinatra syntax, then you’ll be right at home. If not, I highly suggest you check it out.
First,
# In a file called media_processor.rb
require "rubygems"
require "processor_pool"
# Set the access_key_id and the secret_access_key provided by amazon
access_key_id, secret_access_key = 'accesskey', 'somethinglongandfuzzyhere'
# And start the processors!
ProcessorPool.start(access_key_id, secret_access_key)
Add to the end of the file
get '/' do
"Show me the money"
end
That’s it. Start your server up with
ruby media_processor.rb
And now you are ready to go to allow a user to hit the EC2 cloud (more on this later) and be responded with the “Show me the money” text.
Replace the above content with the following
class PFile
attr_accessor :filename, :temp_file
@@temp_directory_base = "tmp"
def initialize(f)
self.filename = f[:filename]
self.temp_file = f[:tempfile]
@media_file = nil
@temp_dir
end
def delete!
#File.unlink(self.temp_file)
self.temp_file.delete
end
def store_in_temp_directory
@temp_dir = File.join(@@temp_directory_base, @upload.member_id, @upload.media_file_id)
FileUtils.mkdir_p(@temp_dir)
filename = File.basename(self.filename)
FileUtils.mv(self.temp_file.path, File.join(@temp_dir, filename))
@media_file = MediaFile.new(File.join(@temp_dir, filename))
end
def process!
ImageConverter.new(@media_file).convert
@media_file.delete!
"Processed images for #{@media_file.original_file_name}"
end
def save_to_storage
# DO THE S3 saving here
MediaDirectory.new(@temp_dir).copy_to_s3(STORAGE_BUCKET, upload_prefix, true)
end
def upload_prefix
"#{@upload.member_id}/#{@upload.media_file_id}"
end
end
class ImageConverter
def resize_to(name, image = nil)
img = image || Magick::Image.read(self.file.file_name).first
named_size = self.class.named_sizes[name]
if named_size =~ /c$/
dimensions = named_size.gsub(/c$/, '').split('x')
img.crop_resized!(dimensions[0].to_i, dimensions[1].to_i)
else
img.change_geometry!(named_size) do |cols, rows, img|
img = img.resize(cols, rows)
end
end
img.write(path_to(name))
end
end
post '/new' do
@file = PFile.new(params[:file])
@file.store_in_temp_directory
@file.save_to_storage
redirect params[:success_url] if params[:success_url]
end
If you look closely, we are just using some simple classes to handle our conversion. But ignoring that, we are sending a post to the server with file parameters. We can send the process a file and the rest is history. Notice that this all happens on a remote server. None of this is handled locally.
That’s it.
Of course, you can imagine how you could extend this to include movie types and other types of processing, but for the time being, let’s focus the images. (Although I will be showing how to do this in rails, you can use this with any back-end, PHP, Java, etc.)
Let’s add a convenience method in the application.rb
def url_for_upload_server
if RAILS_ENV == 'production'
# determine the upload server to try to use
p = Processors.get_random_processor(::SERVER_POOL_BUCKET)
"http://#{p.hostname}:4567/new"
else
p = Processors.get_random_processor(::SERVER_POOL_BUCKET)
"http://localhost:4567/new"
end
end
In your upload controller add
@upload_url = url_for_upload_server
Now, in your upload view, add
<% form_for @media, :id => "fileform", :url => upload_url, :multipart => true do %>
...
<% end %>
Lets handle this inline and non-blocking
Add a button field to the end of your form like so:
and this to your javascript file (note, this is jQuery)
function beginFileUpload()
{
// todo: some kind of feedback that upload has started...
$("#uploading").show();
$("#buttonUpload").hide();
$("#validation_code").load('<%= url_for(:controller=>"media_file", :action=>"upload") %>',
{ title: $("#title").val(),
description: $("#description").val()
},
function(responseText, textStatus, XMLHttpRequest) {
$("#validation_code").val(responseText)
if (textStatus == 'success')
completeFileUpload();
else
alert('Error uploading file.');
});
return false;
}
function completeFileUpload()
{
$("#fileform").submit();
return false;
}
With that, you have inline file uploading with inline image processing off-site.