When working with containers, there are a few pivotal considerations that can significantly influence both your workflow and the reliability of the applications you deploy. One of the most crucial aspects to keep in mind is versioning.
Understanding the Process
The lifecycle of a containerized application typically follows a structured path from development to production. Here’s a typical workflow:
Development Phase: When a Pull Request (PR) is merged into the development branch, it's essential to tag the Docker image with a build ID. This tagging helps you track which version of the code is currently running in the development environment.
State or UAT Phase: After the development phase, when changes are merged into the main branch (often aligned with Stage / UAT), the Docker image is retagged. This retagging usually incorporates a release-candidate tag or a Semantic Versioning (semVer) tag, and then it's pushed to the Elastic Container Registry (ECR).
Production Release: Upon approval for production release, often marked by a git tag, the image is again retagged to a semVer release version. It's then pushed to the production ECR, ready for deployment.
Approach
The approach to versioning can vary based on the criticality of the application, however here is what i like to follow:
For Critical Customer Facing Services: I use a detailed tagging format such as either `
app-v1.0.0` or `app-v1.0.0-SHA` or `app-v1.0.0-git-SHA`
. Here, `app-v1.0.0`
indicates the release version, while `git`
as the same suggests represents that subsequent SHA is generated from `git` and not image digest, and `SHA`
represents the `commit ID`. This method ensures traceability and helps in quickly pinpointing the exact code version in production should any issues arise. Some people might call it an overkill but over my career I’ve realized that readability and maintainability will help you in long run than being concise.For Non-Customer Facing services: I use available LTS version for all services that we run in the cluster that is not customer facing. For example, for alpine image we use latest LTS version `alpine:3.14`, same for other images like prometheus `prom:1.2.1`, external-dns etc.
For Non-Critical Applications: Simplicity often trumps detailed traceability. In such cases, I tag these applications with
latest
and avoid the overhead of managing multiple version tags. This approach is less rigorous but can be sufficient for applications where immediate rollback and detailed version tracking are less critical. These include images and services running on dev and test clusters.
Gotcha of using 'Latest' Tag
While using the latest
tag can simplify the development and deployment process, it is important to note that latest
does not always mean stable. The latest
tag refers to the most recent build pushed to a repository, regardless of its stability or suitability for production. This can lead to unforeseen issues if the latest
image contains bugs or incomplete features.
Recommendation: Whenever possible, avoid using the latest
tag for production environments for any services. Instead, opt for specific version tags that refer to stable builds tested in your development and staging environments. This approach reduces the risk of deploying unstable or untested code and provides more control over the versions of the software running in your environments.
Conclusion
The strategy you choose for versioning containers should align with the operational requirements and the criticality of the applications you manage. While rigorous version control is indispensable for critical systems, less stringent methods might suffice for non-critical ones. By asking yourself "How much does versioning matter?" for each project, you can tailor your approach to best meet the needs of your applications and your team.
Remember, the right versioning strategy not only aids in maintaining operational stability but also enhances your team's ability to respond to issues swiftly and effectively.