How to Build and Deploy Docker Containers with Azure Pipelines
Tags: azure, devops
Published: 2020-07-18 23:35:34

{% youtube 272GOWKDqP4 %}


In this YouTube video I show you how to use the Azure Pipelines service to build and deploy containers to App Service. You'll also see how to setup a YAML-based CI/CD pipeline in Azure Pipelines and how to use a Docker file in your build stage to create container images, and then push those images to Azure Container Registry (ACR).

You can get the source code from this step-by-step tutorial from the CloudSkills GitHub organization: [ASP.NET Demo App with YAML CI/CD Pipeline Configuration](https://cloudskills.io/blog/build-azure-pipelines)

**Full Transcript:**

Mike Pfeiffer:
So if you’re just getting into the world of DevOps for the very first time, you’re probably trying to figure out CI/CD pipelines and also perhaps containers, building container images and deploying those. And if you haven’t done that before, it can be kind of confusing. So in this video, I’m going to show you how to build container images and deploy them through Azure pipeline.

Mike Pfeiffer:
Hey, what’s up everybody. It’s Mike Pfeiffer, Microsoft Azure MVP, and host of the cloud skills FM podcasts. This video is basically a step by step training. It’s from our Azure DevOps class over at cloudskills.io, our EZ 400 course. And in this step by step hands on lab, I show you how to set up an Azure DevOps project, how to take a reference application that not only has application code, also has infrastructure code, take that repository, put it in Azure DevOps. And then I walk you through building a CI/CD pipeline that’ll build the container image using the docker file and the repository, and then eventually deploying that the Azure app service. I think you’ll really enjoy this one, especially if you’re new. The things like Azure DevOps and working with containers. So let’s go ahead and get started.

Mike Pfeiffer:
In this first step of the lab, I wanted to show you the sample application we’re going to be using. And I also wanted to remind you about the YAML schema reference in the Azure DevOps documentation. And when you scroll down here, the main thing I wanted to remind you of is the hierarchy of a YAML pipeline. Now, obviously you’re going to have all the code for this, but I realize that looking at an entire YAML pipeline is a little bit intimidating. So just remember what we’re going to do here is we’ll have one YAML documents. We’re going to have stages for the different phases of this pipeline. So we’ll have the first stage for build and tests. So building and testing the application itself. And then we’re going to have a second stage. That stage is going to release the application into a staging environment. And we’re going to keep this simple, we’re not going to do multiple jobs in each stage.

Mike Pfeiffer:
We’re just going to have one job in each stage. And each of those jobs is going to have different steps that have to take place along the way. And this is nothing new from what we’ve seen in the past. I just want to remind you that this hierarchy is very basic, but it’s very easy to get confused by this when you’re looking at a finished YAML document. So let me hop over to visual studio code and show you this application. So we’ve got two things in this repo when you’re going to import this into your project. So there is the application itself. This is a visual studio solution. That’s got three projects in it. We’re going to make use of the actual application itself, the ASP net core web application. And then there’s a unit test project in here. So in this arm templates folder, we have a template here to build a web app in Azure app service.

Mike Pfeiffer:
And then there’s also a template that we’ll deploy a container registry resource. So the cool thing about this implementation is that the pipeline is going to build the infrastructure for us if it doesn’t exist, using the arm templates. And it’s going to do that as part of the build pipeline. So that’s the overall basic project structure for this application. Let’s go to the next video and we’ll start digging into the Azure pipelines YAML document.

Mike Pfeiffer:
Let’s go ahead and break down the pipeline YAML file that we’re going to use to build the CIC pipeline. So along with this sample application, you’ll have an Azure pipelines YAML document. Let me go ahead and move that over to the left here. And this is going to be a very simple implementation, just a couple stages. As we’ve seen in the past, we’re going to have continuous integration set up.

Mike Pfeiffer:
So any changes to the master branch of the repository are going to trigger the pipeline. And so as usual, we’ve got a handful of variables here. I have my build configuration variable and I’ve used the location variable. I’m going to use this in various places throughout both stages to indicate to the tasks where I want to deploy my resources. So there’s a variable for the location for the region we’re going to use. There is a variable for the ACR host name. So the Azure container registry host name, and obviously you’re going to want to use unique values here. So as you basically import this repo, I’ll show you how to do that later, but you’re going to want to change these pieces of information. So these are all DNS names at the end of the day, for the most part. So I’m just going to change these values.

Mike Pfeiffer:
We’ll say this is the fifth version of this that I’m implementing here. And you can use anything you want, as long as it’s compliant for a DNS host name. So your ACR FQDN will be listed here. Just the host name will be set in this variable. The name of your resource group will be included. Whatever you want to name the Docker image that’s going to get stored in the container registry, and then finally the name of the app service web application. So remember that just like any kind of programming environment, we don’t want to hard code any values if we can help it. And especially, we don’t want to repeat ourselves. So you’ll see in this YAML document that we use these values multiple times, but then also not hard coding it in the configuration is a nice practice and it gives us more capabilities going forward to customize things.

Mike Pfeiffer:
So after defining variables, we have our stages declared. And again, we’re just going to have two stages. And you can basically see here that we referenced the first stage, giving it a name called building tests. Notice I’m using comments along the way as well. And the properties for this build and test stage, we’re going to have one job. Job is going to be called build and tests. And then we start basically configuring all the things that we’ve been looking at throughout this program. So in the hosted pool, we’re going to use a virtual machine running Ubuntu 1604, since this is a dotnet core web application, this is cross platform. So we can do the build and test process on Linux. And for this job, we have a collection of steps. Just under the steps indicator here, you can see that we’ve got our first task. So this first task is actually going to deploy our infrastructure for us.

Mike Pfeiffer:
This is the Azure resource group deployment task that we use to launch arm templates. So inside our project, we’re going to be expecting to have a service connection. So this is going to be something I’m going to need to create. You’ll see me do that later. Here, you can see that we’re referencing the resource group name based on the variable. It’s included at the top of the YAML document. Same thing about the location, and then the file that we’re going to use to do the deployment. So the actual arm template itself. So in this building stage, we’re building the container registry resource. That should happen pretty quick. And if it already exists, it will basically be a quick update as well because we’re doing incremental deployment here by default. And notice this input here for this task override parameters. So these are the input parameters that we would actually be using from the command line if we were doing an arm template deployment.

Mike Pfeiffer:
And so what you’ll notice here is instead of using the parameters file that’s basically part of the arm template system, we’re just feeding in the parameter values. And here we’re using variables in Azure pipelines. So we’re able to take the variables that are at the top of this document in reference them inside this override parameters value here. And the one thing I wanted to point out is that sometimes when you’re using variables that have a space like location, how it has west space, us space two in the value, and you’re referencing that somewhere else like right here, we know that from the past, this macro syntax of referencing a variable value can be done by using a dollar sign and open and closing parentheses with the variable name inside it. But if that value is going to end up returning a string that has spaces in it, like in this case West space, US-space two, you want to enclose that in double quotes, just like you see here.

Mike Pfeiffer:
And so that first task is going to take care of deploying the arm template. The second task is just going to do a dotnet restore, just like we’ve seen in the past. So the command to do a dotnet restore, we’re going to then do a dotnet build, and then we’re going to run our unit tests. So again, we’re just using the dotnet core CLI task here to do all these things. After we execute those unit tests, we’ll publish the project files. We’ll go down here, we’ll use the Docker task to build a container image. So this will just be like on your desktop, on your machine and a terminal running Docker build. Notice that we’re going to reference the container registry that we’ve got listed in our variables. And then we’re going to define an image name that we’re going to use for this container image.

Mike Pfeiffer:
So here we’re using our own custom user defined variable called an image name. And then for the tag, we’re using a variable from Azure pipelines, which is build.build ID. So every time a build runs, this build id will be different, and that’ll give us the ability to incrementally version each image for the Docker container. From there, we’ll use the Docker task to go ahead and push that image up into the container registry. So the command for this is called push an image. And again, we’re going to reference the same image name with the same image tag, basically the build ID. And the last two tasks in this build and test stage are to copy the files. So the arm templates into the build artifact staging directory, because we are going to do an arm template deployment in the release stage to deploy the web app infrastructure.

Mike Pfeiffer:
And then finally, once that stuff is copied over to artifact staging directory, we’re just going to publish those build output artifacts in an app called app. So that is the build and tests portion of this YAML pipeline. Let’s go to the next video and we’ll take a look at the staging release. That’s actually going to deploy our application and get everything up and running.

Mike Pfeiffer:
All right. So now that we understand the build and test to stage here in this YAML pipeline, let’s scroll all the way down to the bottom to the second stage. And we’ll just talk here about this release stage. So what we’re going to do here again is we’re just going to specify one job. This is going to be a release job. Again, we’re going to use a Ubuntu server out of the hosted pool. And then in terms of our steps, this is going to be very simple.

Mike Pfeiffer:
So we’ve seen this in the past. Remember, we’re going to not check out the repo in this release process because there’s no need to do that. We’re going to have access to the artifacts from the build stage so we can actually download those build output artifacts so we can reference those here. And then finally, when you get down to the last couple of tasks, number one, we’re going to again, use the Azure resource group deployment task to launch an arm template. And as you can see, we’re setting the resource group name through a variable, the location. And then finally the path to the arm templates itself is listed here. Keep in mind that when you’re working with tasks in a release pipeline like this or a release stage, you can reference the pipeline.workspace here using this system variable that’s available to us to get to your build output artifacts.

Mike Pfeiffer:
So we copied the arm templates in that last couple of tasks in the build and test stage. That’s going to put those eventually in the output artifacts. We can get to them by referencing this variable here. Finally, this task also has some override parameters. So there is no parameters file with that arm template, and then we’re just specifying the inputs here. Finally, the last task in the release stage here is we’re just going to do an Azure RM web app deployment. We’re going to deploy the app service. So we’ve seen this task in a previous lab, but we’re doing something a little bit different this time. We’re deploying a Docker container. So the inputs are just a little bit different. So we’re going to set the service connection. In this case, the app type is going to be a Linux app service environment.

Mike Pfeiffer:
And then when it comes to indicating that web name, this is part of our variable declaration at the top of this document, we’ve got our Docker namespace property, which is going to be set to the ACR host name itself. The Docker repository is the image repository we created when we’re pushing our image up into the ACR environments. And finally, our Docker image tag. Now, the cool thing with this task that you might find useful is there is a web app URI property. So you can create a variable that’s going to be available inside the pipeline after the web app has been built. So this task is going to build a web app. And once it’s complete, we can populate the web app URL variable with the URL to the app service. That way you could have a subsequent task after this, go off and maybe do some testing against that URL, do some UI testing, some performance testing, things like that.

Mike Pfeiffer:
So that’s everything we need to take a look at for this YAML pipeline. Let’s go to the next step in this lab and I’ll show you how to set up the project and get this application set up and going.

Mike Pfeiffer:
So back in the Azure DevOps portal, let’s go in and create a new project here. I’m going to call this Docker app service. So we’ll create this project. And what we’re going to do is import a repository from the cloud skills github organization. So over on github.com/cloudskills, there’s this project here, ASP net core demo, or actually it’s ASP net core Docker demo. And you can see it’s the same files that I was just talking about here. So let’s go ahead and grab the clone URL for this and inside the project, we’ll go to repose, scroll down to imports, and then we’re going to import this repository.

Mike Pfeiffer:
And now that the import is successful, let’s go to project settings on the left hand side, and we’ll scroll down. Let’s go to service connections, and we’ll create a new service connection for this application and this project. So we’re going to pick resource manager here and click next and we’ll do an automatic service principle. I’m just going to scope this at the entire subscription level here. And we’ll call this Azure SC for Azure Service Connection. And with that service connection configure, let’s go back over to the project configuration, we’ll go to repose. Here’s all of our code and everything looks good. And so let’s go ahead and set up the pipeline. We’ll click on setup build, and then we’ll scroll down here and we’ll say we’re going to select an existing Azure YAML file inside this repository. So we’ll pick the Azure pipelines YAML documents, click on continue.

Mike Pfeiffer:
And here you can see that we’ve got all the code that I was pointing out last time. So notice, remember, you’re going to want to replace the values for this. So if you import this, you can go over to repose. You can of course clone and download it on your machine, but you could also just come in here and edit it. And Joe, you just want to make sure that you’re using unique values here. So I’ll change this to CS web app Docker 006. I’ll commit those changes to the master branch. And when we’re looking in repose, I can set up my build selecting existing Azure YAML pipeline.

Mike Pfeiffer:
And then now that that’s been updated, everything looks good there, let’s go ahead and run this pipeline. So down here, you can see that we’ve got built in test and staging. And this is very similar to the lab that we took a look at in the last week of this program. The only difference is this build process is first going to create a brand new resource for us. It’s going to create a container registry instance, which I don’t currently have, and then it’ll build out the infrastructure that I need for app service. So let’s give this thing a second to run and we’ll see what happens.

Mike Pfeiffer:
All right, so it’s been just under 10 minutes, both the build and test stage and the release stage are done. So we’ve got a brand new application and under 10 minutes here from scratch. Going over to the Azure portal, here is the resource group. And here’s my app service plan, my web app itself, app insights resource, the container registry. Let’s go into the web app itself. And then let’s head over to the URL for this application. So we can see a success message. This thing is working great, and this is running inside a container inside app service.

Mike Pfeiffer:
So as the last step to this lab, let’s move to the next video. And then we’ll see how we can update this application. As the last step to testing this out, let’s change the application just to make sure that that kicks off the pipeline and indeed does update our application. So this should not do anything to our existing infrastructure. So we have this infrastructure now, but let’s take a look at updating the app, watching the pipeline execute, and updating this infrastructure with the new container image. So let’s go over to repose here, we’ll go into the application folder.

Mike Pfeiffer:
We’ll go to this first project, ASPnetcore, dotnet core, and then inside pages, we’ll go to the index page and then we’ll edit this. And so on the homepage where it usually says success, we’ll update that to say “Success. You have deployed via Azure DevOps.” And so let’s go ahead and commit that change to the master branch, which will trigger our pipeline. And then we can monitor that by going over to pipelines here on the left and let’s let this run. And once this is complete, I’ll show you what happened in the resource group.

Mike Pfeiffer:
All right, you can see a few minutes later, everything looks good. Build and test and staging deployment is complete. Let’s go over to the Azure portal and in the resource group, let’s first check the web app. So we’ll go to the app service. We’ll go to the URL. All right. And so, as we can see here, the success message is on the page. Everything looks good. Let’s close out of this. And then let’s go back into the resource group. Let’s go to the container registry resource. And then here on the left hand side, if we go to repositories and we look at this repository, we can see that we’ve got two different container image versions. One that’s in there for build 145 and one that’s in there for build 146. That’s the most recent one that we just deployed into app service.

Mike Pfeiffer:
So it brings us to the end of this hands on lab. I hope you enjoyed it. And I will see you in the next one.