Deploying an application reliably and efficiently is one of the many reasons organizations start using containers. Although containers provide a way to deliver value to end-users quickly, you need a way to continuously deliver that value. Using Continuous Integration and Continuous Delivery/Deployment (CICD) in Azure DevOps will solve the need to ship quickly to end-users in an automated fashion.
In this blog post you will learn how to build a Docker image out of a Dockerfile running in a Continuous Integration (CI) pipeline. Once the Docker image is created, it will be added to Azure Container Registry (ACR) which is used to store Docker images.
To follow along in this blog post, you will need the following:
- An Azure account which you can set up here.
- An Azure DevOps organization which you can create by following this tutorial.
- A beginner to intermediate level knowledge of Docker.
- Azure Command-Line Interface (AZ CLI) installed which you can find here.
- A text editor. Visual Studio Code (VS Code) will be used in this blog post which can be downloaded here.
- An understanding of GitHub at a beginner/intermediate level and a free GitHub account which you can sign up for here.
- .NET Core web app which you can clone here. Although using the code found in the GitHub repo isn't necessary, that's the code that will be used in this blog post.
Creating a Container Registry
Before you can create a Docker image, the Docker image needs a place to reside. Azure provides a container registry called Azure Container Registry (ACR). ACR, much like other image registries, provide a location for a Docker image to be stored in. Once the Docker image is stored in ACR, it can be used to build a container in Azure or even outside of Azure. For example - you can use
docker pull to clone the Docker image to a local host.
To create the container registry in Azure, run the following code which will create a basic-level registry inside of a resource group that you specify.
az acr create -g your_resource_group -n name_of_acr --sku basic
-gspecifies a resource group that exists within the Azure DevOps portal and the
-nspecifies the name of the Azure Container Registry
Next, you will need to log into the registry locally. Logging into the registry allows access from a localhost to ACR so you can manage the Docker images. To log into ACR locally, run the following command.
az acr login -n name_of_acr
Once you are logged in locally to the Docker registry, you will need to initiate the admin credentials found in ACR. This is so the Azure DevOps has access to run Docker commands against ACR from the pipeline. To give admin access to ACR, run the following command.
az acr update -n name_of_acr --admin-enabled true
As you can see from the screenshot below, after following the steps above, a container registry will be created. Please note that the container registry you create will have a different name than shown in the screenshot below.
Taking a Look at the Dockerfile
Now that the image registry is created, it's time to write the code that will create the Docker image. The code to create the Docker image resides in a Dockerfile. A Dockerfile creates a Docker image based on the application specifications like ports, what files the application needs, and what the application needs to run. Below you will see the specifications for the .NET Core web application found in the GitHub repository listed in the prerequisites section.
Open up VS Code and access the GitHub repo. You will see a Dockerfile under the web directory.
Open up the Dockerfile.
Let's talk about what the Dockerfile is doing.
- Two images are pulling pulled. One is the .NET Core SDK and the second is the .NET Core runtime.
- The working directory is being initiated as
- Ports 80 and 443 are being exposed inside of the Docker image for web connectivity.
.csprojand all other configuration files from the C# web application code are being copied into the Docker image
To find out more about the Dockerfile syntax, check out this breakdown.
Once complete, do not close the VS Code window.
Pushing the repository to GitHub
In the previous section you took a look at the Dockerfile and all of its dependencies needed to create the Docker image for a C# web application. In the upcoming section Creating the Pipeline, you will be using GitHub as the source for the code base inside of the pipeline. To use Github as the source, you will need to push the code that you cloned in the prerequisites section to your GitHub account.
Please note that this section will not go in-depth with Git and GitHub. Per the prerequisites section, this blog post assumes you have a beginner to intermediate level knowledge of Github and the commands. To learn more about if you don't have beginner to intermediate level knowledge, take a look at the GitHub tutorial found here.
Log into GitHub and create a new repository called web2 in GitHub to store the code that you cloned from the prerequisites section.
Clone the repository down to the localhost so you can copy/paste the C# web app into the newly created web2 repository.
Open up a new VS Code window and put the new VS Code window side-by-side with the Github repo that you cloned in the prerequisites section so you can open up the newly created repository next to it.
Open the newly created repository that was just cloned in the new VS Code window so you can copy the
web directory to the new
web directory from the GitHub repo found in the prerequisites section and paste it into the new
Open up a terminal and Git commit/push the
web2 repository to GitHub so the code in the
web2 repository can be accessed once you create the Azure DevOps pipeline.
Creating the Pipeline
In the previous section you set up a new GitHub repository. The new GitHub repo will be used to contain the code from the GitHub repository in the prerequisites section. In the section Taking a Look at the Dockerfile, on lines 7, 8, and 12, the Dockerfile is copying C# code from the web directory. Without the code existing in the GitHub repository, the Dockerfile will have no code to copy to build the Docker image.
If you are asking yourself Why don't I just link to Mike's GitHub repository, it's because I want you to get a feel for how to do these steps with your own GitHub. That way as you move on and create more Docker images, the process will be comfortable for you.
Initiating the Pipeline
To start the process of the creating the pipeline to create a Docker image, you will need to initialize the pipeline by starting the prerequisites like linking a GitHub profile to the pipeline so the code is accessible.
Open up a web browser and log into Azure DevOps to start creating the pipeline. As you can see in the screenshot below, my organization name is
adminturneddevops but the organization you see will be different.
Choose a project for the pipeline you will be creating to reside in within the Azure DevOps organization.
Once in the project, go to Pipelines and click Pipelines to start creating the new pipeline.
If you don't see this option, it's because you don't have multi-stage pipelines turned on in the preview features. To do this, go to User Settings --> Preview Features --> Turn on Multi-stage pipelines
Click the blue New pipeline button to start the creation of a new pipeline.
Choose the classic option by clicking the Use the classic editor button to not use YAML.
As the code is in the
web2 repository in GitHub, choose the GitHub option and search for the repository. After you find the
web2 repository click the Continue button.
Under the Select a template option, choose Empty job so you can start with no pre-made tasks and instead create the tasks from scratch.
For the name, you can choose whatever you would like as a specific name isn't a requirement. This blog post will use webappdeploy-ci for the name as shown in the screenshot below.
Choosing Pipeline Tasks
The pipeline is now initialized and is ready for task creation. The tasks that will be used are for copying the C# code, building an artifact out of the C# code, and building the Docker image.
Before choosing the tasks, you need to choose the proper build agent. The build agent is the Microsoft-hosted container used to run the pipeline.
Click on Pipeline as shown in the screenshot below.
In the Pipeline option you will see an option to choose an Agent specification. Choose Ubuntu 18.04 as shown in the screenshot below. The reason why is because the Docker image you are deploying is based off of a Linux image. Because it is based off of a Linux image, a Linux operating system is needed to pull down the docker image for the .NET Core SDK and runtime.
After the agent specification is chosen, click the blue + symbol inside of the agent job.
In the Add tasks window, search for copy files and choose the first option. Click the blue Add button.
For the next task, search for publish build artifacts and click the blue Add button.
The last task to search for add and is the Docker task.
After adding the tasks, the pipeline should look like the below screenshot.
Configuring the Tasks
In the previous section you added the neccassary tasks to not only copy and publish the C# web app code, but to build a Docker image out of the code. Now it's time to configure the tasks.
Open up the first task Copy files and add the same settings that you see in the screenshot below. Let's break the task settings down.
- Source Folder is where the C# code is. In this case, the code is in the web directory.
- Contents are the code that you want to copy.
**means all of code from the source folder.
- Target folder is the location to copy the code to. For every pipeline that is ran, Microsoft, on the back end, creates a container to run the pipeline. The container is sometimes used to store data for the course of the pipeline run. In this case, the
$(Build.ArtifactStagingDirectory)is a specific Azure DevOps predefined variable that takes the C# web app code and stores the code in a local path on the pipeline container.
Open up the second task to publish the copied C# web app code and turn it into a build artifact. The screenshot below shows what the settings should be, which is the default.
Let's break down what the task settings mean.
- The path to publish is where the C# web app code is copied to, which is the predefined variable path
- The artifact name, by default is drop. This is a metadata name of the artifact.
- The Artifact publish location is where the artifact will be stored. In this case, it's going to be stored in Azure DevOps, but it other scenarios the artifact can also be stored in a file share.
The third and final task is the Docker task. The Docker task is what is used to build the Docker image.
Let's break down what the task settings mean.
- Container registry is the registry that was created in the previous section Creating a Container Registry. You will need to click the + New button shown in the screenshot below and authenticate to Azure Container Registry by typing in the email and password associated with the Azure acount.
- Container Repository is a metadata name of where the Docker image will be stored. This is completely up to you based on what you would like to name it. For this blog post, the container repo is named
- buildAndPush command is used to build the Docker image and push the image to ACR.
- Dockerfile is used to retrieve the Dockerfile that was seen in the previous section Taking a Look at the Dockerfile.
Deploying the Pipeline
In the previous section you built the pipeline, the tasks, and learned what each task was meant for. Without the tasks, the C# web app code cannot be built and deployed. In this section you will deploy the pipeline and see the Docker image created.
Click the Save & queue button and choose the Save & queue option to deploy the pipeline.
Click the blue Save and run button to run the pipeline.
The pipeline will now start running. Click the Agent job 1 button to see the tasks in action.
The task has run successfully and the Docker image has been built!
Checking the Docker Image in ACR
The Docker image has been successfully created and it's now time to confirm the Docker image in the Azure portal.
Open up a web browser and go to the Container Registry portal. Please note that the container registry you see will differ from the screenshot below.
Under services click on Repositories per the screenshot below.
Click on the corewebapp repository to see the Docker image.
Congratulations! You have successfully created a Docker image with an Azure DevOps pipeline.
In this blog post you took a hands-on approach from start to finish creating a Docker image in the CI process. You created an Azure Container Registry and the Dockerfile for the Docker image. Following the ACR and Dockerfile creation you took the C# web app code from the GitHub repository in the prerequisites section and stored it in your own GitHub repo. Finally you created a new Azure DevOps pipeline to not only build an artifact out of the C# code, but to create a Docker image to be used in any environment.
The CI process is a great place to build the Docker image because the code is already being automatically integrated. If the organization you work for is using Docker, it only makes sense to create a Docker image that's going to be deployed for the application. Since the code is being tested and integrated in the CI process to build an artifact, the next step is to build the Docker image to use the application code.