hatestheinternet

Elastic Beanstalk

This is where it all begins, folks: A little place in the cloud our Drupal installation can call home. Well, it's not technically where it begins, we have to set up RDS, and make our VPC and subnets, and our nats and a whole bunch of other shit so boring I'm falling asleep thinking about it. Hurry up and read more!

This assumes you've already done the boring shit above and set your cloud up in a responsible way, it also presupposes you've got something vaguely remsembling a working Drupal site that's probably 30% my previous diatribe involving the cloud, 70% Stack Overflowing and tweeting about how I should be mopping floors.

Before going any further, here's the way I approach my multi-site Drupal installation: I've found that, if I need a module for one site, it's probably going to prove useful the next time I'm messing around with another, so it might as well already be there. This, along with the usual number of "must have" modules, makes maintenance much easier because I only have to update them in one place. Since the Update module is quite vocal over email, this base site is the only one on which I enable it. The key is remembering to tweak its settings so it checks all projects, not just the modules and themes you have enabled.

Another thing Elastic Beanstalk forces us to consider is our project's life cycle. I manage this using Jenkins, mostly because he's one dapper son of a bitch, minorly(?) because he's the perfect middle man between my repsositories and AWS: He will uncomplainingly mash together any number of apples and oranges, pour the whole mess over some janky shell scripts I wrote, and shoot it off in to the cloud.

Once it lands, I use various bits and pieces of metadata from my CloudFormation templates (because we're Gallant, not Goofus) to set credentials and paths and things because, for our application to have zero knowledge of the environment in which it's running, we need to build a mechanism to get it what it needs. Plus, I really, really like writing shell scripts.

When it was all said and done, I would up with three jobs in Jenkins: A build job (this is an incredibly simple affair, it just checks out my Drupal repository and archives the artifacts), a deployment component (which pretty much does the same thing, only its repository contains the shell scripts, Elastic Beanstalk .config files, and other various miscellany we're going to need), and a promotion job to jam the results of the previous two together, run the scripts that turn the mountain of code in to an Elastic Beanstalk application and deploy it to an environment. Basically, it uses zip to create an archive then the AWS CLI to copy it to S3, create an application version, then deploy.

Since I like to keep things neat and tidy and we're going to have a bunch of things, here's more or less how I structured my S3 bucket:

my-drupal-7
     +--- deploy
     +--- prd
     |     +--- assets
     |     +--- content
     |     `--- sites
     +--- stg
           (children same as prd)

First, and most obvious, the deploy/ directory is where we store the ZIP files ElasticBeanstalk deploys as versions. This is a sibling of prd and stg because they're environments and our application is environment-agnostic. You also don't need to go any deeper if you're just hosting a single site per application, you can just mount my-drupal-7/prd/ on sites/whatever/files/ and call it a day. If you run your Drupal game like mine, we mount my-drupal-7/{environment} somewhere innocuous and make symlinks where we need them.

Since I (to avoid being Drupal Goofus) use the AdvAgg module in unified multi-site mode, I need somewhere to store its aggregations. I use, as I'm sure you've noticed /assets, so I create a symlink inside my webroot to the assets/ directory in the bucket. I could just make add assets/ to the repository and be done with it, but then every instance would have to generate its own aggregations which (I think) really kind of defeats the purpose.

Next up is content/. On my base application, I use core's CSS and JS aggregation so, to make them permanentish, I mount my-drupal-7/env/content on sites/default/files. As with AdvAgg's assets, this ensures that the aggregations will persist when the instance that created them dies and that we don't waste valuable CPU cycles recreating and gzipping the aggregates.

Finally, sites/. For every domain my Drupal site serves, I have its promotion job in Jenkins aws s3 sync a holding directory in the workspace to my-drupal-7/env/sites/whatever.com. When the application deploy scripts run, they iterate over the directories and create symlinks in /var/app/ondeck/sites. For now, this means I have to redploy through the AWS console every time I add a new domain, but expect this to change as it'll inevitably piss me off at some point.

The last piece of the puzzle, since everything's the same as before, is the content buckets. In a perfect world, we could just bolt a Cloudfront distribution prepending prd/sites/whatever.com/files to the keys it serves straight out of my-drupal-7, but since the Image module generates renditions on demand, we need to preserve that rewrite. We could do the same static webhosting trick, but that shit would get mad complicated very quickly, plus we'd have to bake awareness of sites in to the environment, and Homie don't play that.

Just to keep moving, I handled this by adding a boolean parameter to a site's promotion job in Jenkins that will have him create a .contentbucket file containing the name of the content bucket and, after the deployment scripts make the symlink, check if I should mount sites/whatever.com/files. The bucket's redirect rules and CloudFront configuration and everything else stay exactly the same as when we were still living with Claude.

As with everything else I do, I have no idea how many best practices or anything I'm violating by using things this way, but Drupal (as always) seems more than eager to play ball, the Amazon shit behaves itself, and, dammit, it was just fun to build. Really, the only thing that bugs me is having to re-deploy the entire application when I add a new domain, but I don't do it that often and you can't really complain about a couple pieces of hail when life's a bed of cloudses.