I come from the backend world where I built docker images and push them to a registry.
Recently there was the need to develop an angular SPA and host it on azure static webapps. The pipelines are running on azure devops and are defined as yaml.
I have a really simple, working pipeline which does the build and deployment:
trigger:
none
parameters:
- name: buildConfiguration
type: string
default: none
- name: deploymentToken
type: string
default: none
steps:
- task: NodeTool@0
inputs:
versionSpec: '20.x'
displayName: 'Install Node.js'
- script: |
npm ci
npm run build -- --configuration=${{ parameters.buildConfiguration }}
displayName: 'Install dependencies and build Angular app'
- task: AzureStaticWebApp@0
inputs:
app_location: 'dist'
output_location: ''
skip_app_build: true
skip_api_build: true
config_file_location: dist/assets
azure_static_web_apps_api_token: ${{ parameters.deploymentToken }}
displayName: 'Deploy to Azure Static Web Apps'
However, I just thought about what to do when I need to rollback fast to the previous version.
What is a good practice in this case? Should build-artifacts (.zip in this case?) be stored somewhere? Is azure pipeline artifacts appropriate for that?
Currently I would have to keep the release branches (we do git flow) and rerun the pipeline based on that branch, which is basically a new build and does not deploy the exactly the same artifact I guess.
I could also define a new pipeline which takes a git tag (release/x.x.x) and a target environment (static webapp dev, staging, prod) which checks out the specific version tag, does the build and the deployment again.
Would the publish artifact
task be appropriate for something like this?
steps:
- task: PublishPipelineArtifact@1
inputs:
targetPath: $(System.DefaultWorkingDirectory)/bin/WebApp
artifactName: WebApp
I am not sure if I should have a second stage which implements a deployment job. In my mind that job would download the previously published artifact before the deployment.
Later, if an error happens and I need to rollback, I would rerun the deployment stage of a previous pipeline run. Then it just downloads the previous artifact instead of building it anew.
I guess i need a hint for the right direction to take:
Best practice questions are often frowned upon on StackOverflow. To properly answer this could require many other questions related to what problem you're trying to solve, or what parts of the problem are more important than others. For example, how critical is the application, are you trying to optimize costs, reduce operational overhead, improve traceability, or time for recovery?
There is nothing wrong with the suggestions you've provided. Using the publish
task to create a pipeline artifact and using separate stages is a good approach, as you're separating build from deploy. You could accomplish a rollback approach by re-running the "deploy stage" to redeploy the previously built artifact. They key difference between this approach and re-running the entire pipeline is that you'll some time required to produce the artifact. Whether you re-run the entire pipeline or deploy a previous artifact is the same outcome. So if you're only concern is how long it takes to compile, this is a suitable solution.
One thing to keep in mind: pipeline artifacts are not permanent. The default configuration retains 3 versions per branch and 30 days of the "default branch". There is an option to "retain" a build, which overrides the default expiration policies. This can be introduced as automation as part of your pipeline.
The above outlines possible implementation details for re-deploying a previously built artifact based on the git commit. It's easy to understand and maintain. However, there are other approaches that are subjectively better but add other levels of complexity and cost. For example, you could do a blue/green or canary deployment by introducing deployment slots to your infrastructure.
For example, a deployment
job in your Azure pipeline has the ability to tap into the deployment lifecycle and gradually roll out the application.
jobs:
- deployment: deploy
environment: production
strategy:
runOnce:
preDeploy:
steps:
# steps to prepare the deployment,
# e.g. flip traffic to the production slot
deploy:
steps:
# steps to perform the deployment
routeTraffic:
steps:
# steps to test the deployment
# e.g. route a percentage of traffic to the new slot
postRouteTraffic:
steps:
# monitor the traffic, check for errors, etc
on:
success:
steps:
# flip all remaining traffic to the new slot
# record telemetry, re-enable monitoring tools, etc
failure:
steps:
# automatically rollback to the original slot
# record failure telemetry