Hosting providers like WebFaction make it easy to assemble full web sites out of smaller pieces. They provide a control panel that lets you add entries to WebFaction's nginx configuration so you can (for example) serve "/static" URLs using Apache, serve "/blog" from a WordPress instance, and send everything else to your dynamic web application.
When I saw Kenneth Reitz's blog post "Static Sites on Heroku Cedar", I wondered whether a similar arrangement might be possible on Heroku: http://kennethreitz.com/static-sites-on-heroku-cedar.html With a bit of work, I have found that such an arrangement is indeed possible. But I wanted to ask here on the mailing list whether this approach is in the spirit of the service that Heroku is trying to provide - and whether the performance I am seeing is reasonable or not. Building Apache with mod_proxy ------------------------------ The default Heroku buildpack that Kenneth uses in the above blog post does not include mod_proxy, but only mod_rewrite. So the first step was to create a slightly modified fork of the default Heroku PHP buildpack: https://github.com/brandon-rhodes/heroku-buildpack-php Instead of using the Apache and PHP tarballs that Heroku provides under the "php-lp" S3 bucket, I had to build an alternative pair of tarballs and make them available under my own S3 bucket. You can see the procedure for building the tarballs in "README.md" my fork of the repository. I ran the "README.md" steps on a Heroku Cedar instance to make sure that they would be binary-compatible with the Heroku dyno standbox: $ heroku run bash Running `bash` attached to terminal... up, run.2 ~ $ cd /app ~ $ curl -O http://apache.cyberuse.com/httpd/httpd-2.2.22.tar.gz ... and so forth (I actually skipped the "pear" section of the README, because "apt-get" did not seem to be available in the sandbox. Hope it was not important.) This produces two *.tar.gz files that are in great danger of disappearing, because as soon as you exit bash the temporary instance produced by "heroku run" will be destroyed along with its filesystem. So I used "nc" to copy the tarballs off to another server before exiting bash, then put the tarballs on S3 like Heroku does (using s3cmd), and, finally, updated the URLs in "heroku-buildpack-php/bin/compile" to point at the new location. Building a proxy with this custom buildpack ------------------------------------------- Kenneth used an ".htaccess" file to turn off the PHP engine. With this buildpack we can now go farther, and add rewrites that back-end certain URLs with content from another web server on the Internet - in my case, another Heroku app. For example, to serve "/static/*" URLs from local files in your Apache front-end, but forward other URLs back to another Heroku app, you can do something like this: # .htaccess php_flag engine off RewriteEngine on RewriteRule !^static/ http://morning-snow-4827.herokuapp.com%{REQUEST_URI} [P] Of course, be sure to create the same empty "index.php" file that Kenneth recommends, so that your tiny app is recognized correctly as a (not-really) PHP application. So my small front-end app looks like this: /.htaccess (content shown above) /index.php (empty) /static/index.html (says "<html><body><p>This is static content") I can deploy this as an Apache-powered front-end reverse proxy like this: $ heroku create --stack cedar --buildpack http://github.com/brandon-rhodes/heroku-buildpack-php.git $ git push heroku master You can, for the next few days, see the result by visiting the following pair of URLs, the first of which serves the static content, and the second of which (since it does not match "/static") proxies content through to a small botanical web application: http://falling-autumn-5266.herokuapp.com/static/ http://falling-autumn-5266.herokuapp.com/Families/ Performance and impact ---------------------- Some simple tests with "ab" suggest that proxying through a Dyno-resident Apache instance like this adds right around 100ms to every request to the back-end. As usual, the shape of each particular application will determine whether the cheaper static resources make up for the extra expense incurred for dynamic resources: BEFORE All content served by dynamic app: ~410ms AFTER Static content served right from Apache: ~83ms Dynamic content proxied through Apache: ~517ms So here are the questions on which I would like feedback: * Is this approach good or evil? * In particular, is it un-neighborly and unfriendly that each dynamic request through this stack keeps two Heroku dynos busy instead of just one (or, on the contrary, will Heroku love being able to sell me more Dynos as my user base expands)? * Is 100ms of additional delay for dynamic queries reasonable? Or does it reflect that I have done something non-optimal in setting this up? Or does it simply indicate that a Heroku dyno does not make for a fast reverse proxy? * If I were to do this "for real", would I want to create a real honest 3rd-party "nginx" buildpack instead of using Apache? I tweaked Heroku's Apache buildpack for this simple example because it was an easier way for me to get a first proof-of-concept running. * Or, if I were going to do this "for real", should I ask pretty-please if Heroku might add mod_proxy into their default PHP buildpack so that I do not have to maintain my own fork through the years? Thanks for any thoughts or experiences you could share. -- Brandon Rhodes [email protected] http://rhodesmill.org/brandon -- You received this message because you are subscribed to the Google Groups "Heroku" group. To unsubscribe from this group, send email to [email protected] For more options, visit this group at http://groups.google.com/group/heroku?hl=en_US?hl=en
