Skip to main content

Technology overview

This page describes some 'technologies' that are not commonly used in AMCS/Routing. It is intended for developers as a first entrypoint, answering why and how the 'technology' is used in Global Dcc. And where to look for further information and help.

  • Docker: All Global Dcc services (api, backend, dbup, doc, roadnet-manger) are developed to be deployed as Docker containers.
  • Kubernetes: A container orchestration service used to deploy and manage Global Dcc services.
  • Dapr: Several Global Dcc services (api, backend, roadnet-manager) use Dapr to communicate with each other or other dependant services.
  • DbUp: Tool to take care of database creation and upgrades.
  • NSwag: Used for creating the Open API (formerly known as swagger) file and auto genenerating code in GlobalDcc.Api.Client.

Docker

Docker is a collection of tools that allows you to define an image of your application and run it as container.

A Docker image is a description of a filesystem with everything needed to run the application: all dependencies, configurations, scripts, binaries, environment variables, other metadata and a default command (entrypoint) to run. It allows the developer to fully specify "an exclusive VM" for the application.

A Docker container

  • Is a runnable instance of an image. You can create, start, stop, move, or delete a container using the DockerAPI or CLI.
  • Can be run on local machines, virtual machines or deployed to the cloud.
  • Is isolated from other containers and runs its own software, binaries, and configurations (as defined in it image).

If this still does not make much sense, check out Microsoft Doc: What is Docker?. Or try an introductory tutorial, like for example Microsoft's Containerize a .NET app.

Why is it used

From the developers perspective, advantages of using Docker in GlobalDcc are:

  • Reproducible execution: no difference between local and deployed containers of the same image
  • Reproducible builds: the image can be built locally with the exact same steps as in a CI/PR pipeline (no dependency on the agent)

From an architectural and DevOps point of view, there are many other advantages for which we refer to Microsofts Docker into and or the free eBook .NET Microservices Architecture for Containerized .NET Applications.

How is it used

All Global Dcc services (api, backend, dbup, doc, roadnet-manger) are developed to be deployed as Docker containers: The corresponding project folders contain a Dockerfile, which specifies how to generate (build) the image. And which is written and maintained by Developers.

Provided Docker for Desktop is installed, opening the Global Dcc solution in VS (Visual Studio) allows you to:

  1. build the Dockerfile: VS executes the docker build command, generating an image
  2. run the image: VS executes the docker run command
  3. or even debug it: VS volume maps the remote debugger into the container and executes the docker run command with the entrypoint changed to be the remote debugger, waiting for connections, and finally attaches the debugger to the container.

Docker Compose

A fully deployed Global Dcc consists of several interacting containers. In the cloud, we use Kubernetes to 'orchestrate' these. Locally, this is not available/feasible. Instead, one typically uses Docker Compose: a tool for defining and running multi-container Docker applications. It uses a yaml configuration file that describes these apps and has even some support in Visual Studio. Unfortunately, since Global Dcc uses Windows-containers with Dapr, this is not an option at the moment.

Still, we use Docker Compose to run integration tests by running a test container together with several containers for dependencies (see Testing for details).

References

Kubernetes

Kubernetes is an open source platform to 'manage containerized services'. Global Dcc Developers do usually not work directly with it: They use Docker to define the images. While DevOps deploys those images to Kubernetes (or AKS, Azure Kubernetes Service, to be precise).

Nevertheless, to understand the fully deployed Global Dcc and for a smoother interaction with DevOps, it is advised that Developers have a basic understanding of Kubernetes and its terminology:

Dapr

The Distributed Application Runtime (Dapr) is an open source runtime whose API helps to abstract away common recurring challenges in the development of micro services. Some examples are messaging between services, directly invoking other services, storing data and using external secret managers. See Dapr Building Blocks for a full overview of what it provides. It is designed for containers in the cloud (supports Kubernetes).

For a general overview and introduction to Dapr terminology like component and building block, see

Why is it used

By using Dapr, Global Dcc Development can focus on the core code, where their expertise lies. All 'communication code' is provided by Dapr as third party. Dapr is open source, pushed by Microsoft and seems to be widely used. We can therefore hope for fewer bugs and a higher overall quality of the 'communication code'. Moreover, it helps to remove any strong dependency on the concrete cloud provider and tech stack.

Let's take for example the pubsub component (short for publish-subscribe messaging pattern):

  • Before Dapr, Global Dcc itself contained custom code to call the ASB (Azure Service Bus) API. Such code needs to be well tested, maintained and evolve with the Azure Service Bus support. Moreover, it encourages to rely on specialized functionality that ASB provided, but other message brokers do not. This tied the GlobalDcc code to ASB as a message broker and Azure as the cloud provider.

  • By switching to Dapr, that code was replaced by calls to the Dapr pubsub API instead. The backing component is still ASB, but the apps itself are unaware and the code size and complexity reduced considerably. Moreover, ASB can now be easily exchanged with Redis as backing component, without any change of the source code (integration test do this). Or the whole app can be moved to another cloud provider, since Dapr provides both several generic as well as cloud specific pubsub brokers for Azure, AWS and GCP.

How is it used

Each app that uses Dapr (api, backend, roadnet-manager) requires its own Dapr sidecar: A lightweight process, running 'next' to the app. The app and its sidecar communicate over HTTP (or gRPC, if enabled) and thus must know each others endpoint. On startup, the sidecar loads the configured components and once its app becomes available, will query it to detect which components is wishes to use. The sidecars configured components are defined by yaml files: examples for local development can be found in the folder /dev/dapr/components/.

Note that GlobalDcc apps have a specific health check to ensure their sidecar is healthy. That means that an app can only be successfully run together with a Dapr sidecar. On Azure, the sidecars are injected by AKS, when running locally Docker Compose handles the sidecars.

GlobalDcc uses Dapr components for:

  • Dcc request between the apis and backends. The use of the Dapr state, pubsub and queue components is displayed in the architecture diagram img.
  • Road net version updates: A pubsub component and a state component, both shared among api, backend and roadnet-manager.

At the time of writing, the deployed Global Dcc uses the components

  • queue: Azure Service Bus Queue
  • pubsub: Azure Service Bus
  • state: Azure Blob Storage

Whereas for the integration tests, those components are replaced by containers in order to run test reproducibly, without relying on cloud resources:

  • queue: RabbitMQ (via Docker container)
  • pubsub: Redis (via Docker container)
  • state: Redis (via Docker container)

The exact choice of components - RabbitMQ, an open-source message broker, and Redis,an open-source in-memory key-value store - is not really relevant. The important points are that they are supported by Dapr as drop-in replacements for some components and that they are available as Docker images. One complication comes from the backend being a Windows image: We can not use the official RabbitMQ and Redis images, as they are for Linux. Instead, we rely on Windows ports from 'the open source community'.

References

DbUp

DbUp is used to handle creation and upgrades of the database.

Why do we use DbUp

We need to handle database changes and we do not want to execute the script for that manually. In many other solutions we use Entity Framework, but the database in GlobalDcc is rather simple plus it does not match EF's way of doing things.

How do we use DbUp

DccCache.Dbup (in tools) use DbUp. The Dockerfile for DccCache.Dbup is used in the pipeline (BuildDbUp job). DccCache.Dbup is also used in the tests.

NSwag

We use NSwag for generating the Open API file and for auto generating the code that calls GlobalDcc.Api.Server in GlobalDcc.Api.Client. For details see the NSwag page.

Why do we use NSwag

By using a tool to auto generate the client, we avoid having to add this code ourselves when adding new methods to a controller. This means fewer things that can go wrong, and a bit less work.

How do we use NSwag

NSwag runs everytime we build GlobalDcc.Api.Server in Debug. See details here.