Serverless, DevOps, and CI/CD: Part 3
Managing environments and versions
One of the most frequent conversations I get when working with business on serverless architectures is if I have any recommendations on how to manage environments. There are many different approaches and strategies that can be taken, but I want to show one way you can deploy your serverless apps across different environments automatically and seamlessly.
When working with changes or updates to apps in production, even after running unit tests or even integration tests, you likely don’t want to push your changes directly into production. As many tests as you may have, there are always still some variables you want to validate in the hosted cloud environment first.
Luckily Azure DevOps makes managing deployments across environments very simple. In the previous post, we set up a single environment to push changes. Since this will be the first environment, it will be our pre-prod environment. You may have many stages and environments, but the process is usually the same.
- Deploy the build to the environment
- Validate things work as expected
- Approve for future environments
It’s best to automate wherever you can, so for my sample app, I’m going to keep all 3 of these pieces automated.
In my release definition, I’m going to click Clone on my pre-prod environment to setup my production one. All I need to change is the destination of the Azure Function publish action to my production app (or production staging slot).
I can now setup pre-deployment actions.
Release gates allow me to set up rules that can automatically validate the health of an environment before continuing. I could wait a few minutes and check to make sure no Azure Monitor or App Insights alerts have fired. I can also call other Azure Functions or APIs to validate things are working.
To do my validation I wrote a simple C# function that will take the URL of a function and do one call to see if odd or even. So I can use Azure Functions to make sure my Azure Functions are working.
I don’t need to and likely shouldn’t do a release gate for each of my unit tests — chances are if it works for a few it should work for all — but you can have as many automated release gates as you need.
After configuring, now my build pipeline will publish to the Dev environment, wait a few seconds, and then call the dev environment and ensure all appears well before automatically continuing to deploy to production.
Environment variables and datasets
It’s very likely that each of my environments will have its own unique set of data sources and downstream systems. If I am writing a function to interact with CosmosDB, my Dev function should not be writing data into my production CosmosDB dataset. Instead, I may have a “dev” collection or database and can use the application settings on the function to point to the right values. Any values in my code that will change based on the environment should be environment variables/app settings. This way I can deploy the exact same code but have the behavior vary based on the environment.
So in my sample, instead of calling a static URL if odd or even, I will make that environment variable. In my pre-prod environment, this URL will point to a test API (maybe even an Azure Function proxy mock), and my prod function will have an app setting to set this environment variable against my real API.