Azure Container Instances - C# .NET Core apps running as containers in your private Azure Container Registry

I've recently discussed a couple of tips and tricks around running Azure Kubernetes Services, running your own C# code as containers and how you can deal with different scenarios when using Kubernetes in the Microsoft cloud - at small scale and big.

Today I had another talk with someone, where they have pretty much the same requirements as would be fitting for AKS, but instead want just a single container, or at most a handful of them. Requirements on no-maintenance, no container-orchestration and no infrastructural decisions to think about - just run the code.

That's the game we're playing today. Tag, you're it.

Build your .NET Core app as a Container image

This step is optional, as I'm sure you've already got a dozen apps lying around just waiting to be containerized and enter the modern world of micro-services and cloud infrastructure.

If you don't have any specific app in mind that you want to bring over, then in this step I'm walking you through creating a simple C# Console app, the steps to build this into a docker image, tag and push it to your Azure Container Registry.

Assumptions

  • You already have an Azure Container Registry. If not, just create one.
  • You obviously need an Azure subscription.
  • You have the Docker tools installed on your machine, so you can run Docker commands.
  • You have the Azure CLI installed.
  • You have a test Azure Storage Account you want to insert dummy data into.
GitHub source code: https://github.com/Zimmergren/dotnet-core-aci-demo

Build .NET Core Application and push to Azure Container Registry

I've walked through exactly these steps before, but for the sake of clarity in this blog post, I'm elaborating these steps again.

If you're using the code from my GitHub sample, then the following steps should work as-is. If you however are pushing your own apps to your ACR, then you should replace any filenames and contents in the below commands to suit your own code. Great, let's roll.

Build the C# .NET Core project

With the code from my sample, or your own code, you need to make sure to build and publish the binaries.

Run the following command from the project root:

dotnet build && dotnet publish


dotnet build, dotnet publish and verifying the output

Build the Dockerfile into a Docker image

From the folder that contains the Dockerfile, run this and tag it with whatever name you want. Just be sure to remember it, as you'll need it in the sequencing steps:

docker build . -t aci-demo-app


docker build, with a tag named aci-demo-app

Ensure Admin user is Enabled on ACR

az acr update -n acrdemomagic -g demos --admin-enabled true

This will give you the following output, indicating that the adminUserEnabled is now set to true. If it were already set to true, it would just keep that value. Also, the -g demos command specifies the name of the resource group where my ACR is located, which indeed is called just that, demos.

Output of above command:

{
   "adminUserEnabled":true,
   "creationDate":"2019-02-18T19:37:36.716694+00:00",
   "id":"/subscriptions/YOUR_SUBSCRIPTION_GUID/resourceGroups/demos/providers/Microsoft.ContainerRegistry/registries/acrdemomagic",
   "location":"westeurope",
   "loginServer":"acrdemomagic.azurecr.io",
   "name":"acrdemomagic",
   "provisioningState":"Succeeded",
   "resourceGroup":"demos",
   "sku":{
      "name":"Basic",
      "tier":"Basic"
   },
   "status":null,
   "storageAccount":null,
   "tags":{

   },
   "type":"Microsoft.ContainerRegistry/registries"
}

Get credentials for the Admin user of your Azure Container Registry

In order to know the password of the Admin user of the ACR, you can either check it out in the Azure Portal, or simply run these quick Azure CLI commands to figure it out. The Admin username is the same as your ACR name (in my case acrdemomagic).

Command to retrieve password from the Azure CLI:

az acr credential show -n acrdemomagic -g demos --query "passwords"

[
  {
    "name": "password",
    "value": "RvHy7+an9u9CAHBUb0tgB=Pkkc7w"
  },
  {
    "name": "password2",
    "value": "Racfw4X7BrbMOfZTtJrEiMGhRd="
  }
]

Push the Docker image to Azure Container Registry

In order to take the currently built Docker image and send it off to your ACR, you should tag it accordingly, log in to ACR and then push it. Follow my lead, good fellow! You should have the Username and Password already from the previous steps.

Login:

> docker login acrdemomagic.azurecr.io -u acrdemomagic
Password:
Login Succeeded

Tag, and push the Docker image:

docker tag aci-demo-app acrdemomagic.azurecr.io/aci-demo-app:latest
docker push acrdemomagic.azurecr.io/aci-demo-app:latest

If all things are well, you should see the successful push come through:

docker tag, docker push

Verify that the image is indeed in the ACR

Quick check to ensure that the image is where we think we put it:

az acr repository list -n acrdemomagic
[
  "aci-demo-app"
]

This enables us to dig into the new repository aci-demo-app and find out a bit more, should we want to:

az acr repository show -n acrdemomagic --repository aci-demo-app
{
  "changeableAttributes": {
    "deleteEnabled": true,
    "listEnabled": true,
    "readEnabled": true,
    "writeEnabled": true
  },
  "createdTime": "2019-02-18T20:44:19.5626376Z",
  "imageName": "aci-demo-app",
  "lastUpdateTime": "2019-02-18T20:44:19.6306895Z",
  "manifestCount": 1,
  "registry": "acrdemomagic.azurecr.io",
  "tagCount": 1
}

Summary

Great, now we have accomplished the following already:

  • Built a C# application in .NET Core 2.x
  • Build a Docker image based on said C# application
  • Enabled Admin user for the ACR
  • Found out the credentials to the ACR
  • Tagged and Pushed this Docker image into our ACR

We're ready for the next steps, which is to create an Azure Container Instance, ACI, and see our image in action.

Create an Azure Container Instance with your private Azure Container Registry images

If you've buckled up and prepared for some deep technical magic, I'm sorry to disappoint. There's a lot more of that stuff when managing certain cloud services, but with ACI it's pretty straight forward. A single container instance, running your code with little to no management efforts.

Create an Azure Container Instance with the Azure CLI

For the following steps, you need to have the Username and Password of your ACR at hand. You can enable this using either the Azure CLI, or from the Azure Portal.

To follow suit with the subsequent commands in the Azure CLI, here's how to enable the Admin user if it isn't already, on your Azure Container Registry.

Create the Azure Container Instance from your private image

We have the image in ACR, we have the credentials to connect to it, so now we just need to create our ACI instance. We'll continue to use the Azure CLI for this purpose (single line, I made them multi-line for visual appeal):

az container create 
  -n my-aci-app 
  -g demos 
  --image "acrdemomagic.azurecr.io/aci-demo-app:latest" 
  --registry-username "acrdemomagic" 
  --registry-password "YOUR_ACR_PASSWORD" 
  -e AzureStorageAccountConnectionString="YOUR_STORAGE_CONNECTION_STRING"
{
  "containers": [
    {
      "command": null,
      "environmentVariables": [
        {
          "name": "AzureStorageAccountConnectionString",
          "secureValue": null,
          "value": "REDACTED_BUT_YOUR_VALUE_WOULD_GO_HERE"
        }
      ],
      "image": "acrdemomagic.azurecr.io/aci-demo-app:latest",
      "instanceView": {
        "currentState": {
          "detailStatus": "",
          "exitCode": null,
          "finishTime": null,
          "startTime": "2019-02-18T20:53:37+00:00",
          "state": "Running"
        },
        "events": [
          {
            "count": 1,
            "firstTimestamp": "2019-02-18T20:53:24+00:00",
            "lastTimestamp": "2019-02-18T20:53:24+00:00",
            "message": "pulling image \"acrdemomagic.azurecr.io/aci-demo-app:latest\"",
            "name": "Pulling",
            "type": "Normal"
          },
          {
            "count": 1,
            "firstTimestamp": "2019-02-18T20:53:33+00:00",
            "lastTimestamp": "2019-02-18T20:53:33+00:00",
            "message": "Successfully pulled image \"acrdemomagic.azurecr.io/aci-demo-app:latest\"",
            "name": "Pulled",
            "type": "Normal"
          },
          {
            "count": 1,
            "firstTimestamp": "2019-02-18T20:53:37+00:00",
            "lastTimestamp": "2019-02-18T20:53:37+00:00",
            "message": "Created container",
            "name": "Created",
            "type": "Normal"
          },
          {
            "count": 1,
            "firstTimestamp": "2019-02-18T20:53:37+00:00",
            "lastTimestamp": "2019-02-18T20:53:37+00:00",
            "message": "Started container",
            "name": "Started",
            "type": "Normal"
          }
        ],
        "previousState": null,
        "restartCount": 0
      },
      "livenessProbe": null,
      "name": "my-aci-app",
      "ports": [],
      "readinessProbe": null,
      "resources": {
        "limits": null,
        "requests": {
          "cpu": 1.0,
          "memoryInGb": 1.5
        }
      },
      "volumeMounts": null
    }
  ],
  "diagnostics": null,
  "id": "/subscriptions/YOUR_SUBSCRIPTION_GUID/resourceGroups/demos/providers/Microsoft.ContainerInstance/containerGroups/my-aci-app",
  "imageRegistryCredentials": [
    {
      "password": null,
      "server": "acrdemomagic.azurecr.io",
      "username": "acrdemomagic"
    }
  ],
  "instanceView": {
    "events": [],
    "state": "Running"
  },
  "ipAddress": null,
  "location": "westeurope",
  "name": "my-aci-app",
  "osType": "Linux",
  "provisioningState": "Succeeded",
  "resourceGroup": "demos",
  "restartPolicy": "Always",
  "tags": {},
  "type": "Microsoft.ContainerInstance/containerGroups",
  "volumes": null
}

Investigating the json a bit from above, you can follow the instanceView.events property's collection of events:

  • Pulling image
  • Pulled image
  • Created container
  • Started container

Alas, it looks like the events were all successful and that this works. But let's make sure, again.

Verify that the Azure Container Instance is running

Verify that the instance is running by using the az container attach command to attach to the output and error streams:

 az container attach -n my-aci-app -g demos
Container 'my-aci-app' is in state 'Running'...
(count: 1) (last timestamp: 2019-02-18 20:53:24+00:00) pulling image "acrdemomagic.azurecr.io/aci-demo-app:latest"
(count: 1) (last timestamp: 2019-02-18 20:53:33+00:00) Successfully pulled image "acrdemomagic.azurecr.io/aci-demo-app:latest"

(count: 1) (last timestamp: 2019-02-18 20:53:37+00:00) Created container
(count: 1) (last timestamp: 2019-02-18 20:53:37+00:00) Started container

Start streaming logs:
MESSAGE: I was processesed at 2019-02-18 21:00:11Z. MACHINE: wk-caas-4fd3ba8c40924964bad21c641ec80c2c-df0ab73db9f91fcad7062a. 
MESSAGE: I was processesed at 2019-02-18 21:00:11Z. MACHINE: wk-caas-4fd3ba8c40924964bad21c641ec80c2c-df0ab73db9f91fcad7062a. 
MESSAGE: I was processesed at 2019-02-18 21:00:11Z. MACHINE: wk-caas-4fd3ba8c40924964bad21c641ec80c2c-df0ab73db9f91fcad7062a. 
MESSAGE: I was processesed at 2019-02-18 21:00:11Z. MACHINE: wk-caas-4fd3ba8c40924964bad21c641ec80c2c-df0ab73db9f91fcad7062a. 
MESSAGE: I was processesed at 2019-02-18 21:00:12Z. MACHINE: wk-caas-4fd3ba8c40924964bad21c641ec80c2c-df0ab73db9f91fcad7062a. 
MESSAGE: I was processesed at 2019-02-18 21:00:12Z. MACHINE: wk-caas-4fd3ba8c40924964bad21c641ec80c2c-df0ab73db9f91fcad7062a.

The container is running, image pull and creation and start of container was a success - and we can indeed see the log messages from our C# .NET Core application. The machine name reported is the machine name of the ACI.

If we peek into the Azure Storage Explorer to verify that the table indeed has some data, we can also see that it's working on that end:

Azure Storage Explorer displaying near 100 000 items after running for just a few minutes.

Delete the Azure Container Instance, to avoid cost for the demo app ;)

Given the number of non-stop transactions we're making in this sample app, it's a wise decision to kill it.

az container delete -n my-aci-app -g demos

Summary

This was fun. We have touched on these topics:

  • C# .NET Core apps can easily be containerized
  • Build a Docker image out of our C# code
  • Enable Admin user for our ACR
  • Grab the credentials from the ACR
  • Login and Push the Docker image to ACR
  • Verified the image does indeed exist in the ACR
  • Created an ACI with our new private ACR image
  • Verified that the instance is running
  • Deleted it again, so we could start this all over ;)

I hope it helps. Please do leave a comment!