Hosting endpoints in Docker Linux containers

Component: NServiceBus
NuGet Package NServiceBus (7.x)

This sample demonstrates how to use Docker Linux containers to host NServiceBus endpoints communicating over the RabbitMQ transport.

Prerequisites

This sample requires that the following tools are installed:

Running the sample

Running the sample involves building the code, preparing it for deployment, building container images and finally starting the multi-container application.

Building and publishing binaries

The first step is to build the binaries using the .NET Core command line tools:

$ dotnet build

The compiled binaries need to be prepared for deployment into the container:

$ dotnet publish

Building container images

The prepared binaries and container image definitions (Dockerfiles) are enough to build container images for both the Sender and the Receiver:

$ docker-compose build

Starting containers

When the container images are ready, the containers can be started:

$ docker-compose up -d

Observing containers

Both containers log to the console. These logs can be inspected:

$ docker-compose logs sender
$ docker-compose logs receiver

Stopping and removing containers

The containers can be stopped and removed:

$ docker-compose down

Code walk-through

This sample consists of Sender and Publisher endpoints exchanging messages using the RabbitMQ transport. Each of these three components runs in a separate Docker Linux container.

Endpoint Docker image

Each endpoint is a container built on top of the official microsoft/dotnet:2.0-runtime image from Docker Hub. The container image is built using endpoint binaries from the bin/Debug/netcoreapp2.0/publish folder:

FROM microsoft/dotnet:2.0-runtime
WORKDIR /Receiver
COPY ./bin/Debug/netcoreapp2.0/publish .
ENTRYPOINT ["dotnet", "Receiver.dll"]
Run dotnet build and dotnet publish commands to generate endpoint binaries before creating the image.

Multi-container application

Endpoint container images for the Sender and the Receiver are combined with an official RabbitMQ image to create a multi-container application using Docker Compose:

version: "3"
services:   
    sender:
        image: sender
        build:
            context: ./Sender/
            dockerfile: Dockerfile
        depends_on:
            - rabbitmq
    receiver:
        image: receiver
        build:
            context: ./Receiver/
            dockerfile: Dockerfile
        depends_on:
            - rabbitmq
    rabbitmq:
        image: "rabbitmq:3-management"
        ports:
            - "15672:15672"

Transport configuration

Endpoints configure the RabbitMQ transport to use the broker instance running in the rabbitmq container:

var transport = endpointConfiguration.UseTransport<RabbitMQTransport>();
transport.ConnectionString("host=rabbitmq");
transport.UseConventionalRoutingTopology();
var delayedDelivery = transport.DelayedDelivery();
delayedDelivery.DisableTimeoutManager();

Waiting for RabbitMQ broker to become available

Docker Compose manages the dependencies between the containers and starts the rabbitmq container first but there is still a delay between when the RabbitMQ container starts and when the broker starts to accept client connections. Endpoints must wait for the broker to become available before starting the endpoint:

await RabbitHelper.WaitForRabbitToStart()
    .ConfigureAwait(false);

var endpointInstance = await Endpoint.Start(endpointConfiguration)
    .ConfigureAwait(false);
public static async Task WaitForRabbitToStart()
{
    var factory = new ConnectionFactory
    {
        Uri = "amqp://rabbitmq"
    };
    for (var i = 0; i < 5; i++)
    {
        try
        {
            using (factory.CreateConnection())
            {
            }
            return;
        }
        catch (BrokerUnreachableException)
        {
        }
        await Task.Delay(1000).ConfigureAwait(false);
    }
}

Related Articles

  • Hosting
    Outlines the various approaches to endpoint hosting.

Last modified