One of the main problems teams face when practicing continuous delivery is to manage zero downtime deployments to the production environments. The goal is to deploy as soon as possible and depending on the heartbeat of the organization, this becomes a higher priority to manage active users without losing their data and sessions during a deployment process. In this post I'll share some of the ideas and approaches that are been used for achieving the goal of zero downtime deployments.
An important process for reducing risks and managing a zero deployment downtime is by following the blue-green deployment technique. In a blue-green deployment scenario, the approach is to bring up a parallel green environment and once everything is tested and ready to go, you simply switch all the traffic to the green environment and leave the blue environment idle. This also helps in easy rollback and switch to the blue environment if anything goes wrong in the current installation.
In a horizontally scaled environment, where you have multiple servers handling the load where the traffic is routed to one of the servers based on the load balancer scheduling algorithm, you can update the servers one-by-one and bring them online after the updates. The same approach will be used in this scenario also, but with the only difference that there will be N blue and N-1 green servers where N is the number of servers in each group in the web farm.
As long as deployment of application code is only considered, there is no problem managing that with a zero downtime requirement. But consider the deployment scenario which involves changes in the database schema as well. You can’t now update the DB schema first and continue using the old application code to use the new schema as it will create inconsistencies considering the code is written to work on an old DB schema. This involves taking extra precautions or considerations with updates that involve DB schema changes. When it involves database changes two approaches that helps the most are:
Strive for backward compatibility by performing schema changes that won't affect the existing code and also by ensuring that the deployed code can work with the old schema.
Some of the points to consider would be to:
- Perform schema changes in a way that won’t break existing code
- New columns added are always NULLABLE
- New columns provide a default value if it does not exist.
- Don't delete columns until none of the code uses them, or can handle their absence.
- Use triggers or similar mechanisms to populate values that are important for one deployed version of the application.
- Enforce referential integrity only when it makes sense.
Have an expansion and contraction database script:
This allows you to handle database changes that are safe to apply without breaking backward compatibility with the application code. Changes like creating new tables, adding columns or tweaking indexes etc. can be handled using the expansion scripts with a trigger or scripts that fills the default values. Once the application code is updated, you can execute the contraction script to clean up any database structure or data that is no longer needed.
You should plan to execute the expansion scripts prior to updating the application code and the contraction scripts once the application code has been updated and is in a stable state. This produces a nice benefit of decoupling database migrations from application deployments.