In Flow: The Mura Blog

A Modern Development Pipeline with Mura and AWS

October 25, 2018

It may be no surprise that when it comes time for an infrastructure migration, there will be some inevitable headaches. 

In fact, according to a report from TrustMarque, while 91% of CIOs and IT decision makers are looking to migrate in the next 3-5 years, more than half (56%) of CIOs still claim the complexity of their current IT infrastructure is slowing their migration to the cloud.

Scientific American Development Team Lead Nick Sollecito knows first-hand how difficult this process can be. So when his team decided to migrate Scientific American's infrastructure and automation pipeline from on-premise hosting to the cloud using Travis-CI, Docker Swarm, and Amazon Web Services, he made the decision to utilize the "Twelve-Factor App" methodology - a set of best practices which define an ideal way to manage your application and help deploy, manage, maintain, and scale your services in a cloud environment. 

Many teams struggle with overly complicated, slowed-down development... the Twelve-Factor App solves these issues by prescribing best practices for building and maintaining modern applications.

"We took a lot of inspiration from the Twelve-Factor App when we were planning out our migration," Sollecito says. 

Many teams struggle with overly complicated, slowed-down development. With guidance on things like configuration, deployment, and runtime that often slows you down and hinders performance, the Twelve Factor App solves these issues by prescribing best practices for building and maintaining modern applications.

The Twelve-Factor App

1. Codebase
You need to have one, and only one codebase that is fully tracked, with version control and multiple deployments. One codebase makes code tracking and versioning simple, while also easing the collaboration of a development team working on the same application. Deployments should be automatic, so everything can run in different environments without work.

Code should remain the same regardless of where the application is being deployed. However, configurations can vary. 

"This was a big one for us because in our old environment we had multiple code bases," Sollecito says. "It kind of became a mess. We took a step back and looked at how we structured things and we identified all the services that we would need to deploy, and created a master services directory."

2. Dependencies
You need to explicitly declare and isolate dependencies. By utilizing Docker-Compose and Docker Stack files, you can define your dependencies for each environment.

3. Config
You need to store config in the environment. Configurations are a central part of any app, specifically when you need to support multiple environments. There should be a strict separation between config and code. Code should remain the same regardless of where the application is being deployed. However, configurations can vary. 

"This was one of the areas that I think was critical with the changes in Mura 7.1 and in Lucee," Sollecito says, referencing the ability to define environment-specific configuration and Mura config or "settings.ini" variables, within the Docker-Compose file itself.

4. Backing Services
The code for a Twelve-Factor App makes no distinction between local and third-party services. To the app, both are attached resources, accessed via a URL or other credentials stored in the config. 

 The separation of build, release, and run stages makes continuous integration and delivery possible.

"What is nice about this is that you get everything defined in one place and it's easy to change out and swap out services if you need to," Sollecito says. "This was a really hard thing for us when we were testing different cache engines."

5. Build, Release Run
You need to have strictly separate build and run stages. The separation of build, release, and run stages makes continuous integration and delivery possible.

According to Sollecito, it should be "impossible to make changes to the code at runtime, since there is no way to propagate those changes back to the build stage."

6. Processes
You should execute the app as one or more stateless processes. This is a key factor for creating a flexible and scalable app. You should deploy the app into the execution environment as one or more processes, and the processes are stateless and share nothing.

"If you're using Docker, using Swarm, and you're following best practices, you kind of get that for free," Sollecito says.

7. Port Binding
You should export services via port binding. A Twelve-Factor App exports HTTP as a service by binding to a port, and listening to requests coming in on that port. 

"Port binding, I think, is really easy and also comes free with Docker Swarm. In your Docker file you expose a port and in your Docker-Compose file, you can map the port to really anything you want," Sollecito says. "We found this to be extremely helpful as we use a lot of stacked backing services. Through port mapping, we're able to easily make sure that they can communicate with each other."

8. Concurrency
You should scale out via the process model. Keep small parts working independently, and running them as separate processes. By doing so, you will be able to scale, and if you're using stateless processes, this scaling should be seamless.

" We are experiencing zero-downtime deploys. We are deploying several times a day and that's been working well, and overall we've seen improved performance and improved visibility for defects. We now have a solid continuous development pipeline to continuously improve upon."

"If you're following the Twelve-Factor App then you're not making changes to the containers - they're immutable - you're able to scale out," Sollecito says. "You're basically following this already."

9. Disposability
Maximize robustness with fast startup and graceful shutdown. You should make sure changes can take effect very quickly, while also ensuring you can start up and shut down rapidly, and also handle a crash.

"We spent a lot of time on this trying to get to that zero downtime deployment," Sollecito says. "When we were initially testing traffic and we would run live traffic and we would test the deploy, we were getting tons of errors. It took a lot of trial and error to try to figure this one out."

10. Dev/Prod Parity
Keep development, staging, and production as similar as possible. Your development environment should be as similar to your production environment as possible. You should design for continuous deployments by keeping fewer gaps between production and development environments.

"This was a big one for us because we started out with our configuration being everywhere and now that we've sort of consolidated everything, all of our configuration is in our repo," Sollecito says. "Docker-Compose helped us keep it all in sync across all the environments."

"So far our biggest challenge here, and something we still have yet to solve, is just having the right data in the right places," he says. "But overall we're finding, from a configuration standpoint and from a deployment standpoint, we've done a pretty good job of keeping the Dev/Prod Parity in check."

11. Logs
Treat logs as event streams. Log files should keep track of a variety of things, from the mundane to the critical.
    
"You definitely want to make sure that everything is being logged," Sollecito says. "If you're not logging all your appropriate logs then things are just going to get lost and you'll never figure out what's going on."

12. Admin Processes
Run admin/management tasks as one-off processes. "Admin management processes kind of run as part of the application itself. We don't really do much outside of that," Sollecito says. "But it's basically saying that if you're running scripts, like deploy scripts or admin processes, they should be running alongside your app."

Worth the effort

For most who have built applications in the past, the Twelve-Factor App methodology should seem pretty straight-forward. The basic principles are rooted in fundamental system designs, and cutting any of these corners is a bad idea. However you interpret or apply them to your own projects, it is wise to follow this proven set of best practices for architecting, building, and maintaining modern applications. 

"Overall, we're pretty happy with the success of the migration," Sollecito says. " We are experiencing zero-downtime deploys. We are deploying several times a day and that's been working well, and overall we've seen improved performance and improved visibility for defects. We now have a solid continuous development pipeline to continuously improve upon."

More from Scientific American and Blueriver

[Video] A Modern Development Pipeline with Mura and AWS

Development Team Lead Nick Sollecito from Scientific American provides an overview of the experience and lessons learned migrating SA's infrastructure and automation pipeline using Travis-CI, Docker Swarm, and Amazon Web Services in this free, on-demand presentation.