people travel. A big part of how we do that online is powered by various Ruby on Rails applications behind the scenes. Today, I want to share how we use packages to deploy those Rails apps.
gathered together into a format that can be distributed to other systems. A package may include metadata like a name, version, description, and dependencies. This metadata helps the packaging system know how to install the package.
Yeah, we typically think of packages for system level software like web servers and databases. Before I joined Lonely Planet I wasn’t familiar with this pattern, but the more I’ve seen it in action, the more I like it. It brings some nice advantages that we’ll get to shortly.
fetching source code, installing gems, compiling assets, migrating the database, and restarting the app. Our packaging process handles 4/5 of those steps. We handle migrations separately. Looks look at how our packaging process works.
so a key point of the process I’m about to describe is that it is completely automated. There is no one hand crafting these packages and tweaking configuration flags. When a developer gets the :thumbsup: on a PR and merges into master, our continuous integration server (Jenkins) begins the automated process of moving that code to production.
that it’s more self-contained and so the actual deploy is faster. We add the deployment flag and the path flag so that gems are installed into the vendor directory instead of on the system. One thing to note here is that you want to make sure that your build system closely matches production because otherwise you’ll run into issue with libraries being in different places etc.
we don’t include them in the package because our app servers do not serve any asset requests. We push these up to S3 to be served by our CDN. We DO however include the asset manifests in the package because the application needs those during startup.
we use fpm. fpm is an awesome tool that creates a nice abstracted interface to iron out the quirks of various packaging systems. We run Ubuntu Server in production so we output .deb files, but you could adapt this to your platform of choice.
being installed. For example, the nokogiri gem depends on libxml and libxslt (available as packages). We can specify those dependencies here and they will be installed along with the app.
to our private package repository. The repository itself is static files served over http. We host the package repository on S3 so it is pretty fast and very reliable. The tool we use to manage the repository is deb-s3.
apt will recognize that a newer version of the package is available. It installs the newer version and restarts the application using a configured post-install hook.
happens we only need to copy the bits from the package repository and restart. This also really nice when we need to scale up or replace a server. Rollbacks are fast too because the repository (or better yet, the local cache) has previous version so we can quickly roll back to a previous version.
interaction with 3rd party services to the packaging step. We’re not making calls to fetch source code and gems over X number of servers for each deploy. S3 is fairly reliable and the package repository has no moving parts so it makes a solid dependency. In the past 6 months we’ve not had an issue related to deploying the packages.
some ideas of how to optimize your deployment process. For me, the big takeaway (whether you use packages or not) is to focus on making your deployment fast, reliable, and low-impact. Thank you for letting me describe this process. I’d love to hear any feedback or questions you might have for me.