Run non-java webapps on Elastic Beanstalk.
This is alpha quality software. This project, for the moment, should be considered nothing more than an exercise in yak shaving. Do not use this in a production environment. That said...fiddling around is good.
How it works
ElasticBand forks a process listening on a free HTTP port. All incoming
requests are then proxied to that process. Depending on how your
set up, it will use a
Runtime to determine how to set up that subprocess
correctly given the potentially stripped down AWS image your application has
For the moment, there is are
virtualenv runtimes, but it wouldn't
take a monster effort to implement new ones.
For an application that really only depends on Python, this runtime may be enough. An example is as follows:
First, the simplest python web server Googling could pull up.
import sys import BaseHTTPServer from SimpleHTTPServer import SimpleHTTPRequestHandler HandlerClass = SimpleHTTPRequestHandler ServerClass = BaseHTTPServer.HTTPServer Protocol = "HTTP/1.0" port = int(sys.argv) server_address = ('127.0.0.1', port) HandlerClass.protocol_version = Protocol httpd = ServerClass(server_address, HandlerClass) sa = httpd.socket.getsockname() print "Serving HTTP on", sa, "port", sa, "..." httpd.serve_forever()
web.xml definition. Note how
application.command is the name of
the script followed by
%d. This will be the given port number the application
should start serving HTTP on (referenced above where we get the port from
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4"> <context-param> <param-name>application.command</param-name> <param-value>main.py %d</param-value> </context-param> <context-param> <param-name>application.runtime</param-name> <param-value>python</param-value> </context-param> <listener> <listener-class>vistarmedia.elasticband.servlet.ContextListener</listener-class> </listener> <filter> <filter-name>guiceFilter</filter-name> <filter-class>com.google.inject.servlet.GuiceFilter</filter-class> </filter> <filter-mapping> <filter-name>guiceFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
Anything more involved, of course, is going to have external requirements. The
odds that they'll be installed on a vanilla Elastic Beanstalk image are about
zero. In fact,
virtualenv itself isn't installed. If the runtime can't find
it, a wrapper will be downloaded to bootstrap from there.
Each time the application server (ie: Tomcat) is started, the virtualenv will be set up from scratch. This is pretty darn slow, but seems most correct to me. One problem here is that if pypi is having problems that day, your boot could hang or fail waiting for packages to come down.
Check out the example directory for an example of a virtualenv setup.
When this project is compile, it'll generate an empty war file which won't do
much. This isn't enough to upload to Elastic Beanstalk. The supplied
command in the root will help create it.
$ mvn clean package $ cd example $ ../package -o example.war -w ./web.xml ./site ../target/elasticband-0.1-SNAPSHOT.war
If that completes successfully, you'll have an
example.war ready to be pushed
up to Elastic Beanstalk.
This project was hacked out in an evening to see if I could. It's not presently used in a production environment as far as I know. Some of the code is very round about. Patches are welcome.
- The Async HTTP Client seems a bit overkill since the servlet processing thread must block until completion, but the idea of using http-client just hurts me.
- The target is Elastic Beanstalk, so perhaps there's a way to force Tomcat to handle async responses? It does't seem to want to run the servlet 3.0 API, but there is this, but I don't think EB is using APR or NIO HTTP connectors.
- Static files served directly by the servlet container
- There are some issues on AWS I can't recreate locally where the environment will try to spawn many runtimes. /me scratches head.
- Of course, some non-python runtime
- When the container starts up, it doesn't wait to see when the subprocess binds to the port, so the first N requests probably just bomb. Not a big deal w/ EB due to the LB health checks