The phrase ‘modern app’ is used frequently in blogs and technical articles but the definition has always been subjective. When I speak about a modern app, here’s what I have in mind.
What is a modern app?
Modern applications are built with modular architectural patterns, serverless operational models, and agile developer processes. They allow you to innovate faster, reduce risk, accelerate time to market, and decrease your total cost of ownership (TCO). One of the ways for a business to iterate and innovate faster is to adopt modern DevOps practices - especially CI / CD. This is another phrase that you have likely come across at a conference or in the media. CI / CD is short for Continuous Integration / Continuous Deployment. In some cases the ‘D’ also stands for ‘Delivery’ but for the purpose of this article, we will go with Deployment. If you have ever wondered how to modernize your app by adopting CD best practices, this article is intended for you.
A caveat - While the article illustrates Continuous Deployment with the use of AWS Services such as AWS CodeDeploy and Amazon Elastic Container Service (ECS), the principles remain the same for the cloud provider and tooling of your choice.
Draw the balance between agility and reliability
The dream of every developer has been to write code, hit a big red button and then see this code in production. This can be a good or a bad thing. In some ways, this is the most agile any development process can be. Who needs checks and balances, amirite? But this is also a *very* bad thing. Bugs, and errors can crash your application and give your customers a terrible experience. So how do you draw the balance between agility and reliability?
Continuous Deployment is one way to get closer to achieving that dream. Along with continuous integration - where developers regularly merge their code changes into a central repository after which automated builds and tests are run - CD is the practice where code changes are automatically prepared for a release to production. Continuous deployment expands upon continuous integration by deploying all code changes to a testing environment and/or a production environment after the build stage. There can be (and usually are) multiple, parallel test stages before a production deployment. The difference between continuous delivery and continuous deployment is the presence of a manual approval to update to production. With continuous deployment, production happens automatically without explicit approval. As good as pushing that big, red button.
How to succeed with continuous deployment?
The goals for effective continuous deployment are
- Automatically deploy new changes to staging environments for testing,
- Deploy to production safely without impacting customers
- Deliver faster by reducing change lead time and change failure rate.
With the cloud, implementing this practice is easier than ever. Let’s take the example of AWS CodeDeploy which enables automated deployments with virtual servers, containers, serverless and even on-premise workloads.
Continuous deployment applied to AWS CodeDeploy
Here’s an example of how AWS CodeDeploy automates your deployments on Amazon ECS. (This assumes you have the prerequisites of an app running on AWS with the relevant IAM permissions and policies.)
CodeDeploy needs an Application Specification or AppSpec file to manage deployments. This is typically formatted in YAML (or JSON) and looks something like this:
- source: /
- location: scripts/install_dependencies.sh
- location: scripts/change_permissions.sh
- location: scripts/start_server.sh
- location: scripts/create_test_db.sh
- location: scripts/stop_server.sh
As you can see, there are different sections in the AppSpec file. Sections such as version and os are self-explanatory. You can set app configurations and also specify the Operating System used.
The 'hooks' section contains mappings that link deployment lifecycle event hooks to one or more scripts. Each deployment hook is executed once per deployment to an instance. You can specify one or more scripts to run in a hook. Each hook for a lifecycle event is specified with a string on a separate line. There are different lifecycle events such as ApplicationStop, BeforeInstall, and ApplicationStop so that automated scripts can run at each of these events. For instance, in the ApplicationStart hook, the 2 scripts start a server and create a database. Similarly, the ApplicationStop hook has a script that stops the server.
Understanding deployment types and configurations
So, you’re managing your deployments with an AppSpec file, that is just the start to an automated deployment. The next step is to determine how to eliminate downtime, and minimize errors to ensure that your customers are not impacted. The key to this is to choose the right deployment type and configuration for your application. CodeDeploy provides two deployment type options:
- 1. In-place deployment where the application on each instance in the deployment group is stopped, the latest application revision is installed, and the new version of the application is started and validated.
- 2. Blue/Green Deployments - which we will deep dive into now.
Blue/Green deployment strategy
We want to build modern apps, and modern apps need modern solutions. One way to modernize your app is by adopting the blue/green deployment strategy. This is a deployment strategy in which you create two separate, but identical environments. One environment (blue) is running the current application version and one environment (green) is running the new application version. Once testing has been completed on the green environment, live application traffic is directed to the green environment and the blue environment is deprecated.
This strategy increases application availability and reduces deployment risk by simplifying the rollback process if a deployment fails. The traffic redirection can happen in one of three ways.
- All-at-once: All traffic is shifted from the ECS task set to the updated function or task set all at once. This can be done by choosing the CodeDeployDefault.ECSAllAtOnce configuration. As the name suggests, this shifts all traffic to the updated Amazon ECS container at once
- Linear: Traffic is shifted in equal increments with an equal number of minutes between each increment. You can choose from predefined linear options that specify the percentage of traffic shifted in each increment and the number of minutes between each increment. There are two preset configurations you can use and the names are self-explanatory - CodeDeployDefault.ECSLinear10PercentEvery1Minutes which shifts 10% of traffic every minute until all traffic is shifted, and CodeDeployDefault.ECSLinear10PercentEvery3Minutes which shifts 10% of traffic every three minutes until all traffic is shifted.
- Canary: For a Fun Fact about why it’s called a ‘canary’ deployment, scroll all the way to the bottom of the article! Here, traffic is shifted in two increments. You can choose from predefined canary options that specify the percentage of traffic shifted to ECS task set in the first increment and the interval, in minutes, before the remaining traffic is shifted in the second increment. CodeDeployDefault.ECSCanary10Percent5Minutes shifts 10% of traffic in the first increment. The remaining 90 percent is deployed five minutes later while CodeDeployDefault.ECSCanary10Percent15Minutes shifts 10% of traffic in the first increment and the remaining 90 percent is deployed 15 minutes later.
The subtle difference between linear and canary deployments is in how the traffic is shifted after the first X percent. With linear, it follows a... well, linear progression of shifting, but with a canary deployment, 90% of the traffic is shifted at once, AFTER a certain amount of time. In this passage of time, if there are no alarms or errors, it is assumed that the new update is good to go. Linear and canary deployments through CodeDeploy limit the exposure of live traffic to the new application version to a percentage of total traffic in order to monitor performance before routing remaining traffic with confidence. You can also set up Amazon CloudWatch alarms to alert you if an issue is detected. CodeDeploy can then automatically redeploy a previously deployed stable revision of an application as a new deployment.
Trust the process
Modernization is a journey and not something a business can achieve overnight. Setting up continuous deployment processes are built-up over time. From personal experience, I’ve found that taking a ‘pessimistic view’ - where you find any reasons to fail a deployment, works wonders in building a robust deployment pipeline that minimizes downtime for your customers. CI / CD is an approach that is (pardon the pun) continuously evolving - so the best time to start modernizing is now!
Also, as promised here’s a Fun Fact™ - A canary deployment is named as an allusion to caged canaries (birds) that miners would carry down into the mine tunnels with them. If dangerous gases such as carbon monoxide collected in the mine, the gases would kill the canary before killing the miners, thus providing a warning to exit the tunnels immediately. So, in the case of canary deployments, if one of the deployments causes an error and triggers an alarm, the deployment is then rolled back and the customer is not impacted. Now you know 🤓