Docker and Webapp

In this page we will create a WebApp that serves a Docker image and see how to put it in CI/CD.

Create a Web App

If you are creating a web app in Azure portal, start with selecting Docker Container as Publish property in Basics tab. Choose Linux as Operating System. For this example a Standard SKU would just do fine.
Next, on the Docker-tab select your existing azurecr.io container registry and specify the Image:Tag you want to serve.

My tempate looks something inline with the following:

"resources": [{
        "apiVersion": "2018-11-01",
        "name": "[parameters('name')]",
        "type": "Microsoft.Web/sites",
        "location": "[parameters('location')]",
        "tags": {},
        "dependsOn": ["[concat('Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]"],
        "properties": {
            "name": "[parameters('name')]",
            "siteConfig": {
                "appSettings": [{
                        "name": "DOCKER_REGISTRY_SERVER_URL",
                        "value": "[parameters('dockerRegistryUrl')]"
                    }, {
                        "name": "DOCKER_REGISTRY_SERVER_USERNAME",
                        "value": "[parameters('dockerRegistryUsername')]"
                    }, {
                        "name": "DOCKER_REGISTRY_SERVER_PASSWORD",
                        "value": "[parameters('dockerRegistryPassword')]"
                    }, {
                        "name": "WEBSITES_ENABLE_APP_SERVICE_STORAGE",
                        "value": "false"
                    }
                ],
                "linuxFxVersion": "[parameters('linuxFxVersion')]",
                "appCommandLine": "",
                "alwaysOn": "[parameters('alwaysOn')]"
            },
            "serverFarmId": "[concat('/subscriptions/', parameters('subscriptionId'),'/resourcegroups/', parameters('serverFarmResourceGroup'), '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]",
            "clientAffinityEnabled": false
        }
    }, {
        "apiVersion": "2018-11-01",
        "name": "[parameters('hostingPlanName')]",
        "type": "Microsoft.Web/serverfarms",
        "location": "West Europe",
        "kind": "linux",
        "tags": {},
        "dependsOn": [],
        "properties": {
            "perSiteScaling": false,
            "maximumElasticWorkerCount": 1,
            "isSpot": false,
            "reserved": true,
            "isXenon": false,
            "hyperV": false,
            "targetWorkerCount": 0,
            "targetWorkerSizeId": 0
        },
        "sku": {
            "Tier": "Standard",
            "Name": "S1",
            "size": "S1",
            "family": "S",
            "capacity": 1
        }
    }

 

As a security matter I have to point out the fact that the Web App is connecting to Azure Container Registry using 3 configuration items i.e. Server Url, Server Username and Server Password. These items are visible in Azure Portal Configuration:
DOCKER_REGISTRY_SERVER_URL, DOCKER_REGISTRY_SERVER_USERNAME, DOCKER_REGISTRY_SERVER_PASSWORD

Deploy Docker Image

In both cases where the Docker image gets pulled from Container Registry, you need to restart the instance in Container Instance and also in Web App Docker instance.

An other option would be to move the pull task into Azure Pipeline using. My example is defined as follows:

steps: 
– task: AzureRmWebAppDeployment@4 
  displayName: ‘Deploy Azure App Service’ 
  inputs: 
    azureSubscription: ‘$(Parameters.ConnectedServiceName)’ 
    appType: ‘Web App for Containers (Linux)’ 
    WebAppName: ‘$(Parameters.WebAppName)’ 
    DockerNamespace: ‘https://securedcontainerregistry.azurecr.io’  
    DockerRepository: ‘https://securedcontainerregistry.azurecr.io/securedazurelib’  
    DockerImageTag: ‘latest’  
    StartupCommand: ”

Docker and Container Registry in Azure

The purpose of this page is to show the steps to create a simple webapp running as a docker container in Azure.

Create Container Registry

I am using a new Azure Container Registry in my resource group called SecuredContainerRegistry which I will refer to throughout this page. I have created this using a Basic SKU which is sufficient enough for this purpose. If you need a private endpoint you need to change he SKU to premium.

"resources": [{
        "type": "Microsoft.ContainerRegistry/registries",
        "apiVersion": "2020-11-01-preview",
        "name": "SecuredContainerRegistry",
        "location": "[resourceGroup().location]",
        "dependsOn": [],
        "tags": "[variables('tagsArray')]",
        "sku": {
            "name": "Basic",
            "tier": "Basic"
        },
        "properties": {
            "adminUserEnabled": true,
            "publicNetworkAccess": "Enabled",
            "zoneRedundancy": "Disabled"
        }
    }

The main change after creating the Container Registry using Default options is to enable Admin user which allows us to login used by docker.

Next important change we do on this resource is to register this resource in AAD by giving it a System assigned Identity using the portal.

Add Service connection

Next you need to add a service connection in your Azure Devops project using service principal authentication that to get access to Azure Container Registry. In the popup select Azure Container registery as Registry type, then select your azure container registry and give the service connection a name.

Buid and Deploy Docker project

Create a .net core application including a Dockerfile for windows. When you choose in Visual Studio it generates a Dockerfile which is not completely working. The following example is changed version of that file which works fine:

#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
#Depending on the operating system of the host machines(s) that will build or run the containers, the image specified in the FROM statement may need to be changed.
#For more information, please see https://aka.ms/containercompat


FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443


FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /src
COPY ["*.csproj", "SecuredWebApi/"]
RUN dotnet restore "SecuredWebApi/SecuredWebApi.csproj"
WORKDIR "/src/SecuredWebApi"
COPY . .
RUN dotnet build "SecuredWebApi.csproj" -c Release -o /app/build


FROM build AS publish
RUN dotnet publish "SecuredWebApi.csproj" -c Release -o /app/publish


FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "SecuredWebApi.dll"]

Create Container Instance

Next step is to create an Azure Container instance. In my example I gave it the name container-instance. During the creation you need to connect this to a container registry. You can choose Azure Container Registry created in the first step above. Once you have a successful build you will have an image available to associate it with it. Based on my sample project I named this instance secured-container-instance and Azure portal automatically recognizes the container registery when you select the Image Source from Azure Container Registry. For this project I exposed ports 80 and port 443
resources": [ { "location": "westeurope", "name": "secured-container-instance", "type": "Microsoft.ContainerInstance/containerGroups", "apiVersion": "2021-03-01", "properties": { "containers": [ { "name": "secured-container-instance", "properties": { "image": "securedcontainerregistry.azurecr.io/securedazurelib:latest", "resources": { "requests": { "cpu": "1", "memoryInGB": "1.5" } }, "ports": [ { "protocol": "TCP", "port": 80 }, { "protocol": "TCP", "port": 443 } ], } } ], "restartPolicy": "[parameters('restartPolicy')]", "osType": "Linux", "imageRegistryCredentials": [ { "server": "securedcontainerregistry.azurecr.io", "username": "[parameters('imageUsername')]", "password": "[parameters('imagePassword')]" } ], "ipAddress": { "type": "Public", "ports": "80 (TCP), 443 (TCP)" } }, "tags": {} }

Start the instance

By starting the instance on Container the image gets pulled and deployed in the container instance. On the Azure Portal you can look into the public ip adress and check the website is running and accessible.

A container instance can be started using docker command: docker run securedcontainerregistry.azurecr.io/securedazurelib:latest The following job tasks will start an instance in an Azure pipeline

jobs:
  - job: RunTest
    workspace:
      clean: all
    pool:
      vmImage: 'ubuntu-latest'
    steps:
    - task: Docker@2
      displayName: Login to ACR
      inputs:
        command: login
        containerRegistry: securedcontainerregistry
    - script: |
        docker run securedcontainerregistry.azurecr.io/somerepo/securedazurelib:latest

Security

You can register the container instance in AAD using Manage Identity and then assign a role in KeyVault for that identity to allow access to secrets.