diff options
Diffstat (limited to 'site/src/docs')
24 files changed, 3761 insertions, 0 deletions
diff --git a/site/src/docs/SimpleLighttpdMongrelSetup.jpg b/site/src/docs/SimpleLighttpdMongrelSetup.jpg Binary files differnew file mode 100644 index 0000000..bdb2bb1 --- /dev/null +++ b/site/src/docs/SimpleLighttpdMongrelSetup.jpg diff --git a/site/src/docs/apache.page b/site/src/docs/apache.page new file mode 100644 index 0000000..ab26510 --- /dev/null +++ b/site/src/docs/apache.page @@ -0,0 +1,481 @@ +--- +title: Apache +inMenu: true +directoryName: Apache +--- + +h1. Apache Best Practice Deployment + +h3. By Charles Brian Quinn + +The preferred setup (for now) is to put Mongrel behind an Apache 2.2.3 +server running mod_proxy_balancer. Apache is a proven web server, runs +half the Internet, and is a pain to configure. These instructions should +get you started, but refer to the Apache folks for anything more complex +or weird. + +When you're just starting out, don't bother with doing anything but +running just Mongrel. Mongrel is slower than Apache, but not so slow that +small installations will notice it. The worst thing you can do is +try to learn Apache install when you're also trying to learn Ruby on Rails +and Mongrel too. Start small, then *when you need*, build up to the big stuff. + +h2. A simple single mongrel configuration + +Start up a single mongrel instance on port 8000: + +<pre><code> + $ mongrel_rails start -d -p 8000 \ + -e production -P /full/path/to/log/mongrel-1.pid +</code></pre> + +Now, we'll tell Apache to simply proxy all requests to the mongrel server +running on port 8000. Simply add the following to your httpd.conf or in +a vhost.conf file: + +<pre><code> + <VirtualHost *:80> + ServerName myapp.com + ServerAlias www.myapp.com + + ProxyPass / http://www.myapp.com:8000/ + ProxyPassReverse / http://www.myapp.com:8000 + ProxyPreserveHost on + </VirtualHost> +</code></pre> + +That's it, in a nutshell. Several things to note in this configuration: + +1) This configuration forwards all traffic to mongrel. This means mongrel +will serve images, javascript, files, and everything else. It's quite fast +at this, but Apache can do it better. + +Here are some basic proxypass rules you can add to tell the ProxyPass not +to forward on requests to certain documents/requests: + +<pre><code> +ProxyPass /images ! +ProxyPass /stylesheets ! +#continue with other static files that should be served by apache + +Alias /images /path/to/public/images +Alias /stylesheets /path/to/public/stylesheets +#continue with aliases for static content +</code></pre> + +For a more detailed set of rules for forwarding on all dynamic content to mongrel, +see the more detailed configuration below for more details. + +2) In this configuration, it is entirely possible that two users (web +requests) could hit your application at the exact same time, and one would +have to wait literally milliseconds until the first request is finished +before having a turn at the mongrel instance. Unless you've got some really +long HTTP processes, the nature of the HTTP protocol is pretty good at waiting +in line. Only you can determine ("through metrics":http://mongrel.rubyforge.org/docs/how_many_mongrels.html) how long and +how many users will come at your application at the exact same time. + +Sufficient to say, if you're ready to start scaling with multiple mongrel +instances, read on. + +h2. Using multiple mongrel instances with mod_proxy_balancer + +First, let's start up a few mongrel instances (*nix-style): + +<pre><code> +$ mongrel_rails start -d -p 8001 \ + -e production -P log/mongrel-1.pid +$ mongrel_rails start -d -p 8002 \ + -e production -P log/mongrel-2.pid +$ mongrel_rails start -d -p 8003 \ + -e production -P log/mongrel-3.pid +$ mongrel_rails start -d -p 8004 \ + -e production -P log/mongrel-4.pid +</code></pre> + +You can also use "mongrel_cluster":http://mongrel.rubyforge.org/docs/mongrel_cluster.html by "Bradley Taylor":http://fluxura.com/ for +managing several mongrel instances with a configuration file (and sysv init +scripts for *nix servers). + +We're going to be requiring the use of mod_proxy_balancer, a "new feature":http://httpd.apache.org/docs/2.2/new_features_2_2.html in +Apache 2.1/2.2 and above to proxy requests to +our mongrel instances. This software based HTTP load balancer will distribute +requests evenly (applying a weighting and selection algorithm) to our mongrel +instance(s). It even comes with a swell load-balancing manager page for +monitoring incoming requests. For more information, see: +"Apache's mod_proxy_balancer Documentation":http://httpd.apache.org/docs/2.2/mod/mod_proxy_balancer.html. + +h2. Obtaining Apache 2(.1+) + +I won't go into too many details, as windows and the various linux +distributions all have several methods for obtaining apache2, but you will need +the use of the following modules: + + * mod_proxy, mod_proxy-html, and mod_proxy_balancer + * mod_rewrite + * mod_deflate + * mod_headers + * (optional) mod_cache and one of mod_memcache or mod_filecache + * (optional) mod_ssl + +If you're compiling from source, this configuration should do the trick: + +<pre><code> +#./configure --enable-deflate --enable-proxy --enable-proxy-html \ +--enable-proxy-balancer --enable-rewrite --enable-cache \ +--enable-mem-cache --enable-ssl --enable-headers +</code></pre> + +h2. Configuring Apache2 + +A good practice is the separation of apache configuration files. Recommended +by several other good guides, we'll be storing information for our application +in several different files. Put these files somewhere that apache2 knows +about. Apache is quite good about scanning for all .conf files in certain +directories. + +h3. myapp.common + +Apache lets you include common configuration items into another configuration +so you can cut down on repetition. What we're going to do is make a file +that has all the common junk that every Mongrel application needs to +work at all, then we'll just include this in little .conf files for +any application we deploy. + +Notice that this file doesn't end in .conf since it's not a real configuration +file, but you can name it however you wish. + +<b>Important Update: "typo fixed in IE deflate rules":http://rubyforge.org/pipermail/mongrel-users/2006-October/001868.html</b> + +<pre><code> + ServerName myapp.com + DocumentRoot /var/www/myapp.com/current/public + + <Directory "/var/www/myapp.com/current/public"> + Options FollowSymLinks + AllowOverride None + Order allow,deny + Allow from all + </Directory> + + RewriteEngine On + + # Uncomment for rewrite debugging + #RewriteLog logs/myapp_rewrite_log + #RewriteLogLevel 9 + + # Check for maintenance file and redirect all requests + # ( this is for use with Capistrano's disable_web task ) + RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f + RewriteCond %{SCRIPT_FILENAME} !maintenance.html + RewriteRule ^.*$ /system/maintenance.html [L] + + # Rewrite index to check for static + RewriteRule ^/$ /index.html [QSA] + + # Rewrite to check for Rails cached page + RewriteRule ^([^.]+)$ $1.html [QSA] + + # Redirect all non-static requests to cluster + RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f + RewriteRule ^/(.*)$ balancer://mongrel_cluster%{REQUEST_URI} [P,QSA,L] + + # Deflate + AddOutputFilterByType DEFLATE text/html text/plain text/css + # ... text/xml application/xml application/xhtml+xml text/javascript + BrowserMatch ^Mozilla/4 gzip-only-text/html + BrowserMatch ^Mozilla/4.0[678] no-gzip + BrowserMatch \bMSIE !no-gzip !gzip-only-text/html + + # Uncomment for deflate debugging + #DeflateFilterNote Input input_info + #DeflateFilterNote Output output_info + #DeflateFilterNote Ratio ratio_info + #LogFormat '"%r" %{output_info}n/%{input_info}n (%{ratio_info}n%%)' deflate + #CustomLog logs/myapp_deflate_log deflate +</code></pre> + +h3. myapp.conf + +We then take the above commmon file and include it in our configuration file +for this application deployment. + +If you're using virtual hosting (a pretty good idea, even when you're the only +one on the server), your sample configuration can be this simple: + +<pre><code> + <VirtualHost *:80> + Include /etc/httpd/conf.d/myapp.common + + ErrorLog logs/myapp_errors_log + CustomLog logs/myapp_log combined + </VirtualHost> +</code></pre> + +h3. myapp.proxy_cluster.conf + +This is the meat of our configuration, and goes hand in hand with our mongrel +(or mongrel_cluster) configuration. This configuration tells the apache2 +mod_proxy_balancer to proxy requests to 3 mongrel instances running on ports +8000, 8001, and 8002. + +<pre><code> + <Proxy balancer://mongrel_cluster> + BalancerMember http://127.0.0.1:8000 + BalancerMember http://127.0.0.1:8001 + BalancerMember http://127.0.0.1:8002 + </Proxy> +</code></pre> + +If you had an seperate application server, you could balance to it easily by +replacing the 127.0.0.1 with the ip or hostname of your application server, but +be sure to make them listen on an external interface (rather than 127.0.0.1). + +When you add an additional mongrel to your mongrel_cluster, you can simply add +an additional BalancerMember to this file, restart apache (or reload) and +you're all set. + +h3. (optional) myapp.proxy_frontend.conf + +This optional file will setup the balancer-manager -- a simple front-end for +viewing how your requests are being handled. This balancer in the +configuration below will only work from the localhost, so no one else (or +possibly you) can view it unless you alter the "Deny" and "Allow" lines. + +<pre><code> +Listen 8080 +<VirtualHost *:8080> + <Location /> + SetHandler balancer-manager + Deny from all + Allow from localhost + </Location> +</VirtualHost> +</code></pre> + +h3. SSL Requirements + +In order for mongrel to know that this request has a forwarded protocol of +https, we'll need to add a special header (hence the addition of mod_header, +included in most apache2 builds). + +<pre><code> + Include /etc/httpd/conf.d/myapp.common + + # This is required to convince Rails (via mod_proxy_balancer) that we're + # actually using HTTPS. + RequestHeader set X_FORWARDED_PROTO 'https' +</code></pre> + +You need this mostly so that redirects go back to https and so you can +spot when people are coming through SSL or not. + +h2. Automation, Automation, Automation + +There are several great tools that automate the setup of Apache for use with +mongrel and mongrel_cluster. The "RailsMachine gem":https://support.railsmachine.com/index.php?pg=kb.chapter&id=18 can +automate an entire setup of a Rails application. Also, "Slingshot Hosting":http://www.slingshothosting.com/ has +a sample set of "Capistrano recipes":http://www.slingshothosting.com/support/capistrano that automatically setup Apache2 and mongrel +through the @rake remote:setup@ task. Be sure to check out both for some ideas. + +h2. Running Multiple Rails Apps with Mongrel + +The newest version of Mongrel supports multiple Rails applications through +the use of the --prefix command. The Apache magic for proxying a +single application is here assuming your prefix is app1: + +<pre><code> +ProxyPass /app1 http://127.0.0.1:3000/app1 +ProxyPassReverse /app1 http://127.0.0.1:3000/app1 +</code></pre> + +You need to have the proxy pass the new directory name. + +Thanks to Joey Geiger and others of the mongrel list for these +instructions. + +h2. Success Stories + +Martins on the mongrel-list has submitted this simple apache configuration. It serves up static content with apache, and forwards dynamic content on to mongrel using ProxyPass. Thanks Martins: + +<pre><code> +<VirtualHost *> + ServerName myapp.tld + ServerAlias www.myapp.tld + + DocumentRoot /var/www/sites/myapp/current/public + + <Directory "/var/www/sites/myapp/current/public"> + Options FollowSymLinks + AllowOverride None + Order allow,deny + Allow from all + </Directory> + + RewriteEngine On + + # Check for maintenance file. Let apache load it if it exists + RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f + RewriteRule . /system/maintenance.html [L] + + # Let apache serve static files + RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f + RewriteRule (.*) $1 [L] + + # Don't do forward proxying + ProxyRequests Off + + # Enable reverse proxying + <Proxy *> + Order deny,allow + Allow from all + </Proxy> + + # Pass other requests to mongrel instance + ProxyPass / http://127.0.0.1:8200/ + ProxyPassReverse / http://127.0.0.1:8200/ + +</VirtualHost> +</code></pre> + +Phillip Hallstrom has submitted this apache configuration, which includes support for having static directories handled by Apache, php support, and hiding svn directories. + +<pre><code> +<VirtualHost *:80> + + ServerName myserver.com + DocumentRoot /path/to/my/app/public + + <Directory "/path/to/my/app/public"> + Options FollowSymLinks + AllowOverride None + Order allow,deny + Allow from all + </Directory> + + <Proxy balancer://mongrel_cluster> + BalancerMember http://127.0.0.1:8805 + </Proxy> + + RewriteEngine On + + RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} -d + RewriteRule ^(.+[^/])$ $1/ [R] + + RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} \.php + RewriteRule ^(.*)$ $1 [QSA,L] + + RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f + RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME}/index.html -f + RewriteRule ^(.*)$ $1/index.html [QSA,L] + + RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f + RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME}/index.php -f + RewriteRule ^(.*)$ $1/index.php [QSA,L] + + RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} -d + RewriteRule ^(.*)[^/]$ $1/ [QSA,L] + + RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f + RewriteRule ^/(.*)$ balancer://mongrel_cluster%{REQUEST_URI} [P,QSA,L] + + AddOutputFilterByType DEFLATE text/html + AddOutputFilterByType DEFLATE application/x-javascript + AddOutputFilterByType DEFLATE text/css + AddOutputFilterByType DEFLATE text/plain + AddOutputFilterByType DEFLATE text/xml + AddOutputFilterByType DEFLATE application/xml + AddOutputFilterByType DEFLATE application/xhtml+xml + + BrowserMatch ^Mozilla/4 gzip-only-text/html + BrowserMatch ^Mozilla/4.0[678] no-gzip + BrowserMatch bMSIE !no-gzip !gzip-only-text/html + + php_value include_path /path/to/my/app/php:/usr/local/lib/php:. + php_value auto_prepend_file /path/to/my/app/php/auto_prepend.php + + # this not only blocks access to .svn directories, but makes it appear + # as though they aren't even there, not just that they are forbidden + <DirectoryMatch "^/.*/\.svn/"> + ErrorDocument 403 /404.html + Order allow,deny + Deny from all + Satisfy All + </DirectoryMatch> + +</VirtualHost> +</code></pre> + +Jens Kraemer reports this differing proxy setup that uses the P option in Rewrite rules so as not to use the ProxyPass directive: + +<pre><code> + # Don't do forward proxying + ProxyRequests Off + + # Enable reverse proxying + <Proxy *> + Order deny,allow + Allow from all + </Proxy> + + RewriteEngine On + + # Check for maintenance file. Let apache load it if it exists + RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f + RewriteRule . /system/maintenance.html [L] + + # Rewrite index to check for static + RewriteRule ^/$ /index.html [QSA] + + # Let apache serve static files (send everything via mod_proxy that + # is *no* static file (!-f) + RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-f + RewriteRule .* http://127.0.0.1:8200%{REQUEST_URI} [L,P,QSA] +</code></pre> + +the P option to the last rule replaces the ProxyPass and +ProxyPassReverse directives. + + +h2. SVN Security + +If you use svn to issue checkouts instead of exports, you'll need to hide those pesky .svn directories. This works: + +<pre><code> +# this not only blocks access to .svn directories, but makes it appear + # as though they aren't even there, not just that they are forbidden + <DirectoryMatch "^/.*/\.svn/"> + ErrorDocument 403 /404.html + Order allow,deny + Deny from all + Satisfy All + </DirectoryMatch> + +</VirtualHost> +</code></pre> + +h2. Reading REMOTE_USER from mongrel through proxy + +Jon Reads reports successfully reading the REMOTE_USER variable: + +After many hours trying to solve the same problem I found this post: "Forcing a proxied host to generate REMOTE_USER":http://www.nabble.com/Forcing-a-proxied-host-to-generate-REMOTE_USER-tf1114364.html#a2914465 + +and can confirm that the following works for me when put in the Proxy +directive on Apache 2: + +<pre><code> + RewriteEngine On + RewriteCond %{LA-U:REMOTE_USER} (.+) + RewriteRule . - [E=RU:%1] + RequestHeader add X-Forwarded-User %{RU}e +</code></pre> + +h2. References and Other Guides + +[1] "Time For A Grown-Up Server: Rails, Mongrel, Apache, Capistrano and You":http://blog.codahale.com/2006/06/19/time-for-a-grown-up-server-rails-mongrel-apache-capistrano-and-you/ + +[2] "Bradley Taylor's Fluxura":http://fluxura.com/ and "RailsMachine":http://www.railsmachine.com/ + +[3] "Slingshot Hosting Automated Capistrano Recipe":http://www.slingshothosting.com/support/capistrano + +Thanks to many users on the mongrel list for making it easy for me to compile all these tips and tricks as they come across the list. + diff --git a/site/src/docs/choosing_deployment.page b/site/src/docs/choosing_deployment.page new file mode 100644 index 0000000..7a91e25 --- /dev/null +++ b/site/src/docs/choosing_deployment.page @@ -0,0 +1,178 @@ +--- +title: Choosing Deployment +inMenu: false +directoryName: Choosing Deployment +--- + +h1. Choosing The Best Deployment + +A major motivation for writing Mongrel was to make it easy to deploy Ruby web applications +within existing infrastructures and organizations. Most of the companies I tried to +pitch "Ruby on Rails":http://www.rubyonrails.org/ to loved how fast and easy it was to develop, +but ran screaming when I talked about FastCGI. + +Mongrel fixes this by just being a plain HTTP server that is still very fast. It's easy to +deploy, cluster, manage, and scale with existing web application technologies. You can +put it behind hardware load balancers, load balancing web servers, proxy servers, or just +by itself. + +With this flexibility comes a bit of confusion though. Many people start off wondering how best to +deploy Mongrel and then get confused with all the different options available. This document +hopefully will help you pick the right deployment for your needs. They are listed in order +from smallest/simpler to largest/complex. + +h2. Deployment Considerations + +There are a few initial things you'll have to consider when picking a deployment: + +* Concurrency -- Rails is not thread safe, so if you have giant actions that take +minutes to complete then take that into consideration. +* Requests/Second -- Notice I didn't say "users"? Users is a useless measurement of +your performance requirements. You want to know how many *requests per second* you have to process. +* Content Size -- What's the size of the content you're serving. Mongrel is pretty decent +at static files, but it can't beat a good solid web server for pushing out big files. +* Dynamic vs. Static -- Mongrel can serve static content for small sites, and of course do +dynamic Ruby generated content, but you'll want to figure out what the mix is so you can scale it later. +* Available Resources -- You can't cram a truck into a breadbox. Make sure you have the right deployment +for your resources such as memory, disk, CPU, etc. + +h3. Scalability Means Expansion Not Speed + +I'm not sure where the term "scalability" changed from it's original meaning, but you should +throw out any notion of scalability meaning "high performance". Scalability is about resources +and how easy it is for you to expand those resources to meet new demand. This does not mean +how many resources you can buy now for *all* the demand you'll ever need. It means the ability +to start small now, and then *after* you're the next google you can expand. + +h3. Start Small + +Everyone immediately jumps to the end of this document and starts with the absolute most complex and +"scalable" deployment they can muster. What you really want to do is start with a very small and simple +setup, and then expand as needed. This mitigates the risk that you'll buy a bunch of stuff you really +don't need and keeps your initial pain and costs low. + +h3. Automate, Automate, Automate + +You're a damn programmer. You should be scripting the hell out of your deployment just like +you do your testing. Automation reduces human error, makes your setups consistent, and many +times can become a project itself just like "Capistrano":http://manuals.rubyonrails.com/read/book/17 +did. + +h1. The Recommendations + +Once you've figured out all the basic things about your planned deployment you can start deciding +which one you need at the moment, and what you might need in the future. + +Wait. You did *plan* this deployment right? Ok, go back and actually plan it. This document +is part of your planning, but you need to do your homework first to make a good decision. + +h2. Just Mongrel + +Mongrel is actually pretty fast even when compared with web servers like "Apache":http://httpd.apache.org/ +for serving static files. If your web application is just starting out, doesn't need to coexist with +things like PHP, and you don't need SSL, then consider just running Mongrel by itself. + +This is especially attractive if your application can utilize page caching. Mongrel is pretty quick +for page cached sites and is able to handle quite a few concurrent connections. + +h3. Disadvantages + +Mongrel will break down pretty quick if you have lots of people accessing Rails actions that are +slow. It can still perform like a champ for most small needs, but if you start getting more +serious then you need one of the next solutions. + +h3. Advantages + +The easiest to manage, especially on win32. You just turn it on and go home. Maybe write a +few /etc/init.d/ start-up scripts for your server if you're running a POSIX system. + + +h2. Behind TCP Balancers + +You still don't need SSL, but you do need to make a small cluster of Mongrel servers. If this +is the case, then you can simply grab "mongrel_cluster":/docs/mongrel_cluster.html and either +get "Pen":http://siag.nu/pen/ or "Balance":http://www.inlab.de/balance.html and do a simple +cluster. + +h3. Advantages + +This configuration is very easy to setup, gives you decent throughput and concurrency, and is +easy to manage. Using mongrel_cluster gives you simple commands to control whole clusters of +Mongrel servers and Pen or Balance simply accepts connections on one port and forwards them +to one of the backend Mongrel ports. + +h3. Disadvantages + +No SSL and you're still relying on Mongrel to serve the files. A cluster of Mongrel servers +is no slouch though. I think many sites that don't need SSL could run with this configuration +happily for years. A real good mix is to combine this with Rails' @asset server@ configuration +and a simple "thttpd":http://www.acme.com/software/thttpd/ install to serve your static files. + +h2. Behind HTTP Balancers + +If you need something like the above, but you also need SSL, then simply swap out Balance or Pen +and use "Pound":/docs/pound.html instead. Pound is a very flexible HTTP balancer that +also supports SSL. This gives you the same advantages of simple deployment but adds the security +you need. + +h3. Advantages + +The same as using a TCP balancer, except you now have SSL and you can do some more creative +routing. A good example is if your site has to use a PHP web app for serving ads. You can +have pound take requests for the advertising URIs and route them to the PHP application, and +then transmit everything else to your Mongrels. It's very flexible and much easier to install. + +h3. Disadvantages + +You're still relying on Mongrel to serve the files and passing that through Pound, but as +mentioned before Mongrel is pretty quick with static files. Don't underestimate it. + +h2. Behind Web Servers + +If you have complex static file serving needs, need to host a PHP application at the same time, +or have complex authentication requirements, then you should use a regular web server. Mongrel +has good instructions for quite a few web servers and supports any server that has some form +of @mod_proxy@ style support. + +* "Apache":/docs/apache.html +* "Litespeed":/docs/litespeed.html +* "Lighttpd":/docs/lighttpd.html *not recommended* + +Each of the above documents describe how to get an initial configuration of Mongrel running behind +that type of web server. You should also combine this with "mongrel_cluster":/docs/mongrel_cluster.html +to manage the cluster of mongrel servers. + +h3. Advantages + +Since you have a real web server handling the initial HTTP traffic you can easily do page caching +and other magic to speed things up and avoid even talking to Mongrel. It also means you can put +in authenticators, other web platforms, and extensions that your application might need. Many +web servers are also installed and configured by default on most platforms and you just need +to add the Mongrel specifics to get it going. + +h3. Disadvantages + +This configuration can quickly descend into madness with complexity. Apache is +notorious for having a horribly complex configuration file. Another +disadvantage of real web servers is they almost always start of as web servers, +and then proxy support is bolted on as an extra. Apache's latest proxy support is +really good as well as Litespeed's, but Lighttpd's proxy support is really bad (as of Mongrel 0.3.13). + + +h2. Behind Hardware + +If you are getting really really serious about your web application and you need to +serve up lots of Mongrels then you should take a look at a hardware load balancer. +They're really expensive, but they're usually worth it if you're in the big leagues. + +h3. Advantages + +Big big big loads and the ability to handle the SSL in hardware for many products. +Also things like smart virus filtering, routing and other goodies, but you'll +pay for it. + +h3. Disadvantages + +Expensive, Expensive, Expensive. Did I mention Expensive. One more time Expensive. +They're also a royal pain to setup properly, but then that's why their Expensive. + diff --git a/site/src/docs/contrib.page b/site/src/docs/contrib.page new file mode 100644 index 0000000..6f3c6c1 --- /dev/null +++ b/site/src/docs/contrib.page @@ -0,0 +1,199 @@ +--- +title: Contribute +inMenu: true +directoryName: Contribute +--- + +h1. Contributing To The Mongrel Canon + +The documentation for Mongrel is typically written by me (Zed), but other +people have contributed lots of blog articles about Mongrel, actual site +documentation, or reviews and enhancements. It's pretty easy to contribute +documentation to the Mongrel canon, although what gets in is strictly +controlled in order to make sure the information quality is high. + +We are always looking for grammar and spelling freaks to send in suggestions, +and anyone who has done a difficult deployment to post their experiences to +the mailing list. Typically if your experiences get a positive response +then you may be asked to write up some documentation on it for the canon. + + +h2. What We Need + +The Mongrel project strives to be simple to use so that people don't +necessarily need to read many wizardly tomes to get started. Once people have +Mongrel figured out they typically want to do advanced things like deploy their +application in a well configured production setting or extend it. + +With that in mind, we are not really looking for introductory documentation +unless it is in languages other than English. The initial getting started for +Mongrel is pretty thin because it is so simple. + +Also, the code for Mongrel is well documented and constantly updated as work is +done. If you find errors in the "Mongrel RDoc":/rdoc/index.html then tell the +"mailing list":http://rubyforge.org/mailman/listinfo/mongrel-users and it'll +get fixed right up. + +What we are looking for is anything that's blank on the main +"documentation":/docs/index.html page and anything that is suitable for the FAQ +or a quick tip or trick. + +When writing your documentation make sure you keep it very short and you focus +on the details people need to get something working. The end results should be +functioning, but maybe not a complete massive google capable cluster. A really +good model to follow is the "cut-and-paste HOWTO". In this style of +documentation it's assumed that people will simple select the commands they +have to type and it will work. + +Finally, do not focus on system specific issues such as Debian install, RedHat +package management, setting up MySQL, etc. These topics are really needed, but +they don't fit in with documentation on Mongrel. What you can do is place a +statement that lists what people should already have configured prior to +starting the instructions, especially referencing other instructions they +should read. + + +h2. Getting Credit + +A cornerstone of the Mongrel project is that everyone deserves credit for the +free work they do. There's an "attributions":/attributions.html page that I +try to use to list people who've contributed work, cash, coffee, comments, etc. +Sometimes people get dropped, but if you think you were missed then say +something. It's important that people who work for free get recognized for +their contributions. + +When you write your documentation, feel free to add a by-line at the top and +link to your blog. Keep in mind that "RubyForge":http://www.rubyforge.org/ +doesn't like people linking to commercial entities, but personal blogs or other +open source projects are just fine. If your boss sponsored the time for you to +develop the documentation and contribute it, then feel free to mention their +name in the documentation and say thanks. + +h2. Avoid "Works-For-Me Syndrome" + +A very, very important thing to strive for is that people can take your +documentation, and with a good knowledge of their own system, can get through +the instructions with minimal difficulty. Systems change, so you may have to +keep track of the latest releases of packages and how your instructions might +need an update. + +What you *don't* want to do is slam out some half-assed set of instructions +that really only work on your little custo-hacked Debian/RedHat frankenstack. +It's a really good idea to try your instructions out on a fresh machine using a +different flavor of Linux or Unix just to make sure that you didn't miss +anything. + +h1. Writing The Documentation + +Now that you've got something to say, it's time to go through the trouble of +saying it. You should read through the documentation that is currently written +and see if you can get the same flavor. Feel free to be quirky and write in +your own style, just make sure the instructions are good. + +To start writing you're going to need a few tools and to dip into subversion +land for a few seconds. + +h2. Installing Webgen And Friends + +The site is actually generated from static files that use +"webgen":http://webgen.rubyforge.org/ to generate the site. The contents are +"Textile":http://textism.com/tools/textile/ and you can use the "hobix textile +reference":http://hobix.com/textile/ to guide you. + +To install webgen do the following: + + gem install webgen bluecloth redcloth + +The install may complain that you need other gems installed, so feel free to +install those as well. You typically don't need them since you're just gonna +generate the site from the source so you can confirm that what you're writing +fits. + +You should also make sure that you have +"subversion":http://subversion.tigris.org/ and/or "svk":http://svk.elixus.org/ +installed. These instructions will use subversion. + +You may also want to install Rake and any tools your operating system needs to +build Mongrel itself, just in case you want to have a full build. This isn't +needed for writing documentation though. + +h2. Getting The Site + +You now just need to grab the source from the anonymous subversion repository: + + svn checkout svn://rubyforge.org/var/svn/mongrel mongrel + +People using svk can do this with: + + svk mirror svn://rubyforge.org/var/svn/mongrel //mirror/mongrel + +And then use the normal svk commands to check it out: + + svk checkout //mirror/mongrel mongrel + +You should probably make sure you're in some kind of *projects* directory so +that you don't have a bunch of garbage lying around. Keep in mind also that +svk is sensitive to you moving this directory--you have to "detach" it first. + +h2. Writing or Editing Your Page + +The Mongrel web site is held in the @doc/site@ directory. In this directory +there's a @src/docs@ directory where most of the documentation is kept. + +What you have to do is find the page you are going to edit it, and open it in +your "favorite editor":http://vim.sourceforge.net/ to go to work. You'll want +to make sure you have a preamble like this on any new pages: + +<pre> +<code> + --- + title: OneWordMenuTitle + inMenu: true + directoryName: MyTitle + --- +</code> +</pre> + +@MyTitle@ can be pretty much any length, but I typically have it match the one +word title. The @OneWordMenuTitle@ will show up in the left hand navigation +menu, unless you set @inMenu@ to @false@. + +With that done, just use the "hobix textile +reference":http://hobix.com/textile/ and write your documentation. + +h2. Reviewing Before Submitting + +Reviewing your documentation requires that you simply run @webgen@ and then +look at the resulting .html page in your browser of choice. Some important +formatting issues to watch for are: + +* Make sure that you put <pre><code>...</code></pre> around code sections or use the textile syntax. +* Make sure that code sections you have are not too wide since the site has a fixed width +main section. +* You can include images if you think a diagram will make more sense. +* Use section heading to break up the instructions into discrete pieces. + +h2. Submitting Your Documentation + +The easiest way to submit changes to the documentation is to simple send me +(zedshaw AT zedshaw dot com) the page and I'll put it up after reviewing it. +If you have your own web site then you can also put the page on there +temporarily. + +When you have several changes to multiple documents then you'll need to +generate a patch and send that in. With svk you'll use the @svk patch@ command +and then mail me the results. With subversion you should be able to just do +@svn diff@ and I'll be able to use it. + +Make sure when you generate patches that you aren't including changes to the +Mongrel source (unless that's your intent). It's better to hand in site and +code patches separately. + + +h1. Getting And Giving Credit + +Don't forget that you deserve credit for working on this document, but that you +also need to reference sources you read while writing it. Put you name at the +top as the author, and then list at the bottom other documents you referenced +or people who helped you. + diff --git a/site/src/docs/debian-sarge.page b/site/src/docs/debian-sarge.page new file mode 100644 index 0000000..56ae075 --- /dev/null +++ b/site/src/docs/debian-sarge.page @@ -0,0 +1,289 @@ +--- +title: Debian HOWTO +inMenu: true +useERB: false +directoryName: Documentation +--- + +h1. Debian + +h3. By Chris McGrath + +There's been issues with people installing on Debian, so this document is an +attempt to detail how to do it. There are actually three different ways (at +least! You may find more). The easy way worked first time I attempted it on +a new VPS I was installing. The frankinstall way was discovered when I +attempted to recreate the easy way to test the process on a brand new local +Debian install. The backport your own way was worked out to avoid the +frankinstall you end up with using the second method. + +h2. How to find out which one you need + +Debian 3.1 only has Ruby 1.8.2, so you'll need to get Ruby 1.8.4 which is in testing. So +add the following to your /etc/apt/sources.list. + +<pre> + <code> + deb ftp://ftp.uk.debian.org/debian/ testing main + </code> +</pre> + +*Make sure and change the .uk. to your local mirror.* + +Now pin the testing repo so you don't install stuff from it accidentally. +You'll need to edit /etc/apt/preferences, which doesn't exist on a bare install +so create it and add the following three lines: + +<pre> + <code> + Package: * + Pin: release a=testing + Pin-Priority: 200 + </code> +</pre> + +OK, now to find out whether you're a lucky bunny or you have displeased your +personal favorite deity. As root do: + +<pre> + <code> + root# apt-get update + root# apt-get -s -t testing install ruby irb rdoc ri ruby1.8-dev \ + libzlib-ruby libopenssl-ruby1.8 + + </code> +</pre> + +*Notice the -s it's important* + +Examine the output, if you see the following: + +<pre> + <code> + The following packages will be REMOVED: + base-config initrd-tools kernel-image-2.6.8-2-386 + </code> +</pre> + +Then you're one of the unlucky ones, sorry. Skip to the backport way +(preferred) or if you're feeling brave the frankinstall way. + +h2. Installing the basic stack + +If your still reading, congratulations! You don't have kernel issues and you +should be up and running pretty quickly. + +h3. Ruby + +First actually install Ruby, so as root do: + +<pre> + <code> + root# apt-get -t testing install ruby irb rdoc ri ruby1.8-dev \ + libzlib-ruby libopenssl-ruby1.8 + </code> +</pre> + +h3. RubyGems + +Debian don't provide packages for RubyGems, so we have to get it from somewhere +else. A kind soul has made debs for RubyGems 0.8.11 available, so add the +following to your /etc/apt/sources.list + +<pre> + <code> + deb http://www.sgtpepper.net/hyspro/deb unstable/ + </code> +</pre> + +The final / is important. To install RubyGems do: + +<pre> + <code> + root# apt-get update + root# apt-get install rubygems + </code> +</pre> + +This package doesn't copy gem files to /usr/bin, instead it puts them in +/var/lib/gems/1.8/bin so we need to add that to the path. Edit /etc/profile and +add <code>:/var/lib/gems/1.8/bin</code> to the end of each PATH statement, +there's one for root and one for other users in the standard Debian +install. You'll need to log in and out again to get your path setup +correctly. + +(NOTE: Some people say just install rubygems from source if this doesn't work +for you). + +h3. GCC & Friends + +We're nearly ready to install Mongrel, if you're doing a frankinstall go back +to that page for the details. For the rest of us, the command is: + +<pre> + <code> + root# apt-get install build-essential + </code> +</pre> + +h3. Rails & Mongrel + +Time to install Rails and Mongrel at last! + +<pre> + <code> + root# gem install rails --include-dependencies + root# gem install mongrel mongrel_cluster --include-dependencies + </code> +</pre> + +Pick the latest ruby version of Mongrel from the menu. Wait for it to finish. *CONGRATULATIONS* + +h3. Testing + +To make sure the congratulations weren't premature, lets check it works. Login +as a normal user and do: + +<pre> + <code> + user$ rails test_app + user$ cd test_app + user$ script/generate controller HelloWorld index + </code> +</pre> + +Edit <code>app/views/hello_world/index.rhtml</code> to look like: + +<pre> + <code> + <h1>Hello World</h1> + <p><%= Time.now.to_s(:long) %></p> + </code> +</pre> + +Lets check it's all running. + +<pre> + <code> + user$ mongrel_rails start + </code> +</pre> + +Point your browser at http://999.999.999.999:3000/hello_world + +(NOTE: Replace 999.999.999.999 with your IP address.) + +h1. Backporting 1.8.4 from testing + +Sorry you're here, but hey lets get this done. This involves building your own +debs for Ruby 1.8.4 from source, so you don't have to upgrade your kernel and +install the build tools from testing like in the frankinstall. + +h2. Building your own Ruby debs + +First up, you need to *remove* the line we added before in +/etc/apt/sources.list and clear out /etc/apt/preferences, so do that now. We're +going to get the source from testing, so add the following to +/etc/apt/sources.list + +<pre> + <code> + deb-src ftp://ftp.uk.debian.org/debian testing main + </code> +</pre> + +*Make sure and change the .uk. to your local mirror.* + +And do the following as root: + +<pre> + <code> + root# apt-get update + root# apt-get install devscripts # if you don't have them already + root# mkdir scratch + root# cd scratch + root# apt-get source ruby1.8 + root# apt-get build-dep ruby1.8 + root# cd ruby1.8-1.8.4 + root# debuild -us -uc + root# cd .. + root# rm ruby1.8-elisp # unless of course you've emacs installed + root# dpkg -i *.deb + root# ln -s /usr/bin/ruby1.8 /usr/bin/ruby + root# ln -s /usr/bin/irb1.8 /usr/bin/irb + root# ln -s /usr/bin/ri1.8 /usr/bin/ri + root# ln -s /usr/bin/rdoc1.8 /usr/bin/rdoc + </code> +</pre> + + +h3. To be worked out: + +* No libzlib-ruby deb made... couldn't figure out how to get it done +* Install rubygems - when I ignored that and tried to use Ezra's sgtpepper.net mirror it wants to install ruby-1.8.2 + + +h1. Frankinstall of Mongrel on Debian Sarge + +If you're here I'm really, really sorry. I hope this doesn't mess up your +machine. If you're going to do this I'd consider just using testing itself, or +Ubuntu or something. Anyways, it's here for completeness, I had to re-install +Debian four times to figure this out so I'm damn well writing it up. + +This details how to go from a basic debian-31r2-i386-netinst.iso install to +having Ruby 1.8.4, RubyGems, Rails and Mongrel running (and a good part of +testing). I'm not going to cover MySQL or any of that, this is purely about +the dog. + +When I say a basic install, I mean a basic install. I entered linux26 at the +boot menu to get a 2.6 based system. Let it do it's stuff, then when the +installer asks what kind of system you want I chose to manual package +selection, and only installed ssh. + +I'm going to assume you got as far as adding the testing stuff to +/etc/apt/sources.list and /etc/apt/preferences from the Debian HOWTO page. The +next thing you need to read is +"this":http://www200.pair.com/mecham/spam/kernel.html, which explains why we +have to do what we now have to do. + +h3. Upgrading the kernel + +<pre> + <code> + root# apt-get -t testing install initrd-tools + </code> +</pre> + +This installed 0.1.84.1, so next time to install an updated kernel that can handle udev. + +<pre> + <code> + root# apt-get -t testing install linux-image-486 + </code> +</pre> + +Lots of stuff gets installed, including a 2.6.15 kernel. It asks you if you +want to upgrade glibc during this so say yes. When it's finished, reboot the +system. + +When it comes back up (and lets hope it does), time to install Ruby. Now you've +upgraded the kernel this is the same procedure the lucky people get to use. So +go and follow the instructions for Ruby and RubyGems there, then come back here +when it's time for GCC and it's toolchain. + +h3. Installing the testing version of the GCC toolchain + +Because of glibc issues now you've upgraded your kernel, you're back here. It's +actually not that different a command from the lucky people, it just installs a +bunch more stuff from testing than you might be comfortable with. + +<pre> + <code> + root# apt-get -t testing install build-essential + </code> +</pre> + +When it's finished, and you have your frankinstall, it's time to install Rails +and Mongrel at last! Back to the luck people page, it's the same from here on +in. Good luck! I don't guarantee you'll have a trouble free ride installing +whatever else you need to get going, but I hope you do. diff --git a/site/src/docs/distributed_worker.page b/site/src/docs/distributed_worker.page new file mode 100644 index 0000000..a74913e --- /dev/null +++ b/site/src/docs/distributed_worker.page @@ -0,0 +1,105 @@ +--- +title: Offloading +inMenu: true +directoryName: Offloading +--- + +h1. Long Tasks For Slow Rails + +You've got the best idea ever for a web site. It's a fantastic +Franken-stack that takes requests from the internet, converts them +to giant PDFs with latex, puts them onto 20 FTP servers, and then +encrypts them using 2^718 bit Eliptic Curve Encryption. + +Best of all, in order to avoid a "single point of failure" you've +decided that all this monstrous Rube Goldberg Architecture needs to +be run via a series of IO.popen calls to various Perl scripts. + + +h2. How Bad Ideas Begin + +Yes, this is contrived but not by much. I've actually had people report +architectures very close to this with pride. I have no idea why +making something complex suddenly makes it smart, but oh well, I just work +here. You do what you want, but hear me out for a second before you +continue down this path. + +Designs like this go very wrong very quickly for three main reasons: + +* Complex things are more fragile than simple things. Your application is going +down the same road as the Roman Empire, and just like them you don't realize it. +* Systems with large numbers of interconnections are slower simply because +everything takes time and more interconnections means more time to complete a process. +This isn't always the case, but given two systems that do the same work, I'll +take the simpler less connected version since I know I can make that faster. +* Complex things do not change easily, which feeds into their fragile nature and +means they can't improve in performance. + +An excellent example of the above three conditions is this wonderfully hilarious +"stack trace":http://ptrthomas.wordpress.com/2006/06/06/java-call-stack-from-http-upto-jdbc-as-a-picture/ (and the comments to back it up). Somehow it doesn't dawn on the author +that his "Business Logic" box is pointing at one line. The comments are full +of statements that support this type of design, but I bet half this crap isn't +really necessary. *This* my friends is the classic Rube Goldberg Architecture. + +Your Ruby brain is laughing at this, and now you want to do the exact same +thing? Start laughing at yourself my friend because you're next. + + +h2. Down With Complexity + +Before you start offloading tons of work to external programs and designing your +Franken-stack, step back and ask this very simple question: + + "How could I do the same thing with less stuff?" + +Your goal for the next two hours is to remove anything that can be done +simpler, isn't needed, or just simply adds overhead. You want to ignore +that voice in your head screaming, "*But how will you get a job!?*" Yell +back at it, "*I have a job!*" And then do your job. Create a system that +does what it's supposed to with the least amount of resources. No more, no +less. If you need to add something, add it later. Right now an 80% solution +that works is better than a 99% solution that's out 6 years from now. + + +h1. The Distributed Worker Pattern + +You've simplified your Rube Goldberg Architecture down to the bare minimum +and you've thought of simpler ways to do your processing, but you *still* +have to call an external program. There's no way to turn this program into +a server, and the program takes a long time to run. + +Whatever you do, don't use IO.popen() to run it. Don't use exec. Nothing. +People think that calling these functions to run an external program suspends +the current Rails request while the external program runs. That's right. What's +wrong is that it *suspends every other request as well*. Mongrel will still accept +connections and happily queue them all up, but it waits for Rails to exit this +request before it gives it the next one. + +What you *need* to do is give this request to a special server called a +"Distributed Worker". This is a simple pattern where you hand something +that takes forever to a server that knows how to do two things: + +# Run the request to produce a result. +# Report status to the requester when asked. + +The typical scenario for using a Distributed Worker is something like this: + +# You have a Rails server and a Worker server running. They talk using DRb. +# Request comes into Rails, and an action builds the information needed by the Worker. +# Rails submits the request to the Worker and takes a ticket. It stuffs this into the user's +session and then sends them to a "status action". +# The Worker begins working on the request identified by the ticket. +# At periodic intervals (probably with JavaScript) the client hits the status action which +in turn takes the ticket and asks the Worker for status. +# When the Worker is done it tells the status action in one of the status responses and +the status action goes to a "collector action" that picks up the results using the ticket. +# Finally, the collector gets the result from the worker and presents it to users. + +If you're smart, you can actually have all this going on in the "background" of the +user interface in such a way that the user just sees requests queue up and slowly change +state until they are done. + +The particulars of actually implementing this pattern are left to you, since +the idea is that it's probably different for everyone. There is one project +though that makes this whole process generic and fairly easy called +"BackrounDRb":http://backgroundrb.rubyforge.org/ thanks to Ezra Zygmuntowicz. diff --git a/site/src/docs/frameworks.page b/site/src/docs/frameworks.page new file mode 100644 index 0000000..7a55edb --- /dev/null +++ b/site/src/docs/frameworks.page @@ -0,0 +1,34 @@ +--- +title: Frameworks +inMenu: true +directoryName: Frameworks +--- + +h1. Framework Issues + +As known issues specific to different frameworks are reported they get +posted here. Problems reported here aren't a sign that a framework is +worse or better than others, just a reflection of people's experiences. + +h2. Rails + +* Rails is not thread safe so if you have long running actions then you can potentially +block the server. Keep in mind that this is the case for *any* server, it's just more +pronounced for Rails. +* You can easily kill a Rails server by not closing files. Always open files in blocks. +* If you turn on @allow_concurrency@ them Mongrel will run your application without any thread locking. +This can be dangerous so test it first, and I'm pretty sure it won't work for most complex applications. +* Mongrel will honor a HUP request and try to do an internal reload, but this usually doesn't work the +way you think. Mongrel's shutdown process is very nice and safe, so just restart. + + +h2. Camping + +* Running Camping and Rails in the same Ruby interpreter +causes all sorts of problems with ActiveRecord. You either run one or the +other but not both. + + +h2. Nitro + +* None reported yet. diff --git a/site/src/docs/gem_plugin.page b/site/src/docs/gem_plugin.page new file mode 100644 index 0000000..0e2ec96 --- /dev/null +++ b/site/src/docs/gem_plugin.page @@ -0,0 +1,335 @@ +--- +title: Writing Plugins +inMenu: true +directoryName: Documentation +--- + +* Jun 16: These docs are out of date and will be updated soon. * + +h1. Writing Mongrel Plugins with GemPlugin + +Mongrel uses a system called "GemPlugin":http://mongrel.rubyforge.org/gem_plugin_rdoc +to let people create simple "RubyGems":http://rubyforge.org/projects/rubygems/ +extensions that get loaded dynamically when Mongrel starts. This help document +is more for experienced Ruby developers interested in extending Mongrel with +new commands. + +*NOTE:* You can currently only write new commands +such as "mongrel_rails dostuff". Handlers and +Filters are on their way. + +h2. Why Plugins for Mongrel? + +If you've ever used other systems that have plugins you'll find there are +several painful parts to writing your own plugin: + +* You don't have their developer tools so it takes forever to get started. +* You don't know how to structure the project or even where to start. +* Once you get it built you have to figure out how to register and install it. +* When you want to update it you have to somehow get the update out to users. +* You have to manage dependencies between your plugin and other plugins. +* Including and using your own resources (images, html files, etc.) or config files + is really painful. Not sure why this is but it's really annoying. + +Most people solve many of the problems by the "sourdough method". They +take a plugin that currently works, copy it, and base their plugin +on the prototype. This works great until you want to do something +original or until you find out the original author did something wrong. + +GemPlugin solves this problem by relying on the regular RubyGems +package management system to give you: + +* A simple way to package, distribute, manage, and update your Mongrel plugins. +* Dynamic loading based on the gem dependencies and not on specific configurations. +* Segregated extensions for Mongrel. +* All the capabilities of RubyGems. GemPlugin doesn't mess with them at all. +* A little generator that starts your project off right similar to how Rails generators work. +* The ability to package resources (images, configs, etc.) with your gem and then load them + dynamically no matter where the gem eventually gets installed. + +In the end GemPlugins are just RubyGems that get loaded a special way +so they are activated right away. The only extra thing is a bit of +"magic" that puts plugins into a nice little namespace outside of +the usual Ruby module and class hierarchy. + +The advantage for Mongrel is that people can write their own +plugins and distribute them easily without anything more complex +than what all Ruby developers already have: RubyGems. + +h2. How They Work + +Mongrel plugins (a.k.a. GemPlugins) are really very simple. Basically +when you use the GemPlugin API you tell it what kind of dependencies +will trigger a gem to be loaded. GemPlugin then goes through all of +the installed gems and loads any that meet the dependency description. +Then your plugin just registers itself in the right "plugin category" +and everything starts working. The user of your plugin won't have to +do too much unless you want to give them additional configuration. + +The best way to understand this is to actually build a plugin +and watch it work. + + +h2. Your First Plugin + +Make sure that you have rubygems, mongrel, gem_plugins, and +rake all installed and working right. + +Yes, this means that you are forced to use RubyGems. People who really don't +like RubyGems are free to take the core of the Mongrel Server API and re-use it +as they wish. Just using Mongrel doesn't require this return so feel free to +use it like crazy in your commercial software or other licensed project. + +Once you have all that installed you need to simply find a nice quiet +place to generate your initial plugin directory with the *gpgen* +command: + + $ gpgen myplugin + +Let's say we're going to reimplement the existing mongrel_status example +command which just prints out the PID of a running Mongrel server +on any POSIX system. (Win32 folks will have to play along). + +Just do the following: + + $ gpgen mongrel_status + Creating directory mongrel_status + Creating file mongrel_status/COPYING + Creating directory mongrel_status/lib + Creating file mongrel_status/LICENSE + Creating file mongrel_status/Rakefile + Creating file mongrel_status/README + Creating directory mongrel_status/resources + Creating directory mongrel_status/tools + Creating directory mongrel_status/lib/project + Creating file mongrel_status/lib/project/init.rb + Creating file mongrel_status/resources/defaults.yaml + Creating file mongrel_status/tools/rakehelp.rb + Creating proper 'mongrel_status/lib/mongrel_status/init.rb' file + +This creates the skeleton of your plugin project. There's not too many +files in here, but let's cover what each one does: + +* *mongrel_status/COPYING* -- Your license or copying restrictions. +* mongrel_status/lib -- Where you store your source for the plugin to use. +* *mongrel_status/LICENSE* -- Your license again. +* *mongrel_status/Rakefile* -- Builds your stuff. +* *mongrel_status/README* -- Instructions for using your plugin. +* mongrel_status/resources -- A place to put files your plugin might need. +* mongrel_status/tools -- Tools used by rake. +* mongrel_status/lib/mongrel_status -- Your init.rb goes here. +* *mongrel_status/lib/mongrel_status/init.rb* -- Required to initialize your plugin. +* mongrel_status/resources/defaults.yaml -- Default configuration options. +* mongrel_status/tools/rakehelp.rb -- Used by the Rakefile. + +The files in bold are the ones you're going to have to edit to create your +plugin. + + +h2. COPYING, LICENSE, and README + +At first you can probably just skip these, but when you go to +distribute your plugin you'll need to make sure that you have +some kind of license on it and some basic instructions for +people to read. + +Check "OSI":http://www.opensource.org/ for a list of some available licenses. + + +h2. Rakefile + +This is the first place you need to go in order to setup your Mongrel command +correctly as a Mongrel plugin. The file is pretty small, but if you're +not familiar with Rake syntax you can "read the Rake docs":http://rake.rubyforge.org/ +for more advanced help. + +To get you started though, here's what you have to change: + +Change the version from 'version="0.1"' to whatever you want. + +Go the setup_gem block and add mongrel as a dependency, set yourself as the author, and change the summary: + + setup_gem(name, version) do |spec| + spec.summary = "The mongrel_status GemPlugin" ## change this + spec.description = spec.summary + spec.author="Nobody" ## change this + spec.add_dependency('gem_plugin', '>= 0.2') + spec.add_dependency('mongrel', '>= 0.3.10') ## add this + spec.files += Dir.glob("resources/**/*") + end + +That's it for the Rakefile. It won't do much since you still have to setup the actual command +in the mongrel_status/lib/mongrel_status/init.rb file, but you could build this right now +and install it: + + $ rake + $ gem install pkg/mongrel_status-0.1.gem + +Not so glamorous yet you'll get there soon. + +*You might need to do 'gem uninstall mongrel_status' if you installed that already.* + + +h2. lib/mongrel_status/init.rb + +This is where the magic really start to happen. In this file is just a +little nothing class you need to edit in order to get everything working +right. Here's the contents that you should have: + + require 'gem_plugin' + class ChangeME < GemPlugin::Plugin "/somecategory" + end + +You'll want to change this file to be the following: + + require 'gem_plugin' + require 'mongrel' + class Status < GemPlugin::Plugin "/commands" + include Mongrel::Command::Base + end + +This doesn't quite do anything useful as a command yet, but now if we +remove the gem and rebuild we'll be able to see *mongrel_rails* list +the command as being available: + + $ gem uninstall mongrel_status + $ rake + $ gem install pkg/mongrel_status-0.1.gem + $ mongrel_rails + Available commands are: + + - restart + - start + - status + - stop + + Each command takes -h as an option to get help. + $ + +See how your list of available commands now has "status" listed? +Try doing 'gem uninstall mongrel_status' and run *mongrel_rails* +again to see the command go away. Keep doing this until the +magic wears off. + + +h2. Explaining the PFM + +How the hell does GemPlugin do that? I mean, you just tweaked a +Rakefile and made a class and *somehow* mongrel_rails gets +a brand new command without you ever touching Mongrel. Magic +right? No, PFM. + +Here's how all of this works (for the engineers who can't use +something until they've analysed it's complete chemical composition): + +# Your gem has two dependencies: gem_plugin and mongrel. +# Your gem also has a file lib/mongrel_status/init.rb +# This init.rb file has 'class Status < GemPlugin::Plugin "/commands"' + which registers it as a Plugin at the "/commands" category. +# When *mongrel_rails* starts it tells GemPlugin to load all the plugins (gems) + that depend on both gem_plugin and mongrel (but not rails). +# The GemPlugin system then goes through all the installed gems and simply + does a require on the init.rb when it finds a gem with the right dependencies. +# Since your gem depends on the right stuff, and has a lib/mongrel_status/init.rb + it gets loaded properly and *mongrel_rails* can list it. +# The only remaining magic is the Status class is loaded by init.rb and that + puts it into the /commands category of plugins. Mongrel rails considers + and plugin in this category to be a valid Mongrel command. + +What happens in someone else puts a Plugin in the /commands category? Well, +since *mongrel_rails* is only loading gems which depend on *both* gem_plugin +and *mongrel* then there's no problems. You Snafu project can be safe knowing +it's /commands won't get loaded. + +An additional piece of magic is that *mongrel_rails* holds off on loading +and GemPlugin that depends on gem_plugin, mongrel, *and* rails. These +plugins are intended to be rails specific extensions or other things +that you want Mongrel to have, but only *after* Rails is loaded and configured. + +What the above paragraph means is that, yes, you can actually distribute plugins +for Rails using the Mongrel GemPlugins without having to go through the usual +Rails plugin system. This isn't tested yet, but feel free to try it out. + + +h2. The Final Touches + +The only thing that would be left is to actually implement the full +mongrel_status command. Rather than put the code into this document, +you can just read what's in the +"Subversion repository":http://rubyforge.org/plugins/scmsvn/viewcvs.php/*checkout*/trunk/projects/mongrel_status/lib/mongrel_status/init.rb?root=mongrel&rev=91 +and put that into your own init.rb. + +What the code there does is first sets up the command line options available, +then validates them, and finally just runs the command in order to do it. +The code should be easy to follow but let me know. + +h3. A Note On Modules + +If you want you can put your plugin into a module and then it will be registered +under "/commands/modname::cmdname". So you did this: + + require 'gem_plugin' + require 'mongrel' + module Examples + class Status < GemPlugin::Plugin "/commands" + include Mongrel::Command::Base + end + end + +Then the command would show up from mongrel_rails as "examples::status" and +would be known as "/commands/examples::status". + + +h2. Big Tricks With GemPlugin + +"GemPlugin":http://mongrel.rubyforge.org/gem_plugin_rdoc/ has a couple of other +methods that will really help you when writing a plugin that needs to have +some default configurations or possibly have external data resources. + +The first is the GemPlugin::Manager.instance.resource method. What this method +does is when given a gem and a path, it find the actual path to that resource +in the installed gem directory. These resources are packaged up out of your resources/ +directory in your plugin source. + +Using the current example if I wanted to get to the mongrel_status/resources/defaults.yaml +file I'd do: + + defaults = GemPlugin::Manager.instance.resource "mongrel_status" "/defaults.yaml" + +And then try to load that as a YAML file. The *resource* method returns nil if +no such file exists for the given plugin, and the plugin has to be loaded first. + +The problem with doing this for something like default configuration options is +that you'll end up writing a ton of the same code for all your plugins. To help +with this, GemPlugin also has a *config* method which takes a set of options, and +then tries to load the *resources/defaults.yaml* file as a set of defaults (modified +by the options). + +Let's say you want to default to having debug off, but allow the user to pass in some +options to turn it on. This is how you'd do it: + + options = GemPlugin::Manager.instance.resource "mongrel_status", user_ops + +What this does is simply use the *resource* method to locate and load the resources/defaults.yaml +file, and then merge the *user_ops* hash into them so *user_ops* override the defaults. + +The point of these two pieces of functionality is that now you can completely package +all the external resources your plugin needs and access it without much user +intervention. It would be a good idea to let the user override these paths. + + +h2. Rails Plugins Without Rails Plugins + +Ruby on Rails already has "an extensive set of plugins":http://wiki.rubyonrails.org/rails/pages/Plugins +that you can choose from to make your application. One thing you can do though is +instead of using the normal Rails plugin system you could just make a GemPlugin +that depends on gem_plugin, mongrel, and *rails* gems. When you do this it will be loaded +after Mongrel configures Ruby on Rails. If you put your code in to the init.rb and +use the resources and configuration features of GemPlugin then you could package a +very nice little plugin without having people go through the normal plugin method. + +I'm not sure if this has any advantages or disadvantages over the current system, but +other Mongrel supported frameworks (like Camping and Nitro) can use this same technique +to load plugins that are specific to how Mongrel works. + + diff --git a/site/src/docs/how_many_mongrels.page b/site/src/docs/how_many_mongrels.page new file mode 100644 index 0000000..7911293 --- /dev/null +++ b/site/src/docs/how_many_mongrels.page @@ -0,0 +1,90 @@ +--- +title: Tuning +inMenu: true +directoryName: Tuning +--- + +h1. How Many Mongrel Instances Should I Run? + +There is no set number that is "best" since that depends on factors like the +type of application, server hardware, how dynamic the appication is, etc. + +I've found that 8-12 mongrel processes per CPU is about right, but I determined +this by starting with 1 and then doing the following: + +h2. Baseline Your Server + +Pick a URL to a small file that is running on your apache server and is not +served by Mongrel at all. This URL will be your "best possible baseline". + +Build your baseline measurement first. Using httperf, measure the speed of +your URL so that you know how fast you could possibly get if you served +everything static in ideal situations. + +**Make sure you do this on a different machine over an ideal network.** +Not your damn wifi over a phone line through sixteen poorly configured routers. +Right next to the box your testing with a fast switch and only one hop is the +best test situation. This removes network latency from your test as a +confounding factor. + + +h2. Baseline Rails and Mongrel + +Pick a page that's a good representative page for your application. Make sure +you disable logins to make this test easier to run. Hit this Rails page and +compare it to your baseline page. + +* If your rails measurement is *faster* than your baseline + measurement then you screwed up. Rails shouldn't be faster than a file + off your static server. Check your config. +* If your rails measurement is *horribly slow* compared to baseline + then you've got some config to do before you even start tuning the + number of process. Repeat this test until one mongrel is as fast as + possible. + +h2. Tweak + +Once you've got a Rails page going at a reasonable speed, then you'll want to +increase the --rate setting to make sure that it can handle the reported rate. + +Finally, alternate between adding a mongrel process and running the test with +the next highest rate you'd get. Stop when adding one more server doesn't +improve your rate. + +*Make sure you run one round of the test to get the server "warmed up", and +then run the real one.* Heck, run like 5 or 6 just to make sure you're not +getting a possibly bad reading. + +h2. Example + +# Run the test and find out that one mongrel can support a @--rate@ of 120 req/second. +# Add another mongrel and run the test again with @--rate@ 240. It handles this + just find so you add another and get @--rate@ 360. +# Try another one and it dies. Giving @--rate@ 480 gets you only a rate of 100. + Your server has hit it's max and broke. +# Try tuning the @--rate@ down at this and see if it's totally busted (like, 4 mongrels + only gets you @--rate@ 380) or if it's pretty close to 480. +# That should do it. A good practice is to also look at the CPUs on the + server with top and see what kind of thrashing you give the server. + + +h2. HTTPERF + +Here's the commands I use for each test, but read the man page for +httperf so that you learn to use it. It's an important tool and just +cut-pasting what I have here is not going to do it for you. + +* @httperf --server www.theserver.com --port 80 --uri /tested --num-conns <10 second count>@ + +* @httperf --server www.theserver.com --port 80 --uri /tested --num-conns <10 second count> --rate <reported req/sec>@ + +Where @<10 second count>@ is enough connections to make the test run for 10 +seconds. Start off with like 100 and keep raising it until it goes for 10 +seconds. + +Where @<reported req/sec>@ is whatever httperf said the estimated +requests/second were. What you're doing here is seeing if it really can handle +that much concurrency. Try raising it up and dropping it down to see the +impact of performance on higher loads. + +Have fun. diff --git a/site/src/docs/howto.page b/site/src/docs/howto.page new file mode 100644 index 0000000..f59bcfa --- /dev/null +++ b/site/src/docs/howto.page @@ -0,0 +1,490 @@ +--- +title: HOWTO +inMenu: true +directoryName: Documentation +--- + +h1. Mongrel HOWTO + +After you have "Mongrel running":started.html you can start +to configure Mongrel and tune it to your specific configuration. +The "documentation":/docs/index.html page has documentation +on the various web servers you can run, but this document +will give you tips and tricks on getting Mongrel running. + + +h2. Every Start Option Explained + +Mongrel is a self-documenting program by giving you an extensive help +listing for each command. Simply running *mongrel_rails start -h* +will print out each possible option and what it does. Most of +the options are similar to what you've been using already with +script/server. + +These options are also used in the -C config file option to +set them without using the command line. The name used in the +config file is slightly different since it's a YAML file. The +name to use is in parenthesis after the option name. The +option names are different from the command line option names +mostly for historical reasons. + + +<dl> +<dt>-e, --environment (:environment)</dt> +<dd> +Configures your Rails environment to what you need. +<ul><li><b>Default:</b> development</li></ul> +</dd> +</dl> + +<dl> +<dt>-d, --daemonize (:daemon)</dt> +<dd> +If given (no options) then Mongrel will run in the background. +<b>No Win32.</b> +<ul><li><b>Default:</b> false</li></ul> +</dd> +</dl> + +<dl> +<dt>-p, --port (:port)</dt> +<dd> +Port to bind to when listening for connections. +<ul><li><b>Default:</b> 3000</li></ul> +</dd> +</dl> + +<dl> +<dt>-a, --address (:host)</dt> +<dd> +Address to bind to when listening for connections. +<ul><li><b>Default:</b> 0.0.0.0 (every interface)</li></ul> +</dd> +</dl> + +<dl> +<dt>-l, --log (:log_file)</dt> +<dd> +Where to dump log messages in daemon mode. Use an *absolute* path. +<b>No Win32.</b> +<ul><li><b>Default:</b> $PWD/log/mongrel.log</li></ul> +</dd> +</dl> + +<dl> +<dt>-P, --pid (:pid_file)</dt> +<dd> +Where to write the PID file so <b>start</b> and +<b>stop</b> commands know the Process ID. Use *absolute* paths. +<b>No Win32.</b> +<ul><li><b>Default:</b> $PWD/log/mongrel.pid</li></ul> +</dd> +</dl> + +<dl> +<dt>-n, --num-procs (:num_processors)</dt> +<dd> +Maximum number of concurrent processing threads before +Mongrel starts denying connections and trying to kill +old threads. +<ul><li><b>Default:</b> 1024</li></ul> +</dd> +</dl> + +<dl> +<dt>-t, --timeout (:timeout)</dt> +<dd> +Time to pause (in hundredths of a second) between accepting +clients. Used as a throttle mechanism. +<ul><li><b>Default:</b> 0</li></ul> +</dd> +</dl> + +<dl> +<dt>-m, --mime, (:mime_map)</dt> +<dd> +A YAML file that maps from file extensions to MIME types +for static files. It's important that if you are using +page caching or you have a different language setting--like +UTF8--then you have to configure this. Read more below. +<ul><li><b>Default:</b> not set.</li></ul> +</dd> +</dl> + +<dl> +<dt>-c, --chdir (:cwd)</dt> +<dd> +Directory to change to prior to starting Mongrel. "cwd" means +"change working directory". +<ul><li><b>Default:</b> . (current directory)</li></ul> +</dd> +</dl> + +<dl> +<dt>-r, --root (:docroot)</dt> +<dd> +Document root where Mongrel should serve files from. +If you are putting Mongrel under a different base URI, and +you want it to serve files out of a different directory then +you need to set this. +<ul><li><b>Default:</b> public</li></ul> +</dd> +</dl> + +<dl> +<dt>-B, --debug (:debug)</dt> +<dd> +Turns on a debugging mode which traces objects, threads, files +request parameters, and logs accesses writing them to log/mongrel_debug. +This option makes Mongrel <b>very</b> slow. +<ul><li><b>Default:</b> false</li></ul> +</dd> +</dl> + +<dl> +<dt>-C, --config (NONE)</dt> +<dd> +Specifies a configuration YAML file that sets options you're +reading about right now. Read "Command Line Settings" below +for more information. Use *absolute* paths. +<ul><li><b>Default:</b> no default</li></ul> +</dd> +</dl> + +<dl> +<dt>-S, --script (:config_script)</dt> +<dd> +A special Ruby file that is run after Rails is configured +to give you the ability to change the configuration with +Ruby. This would be where you can load customer Mongrel +handlers, extra libraries, or setup additional Ruby code. +This option is fairly advanced so use with caution. +<ul><li><b>Default:</b> not set</li></ul> +</dd> +</dl> + + +<dl> +<dt>-G, --generate (NONE)</dt> +<dd> +Takes whatever options you've set for Mongrel, and the +current defaults, and then writes them to a YAML file +suitable for use with the -C option. +<ul><li><b>Default:</b> not set</li></ul> +</dd> +</dl> + +<dl> +<dt>--prefix uri</dt> +<dd> +A URI to mount your Rails application at rather than the default +/. This URI is stripped off all requests by Rails (not Mongrel) +so it <b>cannot</b> end in /. +<ul><li><b>Default:</b> not set</li></ul> +</dd> +</dl> + + +<dl> +<dt>--user USER</dt> +<dd> +<b>Must have --group too.</b> +The user to change to right after creating the listening socket. +Use this if you have to bind Mongrel to a low port like port 80, +but don't want Mongrel to run as root. <b>Not useful in Windows.</b> +<ul><li><b>Default:</b> not set</li></ul> +</dd> +</dl> + + +<dl> +<dt>--group GROUP</dt> +<dd> +<b>Must have --user too.</b> +The group to change to right after creating the listening socket. +<b>Not userful in Windows.</b> +<ul><li><b>Default:</b> not set</li></ul> +</dd> +</dl> + +h2. Configuration Files + +When Mongrel runs with just *mongrel_rails start* it has +reasonable defaults for most people's development work with Rails. +It tries to be as similar to the existing @script/server@ command as +possible. + +When you need to run Mongrel in production (or if you're doing +wicked fancy stuff) then you'll need to start using a few +configuration files. Problem is the configuration file is in +this weird YAML syntax that most people just hate. Rather than +describe the file's syntax and all possible options, Mongrel has +a -G (generate) feature that will take any command line options +you give it, generate the YAML file to replicate those options, and +then exit. For example, you could make a config file like this: + + @mongrel_rails start -G mongrel_8080.yml -e production -p 8080@ + +And it'll write all the options possible to mongrel_8080.yml, but +with your specific changed for environment (-e production) and +port (-p 8080). + +When you run a configuration file with -C, don't pass other options. +Rather than have complex rules about whether a configuration file or +command line option wins, mongrel_rails just uses configuration file +and defaults, or command line options and defaults. Basically don't mix, +it won't work. + + +h2. MIME Types + +Mongrel comes with a very small set of default MIME types. +The main theme with Mongrel is that it doesn't interfere with +the frameworks it hosts. Many frameworks do their own +MIME parsing and control, so Mongrel only has just enough to +serve up a few static files. + +The default types are defined in DirHandler as a constant +and are: + + <pre><code> + MIME_TYPES = { + ".css" => "text/css", + ".gif" => "image/gif", + ".htm" => "text/html", + ".html" => "text/html", + ".jpeg" => "image/jpeg", + ".jpg" => "image/jpeg", + ".js" => "text/javascript", + ".png" => "image/png", + ".swf" => "application/x-shockwave-flash", + ".txt" => "text/plain" + } + </code></pre> + +Notice that it's just a hash mapping from extension (*with period*) +to the type that needs to be set. + +To change this you just need to write a YAML file that sets +up your new types or changes these: + + <pre> + <code> + --- + .rss: text/xml + </code> + </pre> + +This would add .rss with the @text/xml@ MIME type. + +One problem that comes up quite frequently is that Mongrel's +DirHandler isn't quite smart enough to know that a page cached +/feed/rss.html should really be an RSS file with text/xml. +Mongrel really doesn't have much information to go on, but it +will happily serve this file up as @text/html@. The best +solution to this is to just not use Mongrel's DirHandler, but +instead use a real web server. Another option is to write a +special handler for that URI which knows about it. + +You might also need to edit this file if, for example, you use a different encoding such as UTF8. +You'll want to change all of these MIME types to have the +proper ending. For example,if you wanted @charset=EUC-JP@ for +all your returned static documents, then you'd do: + + <pre> + <code> + --- + .js: text/javascript; charset=EUC-JP + .htm: text/html; charset=EUC-JP + .html: text/html; charset=EUC-JP + .css: text/css; charset=EUC-JP + .txt: text/plain; charset=EUC-JP + </code> + </pre> + +You'd also probably need to do this with your Rails pages. + +*NOTE:* I'm looking for a method to fix this with a setting or detection. + + +h2. Command Line Settings + +Sometimes it's a real pain to set all the command line options +you need to run Mongrel in production. Instead of setting the +options on the command line, you can have Mongrel generate a +configuration file for you with -G and then pass this (modified) +file to the -C option next time you start. + +For example, if you do this: + + mongrel_rails start -G config/mongrel_opts.conf + +Then the mongrel_options.conf will have: + + <pre> + <code> + --- + :config_script: + :environment: development + :pid_file: log/mongrel.pid + :num_processors: 1024 + :docroot: public + :timeout: 0 + :host: 0.0.0.0 + :mime_map: + :port: 3000 + :daemon: false + :cwd: /home/zedshaw/projects/mongrel/testapp + :includes: + - mongrel + :debug: false + :log_file: log/mongrel.log + </code> + </pre> + +The @:blah:@ (two colons) syntax is just how YAML does things. +You can then either just edit this file and use it with: + + mongrel_rails start -C config/mongrel_opts.conf + +Or, you can run the start command again with -G and all the +options you need to set and it will properly generate the +config file again. + + +h2. Mongrel Configure Scripts + +Mongrel uses a small DSL (Domain Specific Language) to configure +it's internal workings. It also lets *you* use this DSL and +regular Ruby to alter it's internal workings. The options that +turn it on are -S or @:config_script:@ in the config file. + +Doing this is fairly advanced, but here's how I would create a +second DirHandler that sits in another directory. First, create +a config/mongrel.conf file with this in it: + + @uri "/newstuff", :handler => DirHandler.new("/var/www/newstuff")@ + +And then do this: + + mongrel_rails start -S config/mongrel.conf + +Now when people go to /newstuff they get the files listed there. + +This is actually a Ruby file, so you can run +most Ruby code you need, require libraries, etc. + +Main usage for this is to create handlers which run inside Mongrel +and do extra work. + +For more information, read the "RDoc":/rdoc/ for +"Mongrel::Configurator":/rdoc/classes/Mongrel/Configurator.html +on what functions are available. + + +h2. POSIX Signals Used + +When you run Mongrel on a POSIX compliant system (meaning *not* Win32) +you are able to control with signals similar WEBrick or FastCGI. + +The signals Mongrel running Rails understands are: + +* *TERM* -- Stops mongrel and deleted PID file. +* *USR2* -- Restarts mongrel (new process) and deletes PID file. +* *INT* -- Same as USR2, just convenient since CTRL-C is used in debug mode. +* *HUP* -- Internal reload that might not work so well. + +You can use the -S configure script to add your own handlers +with code like this: + + <pre><code> + trap("USR1") { log "I'm doing stuff." } + </code></pre> + + +h2. Super Debugging With Rails + +When you use the -B option Mongrel produces *tons* of +useful debugging output. The debugging output is actually +implemented as a small set of handlers in lib/mongrel/debug.rb +if you're interested in writing your own. + +The files that get generated are: + +* *rails.log* -- Logs all request parameters exactly as they come to Rails from Mongrel. +* *objects.log* -- Logs a top 20 count of object types before and after each request. +* *files.log* -- Logs open files before and after each request. +* *threads.log* -- Logs active threads before and after each request. + +You use these log files to track down weird Rails behavior in your +application. Classic example is if your Rails server stops answering +requests after a certain amount of time. #1, #2, and #3 cause of this is +that you are opening files and not closing them. Turning on -B and +look in the @files.log@ file will show you exactly what files are +being leaked. + +Another place this helps is if you see that your application is generating +a lot of RAM. Look in @objects.log@ and you'll see right away what is the worst +offending Object. + +Finally, the @threads.log@ will tell you if you're leaking threads. +This happens mostly with people who use IO.popen and don't properly +clean up the results. IO.popen in Ruby threads is very tricky, +and you're better off putting this work into a DRb server anyway. + + +h2. Installing GemPlugins: mongrel_cluster + +Mongrel is extensible via a system called GemPlugins. They +are basically autoloaded RubyGems which you install and are +configured based on how they depend on Mongrel. + +A good example is the @mongrel_cluster@ GemPlugin written +by Bradley Taylor from RailsMachine. It gives you a nice +management system for a cluster of Mongrel servers. This +is very handy when you are running a large scale deployment +and I recommend everyone uses it. + +You install it simply with: + + $ gem install mongrel_cluster + +Once it's installed you can do @mongrel_rails -h@ and it'll +show you the new commands: + +* cluster::configure -- Configures your cluster. +* cluster::restart -- Restarts it. +* cluster::start -- Yep, starts it. +* cluster::stop -- And, yes, stops it. + +You can then pass --help to each one to find out the options +it gets. You then use it like so: + + + $ mongrel_rails cluster::configure -p 8080 -e production -a 127.0.0.1 + $ mongrel_rails cluster::start + $ mongrel_rails cluster::stop + +If you don't like mongrel_cluster (shame on you!) then you can +easily remove it with: + + $ gem uninstall mongrel_cluster + +And all the commands go away. + + +h1. More Documentation + +This should get you started with intermediate Mongrel usage. +There quite a few more documents in the "Documentation":/docs/index.html +section in various states of completion. + +If you'd like to write one of these documents, then join the +"mailing list":http://rubyforge.org/mailman/listinfo/mongrel-users +and volunteer. + + +h1. Credits + +Thanks to "Jamie van Dyke":http://www.fearoffish.com/ and mly on #caboose for correcting some +grammar mistakes. diff --git a/site/src/docs/index.page b/site/src/docs/index.page new file mode 100644 index 0000000..8e2a0ff --- /dev/null +++ b/site/src/docs/index.page @@ -0,0 +1,107 @@ +--- +title: Documentation +inMenu: true +directoryName: Documentation +--- + +h1. Available Documentation + +We've got a small set of documentation to get people going. Most of it is +geared toward Ruby on Rails but other projects using Mongrel should have their +own docs that you can refer to based on these. + +h2. Contributing Documentation + +As you can see many of the pages here are empty. This is because they are +being written mostly by one person (Zed) but some folks are now contributing +documentation so things will go faster. If you'd like to contribute some +documentation then read the "instructions on how to do it":contrib.html and +contact the "mailing list":http://rubyforge.org/mailman/listinfo/mongrel-users +to announce that you'd like to work on something. + +h2. Getting Started + +Start here to get a good grounding in getting Mongrel up and running. These +documents are targeted at developers who will be starting off using Mongrel and +might want to throw in a few little tricks. Serious deployments should check +out *Deployment Best Practices*. + +* "Getting Started":started.html -- Installing and Other things +* "Win32 HOWTO":win32.html -- Specific instructions for running on windows. +* "HOWTO":howto.html -- Doing advanced stuff with Mongrel. +* "Using mongrel_cluster":mongrel_cluster.html -- Nifty plugin for managing your clusters. +* "Choosing A Deployment":choosing_deployment.html -- How to pick a best practice. +* "Security":security.html -- Security issues to look at (for any web application). +* "Frameworks":frameworks.html -- Specific problems and things to know when you run different frameworks. + +h2. Deployment Best Practices + +These documents are continually changing as deploying Mongrel becomes more +solid and certain options and configurations start to work the best. Each one +is a *best practice* which means that if you do something different then you'll +have to do your own research. Best way to work it is to do the *best practice* +exactly as described, then try to do something weird from there. + +* "Apache":apache.html -- The current preferred way to host Mongrel. +* "Litespeed":litespeed.html -- Another good option, but not open source. +* "Lighttpd":lighttpd.html -- Using mod_proxy to do a cluster. +* "Pound":pound.html -- Small scale and dead simple with SSL. +* "Pen or Balance":pen_balance.html -- Smaller scale without SSL. + + +h2. Advanced + +You are a grand master and have answered a few questions on the Mongrel mailing +list so now it's time to get fancy. + +* "Writing Mongrel Plugins":gem_plugin.html -- Writing a GemPlugin for Mongrel. +* "Distributed Worker":distributed_worker.html -- A common pattern for actions that take forever and block Rails. +* "Upload Progress Without Rails":upload_progress.html -- Uploading without blocking Rails and giving the user progress. + + +h2. Ruby API Documentation + +People writing plugins will want these. + +* "Mongrel RDoc":/rdoc/index.html +* "GemPlugin RDoc":/gem_plugin_rdoc/index.html + + +If there's documentation you'd like then feel free to E-mail the list or post +to the tracker. + +h1. Other People's Documentation + +Many other folks have written documentation that they post to their blogs for +people to use. I've based a lot of the documentation here on their writings, +so you should go check out their blogs and shoot them a thanks when you +can. + +* "What About Apache to Mongrel for Rails Applications":http://weblog.textdrive.com/article/219/what-about-apache-to-mongrel-for-rails-applications +* "Apache 2.2 worker on solaris to a remote mongrel":http://weblog.textdrive.com/article/223/apache-22-worker-on-solaris-to-a-remote-mongrel +* "Apache 2.2, mod_proxy_balancer and Mongrel":http://weblog.textdrive.com/article/224/apache-22-mod_proxy_balancer-and-mongrel +* "Scaling Rails with Apache 2.2, mod_proxy_balancer and Mongrel":http://blog.innerewut.de/articles/2006/04/21/scaling-rails-with-apache-2-2-mod_proxy_balancer-and-mongrel +* "Dead Simple Deployment":http://brainspl.at/articles/2006/04/26/dead-simple-deployment +* "Deployment Strategies for Rails on Windows servers":http://www.napcs.com/howto/rails/deploy/ +* "mongrel_cluster-0.1.1: the bird dog (capistrano support!)":http://fluxura.com/articles/2006/04/24/easy-mongrel-clustering-with-mongrel_cluster +* "Easy Mongrel Clustering with mongrel_cluster":http://fluxura.com/articles/2006/05/01/mongrel_cluster-0-1-1-the-bird-dog-capistrano-support + + +h1. Frequently Asked Questions + +When people ask questions really frequently the results end up in the +"FAQ":../faq.html. + + +h1. Mailing Lists + +There's a "mailing list":http://rubyforge.org/mailman/listinfo/mongrel-users +that you should subscribe to if you're looking for help or are interested in +tracking Mongrel. We post announcements of pre-release gems you can play with +to this mailing list and also discuss development of Mongrel there. + +Before you start asking for features you should read about +"bikeshedding":http://www.catb.org/jargon/html/B/bikeshedding.html and +understand that we're really nice, but sometimes code speaks better than +rhetoric. + diff --git a/site/src/docs/lighttpd.page b/site/src/docs/lighttpd.page new file mode 100644 index 0000000..973fd2c --- /dev/null +++ b/site/src/docs/lighttpd.page @@ -0,0 +1,278 @@ +--- +title: Lighttpd +inMenu: true +directoryName: Documentation +--- + +*I'm sad to say that I have to recommend people not use lighttpd anymore.* +The author hasn't updated the mod_proxy plugin and isn't providing too much +support for the bugs it has. If you're running lighttpd and you constantly +see 500 errors that are never recovered, then you should switch to Apache +or another web server that can handle properly balancing backends. + + +h1. Using Lighttpd With Mongrel + +It is possible to host an application with just Mongrel since it is +able to serve files like a normal web server. Still, no matter +how fast Mongrel gets it probably can't compete with something +like lighttpd for serving static files. Because of this I've +devised a simple way to setup a lighttpd+Mongrel setup that +demonstrates clustering four Mongrel servers running the same +application as backends. + +This is very similar to a FastCGI or SCGI setup except that +you're just using regular HTTP. Read through the "HOWTO":howto.html +for information on other possible deployment scenarios. + + +h2. The Goal + +What we want to do is put a lighttpd on the internet and then +have it proxy back to one of four Mongrel servers. + +!SimpleLighttpdMongrelSetup.jpg! + +This is actually really trivial and probably doesn't need a diagram +but I got bored just writing it up. + +How it all works is pretty simple: + +# Requests come to the lighttpd server. +# Lighttpd takes each request, and sends it to a backend depending + on how you configure it: + * hash -- Hashes the request URI and makes sure that it goes to the same backend. + * round-robin -- Just chooses another host for each request. + * fair -- "Load based, passive balancing." No idea what that means, but if it's like + the rest of lighttpd it probably means it will overload the first one and if that one's + busy then it starts using the next ones. +# Each backend doesn't really care about any of this since it's just a web server. + + +h2. Lighttpd Configuration + +For lighttpd you need to have *mod_proxy* in your server.modules setting: + + server.modules = ( "mod_rewrite", "mod_redirect", + "mod_access", "mod_accesslog", "mod_compress", + "mod_proxy") + +Then you need to tell lighttpd where the other backends are located: + + proxy.balance = "fair" + proxy.server = ( "/" => + ( ( "host" => "127.0.0.1", "port" => 8001 ), + ( "host" => "127.0.0.1", "port" => 8002 ), + ( "host" => "127.0.0.1", "port" => 8003 ), + ( "host" => "127.0.0.1", "port" => 8004 ) ) ) + +When I used lighttpd 1.4.9 and set proxy.balance="round-robin" I had an excessive number of +500 errors for no real reason. The "fair" setting seems to be the best, but if you +have a large number of fairly random URIs you should try "hash" too. + +*For the rest of this tutorial we'll assume you're running lighttpd on port 80.* + + +h2. Mongrel Configuration + +Mongrel is pretty easy to setup with this configuration on either Win32 or Unix, but +since lighttpd doesn't compile so easily on Win32 I'll just show the Unix method +for starting it: + + $ mongrel_rails start -d -p 8001 \ + -e production -P log/mongrel-1.pid + $ mongrel_rails start -d -p 8002 \ + -e production -P log/mongrel-2.pid + $ mongrel_rails start -d -p 8003 \ + -e production -P log/mongrel-3.pid + $ mongrel_rails start -d -p 8004 \ + -e production -P log/mongrel-4.pid + +Now you should be able to hit your web server at port 80 and it'll run against +one of your four Mongrels. + + +h2. Testing Stability and Performance + +As I mentioned before proxy.balance="round-robin" had many stability issues +in lighttpd 1.4.9 but how did I figure this out? Here's how you can do it. + +You use "httperf":http://www.hpl.hp.com/research/linux/httperf/ to first hit each +Mongrel backend with a large request set. + + $ httperf --port 8001 --server 127.0.0.1 \ + --num-conns 300 --uri /test + $ httperf --port 8002 --server 127.0.0.1 \ + --num-conns 300 --uri /test + $ httperf --port 8003 --server 127.0.0.1 \ + --num-conns 300 --uri /test + $ httperf --port 8004 --server 127.0.0.1 \ + --num-conns 300 --uri /test + +After each of these you're looking for the *Connection rate*, *Request rate*, +*Reply rate*, and *Reply status*. You first look at the *Reply status* to make +sure that you got all 2xx messages. Then look at the other three and make +sure they are about the same. + +Then you hit lighttpd with a similar request set to confirm that it handles the base case. + + $ httperf --port 80 --server 127.0.0.1 \ + --num-conns 300 --uri /test + +You should get no 5xx errors. In the case of round-robin there were about 60% +5xx errors even though the Mongrels were functioning just fine. The "hash" method +didn't improve this test's performance since there's only on URI in the test. It +seems the "fair" method is the best you can do right now. + +Finally you hit lighttpd with a 4x rate to see if it could handle the theoretical limit. + + $ httperf --port 80 --server 127.0.0.1 \ + --num-conns 10000 --uri /test --rate 600 + +It will most likely fail miserably and you'll probably see a few 5xx counts in the +*Reply status* line but that's normal. What you're looking to do is keep moving +--rate and --num-conns up/down until you get where the server is just barely +able to accept the requests without slowing down (i.e. your *Request rate* matches +your --rate setting). There will be a point where adding literally one more +to your --rate setting causes the Request rate to tank. That's your setup's breaking +point and is the actual requests/second you can handle. + + +h1. Insane Caching Power Magnet + +Mongrel (as of 0.3.7) by default supports Rails style page caching +in the RailsHandler it uses to serve your applications. What this +means is that if you do a page cached action (which writes a +.html file as well as respond) then Mongrel will just serve up +the cached page instead of bug Rails. + +This does give you a large boost in performance, but still not nearly +as much as if you had lighttpd doing the caching for you. The optimal +configuration would be where lighttpd checks for cached pages and then +served it directly as it already does with FastCGI. + +There are some technical problems with this and the lighttpd mod_proxy, +but thankfully I don't have to go into them because lighttpd now supports +the "power magnet" and Cache Meta Language (CML). CML is a small bit +of the Lua programming language that lets you script lighttpd and tell +it when to cache or not. The power magnet feature of CML lets you put +all requests through one CML script so that you can determine whether +to cache or not. + +h2. Configuration + +In order to get everything to work right you'll need a few pieces +of equipment and make sure they are enabled in your lighttpd build. +The sample build was done on Debian so that everything would work +including mod_rewrite, mod_memcache, mod_cml, and mod_redirect. + +* lua50, liblua50-dev +* libpcre3-dev +* memcached +* Finally make sure you configure with ./configure --with-lua --with-memcache + +Debian people will need to follow these instructions from +"www.debian-administration.org":http://www.debian-administration.org/articles/20 +for installing from source, and will also need to add these lines to your +/etc/apt/sources.list: + + deb-src http://http.us.debian.org/debian stable main contrib non-free + deb-src http://http.us.debian.org/debian unstable main contrib non-free + deb-src http://non-us.debian.org/debian-non-US stable/non-US main contrib non-free + +And then do the following: + +# Make sure you don't have the source extracted in your current directory. +# apt-get install devscripts debhelper build-essential fakeroot +# apt-get update +# apt-get source lighttpd +# nano -w lighttpd-1.4.10/debian/rules +## DEB_CONFIGURE_EXTRA_FLAGS need have at the end: --with-lua --with-memcache +# nano -w lighttpd-1.4.10/debian/lighttpd.install +## debian/tmp/usr/lib/lighttpd/mod_cml.so needs to be added here. + +Then you're supposed to be able to follow the debian-administration +docs but I haven't got it all working yet. Please clue me in +if you get this compiled on Debian. + +h3. lighttpd.conf + +If it all builds right then you'll be able to use this nifty +CML script that Bradley K. Taylor (from railsmachine.net) came +up with. First you need to tweak your lighttpd.conf at the +place where you have the mod_proxy setup from previous: + + $HTTP["host"] == "www.myhost.come" { + server.document-root = "/my/path/to/app/public" + cml.power-magnet = "/my/path/to/app/config/power-magnet.cml" + proxy.balance = "fair" + proxy.server = ( "/" => ( ( "host" => "127.0.0.1", "port" => 8001 ), + ( "host" => "127.0.0.1", "port" => 8002 ) ) ) + } + +This one just has two for simplicity. The big thing is the +document root setting and the power-magnet.cml setting. I wouldn't +put the power-magnet.cml in your public directory. + +h3. power-magnet.cml + +Now for the magic bit of Lua code that tells lighttpd what to do: + + dr = request["DOCUMENT_ROOT"] + + if file_isreg(dr .. "maintainance.html") then + output_include = { dr .. "maintainance.html" } + return CACHE_HIT + end + + f = request["REQUEST_URI"] + + if f == "/" or f == "" then + file = dr .. "index.html" + elseif not string.find(f, "%.") then -- rewrite for cached pages + file = dr .. f .. ".html" + else + file = dr .. f + end + + if file_isreg(file) then + output_include = { file } + return CACHE_HIT + end + + return 1 -- should be CACHE_MISS, but there's a bug in 1.4.10 + +Place this in the /my/path/to/app/config/power-magnet.cml like you +configured above. + +Now if you restart lighttpd and everything worked right you should +be able to see the headers and tell if Mongrel or lighttpd is serving +them. Use curl like this: + + curl -I http://zedapp.railsmachine.net/ + curl -I http://zedapp.railsmachine.net/admin + +The second one should redirect and show a Mongrel header while the +first one should show a lighttpd header. + + +h2. Memcached + +The next installment of this document will tell you how to setup a +memcached so that you can run the lighttpd on a different server +from the Mongrel cluster. Right now they all have to reside +on one machine. + +h2. CML Tricks + +Since Lua is a full blown and fast little language +you can get pretty creative with it. For example you could +have it check dates and times of files, look for processes +that should be running, run commands, check the contents of +a file, etc. + +Take a look at the "Lua Reference Manual":http://www.lua.org/manual/5.0/ +to see what it can do. Ruby people will probably like Lua. + + + diff --git a/site/src/docs/litespeed.page b/site/src/docs/litespeed.page new file mode 100644 index 0000000..bf8c3e2 --- /dev/null +++ b/site/src/docs/litespeed.page @@ -0,0 +1,84 @@ +--- +title: LiteSpeed +inMenu: true +directoryName: LiteSpeed +--- + +h1. LiteSpeed Best Practice Deployment + +h3. by "Alison Rowland":http://blog.alisonrowland.com + +LiteSpeed makes setting up a reverse proxy to Mongrel a snap with its excellent, web-based control panel. LiteSpeed has a built-in load balancer, so it also works well in conjunction with the "Mongrel_Cluster":mongrel_cluster.html plugin. + +h2. Requirements + +These instructions assume you have already completed the following steps: + +* installed the LiteSpeed Webserver, version 2.1.16 or greater[1] (note: not available for Windows), +* set up your application as a virtual host[2], +* installed and configured Mongrel, +* and confirmed that you can start Mongrel and access your app by appending Mongrel's port number to your domain (e.g. mysite.com:8000). + +If you've done all that, then continue reading! + +h2. Configuring Mongrel as an External App + +# Enter your LiteSpeed Administration Panel (usually yourdomain:7080). +# Go to *Server Configuration*. +# Select your app under *Virtual Hosts* in the sidebar at left. +# Go to the *External Apps* tab and click *Add*. +# Choose *Web Server* for the *Type* and click *Next*. + +Fill in the following fields: + +* *Name*: Give this instance of Mongrel a name, such as @myapp-1@. +* *Address*: This should be @127.0.0.1:XXXX@, where @XXXX@ is the port number your mongrel instance is running on. + +The other fields are up to you. Here are some values to start you off with a workable setup: + +* *Max Connections*: 20 +* *Connection Keepalive Timeout*: 1000 +* *Environment*: __leave blank__ +* *Initial Request Timeout (secs)*: 1 +* *Retry Timeout (secs)*: 0 +* *Response Buffering*: No + +Finally, click *Save*. If you're only running a single instance of Mongrel, skip down to the instructions on *Configuring a Context.* Otherwise, read on. + +h2. Load Balancing across Multiple Mongrel Instances + +If you're running more than one instance of Mongrel, or are using Mongrel_Cluster, you'll need to repeat the above directions for every instance of Mongrel, changing the name and port number as appropriate for each. Next, you need to set up a load balancer. + +# Back on the *External Apps* tab, click *Add*. +# Choose *Load Balancer* for *Type* and click *Next*. +# Give it a *Name*, such as @MyApp@ +# In the *Workers* field, enter all of the mongrel instances you set up, using the names you gave them, like so: <br /> +@proxy::myapp-1, proxy::myapp-2, proxy::myapp-3@ +# *Save*. + +h2. Configuring a Context + +Configuring a context prevents LiteSpeed from displaying Mongrel's port number in the URL. + +# Go to your virtual host's *Context* tab, and click *Add*. +# If you're set up to run on just a single Mongrel instance, select *Proxy*. Otherwise, select *Load Balancer*. +# Enter @/@ in *URI*. +# Make sure your *Web Server* or *Load Balancer* is selected in the next field's drop-down menu. +# The other settings are up to you. Most can be left blank. +# *Save*. + +h2. Finishing Up + +The only thing left is to make sure Mongrel is fired up, and, in your LiteSpeed admin panel, click *Apply Changes*, then *Graceful Restart*. You should be good to go! + + +h2. References + +Thanks go to Bob Silva[2] and Rick Olson[3], for their trailblazing articles on LiteSpeed deployment for Rails. + +fn1. "LiteSpeed Technologies":http://litespeedtech.com + +fn2. "Launching Rails at the Speed of Lite with LiteSpeed Webserver":http://www.railtie.net/articles/2006/01/21/up-and-running-in-the-speed-of-light + +fn3. "Setting up LiteSpeed with Mongrel":http://weblog.techno-weenie.net/2006/4/11/setting-up-litespeed-with-mongrel + diff --git a/site/src/docs/mongrel_cluster.page b/site/src/docs/mongrel_cluster.page new file mode 100644 index 0000000..4893206 --- /dev/null +++ b/site/src/docs/mongrel_cluster.page @@ -0,0 +1,138 @@ +--- +title: mongrel_cluster +inMenu: true +directoryName: mongrel_cluster +--- + +h1. Using Mongrel Cluster<br> +<small><small>by Austin Godber</small></small> + +"Mongrel_cluster":http://rubyforge.org/projects/railsmachine/ is a +"GemPlugin":http://mongrel.rubyforge.org/gem_plugin_rdoc that wrappers +the mongrel HTTP server and simplifies the deployment of webapps +using a cluster of mongrel servers. Mongrel_cluster will conveniently configure +and control several mongrel servers, or groups of mongrel servers, which are +then load balanced using a reverse proxy solution. Typical load balancing +reverse proxies include: +* "Apache":apache.html - flexible and complex +* "Lighttpd":lighttpd.html - development has stalled +* "Pound":pound.html - Simple and SSL capable +* "Pen/Balance":pen_balance.html - Simple + +h2. Requirements + +Throughout these instructions we will assume the following: +* All mongrel instances are running on the same machine +* Mongrel *0.3.13* or greater +* "Mongrel_cluster":http://rubyforge.org/projects/railsmachine/ *0.2.0* or greater +* Linux platform (Centos 4.3 in this case, so you will see RedHat like commands). +* You have *sudo* or *root* access + +h2. Preliminary steps + +In general, when deploying a mongrel cluster, none of the mongrel servers will +be listening on a privileged port. This is nice, we don't have to run mongrel as +root, therefore we should create a user for mongrel to run as: +<pre><code> + $ sudo /usr/sbin/adduser -r mongrel +</code></pre> +For the purpose of this example we will just use a freshly minted rails app. +You can adjust these instructions to work with your app, wherever you may have +placed it but lets pretend we are working in */var/www/apps*. So lets go set +up our app and test that mongrel will serve it: +<pre><code> + $ cd /var/www/apps + $ rails testapp + $ cd testapp + $ mongrel_rails start +</code></pre> +You should now be able to see your application at @http://host:3000/@. If you +can, you are good to go. Hit @CTRL+C@ to stop the mongrel server. At a minimum +the log directory of your app has to be writable by the *mongrel* user: +<pre><code> + $ sudo chown -R mongrel:mongrel /var/www/apps/testapp +</code></pre> + +h2. Mongrel Cluster Setup + +With mongrel working and our webapp directory prepared we can proceed with the +mongrel_cluster configuration step: +<pre><code> + $ sudo mongrel_rails cluster::configure -e production \ + -p 8000 -N 3 -c /var/www/apps/testapp -a 127.0.0.1 \ + --user mongrel --group mongrel +</code></pre> +This will write a configuration file in *config/mongrel_cluster.yml*. We have +setup to run our cluster in production mode as the user *mongrel* and will start +3 mongrel servers listening on ports 8000, 8001, and 8002. Now, lets do a quick +test of what we have setup so far: +<pre><code> + $ sudo mongrel_rails cluster::start +</code></pre> +Checking our host on ports 8000, 8001, and 8002 we should now be able to see our +test application. We can stop all of those mongrels with +@sudo mongrel_rails cluster::stop@. + +h2. On Boot Initialization Setup + +At this point, mongrel and mongrel_cluster are setup and working with our sample +webapp. Ultimately, we want this cluster to start on boot. Fortunately, +mongrel_cluster comes with an init script that we can just drop into place. All +we need to do is put the configuration files in */etc/mongrel_cluster* and take care of +a few system tasks: +<pre><code> + $ sudo mkdir /etc/mongrel_cluster + $ sudo ln -s /var/www/apps/testapp/config/mongrel_cluster.yml \ + /etc/mongrel_cluster/testapp.yml + $ sudo cp \ + /path/to/mongrel_cluster_gem/resources/mongrel_cluster \ + /etc/init.d/ + $ sudo chmod +x /etc/init.d/mongrel_cluster +</code></pre> +Now we have a typical System V init script that will launch our mongrel cluster. +Actually, this script will launch any cluster that has a configuration file in +*/etc/mongrel_cluster/*. So when we run */etc/init.d/mongrel_cluster start* it +will start all clusters. Likewise for stop and restart. If we are using a +RedHat like system we can configure mongrel_cluster for startup: +<pre><code> + $ sudo /sbin/chkconfig --level 345 mongrel_cluster on +</code></pre> +For users of Debian, you can use this command to install the script: +<pre><code> + $ sudo /usr/sbin/update-rc.d -f mongrel_cluster defaults +</code></pre> +*NOTE* At this point there are a few issues with this init script that only apply +under certain circumstances. Those issues include: + +* *Shebang line* - The init script uses *#!/usr/bin/env ruby* to find the +appropriate interpreter. Some distribution installs of ruby only give you a +/usr/bin/ruby1.8. You may change the shebang line or simply create a symbolic +link from /usr/bin/ruby1.8 to /usr/bin/ruby[3]. +* *mongrel_cluster_ctl location* - If you have installed your gems in +/usr/local/ you may find that the init script can not find mongrel_cluster_ctl. +To resolve this, you can symbolically link /usr/local/bin/mongrel_cluster_ctl into +/usr/bin/ + +h2. Conclusion + +We have configured mongrel and mongrel_cluster with our webapp and setup +mongrel_cluster to run our cluster at startup. What's missing? Well, unless +your application users expect to have to connect to ports 8000-8002 you had best +check out the reverse proxy options listed above. + +The process of setting up mongrel_cluster will be the same for all of the +reverse proxy deployment options. So this document will likely serve as a reference +for several of the other deployment guides. + +<hr> + +fn1. Thanks to "Bradley Taylor":http://fluxura.com/ for writing +mongrel_cluster and recent improvements in its startup capabilities. The +following people have provided valuable feedback on this document: Alison Rowland. + +fn2. @adduser -r@ is a RedHat-centric way of creating a system account. For +Debian-ish distributions replace that with @adduser --system mongrel@. + +fn3. If you have this problem, you will probably discover other ruby related +executables are also missing. You may want to link irb1.8 and ri1.8 as well, +though only /usr/bin/ruby is necessary for this init script. diff --git a/site/src/docs/osx.page b/site/src/docs/osx.page new file mode 100644 index 0000000..4a4feba --- /dev/null +++ b/site/src/docs/osx.page @@ -0,0 +1,124 @@ +--- +title: OSX +inMenu: true +directoryName: OSX +--- +h1. OS X + Ruby on Rails + Mongrel + MySQL in 15 minutes + +*by "Elliott Hird":http://elliotthird.org/* + +Most tutorials about this seem to involve either manually compiling everything or they take some totally unneccesary long-winded detour. But it's really easy. + +Anyway, let's get started. + +h2. Installing MacPorts + +_(If you already have MacPorts installed, you can skip this step.)_ + +You'll be able to follow "this tutorial":http://trac.macosforge.org/projects/macports/wiki/InstallingMacPorts for the most part, but skip installing X11. + +_Disclaimer: The time spent installing MacPorts does not add up to the time spent following this tutorial. Yes, I cheated._ + +h2. Installing Ruby + +Well, technically, you already have ruby. Look: +<pre> +$ ruby -v +ruby 1.8.2 [stuff follows] +</pre> + +_(The version number may be different for you.)_ + +But depending on what OS X version you're on, it's either broken or outdated. Let's get a working copy of 1.8.5. Fire up your "terminal of choice":http://iterm.sourceforge.net/ and install ruby: +<pre> +$ sudo port install ruby +[lots of text showing macports compiling things] +</pre> +That was easy, wasn't it? + +h2. Installing RubyGems (these titles have very little variation) + +Grab the latest RubyGems version from "here":http://rubyforge.org/frs/?group_id=126 (grab either the .tgz or .zip version, either is fine but the .tgz is smaller) and extract it to wherever you want (in Tiger, just double click on it). + +Open your terminal of choice and install it: +<pre> +$ cd ~/Desktop/rubygems-0.9.0 +$ sudo ruby install.rb +[things] +</pre> +Hopefully that worked. If it didn't, well then, I can't help you. Ask somebody else. + +h2. Did that work? + +Now simply type: +<pre> +$ gem +[boring usage instructions] +</pre> +Yay! RubyGems works (again, if it doesn't, I have no idea what's wrong). + +h2. Installing Rails + +Now: +<pre> +$ sudo gem install rails --include-dependencies +</pre> +After a few minutes, it should dump you back to the prompt without errors. Make sure it works: +<pre> +$ rails +</pre> +If you want to update rails to the latest (at the time of writing) release candidate of 1.2, see the last section. For now, just wait. + +h2. MySQL! + +You might think MySQL, being a big bloated thing, would take all day to compile. Not so - it only took about 2 minutes for me. Your mileage may vary. +<pre> +$ sudo port install mysql +server +</pre> +Note the server variant is selected by using +server. This is required, so just leave it, mmkay? + +After compiling and installing all that, it should give you a notice about how to start MySQL at startup. I highly reccomend doing this, it doesn't make startup any slower. + +If you've told MySQL to start at bootup, reboot now. I'll wait for you. + +h2. Add some Mongrel to the mix + +This one is simple. +<pre> +$ sudo gem install mongrel --include-dependencies +</pre> +Choose the first one and wait for it to install. + +h2. Testing it out + +Alright then. +<pre> +$ cd ~/Code +$ rails test +$ cd test +$ mongrel_rails start +</pre> +If all goes well Mongrel should start up. To test it, "load this":http://localhost:3000. If you see the Rails welcome screen - you're done! + +h2. That's All, Folks! + +No, really. + +Actually, that was a bit more than 15 minutes, wasn't it? Oh well. + +h2. Additional things + +These aren't neccesary, but some people like to do them. + +h3. Updating Rails to 1.2rc1 + +This one is easy. +<pre> +$ gem update rails --source http://gems.rubyonrails.org/ +--include-depdendencies +</pre> + +h3. Securing MySQL + +By default, nobody except localhost can access MySQL, but it allows any user to login (although they can't do anything) and root has no password. I don't see this as a problem since nobody that isn't at your computer can take advantage of this and it's development anyway, but if you want to secure it you're on your own. + diff --git a/site/src/docs/pen_balance.page b/site/src/docs/pen_balance.page new file mode 100644 index 0000000..1799c0f --- /dev/null +++ b/site/src/docs/pen_balance.page @@ -0,0 +1,57 @@ +--- +title: Pen/Balance +inMenu: true +directoryName: Pen/Balance +--- + +h1. Pen/Balance Best Practice Deployment + +Using "Pen":http://siag.nu/pen/ or "Balance":http://www.inlab.de/balance.html to serve +a cluster of Mongrel servers is a simple way to get good concurrency without +going wild on your deployment complexity. What these two programs do is listen on one +port and then proxy the requests to one of the Mongrel servers in your cluster. + +h2. Requirements + +First up, you should learn to use "mongrel_cluster":/docs/mongrel_cluster.html to manage +a cluster of Mongrel servers. It's a simple GemPlugin that simplifies things and also +works better with Capistrano. + +Second, you need to install wither Pen or Balance. Either use your package management +system or install from source. + +Finally, you probably can't do this on win32 unless you use Cygwin. + +h2. Pen + +Once you get Pen installed you just use it like this: + +# Make sure that you can run your application like normal and then +setup "mongrel_cluster":/docs/mongrel_cluster.html so that all of the running Mongrels work. +# Run this command: pen -H 4000 localhost:3000 localhost:3001 +# Check port 4000 to make sure that Pen is proxying correctly. + +As with the Balance instructions below you'll want to create a start-up script so that +Pen gets started on machine reboots. The -H adds a "X-Forwarded-For" header to the +request, which helps if you need to track the client's IP address. There's plenty +of other options for pen as well. + +NOTE: Pen has experimental support for SSL. Try it out if you need SSL. + +h2. Balance + +Balance is pretty simple: + +# Make sure that you can run your application like normal and then +setup "mongrel_cluster":/docs/mongrel_cluster.html so that all of the running Mongrels work. +# Run this command: balance 4000 localhost:3001 localhost:3002 ... +# Hit port 4000 with a browser to see if it's working. + +That's all there is to it. You might want to write a little start-up script that +starts balance on machine reboots. Balance has many other options available if +you need to do more complex stuff, but this is usually all people need. + +h2. Limitations + +We found that Balance has an upper limit of 15 backend servers. I haven't heard much +about Pen, but it's experimental SSL support is interesting. diff --git a/site/src/docs/pound.page b/site/src/docs/pound.page new file mode 100644 index 0000000..a4e166b --- /dev/null +++ b/site/src/docs/pound.page @@ -0,0 +1,148 @@ +--- +title: Pound +inMenu: true +directoryName: Pound +--- + +h1. Pound Best Practice Deployment<br> +<small><small>By Austin Godber</small></small> + +"Pound":http://www.apsis.ch/pound/ is a load-balancing reverse HTTP proxy. It +can also handle SSL connections. Pound, itself, does not serve content but +just acts as a front end to servers that do. In this case pound will sit in +front of a cluster of mongrel servers. This arrangement is similar to that +illustrated on the "Using Lighttpd with Mongrel":lighttpd.html page, except +pound replaces lighttpd. + +h2. Requirements + +We assume that the following: + +* Pound and the mongrel cluster are running on the same machine[1]. +* *Pound 2.0.4* is built and installed, including SSL support if desired. +* The *mongrel* gem is installed. +* The *mongrel_cluster* gem is installed. + +These instructions were performed on CentOS 4.3 using Ruby 1.8.4 from the +CentOS 4 test repository. They should apply on other Linux distributions. +They may work for other OSes, but please see the "pound +website":http://www.apsis.ch/pound/ for additional information. + +h2. Mongrel Cluster Setup + +First we need to prepare our rails application to run in a mongrel cluster. In +this example we will use mongrel_cluster to run three mongrel instances on +ports 8000, 8001, and 8002. We then launch the mongrel cluster: + + $ cd railsapp/ + $ mongrel_rails cluster::configure -p 8000 -N 3 + $ mongrel_rails cluster::start + +We should now have three instances of our rails app running on ports 8000, +8001, and 8002. + +h2. Configuring Pound + +Now we configure pound to proxy requests to the rails cluster we just created. +We will configure pound to accept both HTTP and HTTPS traffic on ports 80 and +443 respectively. Pound will then proxy requests to the *Service*s listed in +the configuration file. Our configuration file (/usr/local/etc/pound.cfg) +looks like this: + +<pre> +<code> +ListenHTTP + Address 0.0.0.0 + Port 80 + Service + BackEnd + Address 127.0.0.1 + Port 8000 + End + BackEnd + Address 127.0.0.1 + Port 8001 + End + BackEnd + Address 127.0.0.1 + Port 8002 + End + End +End + +ListenHTTPS + Address 0.0.0.0 + Port 443 + Cert "/usr/local/etc/test.pem" + # pass along https hint + AddHeader "X-Forwarded-Proto: https" + HeadRemove "X-Forwarded-Proto" + Service + BackEnd + Address 127.0.0.1 + Port 8000 + End + BackEnd + Address 127.0.0.1 + Port 8001 + End + BackEnd + Address 127.0.0.1 + Port 8002 + End + End +End +</code> +</pre> + +Before starting pound, we need to make sure our SSL certificate is present. If +not we can quickly generate a test certificate: + + $ openssl req -x509 -newkey rsa:1024 -keyout test.pem \ + -out test.pem -days -nodes + +It should now be safe to start pound: + + $ sudo pound -f /usr/local/etc/pound.cfg + +Our Rails application should now be available at http://127.0.0.1/ and https://127.0.0.1/ . + +h2. Testing SSL in Rails + +The line @AddHeader "X-Forwarded-Proto: https"@ in the ListenHTTPS section +tells pound to add a header to the request as it is passed back to the mongrel +servers[2]. This will tell the rails application that the request was +originally an SSL request. We can test this with the following simple Rails +controller, app/controller/test_controller.rb: + +<pre> +<code> +class TestController < ApplicationController + def index + @sslyn = request.ssl? + end +end +</code> +</pre> + +And the accompanying view, app/views/test/index.rhtml: + +<pre> +<h1>test</h1> +SSL: < %= @sslyn %> +</pre> + +Visiting @http://127.0.0.1/Test/@ should show @SSL: false@ while visiting +@https://127.0.0.1/Test/@ should return @SSL: true@. + +h2. Building Pound on OSX + +OSX has specific problems when building pound, but you can follow "Trotter Cashion's":http://lifecoding.com/blog/?p=29 +to get everything working. + +<hr> + +fn1. It is not required that pound run on the same machine as the mongrel +servers. It was just chosen for this example. + +fn2. Thanks to Joshua Harvey's post on the Mongrel mailing list for this fix. diff --git a/site/src/docs/security.page b/site/src/docs/security.page new file mode 100644 index 0000000..2781b12 --- /dev/null +++ b/site/src/docs/security.page @@ -0,0 +1,94 @@ +--- +title: Security +inMenu: true +directoryName: Security +--- + +h1. Web Application Security Issues + +Mongrel takes a different approach to security than most web servers. Rather than +relying on massive human efforts to audit all possible code, Mongrel is implemented +using algorithms and methods that are difficult to subvert. There is still auditing +and checks, but Mongrel simply tries to avoid errors by not doing things that cause them. + +Read the "Iron Mongrel Security page":/security.html for information on how security +is done in Mongrel. The main points to remember with Mongrel's security are: + +* Mongrel uses a "Ragel":http://www.cs.queensu.ca/home/thurston/ragel/ generated parser +instead of a hand coded HTTP handler. The grammar is very close to the ABNF specification, so +if you see "BAD CLIENT" errors in your logs, that probably is a bad client. +* Security tests have found that Mongrel stops most security attacks at the protocol level due to +it's correctly written parser *and* it's explicit limits on the sizes of everything. +* The Mongrel reaction to a protocol violation is to close the socket immediately. It doesn't waste +time and resources on bad clients since this is *always* a hack attempt. If it isn't then it's a +poorly written client and the author should learn to write a correct one. +* Mongrel works with all the main clients out there, and ones it doesn't work with are crap living +in a tiny tiny niche of the internet designed by horrible programmers. +* While Mongrel is more strict than other servers, it isn't draconian. The clients that can't get +through are typically skating on the edge of the HTTP grammar where they do not belong. + +Mongrel isn't infallible, but if Mongrel complains about something then you should investigate it. +If you think Mongrel is wrong then shoot a message to the mailing list detailing what it should do +and we'll consider adjusting the grammar. If you think Mongrel should violate the grammar so that +your little WebDAV++ Social Network Bookmark Chat Web 2.0 monstrosity can see the light of day, then +you "should write your own web server":/not_mongrel.html instead. + +h2. Learning About Web Application Security + +You should read the "OWASP":http://www.owasp.org/index.php/Main_Page document +"Top Ten Project":http://www.owasp.org/index.php/OWASP_Top_Ten_Project for information on +the big security risks and how to avoid them. The summary of these is: + +* Unvalidated Input +* Broken Access Control +* Broken Authentication And Session Management +* Cross Site Scripting +* Buffer Overflow +* Injection Flaws +* Improper Error Handling +* Insecure Storage +* Application Denial Of Service +* Insecure Configuration Management + +Many of these errors depend on how you write your application and have little to do with Mongrel. +The two that relate the most to Mongrel are *Buffer Overflows* and *Application Denial of Service*. + + +h2. Buffer Overflows + +Mongrel is doing some heavy C buffer thrashing internally, but several controls are in place to make +sure that things stay safe: + +* Pointers are checked with asserts and if statements to test if there's been a buffer overflow. +* Ragel generates a safe state machine that is accurate to the byte level. +* There are none of the "unsafe" string handling functions except for where absolutely necessary, and +that one spot is checked heavily. +* Input is limited to maximum sizes--which also helps prevent denial of service. + + +h2. Application Denial of Service + +Normally this is things like using algorithms in your application that make it easy for +someone to take down your application with simple queries. A good example is a SQL database +query that when given the right inputs takes forever. It only takes a few of these to +destroy your application's response time. + +Mongrel helps with Denial of Service by doing the following things: + +* Mongrel strict parser boots clients immediately to save on resources and time. +* Mongrel boots clients that go over the (generous) size limits for input. +* When Mongrel gets overloaded it starts denying clients and then finds existing +requests that are taking to long and kills them. It is pretty conservative about this, but +you can use options to tune it more or less severe. +* Mongrel is already using pure HTTP so there's plenty of tools available to help you. +* It's designed to be as efficient as possible, but this has limits because of Ruby. + +Don't count on Mongrel to suddenly make your Rails application withstand a Russian +DOS blackmail operation though. It works hard to prevent DOS potential, but there's only +so much it can do. + +If you're very serious about security then you should check out "mod_security":http://www.modsecurity.org/ +which is an Apache module that has lots of active prevention of such things. It can stop many web +scanner attacks, DOS attempts, and other security attempts. It does slow your server down, but it's +probably worth it if you have something valuable. + diff --git a/site/src/docs/started.page b/site/src/docs/started.page new file mode 100644 index 0000000..5c6f23c --- /dev/null +++ b/site/src/docs/started.page @@ -0,0 +1,83 @@ +--- +title: Getting Started +inMenu: true +directoryName: Documentation +--- + +h1. Getting Started + +The easiest way to get started with Mongrel is to install it via RubyGems +and then run a Ruby on Rails application. You can do this easily: + + $ sudo gem install mongrel + $ cd myrailsapp + $ mongrel_rails start -d + +Which runs Mongrel in the background. You can stop it with: + + $ mongrel_rails stop + +And you're all set. There's quite a few options you can set for the +start command. Use the *mongrel_rails start -h* to see them all. + + +h2. Win32 Install + +Windows has a slight difference since it seems that the win32-service doesn't +get picked up for some people as a dependency. You'll need to do this instead: + + $ gem install win32-service (pick the most recent one) + $ gem install mongrel (pick the win32 pre-built) + $ gem install mongrel_service + +Now you're installed. "Read the Win32 HOWTO for more instructions.":win32.html + + +h2. Updating + +You should be able to do an *gem update* and get the latest version of Mongrel +on any platform you've already installed it on. The caveat to this is if +you've been grabbing test releases from any of the authors directly then +you'll need to *gem uninstall* first to make sure you don't have any buggy +stuff lying around. + + +h1. Help For Commands + +Mongrel uses a fairly comprehensive command/plugin system (documented in the near +future) that has built-in help thanks to optparse. Just pass a -h to any +command and it will dump the help for you: + + $ mongrel_rails start -h + +Also every option has reasonable default options, and will complain if you give +anything invalid. + +See the "HOWTO":howto.html for information on each option and what +it does. + +h1. Running In Development + +Mongrel turns out to be really nice for development since it serves files +much faster than WEBrick. I'm using it for almost all my development Ruby +on Rails work these days. What I do is the following: + + $ mongrel_rails start + +And then do my work like normal with WEBrick. You don't get all the logging +and stuff you get with WEBrick (planned for a future release) but otherwise +it's nice and snappy. + + +h1. More Information + +There's a "mailing list":http://rubyforge.org/mailman/listinfo/mongrel-users that +you should subscribe to if you're looking for help or are interested in tracking +Mongrel. We post announcements of pre-release gems you can play with to this +mailing list and also discuss development of Mongrel there. + +Before you start asking for features you should read about +"bikeshedding":http://www.catb.org/jargon/html/B/bikeshedding.html and +understand that we're really nice, but sometimes code speaks better than rhetoric. + +Finally there's lots of other "documentation.":index.html diff --git a/site/src/docs/upload_progress.page b/site/src/docs/upload_progress.page new file mode 100644 index 0000000..05dcc52 --- /dev/null +++ b/site/src/docs/upload_progress.page @@ -0,0 +1,136 @@ +--- +title: Upload Progress +inMenu: true +directoryName: Upload Progress +--- + +h1. Mongrel Upload Progress Plugin + +One of the really nice things about Mongrel is its simplicity. It's very easy +for someone to take and extend for their own needs. The Mongrel Upload +Progress plugin is an example of how I'm able to extend the Mongrel HTTP +Request object and provide near-realtime progress updates. + +The reason why this is a challenge, is because web servers usually gather the +HTTP request, send it on to the web framework, and wait on a response. This is +fine for most requests, because they're too small to cause an issue. For large +file uploads this is a usability nightmare. The user is left wondering what +whether their upload is going through or not. + +To do this, I've written a Mongrel handler that hooks into some basic Request +callbacks. To use it, you need to install the gem, and create a small config +file for it: + +<pre><code>gem install mongrel_upload_progress + +# config/mongrel_upload_progress.conf +uri "/", + :handler => plugin("/handlers/upload", :path_info => '/files/upload'), + :in_front => true + +# start mongrel +mongrel_rails -d -p 3000 -S config/mongrel_upload_progress.conf +</code></pre> + +That config file tells mongrel to load the Upload handler in front of all other +handlers. <code>:path_info</code> is passed to it, telling upload_progress to +only watch the /files/upload action. There are two more parameters that I'll +get into later: <code>:frequency</code> and <code>:drb</code>. I'm using Rails +as an example, but this should work with any Ruby framework, such as Camping or +Nitro. + +Now that Mongrel is set up, let's create a "basic upload +form":/docs/upload_progress_form.rhtml. If you +look closely you'll notice a few things: + +* A unique <code>:upload_id</code> parameter must be sent to the upload_progress handler. This is so requests don't get mixed up, and the client page has an ID to query with. +* The <form> tag is targetted to an iFrame to do the uploading. Certain browsers (like Safari) won't execute javascript while a request is taken place, so this step is necessary. +* There is a little "javascript library":/docs/upload_progress_javascript.js being used. This handles the polling and status bar updates. +* Notice the form's action is file/upload, just like the upload_progress handler. + +The "Rails controller +actions":/docs/upload_progress_rails.rb for this are very +simple. The upload form itself needs no custom code. The upload action only +renders javascript to be executed in the iFrame, to modify the contents of the +parent page. The progress action is a basic RJS action that updates the +current status. Most of the guts of this are implemented in the javascript +library. + +Here's what happens when you submit the form: + +* The UploadProgress class creates a PeriodicalExecuter and gets ready to poll. +* The browser initiates the upload. +* Every 3 seconds, the PeriodicalExecuter calls the RJS #progress action and gets back the current status of the file. +* Once finished, the iFrame calls <code>window.parent.UploadProgress.finish()</code>, which removes the status bar and performs any other finishing actions. + +How's this work with a single Mongrel process if "Mongrel synchronizes Rails +requests":http://david.planetargon.us/articles/2006/08/08/why-you-need-multiple-mongrel-instances-with-rails? +It's actually very careful about locking, synchronizing only the bare minimum. +The whole time that Mongrel is receiving the request and updating the progress +is spent _in_ Mongrel, so it can happily serve other requests. This is how the +RJS action is able poll while it's uploading. + +This is fine and dandy, but not too many sites run on a single Mongrel. You'll +quickly run into problems with multiple mongrels since only one Mongrel process +knows about the upload. You'll either have to specify a specific mongrel port +to communicate with, or set up a dedicated mongrel upload process. The third +option, is use DRb. + +<pre><code># config/mongrel_upload_progress.conf +uri "/", + :handler => plugin("/handlers/upload", + :path_info => '/files/upload', + :drb => 'druby://0.0.0.0:2999'), + :in_front => true + +# lib/upload.rb, the upload drb server +require 'rubygems' +require 'drb' +require 'gem_plugin' +GemPlugin::Manager.instance.load 'mongrel' => GemPlugin::INCLUDE +DRb.start_service 'druby://0.0.0.0:2999', Mongrel::UploadProgress.new +DRb.thread.join</code></pre> + +Now in addition to starting mongrel, you'll need to start the DRb service too: + +<pre><code>ruby lib/upload.rb</code></pre> + +The Rails app should work the same as before, but now it is using a shared DRb +instance to store the updates. This gives us one other advantage: a console +interface to the current uploads. + +<pre><code># lib/upload_client.rb, a simple upload drb client +require 'drb' +DRb.start_service + +def get_status + DRbObject.new nil, 'druby://0.0.0.0:2999' +end + +# typical console session +$ irb -r lib/upload_client.rb +>> uploads = get_status +>> uploads.list +=> [] +# start uploading in the browser +>> uploads.list +=> ["1157399821"] +>> uploads.check "1157399821" +=> {:size=>863467686, :received=>0}</code></pre> + +Using DRb gives you a simple way to monitor the status of current uploads in +progress. You could also write a simple web frontend for this too, accessing +the DRb client with Mongrel::Uploads. + +One final note is the use of the <code>:frequency</code> option. By default, + the upload progress is marked every three seconds. This can be modified + through the mongrel config file: + +<pre><code>uri "/", + :handler => plugin("/handlers/upload", + :path_info => '/files/upload', + :frequency => 1, + :drb => 'druby://0.0.0.0:2999'), + :in_front => true</code></pre> + + diff --git a/site/src/docs/upload_progress_form.rhtml b/site/src/docs/upload_progress_form.rhtml new file mode 100644 index 0000000..8a9b675 --- /dev/null +++ b/site/src/docs/upload_progress_form.rhtml @@ -0,0 +1,53 @@ +<html> + <head> + <meta http-equiv="Content-type" content="text/html; charset=utf-8" /> + <title>mongrel test</title> + <%= javascript_include_tag :defaults %> +<style type="text/css"> +#progress-bar { + width:500px; + height:25px; + margin:15px; + border:solid 1px #000; + position:relative; +} + +#progress-bar #status-bar { + display:block; + height:25px; + width:0; + background-color:#00f; + border-right:solid 1px #000; + position:absolute; + top:0; left:0; +} + +#progress-bar #status-text { + display:block; + padding: 0 15px; + line-height:25px; + position:absolute; + top:0; left:0; +} +</style> + </head> + <body> + +<p><%= link_to (@upid = Time.now.to_i.to_s), :action => 'status', :upload_id => @upid %></p> +<%= start_form_tag({:action => 'upload', :upload_id => @upid}, {:multipart => true, :target => 'upload', + :onsubmit => "UploadProgress.monitor('#{escape_javascript @upid}')"}) %> + <div id="file-fields"> + <p><%= file_field_tag :data %></p> + </div> + <p><%= link_to_function 'Add File Field', 'UploadProgress.FileField.add()' %> + </p> + <p><%= submit_tag :Upload %></p> +</form> + +<div id="results"></div> +<div id="progress-bar"></div> + +<iframe id="upload" name="upload" src="about:blank"></iframe> + + </body> +</html> diff --git a/site/src/docs/upload_progress_javascript.js b/site/src/docs/upload_progress_javascript.js new file mode 100644 index 0000000..6fbc51f --- /dev/null +++ b/site/src/docs/upload_progress_javascript.js @@ -0,0 +1,113 @@ +var UploadProgress = { + uploading: null, + monitor: function(upid) { + if(!this.periodicExecuter) { + this.periodicExecuter = new PeriodicalExecuter(function() { + if(!UploadProgress.uploading) return; + new Ajax.Request('/files/progress?upload_id=' + upid); + }, 3); + } + + this.uploading = true; + this.StatusBar.create(); + }, + + update: function(total, current) { + if(!this.uploading) return; + var status = current / total; + var statusHTML = status.toPercentage(); + $('results').innerHTML = statusHTML + "<br /><small>" + current.toHumanSize() + ' of ' + total.toHumanSize() + " uploaded.</small>"; + this.StatusBar.update(status, statusHTML); + }, + + finish: function() { + this.uploading = false; + this.StatusBar.finish(); + $('results').innerHTML = 'finished!'; + }, + + cancel: function(msg) { + if(!this.uploading) return; + this.uploading = false; + if(this.StatusBar.statusText) this.StatusBar.statusText.innerHTML = msg || 'canceled'; + }, + + StatusBar: { + statusBar: null, + statusText: null, + statusBarWidth: 500, + + create: function() { + this.statusBar = this._createStatus('status-bar'); + this.statusText = this._createStatus('status-text'); + this.statusText.innerHTML = '0%'; + this.statusBar.style.width = '0'; + }, + + update: function(status, statusHTML) { + this.statusText.innerHTML = statusHTML; + this.statusBar.style.width = Math.floor(this.statusBarWidth * status); + }, + + finish: function() { + this.statusText.innerHTML = '100%'; + this.statusBar.style.width = '100%'; + }, + + _createStatus: function(id) { + el = $(id); + if(!el) { + el = document.createElement('span'); + el.setAttribute('id', id); + $('progress-bar').appendChild(el); + } + return el; + } + }, + + FileField: { + add: function() { + new Insertion.Bottom('file-fields', '<p style="display:none"><input id="data" name="data" type="file" /> <a href="#" onclick="UploadProgress.FileField.remove(this);return false;">x</a></p>') + $$('#file-fields p').last().visualEffect('blind_down', {duration:0.3}); + }, + + remove: function(anchor) { + anchor.parentNode.visualEffect('drop_out', {duration:0.25}); + } + } +} + +Number.prototype.bytes = function() { return this; }; +Number.prototype.kilobytes = function() { return this * 1024; }; +Number.prototype.megabytes = function() { return this * (1024).kilobytes(); }; +Number.prototype.gigabytes = function() { return this * (1024).megabytes(); }; +Number.prototype.terabytes = function() { return this * (1024).gigabytes(); }; +Number.prototype.petabytes = function() { return this * (1024).terabytes(); }; +Number.prototype.exabytes = function() { return this * (1024).petabytes(); }; +['byte', 'kilobyte', 'megabyte', 'gigabyte', 'terabyte', 'petabyte', 'exabyte'].each(function(meth) { + Number.prototype[meth] = Number.prototype[meth+'s']; +}); + +Number.prototype.toPrecision = function() { + var precision = arguments[0] || 2; + var s = Math.round(this * Math.pow(10, precision)).toString(); + var pos = s.length - precision; + var last = s.substr(pos, precision); + return s.substr(0, pos) + (last.match("^0{" + precision + "}$") ? '' : '.' + last); +} + +// (1/10).toPercentage() +// # => '10%' +Number.prototype.toPercentage = function() { + return (this * 100).toPrecision() + '%'; +} + +Number.prototype.toHumanSize = function() { + if(this < (1).kilobyte()) return this + " Bytes"; + if(this < (1).megabyte()) return (this / (1).kilobyte()).toPrecision() + ' KB'; + if(this < (1).gigabytes()) return (this / (1).megabyte()).toPrecision() + ' MB'; + if(this < (1).terabytes()) return (this / (1).gigabytes()).toPrecision() + ' GB'; + if(this < (1).petabytes()) return (this / (1).terabytes()).toPrecision() + ' TB'; + if(this < (1).exabytes()) return (this / (1).petabytes()).toPrecision() + ' PB'; + return (this / (1).exabytes()).toPrecision() + ' EB'; +} diff --git a/site/src/docs/upload_progress_rails.rb b/site/src/docs/upload_progress_rails.rb new file mode 100644 index 0000000..d04d658 --- /dev/null +++ b/site/src/docs/upload_progress_rails.rb @@ -0,0 +1,14 @@ +class FilesController < ApplicationController + session :off, :only => :progress + + def progress + render :update do |page| + @status = Mongrel::Uploads.check(params[:upload_id]) + page.upload_progress.update(@status[:size], @status[:received]) if @status + end + end + + def upload + render :text => %(UPLOADED: #{params.inspect}.<script type="text/javascript">window.parent.UploadProgress.finish();</script>) + end +end diff --git a/site/src/docs/win32.page b/site/src/docs/win32.page new file mode 100644 index 0000000..c3e386f --- /dev/null +++ b/site/src/docs/win32.page @@ -0,0 +1,131 @@ +--- +title: Win32 HOWTO +inMenu: true +directoryName: Documentation +--- + +h1. Mongrel Win32 HOWTO + +Mongrel now supports Win32 much better than previous releases thanks to +some "great people":../attributions.html and their hard work. You can +now run Mongrel with Ruby on Rails as a windows service, ang there are +pre-compiled gems available for people to use. + +*Before reading this document you need to read "Getting Started.":started.html and make sure it works.* + +h2. Installing Service Support + +Mongrel used to have a separate mongrel_rails_service script but this +caused problems and has since been unified into just mongrel_rails +and a special GemPlugin that gives you a set of service:: commands. + +To install the mongrel_service GemPlugin you simply install mongrel and +then do: + + > gem install mongrel_service + +This will give you a set of service commands that you can find out about +by just running mongrel_rails and then passing each one the -h option to +get help. + + +h2. Running The Service + +After you do the gem install, find a Rails application you want to run +and do: + + $ mongrel_rails service::install -N myapp \ + -c c:\my\path\to\myapp -p 4000 -e production + $ mongrel_rails service::start -N myapp + +Now hit the port and poof, works (or should). + +The application will stop if you use: + + $ mongrel_rails service::stop -N myapp + +@NOTE: Stop reports an exception but does stop the service.@ + +Now the really great thing is that you can just do all this from +the Services control panel like your Rails application is a regular +Windows service. + +Even works in development mode, which is pretty nice. I use win32 +at work now and what I have setup is three services: myapp_dev, +myapp_stage, myapp_prod. I point dev and stage at the same +directory but run dev in *development* mode and stage in *production* +mode. Then I have myapp_prod in a separate directory and when I'm +about to claim I've got something to release I'll go simulate a +subversion check-out and run my tests again. + + +h2. Other Service Commands + +There is a full set of service control commands in the mongrel_service plugin. +This lets you use either the Services control panel or a command line script to +manage your Rails applications. What's also nice is that you can register as many +applications as you want, and even the same one with different names. + + +h3. service::install + +If you want to run the same app in different modes then use the *-N* option to the *install* +command: + + $ mongrel_rails service::install -N myapp_dev \ + -c c:\my\path\to\myapp -p 4000 -e development + $ mongrel_rails service::start -N myapp + +You can also use the *-D* option to give the service a different display name in the +Services console. + +h3. service::start + +Pretty much just takes a service name to start up. It will run and print a message +until the service finally starts, which sometimes can take 10-60 seconds. + +h3. service::stop + +Sort of works right now and also only takes a -N parameter. It has a few errors +when it tries to stop a service so we're working on making it cleaner. + +*NOTE:* since mongrel_service 0.3.1, start and stop commands were removed. +Use net start / net stop instead. + +h3. service::remove + +Takes the name (-N) of the service to remove and then removes it from the list. +*This would be how you'd remove a service so you can change it's start-up options.* + + +h2. CPU Affinity + +Mongrel's win32 support actually is able to set the CPU affinity of a running +Mongrel service. This is pretty neat since it means if you're running a +fancy SMP machine or a dual core that pretends to be SMP, then you can +force Mongrel onto one of them and get a nice little boost. + +It's pretty easy to use, just pass the *-u or --cpu* option to the *install* +command and give a CPU of 1-X. That means if you have 4 CPUs and you want +Mongrel on #4 then do: + + $ mongrel_rails service::install -N myapp \ + -c c:\my\path\to\myapp -p 4000 -e production -u 4 + +Pretty much the same command, just one more option and you're done. + + +h2. Making you Service autostart with Windows + +By default, the new Mongrel service get installed to be run _manually_, using +Mongrel's commands or the Service Manager. You could tweak it to start automatically +when Windows start, just use the Service Control commandline tool: + + $ sc config myapp start= auto + +Also, you can configure the services that are neede to make it work, like database +support: + + $ sc config myapp start= auto dependency= MySql + +The space after the equal sign is needed for the command to complete successfully. |