DEV Community

Cover image for Container image promotion across environments - Registry
Davide 'CoderDave' Benvegnù
Davide 'CoderDave' Benvegnù

Posted on • Updated on

Container image promotion across environments - Registry

When you use containers for your application, one of the things you need to think about is how to move (aka promote) the container images you generate across different environments.

In this series, I will explore different ways to do so... with the help of Azure DevOps

Intro

In this article, the first of the series, we will explore the simplest and most used way (at least in my experience) to promote a Container image across different environments.

We will use a non-environment-specific registry as a base repo, and we will push the image to this registry during the build phase. Then, we will take the image and push it to some environment-specific registries in the release phases as we move forward.

The Build

As step "zero", we want to create our container image with all we need for our application to function properly. I will not focus on the image creation at this time.

There are two ways to "install" your code onto a container image: a multi-step Dockerfile which compiles your code AND create the image, or compiling the application outside the container and then simply install it into the image.

While the first approach ensures (in theory) even more immutability, the build process is often way less verbose and more difficult to tune.

I personally prefer using the CI capabilities of Azure DevOps to build the application and then create the image with the result. Let me know if you are interested in this approach so I can write another post about it.

Whatever your approach is, you will at the end have your container image created.

Now what?

As I've mentioned before, we need to push it to an environment-neutral container registry. Even tho we could do this in the release process as well, I normally consider this as part of the build, simply because you want your build process to give you something usable. In our case, usable means having the image ready to be processed, therefore we'd need to have it in the registry.

Build pipeline

As you can see, I simply create the image and then I push it to the registry.

Actually Azure DevOps has a task that can combine both operation, called buildAndPush, but for clarity I prefer keeping the two commands separated.

Let's Release

Ok, now we have our container image, and we have pushed it to the general container registry.

The temptation here would be to pull the image from that very registry and ship it to our server. NO, we won't do that. Instead, we are going to create a Release Pipeline which will use the container registry as input.

Artifact

To do so, simply click on the "Add an artifact" button (after creating a new release pipeline), and select "Azure Container Registry" (or Docker Hub, if you want to use that). Just few parameters to set, and you're ready to go.

Also, be sure to enable the Continuous Delivery flag so this release pipeline can run every time a new container image is pushed to the registry.

Push it

Now, let's talk about the next steps. Let's say you have three different environments: Dev, Test, and Prod. First thing to do is to create three Stages in the Pipeline.

Stages

Then let's edit the Dev stage. We need to:

  • Pull the image from the generic registry
  • Change it's name so it can be pushed to the Dev registry
  • Push it to the Dev registry

The 3 steps

To pull the image from the generic container registry we need to do a little trick. The pull command, in fact, is not directly embedded in the v2 of the Docker task.

Pull the image from the Base Container

So we need to manually type pull as command name, and insert the registry name and the image name in the arguments box.

I have defined variables for the names so I can reuse them across the different environments.

Next we need to tag the image differently, to change the name of the registry. This is because if you have to push an image to a certain registry, you need the image full name as "registryName/ImageName", where registryName is the full qualified domain in case of anything different from Docker Hub (for Azure Container Registry, it would be something like myregistryname.azurecr.io

Tag with new registry

Again, the tag command is not embedded in the v2 of the task so we need to use the arguments box. The use of variables is optional but I recommend it, it just makes everything easier to automate and templatize.

Last step, we need to push the image to the new registry.

And push it

This time, the push command is fully supported so no need for the custom arguments!

And we are done for Dev!

Now we can replicate the same process to the other environments, just changing the source and target registries.

Ideally when pushing to the environment-specific registry you should have a mechanism to notify your target host service (App Service, AKS, Container Instances, etc) of the new image so the deployment can be executed.

And of course you probably want to set some Release Gates or Approvals for deploying to Test and Prod.

Conclusion

This process is probably the most used, however it is not my favorite.

First of all, you need more registries than environments.

Second, and probably more important, it doesn't ensure 1:1 mapping between build and release. And this means that the image you build and the one you deploy might not be the same.

If someone does any change to the images in the generic registry, then you will in fact end up having deployed a different version than the one your CI pipeline generated.

Another issue here is that you cannot directly reference the Build number, or any other parameter that comes from the Build, because you CI and your CD pipelines are not directly related. This may not be a problem for everyone, but I like to version my images using the BuildId. So this won't work for me.

This is why in the next article of the series we will explore another approach, using the Build Artifact capabilities of Azure DevOps to get a fully immutable image to be promoted across environments.

Stay tuned!

Like, share and follow me 🚀 for more content:

📽 YouTube
Buy me a coffee
💖 Patreon
🌐 CoderDave.io Website
👕 Merch
👦🏻 Facebook page
🐱‍💻 GitHub
👲🏻 Twitter
👴🏻 LinkedIn
🔉 Podcast

Buy Me A Coffee

Top comments (3)

Collapse
 
nickraphael profile image
Nick Raphael

This was very helpful. Many thanks. I did find one issue though. In the re-tag task, you need to specify the tag in the first part of the arguments. I was getting an issue where I was getting the latest image despite selecting a previous build when kicking off a release.

Collapse
 
benjaminlucas153 profile image
benjaminlucas153

Hi, Was curious to know what difference the base registry makes. instead why can't we directly push to DEV registry using CI and then promote to TEST and PROD using CD?

Collapse
 
n3wt0n profile image
Davide 'CoderDave' Benvegnù

Hi, thanks for your question. The difference is that if you have a system that pulls the registry and deploys automatically the new version of the image then you probably want to be able to control when and how you deploy. If you push directly to the base registry, which no service should use, you can control the deployment flow to any and every environment. Hope this makes sense.