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

Reply via email to