While I started playing with Azure DevOps, .NET Core and Docker, I did realized that I missed the point where I have a legacy application developed in .net framework and still want to utilize the containerization aspect. Thinking on these lines, I thought to investigate and see how this strategy work for a sample asp.net mvc application built with .net framework 4.6.2.
I have created a sample non-functional asp.net mvc app using VS2017 asp.net template. Since I am more focused on how we can containerize the application and deploy it to AKS following a proper CI/CD process using Azure DevOps, I left the app as it is without any functionalities. I am sure we all know how to create an mvc app, hence I will skip the fundamentals in this post.
Let’s go ahead and follow some of the steps that we would like to perform in order to achieve our goal.
Create a Resource group
This will be our first step where we are going to create a resource group in azure that will hold our azure resources like ACR and AKS.
Create ACR (Azure Container Registry)
In this step we are going to create a container registry in azure. This registry will serve the purpose of storing the docker images which will be pushed by the release pipeline of azure devops project. Once the image has been successfully uploaded or updated, the same release pipeline will trigger the action to deploy the image container to AKS (Azure Kubernetes Service)
Provide the registry details like registry name, resource group (which we have already created), Enable Admin user (which will use the registry name and access key as password for docker to access the registry) and let the SKU be Standard.
Once the registry is created, copy the registry name and password, which we will be using later while defining azure pipeline definition file.
Add Dockerfile to your solution
This is a critical piece where I would like to containerize my application and create images that will be deployed to AKS. In order to do that, I need to add a dockerfile to the root inside the solution folder. This file don’t have any extension. Although VS2017 provides the feature to add containerization support, I will still go ahead an create the dockerfile manually instead of choosing containerization and docker support through VS2017.
The best place to create the dockerfile is using Notepad++. Remember to select All Types while saving the file so that no file extension get associated.
The content of the dockerfile varies based on different requirements. For every type of project, structure of solution, dependencies involved and whether we are using .NET Framework or .NET core, the content varies. Since I am going to use .NET Framework 4.6.1, hence below content is sufficient enough to build images. Remember that the dockerfile will finally help to create container image which we will store in ACR. If you have more than one applications like an ASP.NET MVC app and a RESTful API, we need to create docker-compose.yaml file along with the dockerfile which will consider creation of more than one image since we have two different applications.
The dockerfile defines series of steps that are required to be performed in order to build the container image. Since my application is targeting to .NET framework 4.6.2, hence I have to specify 4.6.2-runtime as build. If it is core than it will be core-sdk.
To get a look on various supported IIS image of docker, you can visit the following link https://github.com/microsoft/aspnet-docker and to get the .net framework versions supported for docker using this link https://hub.docker.com/r/microsoft/dotnet-framework/
Minimal steps that we should define are –
1. Specify the runtime that will be required to build the application
2. Specify the .csproj, .config, etc., that should be copied.
3. Specify where the build and how the build should take place to create the image by docker.
As I have already shared that there can be additional set of steps apart from this sequence based on complexities, dependencies and requirement.
We also need to add .dockerignore which contains list of extensions that should be ignored by docker while creating the image. For e.g., we docker build don’t need bin or obj folders, any other output folder, etc. You can create this file using Notepad++ too but do remember to select All Types while saving the file and don’t give any filename. Just save it as .dockerignore.
That’s all you need for docker.
you might be interested to visit https://github.com/Microsoft/dotnet-framework-docker which has various samples for using docker.
Create an Azure DevOps project
If you are new to Azure DevOps Project, there are tons of labs in https://www.azuredevopslabs.com/ that you might be interested to plug in and get some hands-on experience with Azure DevOps.
In this step we are going to use Azure DevOps Management portal to create a DevOps project and define build and release pipelines. Login to https://dev.azure.com using your Microsoft credentials and create a project.
While I already had my source code in github, .
Once the project has been successfully created, our first step would be to define CI build pipeline. There are two ways to define your CI build pipeline.
1. You can either choose Visual Designer to create the pipeline without YAML
2. Use series of steps to create the pipeline with YAML.
In both the options, the first step is to select the source control directory where the source code resides. Now, just for the case of simplicity, I thought to import my repository from GitHub to Azure Git Repo in the DevOps project. The only reason to do this is, because I have to use GitHub OAuth facility to generate a token and authorize Azure DevOps project to interact with my GitHub account. Anyway, you can go with your preference.
I will be using Visual designer to create my build pipeline. First step is to select the repository.
Creating the pipeline using Visual designer
Next step is to select the template that will build the app. Now, since we are going to use docker support to build container images, we should ideally select Docker container template.
Once you have selected the template, it will create the build definition which will currently have only two steps Build an image and Push an image. We need to add few more steps here.
First we need to change the Agent pool for the pipeline to Hosted VS2017, since MSBuild and NuGet restore will not work in the default Hosted Ubuntu 1604 agent.
Next we need to add the build step of installing NuGet in the agent.
Then we need to add a build step for NuGet Restore.
Next we will add a task for MSBuild
Then we need to configure our task for Build an image.
If you are wondering how to get the azure subscription endpoint, then follow the process of creating a subscription endpoint to Azure Resource Manager by opening Project Settings and selecting service connection
Click on New service connection.
Select Azure Resource Manager from the dropdown.
Provide the resource group name and connection name.
That’s it and your subscription endpoint will get created.
The last task in the build steps for the agent is Push an image. Here we need to configure the settings for pushing the container image created to ACR.
We are all set. Let us trigger the build now.
If everything goes smooth and build is succeeded, then we should be able to see the image getting created and store into ACR repository. Login to your azure portal and verify the container image has been generated.
Creating the pipeline using Step-By-Step process generating azure-pipelines.yml file
If you are using step-by-step procedure instead of Visual designer, then you should select the repository from the below screen.
Select the location of your repository. If you are using github, then you need to use OAuth to authorize azure devops project to connect to your github. If you are using Azure git repo like mine, then you just need to select the repo.
Select ASP.NET template for the pipeline since we are going to build ASP.NET MVC app.
Once you are done, it will create the build definition, queue it and then execute it. This will also create azure-pipeline.yaml file and upload it in your git repo which is a template containing series of steps that will be executed as part of the build.
Once the build is executed, you will be able to view the build summary. Unfortunately, the build has failed and I will come to that issue shortly.
Now, it is important to understand that the build is suppose to create docker images and the azure pipeline should have access to ACR. If you remember, we have stored the ACR user name and password that will be used by docker. Keeping that in mind, we need to rename the azure-pipelines.yml file to azure-pipelines.acr.yml file.
We also need to add dockerId and dockerPassword in the variables section of the file. Value of dockerId will be ACR username and value of dockerPassword will be ACR password from ACR access keys.
When you save the file and commit it to git, it will automatically trigger the CI build.
Unfortunately, I am finding an issue while executing the VSBuild task. Here is the issue.
After various attempts and taking help from different forums, I have found that the issue is happening since we have the Packages folder in our repo. Since, NuGet Restore is happening during the build process, it is finding the packages folder and creating issues here. You might also face the same issue and hence do keep in mind that it is not with related to any missing dll or library references. In order to resolve this issue, you can either delete the packages folder from the repo or rename it. NuGet Restore will automatically generate the packages folder and add all the dependent libraries there. After this fix, I was able to run the build successfully
So far so good. we were able to run the build.
Update your azure-pipelines.acr.yml with the following information building and pushing docker image to the container registry.
Now if you queue the build you might encounter an error where it says that the newly created service endpoint is not authorized to use resources. In order to resolve this issue, edit the pipeline build definition and changes something like the one highlighted, save it and again revert back the changes to original and then save it again. Now if you queue the build, it will work. It’s an issue but we have to live with the workaround.
Issues encountered in the CI process
You might encounter couple of issues during the CI process. They might not be similar but I would like to share some of them along with the resolution which might help you.
Well, when the build got triggered, you might encounter an error which says that dockerfile is not found while building the image.
Do validate that the dockerfile path provided in dockerfile is correct. It is advisable to always keep the dockerfile in the root solution directory and not under any sub-folders or project folders.
You might encounter an error from the dockerfile step of Nuget restore which says ‘nuget’ is not recognized as an internal or external command. This issue will happen when you are using Ubuntu agent or any other agent instead of VS 2017 hosted agent.
Remember to change the agent to Hosted VS 2017.
You might also encounter an error from the dockerfile step of running msbuild which says ‘msbuild’ is not recognized as an internal or external command. This is the same case like NuGet. Changing the agent to Hosted VS 2017, will resolve this issue.
Create Azure Kubernetes Cluster service
In this step we will see how to create an azure kubernetes cluster service (AKS) where we are going to deploy our container image from azure container registry using azure devops release pipeline (CD process).
Login to https://portal.azure.com and select Kubernetes Service to create it.
Provide the basic details like resource group name, cluster name and dns name prefix. Based on your requirement you can choose number of nodes. In my case I will go with node count 1.
In the authentication page, select to create a new service principal or use an existing service principal. Since I already have an existing service principal, I will use the same.
Service Principal Creation
If you are looking forward to know how to create a service principal, then go to Azure Active Directory and select App Registrations from the menu. Click New application registration.
Provide the Registration name and Sign-on URL which can be changed at any point of time. Give any URL you want. Click Create.
Once the app is registered, the Application ID is your Service Principal Client ID
Click Settings, select Keys and add a password. Juts provide the description and duration for the field while keeping the value empty. When you click Save, the password will automatically get generated. Remember to store this password as it cannot be retrieved later if you planning to use it. This password will act as Service Principal Client Secret.
Back to Continuation of AKS creation
Back to the authentication page, let Enable RBAC be false and provide the Service Principal details
Keep the networking and monitoring as it is. Review the details and then click Create.
Once the deployment has started, you should be able to view the status of the deployment in the overview page.
In order to view the Kubernetes Dashboard Web UI, open azure cloud shell and type the following command with your resource group name and AKS cluster name: az aks browse –resource-group dockerdemo-rg –name dockerdemocluster
Browse the URL as shown in the CLI window and you should be able to view the Kubernetes Web dashboard.
We are going to use the AKS to deploy the container images in our next post. Also, I am going to show both usage of Docker Hub and ACR for publishing the image and deploy to AKS. Stay tuned.