Monday, July 20, 2020

PUBLISH DOCKER IMAGES TO AZURE CONTAINER REGISTRY

acr

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_resouce_group

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)

create_registry

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.

registry_details

Once the registry is created, copy the registry name and password, which we will be using later while defining azure pipeline definition file.

registry_access_keys

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.

image

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.

image

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.

image

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.

create_new_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

image

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.

image

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.

image

Next we need to add the build step of installing NuGet in the agent.

image

image

Then we need to add a build step for NuGet Restore.

image

image

Next we will add a task for MSBuild

image

image

Then we need to configure our task for Build an image.

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

image

Click on New service connection.

image

Select Azure Resource Manager from the dropdown.

image

Provide the resource group name and connection name.

image

That’s it and your subscription endpoint will get created.

image

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.

image

We are all set. Let us trigger the build now.

image

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.

image

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.

new-pipeline

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.

repo_selection

Select ASP.NET template for the pipeline since we are going to build ASP.NET MVC app.

select-pipeline

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.

define_azure_pipeline

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.

edit_build_pipeline

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.

update_pipeline_name

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.

image

When you save the file and commit it to git, it will automatically trigger the CI build.

queue_build

Unfortunately, I am finding an issue while executing the VSBuild task. Here is the issue.

pipeline_build_error

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

image

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.

image

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.

image

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.

#Issue 1

Well, when the build got triggered, you might encounter an error which says that dockerfile is not found while building the image.

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.

image

#Issue 2

You might encounter an error from the dockerfile step of Nuget restore which says ‘nuget’ is not recognized as an internal or external commandThis issue will happen when you are using Ubuntu agent or any other agent instead of VS 2017 hosted agent.

image

Remember to change the agent to Hosted VS 2017.

#Issue 3

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.

image

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.

image

Provide the basic details like resource group namecluster 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.

image

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.

image

Provide the Registration name and Sign-on URL which can be changed at any point of time. Give any URL you want. Click Create.

image

Once the app is registered, the Application ID is your Service Principal Client ID

image

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.

image

Back to Continuation of AKS creation

Back to the authentication page, let Enable RBAC be false and provide the Service Principal details

image

Keep the networking and monitoring as it is. Review the details and then click Create.

image

Once the deployment has started, you should be able to view the status of the deployment in the overview page.

image

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


image

Browse the URL as shown in the CLI window and you should be able to view the Kubernetes Web dashboard.

image


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.


Building a custom container-based API using Docker Desktop and Azure App Service

Photo by @chuttersnap / Unsplash.com

Many times when I’ve worked with Microsoft partners and customers in projects the need arises when we need a custom API to be implemented. Perhaps the reason is that we need the versatility and freedom this provides, or simply because the developer feels this is the best approach for the given business problem.

So, while you can host your APIs in multiple ways in Azure, I wanted to try out how easy (or complex!) it is to write a simple API in .NET Core and C#, and publish that to Azure and run it as a Docker container. Thankfully, Visual Studio 2019 aids massively in testing this, although I wanted to script the actual process to make it reproducible, and also malleable for future needs.

At the end of this small exercise, I should have a simple API running in Azure, as a Docker container on Azure App Service. How cool is that?

Configuring pre-requisites

I need to have the following assets in Azure in order to host my solution:

  • resource group to contain all my assets
  • An Azure Container Registry to hold my container
  • Linux-based Web App to run my API – and this requires an App Plan also (these will be created once the container image has been created)

I tend to default to using Azure CLI with Windows Terminal. This allows me to spin up Azure Cloud Shell to run commands remotely from my laptop against my Azure subscriptions.

First, I’ll create a new resource group – just to keep things tidy:

az group create --resource-group custom-api-docker --location westeurope

Next, I’ll create a new Azure Container Registry (ACR) instance. This is a service that will hold my Docker-container images.

az acr create --resource-group custom-api-docker --name customacrimages --sku Basic --location westeurope

And last, I’ll need to enable the admin user account for ACR, as it is disabled by default:

az acr update --name customacrimages --admin-enabled true

Finally, I’ll need a web app. It needs to be Linux-based, as my container will be based on Linux – this is also the default for Docker Desktop.

Next, I need to figure out how to authenticate with the ACR. Usually, you would use Azure Key Vault – I’ve documented this process (in length) with my friend Tobias Zimmergren in a blog post we authored in 2019. For this exercise, I’m using an alternative approach, which admittedly is less enterprise, but much simpler – and might be a better fit in certain scenarios. I’ll extract the credentials directly from ACR:

az acr credential show --name customacrimages

Record the password and username values, as we’ll need these later!

And that’s all there is! To confirm I’m in good shape, I can view the contents of my resource group in Azure Portal:

Creating the API using Visual Studio 2019

Moving on to creating the actual API. As I can easily create a test API in Visual Studio 2019, it’s worth noting that Docker Desktop is also required. This is because we’ll need to test, package and publish our API to Azure Container Registry, and these actions require Docker locally. It’s free, and you can download Docker Desktop (for Mac and Windows) here. It does require Hyper-V locally, though.

The configuration on Visual Studio 2019 is relatively simple, but I do need to set Enable Docker Support checkbox during the creation of a new ASP.NET Core Web App project. You will also need to uncheck “Configure for HTTPS”. It feels counterintuitive, as obviously you should choose HTTPS always, right? Well, the issue here seems to be that once you publish your container to a web app, it will manage HTTPS for you. So if you force your container to use HTTPS, things will fall apart in this scenario. See this FAQ for added details on this issue.

Once the new project loads based on the webapi-template, I can just hit F5 and see that everything compiles and works as it should.

Note: If you hit this error: “docker: Error response from daemon: status code not OK but 500: {“Message”:”Unhandled exception: Drive has not been shared”}.” it’s because you haven’t shared a local drive with Docker. Do this by opening the Docker Desktop control panel (from System Tray), and select the drives you wish to share. I also noted that the mount is case-sensitive in recent Docker Desktop installs. Thus, C:\code\CustomAPI is different from c:\code\CustomAPI. Subtle difference!

Once the project loads, you should see your custom API spewing data:

To verify that the API is, indeed, running through Docker as a container, you can type docker ps in Windows Terminal:

If you want to modify the custom API, open WeatherForecastController.cs under /Controllers. I chose to leave it as-is, just to make this small walkthrough a little bit more readable.

Next, we’ll package the API in a Docker-based container. Open your preferred command-line terminal and navigate to the project folder and run the following:

docker build --tag customapi .

Then we’ll push our custom container to ACR. First, we’ll tag it.

docker tag customapi customacrimages.azurecr.io/customapi:v1.0.0

And then we’ll push the tagger container to ACR:

docker push customacrimages.azurecr.io/customapi:v1.0.0

Let’s verify the ACR holds our image:

az acr repository list --name customacrimages

In order to provision a web app, an App Service Plan is required – and this is what actually specifies the host type to be Linux. I’ve set this to the B1 tier, as I ran out of F1 (Free) tiers. You can try using F1, or just go to the lowest-paid tier with B1:

az appservice plan create --name customapiplan --resource-group custom-api-docker --is-linux --sku B1

And then I’ll provision the actual web app. During provisioning, I’m pointing the web app to use my container from ACR.

az webapp create --resource-group custom-api-docker --plan customapiplan --name customdemoapi --deployment-container-image-name customacrimages.azurecr.io/customapi:v1.0.0

Finally, we’ll instruct the web app to pull our container from ACR:

az webapp config container set --name customdemoapi --resource-group custom-api-docker --docker-custom-image-name customacrimages.azurecr.io/customapi:v1.0.0 --docker-registry-server-url https://customacrimages.azurecr.io --docker-registry-server-user customacrimages --docker-registry-server-password P@ssw0rd1

You could also just open ACR from Azure Portal, and provision it directly through the context menu:

Before we get to test our solution, let’s enable logging – just in case we run into any issues!

az webapp log config --name customdemoapi --resource-group custom-api-docker --docker-container-logging filesystem

Testing the API

By now, our resource group should resemble the following:

And the web app’s Container Settings shows as follows:

Open log stream first with:

az webapp log tail --name customdemoapi --resource-group custom-api-docker

Navigate to https://<hostname>.azurewebsites.net/weatherforecast to verify your API is working:

In summary

And that’s it! A lot of steps, and a few moving parts. You could try publishing directly from Visual Studio also, but I prefer the approach where I’ll get to control how content moves to the cloud.

Free hosting web sites and features -2024

  Interesting  summary about hosting and their offers. I still host my web site https://talash.azurewebsites.net with zero cost on Azure as ...