.NET Core Workers in Azure Container Instances

Avatar

.NET Core Workers in Azure Container Instances

In .NET Core 3.0 we are introducing a new type of application template called Worker Service. This template is intended to give you a starting point for writing long running services in .NET Core. In this walkthrough you’ll learn how to use a Worker with Azure Container Registry and Azure Container Instances to get your Worker running as a microservice in the cloud.

Since the Worker template Glenn blogged about is also available via the dotnet new command line, I can create one on my Mac and edit the code using Visual Studio for Mac or Visual Studio Code (which I’ll be using here to take advantage of the integrated Docker extension).

dotnet new worker

I’ll use the default from the Worker template. As it will write to logs during execution via ILogger, I’ll be able to tell quickly from looking in the logs if the Worker is running.

public class Worker : BackgroundService
{
    private readonly ILogger<Worker> _logger;

    public Worker(ILogger<Worker> logger)
    {
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
            await Task.Delay(1000, stoppingToken);
        }
    }
}

Visual Studio Code’s Docker tools are intelligent enough to figure out this is a .NET Core app, and will suggest the correct Docker file via the Command Palette’s Add Docker files to workspace option.

By right-clicking the resulting Dockerfile I can build the Worker into a Docker image in one click.

The Build Image option will package my Worker’s code into a Docker container locally. The second option, ACR Tasks: Build Image would use Azure Container Registry Tasks to build the image in the cloud, rather than on disk. This is helpful for scenarios when the base image is larger than I want to download locally or when I’m building an application on a Windows base image from Linux or Mac. You can learn more about ACR Tasks in the ACR docs. The Azure CLI makes it easy to login to the Azure Container Registry using the Azure CLI. This results in my Docker client being authenticated to the Azure Container Registry in my subscription.

az acr login -n BackgroundWorkerImages

This can be done in the VS Code integrated terminal or in the local terminal, as the setting will be persisted across the terminals’ environment. It can’t be done using the cloud shell, since logging into the Azure Container Registry requires local shell access so local Docker images can be accessed. Before I push the container image into my registry, I need to tag the image with the URI of the image once it has been pushed into my registry. I can easily get the ACR instance URI from the portal.

I’ll copy the URI of the registry’s login server in the portal so I can paste it when I tag the image later.

By selecting the backgroundworker:latest image in Visual Studio Code’s Docker explorer pane, I can select Tag Image.

I’ll be prompted for the tag, and I can easily paste in the URI I copied from the portal.

Finally, I can right-click the image tag I created and select Push, and the image will be pushed into the registry. Once I have a Docker image in the registry, I can use the CLI or tools to deploy it to Azure Container Instances, Kubernetes, or even Azure App Service.

Now that the worker is containerized and stored in the registry, starting an instance of it is one click away.

Once the container instance starts up, I’ll see some logs indicating the worker is executing, but these are just the basic startup logs and not my information-level logs I have in my Worker code.

Since I added Information-level logs during the worker’s execution, the configuration in appsettings.json (or the environment variable for the container instance) will need to be updated to see more verbose logs.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  }
}

Once the code is re-packaged into an updated Docker image and pushed into the Azure Container Registry, following a simple Restart…

… more details will be visible in the container instance’s logging output.

The Worker template makes it easy to create long-running background workers that you can run for as long as you need in Azure Container Instances. New container instances can be created using the portal or the Azure Command Line. Or, you can opt for more advanced scenarios using Azure DevOps or Logic Apps. With the Worker template making it easy to get started building microservices using your favorite ASP.NET Core idioms and Azure’s arsenal of container orchestration services you can get your microservices up and running in minutes.

Avatar
Brady Gaster

Senior Program Manager, ASP.NET Core

Follow    

19 comments

  • Ben Hayat
    Ben Hayat

    Brady, I didn’t get to read the whole article, but my question is, is the worker available if we are hosting our app on a dedicated server and NOT Azure?

    Secondly, what’s the difference between Worker service and the product called “HangFire”? Do they both accomplish the same thing? If yes, is it better to use yours for long scheduling jobs that is is running 24/7?

    Thanks!
    ..Ben

    • Avatar
      Brady Gaster

      Yes, Ben, you can totally use the Worker template “outside of Azure.” Glenn wrote a fantastic blog last week on a key non-cloud scenario for the Worker project – you can use these to build Windows services, too. So they’re “not just for cloud,” but they sure do make microservice development easier, so they’re great for cloud-native scenarios. Keep watching the blog, we have a few other use-cases on the horizon we’ll be showing off for the Worker. 

    • Avatar
      Brady Gaster

      Pardon me, as I missed the second question – I’m unfamiliar with HangFire. I’ll mention HangFire to Glenn and investigate it myself. But unless Glenn is aware of it, the Worker and HangFire are mutually exclusive concepts. 

    • Avatar
      David Fowler

      Hangfire existed long before the generic host and the worker template and is a valid alternative. It’s much more feature rich. If anything I’d expect a future where more frameworks like hangfire re-plat on top of the generic host. The generic host is trying to be a low level building block for higher level frameworks like hangfire.

  • Avatar
    Vikram Dadwal

    Hi, thanks for this article .

    I have one question though, when i read the ACI document, it was mentioned that it isbideal for scenarios where you run the applixation for some time and then shutdown the container as ACI charges per second. Then why someone will creates a always running worker in ACI ?

    • Avatar
      Brady Gaster

      You’re right in that ACI charges per-second, so it isn’t typically thought of as a host for forever-running containers. That said, the price of ACI instances have been reduced significantly so for some workloads it may be more attractive now. ACI is that it works great for not only testing one’s containers out, but for scenarios in which you have some other “thing” – like scheduled CLI execution or a Logic App running on a recurrence – that would stop and start your instances. 

      ACI billing begins at the time an image is pulled and runs until the container group is stopped, so one should take care to explicitly stop the container group – simply stopping/terminating the containers in the group isn’t a complete shutdown of an ACI container group. 

      We’ll have other articles on this site about options for running Workers – we’ve shown how to use them as Windows services and in container instances, but we’ll have a few follow-up articles that will point out additional scenarios, like Kubernetes and deployment techniques using Azure DevOps. Kubernetes would arguably be a better place for an infinitely-running containerized Worker, but for those non-inifinite scenarios ACI is a convenient option. 

  • Ankit Kumar
    Ankit Kumar

    Hey, Great article, had been looking for windows background service like implementation.However what difference would it make if I just write my service like a console application and run it on docker? 

  • Avatar
    raymond.wong@hl-insurance.com

    Hello Brady. Can I deploy the workers to Azure web apps instead of Container service? What is the difference between them?

  • Avatar
    Jeevanatham Subburaj

    Brady, With new SDK Worker template in csproj, i am getting the following error when i build the docker image. 
    error MSB4236: The SDK ‘Microsoft.NET.Sdk.Worker’ specified could not be found.

    FROM microsoft/dotnet:3.0-runtime AS base
    FROM microsoft/dotnet:3.0-sdk AS build
    Any help would be appreciated.

    • Avatar
      Brady Gaster

      Sorry about that – it’s the nature of the beast that our SDKs are a little ahead of our Docker image publishes. I’m happy to see you putting it through the loop – apologies for the //build/-caused delay in response. 

      Change your Dockerfile to match this below: 

      FROM mcr.microsoft.com/dotnet/core/sdk:3.0 AS baseWORKDIR /app
      FROM mcr.microsoft.com/dotnet/core/runtime:3.0 AS buildWORKDIR /src

Leave a comment