Skip to content

Tutorial of microservice containerization of C#/.NET-Core and Java/Spring-Boot application with a RabbitMQ message broker.

Notifications You must be signed in to change notification settings

MaloPavol/Containerization_of_microservices

Repository files navigation

Containerization of Microservices

This is a small tutorial of microservice containerization. For demonstrative purposes, the applications used are based on two different technologies: microservice A running on C#/.NET-Core-Framework and microservice B running on Java with Spring-Boot.

The microservice-based system created in this tutorial has a simple demonstrative functionality: It fetches current exchange rate from the server of the European Central Bank every 10 seconds. The application can be of course extended to any purpose.

In the following steps we first create Docker image of each application and then use docker-compose up to create, boot and run all containers in the virtual network hosted by the message-broker.

Prerequisites

For this tutorial you will need a few things:

Architecture

This demo solution consists of a message-broker and two microservices, one of which connects to the server of the European Central Bank:

Microservice A (C#/.NET-Core)

Function

To show the interaction between the components, the microservice A continously pushes new requests in the message queue managed by the message-broker. The microservice A is a simple C#/.NET application. In this scenario the microservice A behaves solely as a service consumer.

Detailed prerequisites

To get this app running in Visual Studio, make sure that:

  • Microsoft.NETCore 3.1 is set the framework;
  • Package Newtonsoft.Json,
  • Package System.Text.Encodings .Web and
  • Package RabbitMQ.Client (6.0.0) are included.

Test run

When running the app in the Visual Studio's IDE the execution stops at the attempt to push the first request into the broker's message queue. The reason is simple - the broker is not running yet (which we will fix below). The console should show the following:

(A.0) Demo app initiated
(A.1) FX request USD->EUR prepared, starting loop
(A.2) Initiating request #1
(A.3) Async request task started
(A.4) Request stringified

If you look at the code you notice that this application is not set to connect over the local host, but the message-broker (more details in the sections below).

var factory = new ConnectionFactory(){ HostName = "message-broker" };

The messages are passed by the message-broker in different queues. In this case we need only one queue connecting the microservice A with the microservice B. These messages function as remote procedure calls (RPC). Both services subscribe therefore to the same queue:

private const string QUEUE_NAME = "rpc_queue";

The the remote procedure calls are sent in non-blocking asynchronous messages.

RpcResponse rpcResponse = Task.Run(async () => await GetRpcResult(request)).Result;

Creating a Docker image from .NET-Core

A Docker image is a read-only template for a specific application which can be used to create and run an instance in a container.

The elaboration on how to create a Docker image from a .NET-Core application can be found in the Microsoft documentation, but is also summarized below.

Before creating a Docker image the .NET-Core-application must be

  1. built and
  2. published.

To build your application you can simply use Visual Studio's IDE (e.g. "Build Solution" in the solution explorer's context menu). This creates .dll-files in the bin-folder.

To publish the application you can run the dotnet publish command in command prompt from the DemoApp-directory:

cd microservice_A_(C#.NET)\DemoApp
dotnet publish -c Release

This creates the directory DemoApp\bin\Release\netcoreapp3.1\publish. These dll-files are needed to create the docker-image. For this tutorial a simple Dockerfile with the following contents can be used:

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1
COPY DemoApp/bin/Release/netcoreapp3.1/publish/ App/
WORKDIR /App
ENTRYPOINT ["dotnet", "DemoApp.dll"]

Note: This file is already prepared for you in the directory microservice_A_(C#.NET).

To create a new docker image, run the following command from the directory microservice_A_(C#.NET)/DemoApp where also the Dockerfile is placed.

docker build -t microservice_a-image -f Dockerfile .

Note: You may need to start your Command Prompt with elevated rights ("run as administrator") in order to execute this command.

If you now display all images with the command below, you should see a new repository called "microservice_a-image".

docker images

The image showing on your list should look something like this:

REPOSITORY TAG IMAGE ID CREATED SIZE
microservice_a-image latest c9b3efde6514f 3 hours ago 209MB

This image will be used to create a container running the app later.

Microservice B (Java with Spring-Boot)

Function

After the microservice A pushes its request to the message-brokers's queue, this message is forwarded to the microservice B. This microservice then fetches the current exchange rates from the European Central Bank's server and sends the requested exchange rate back to the message-broker. The message-broker then forwards the response from the microservice B to the microservice A.

The microservice B is hence a service consumer in the relation to the European Central Bank's server and a service provider in the relation to the microservice A.

Detailed prerequisites

The microservice B is a maven-project. All dependacies are predefined in the pom.xml-file. All you need to do is to import as maven in your IntelliJ.

Note that IntelliJ already comes equiped with functionalities to handle maven-projects. However if you want to call maven commands from the command prompt see maven installation guide.

Test run

If you run the microservice B in the IntelliJ's IDE the console should give you the output below. The execution stops at the attempt to connect to the message-broker. The reason is simple - the message-broker is not running yet.

(B.0) Demo app initiated
(B.1) Opening connection to the message broker

In the Java application files you can again see that the microservice B - just as the microservice A - is set to connect the message-broker's host and subscribes to the same message queue:

ConnectionFactory factory = new ConnectionFactory();
factory.setHost("message-broker");
private static final String RPC_QUEUE_NAME = "rpc_queue";

The two microservices would not be able to communicate with each other otherwise.

Creating a Docker image from Java/Spring-Boot

Before creating a Docker image, the Java-application must be

  1. built and
  2. packaged

Note: There are also other alternatives than JAR-packaging that are not addressed in this tutorial.

The packaging method is already defined in the pom.xml-file as JAR. To build and package the microservice B you can simply run the following command in the IntelliJ's terminal or in the command prompt.

mvn clean package

This command creates the file microservice_B-0.0.1-SNAPSHOT.jar in the directory microservice_B_(Java_wSpringBoot)\target.

You can either move the JAR-file to the directory microservice_B_(Java_wSpringBoot) where also the Dockerfile is saved and rename it as referenced in the prepared Dockerfile or change the cofiguration as it fits you.

Note: The JAR-file is has already been created for you in the project files.

The Dockerfile can look as follows (included in the project files). Note that in order to run a Java-based application, the image needs to have a JRE layer for which the openJDK can be used. This application runs on Java 8.

FROM adoptopenjdk/openjdk8:ubi
RUN mkdir /opt/app
COPY microservice_b.jar /opt/app
CMD ["java", "-jar", "/opt/app/microservice_b.jar"]

Once you have your JAR-file and the correct references in your Dockerfile (already included in the project files), you can create the Docker image with the following command:

docker build -t microservice_b-image -f Dockerfile .

If you now display all images with the command below, you should see a new repository called "microservice_b-image".

docker images

The image showing on your list should look something like this:

REPOSITORY TAG IMAGE ID CREATED SIZE
mmicroservice_b-image latest c9b3efde6524f 2 hours ago 466MB

Message Broker (RabbitMQ)

Function

The message-broker is a central piece of the puzzle as it manages the communication between the microservices. In this tutorial the request-reply pattern was used for queuing. For detail see RabbitMQ RPC tutorial:

Detailed prerequisites

The RabittMQ server is all you need for this tutorial.

Test run

After the installation you should be able to find the application RabbitMQ Service - start. You can start it, but as there is no communitation to manage for the message-broker at the time, there will not be much to see. Also note that by default RabbitMQ service starts in the background with every system boot, so by principle you do not need to start it manually.

Creating a Docker image

The image is created automatically for you during the installation. After the installation, you can display all images with the command below. You should see new repositories called "rabbitmq".

docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
rabbitmq management c9b3efde6524f 4 hours ago 181MB
rabbitmq latest c7a4gfde6524f 4 hours ago 151MB

Containerization and Booting

Now that everything is prepared it is time to put all the pieces together. Docker enables the containerization and booting of all microservices in one go. This can be easily done using the configuration for each service in a single docker-compose.yml-file. This file is already prepared for you in the project files:

version: "3"

services:

  message-broker:
    image: rabbitmq:management
    ports:
      - "5672:5672"
      - "15672:15672"
    command: rabbitmq-server
    expose:
      - 5672
      - 15672
    healthcheck:
      test: [ "CMD", "nc", "-z", "localhost", "5672" ]
      interval: 5s
      timeout: 15s
      retries: 1

  microservice_b:
    image: microservice_b-image
    restart: on-failure
    depends_on:
      - message-broker

  microservice_a:
    image: microservice_a-image
    restart: on-failure
    depends_on:
      - message-broker

All three services can be now containerized and booted with a single command:

docker-compose up

This starts the booting sequence:

The execution steps are logged in the console as follows:

microservice_b_1  | (B.0) Demo app initiated
microservice_b_1  | (B.1) Opening connection
message-broker_1  | ...accepting AMQP connection...
message-broker_1  | ...user authenticated...
microservice_b_1  | (B.2) Connection successful.
microservice_b_1  | (B.3) Awaiting RPC requests
microservice_a_1  | (A.2) Initiating request #1
microservice_a_1  | (A.3) Async request task started
microservice_a_1  | (A.4) Request stringified
message-broker_1  | ...accepting AMQP connection...
message-broker_1  | ...user authenticated...
microservice_a_1  | (A.5) RpcClient created
microservice_b_1  | (B.4) Request received: USD->EUR
microservice_b_1  | (B.5) Fetching FX rate from ECB
microservice_b_1  | (B.6) Sending response to A
microservice_a_1  | (A.6) Response received
message-broker_1  | ...closing AMQP connection... 
microservice_a_1  | (A.7) Response: USD->EUR:0.8615

License

This project is released under the MIT license.

About

Tutorial of microservice containerization of C#/.NET-Core and Java/Spring-Boot application with a RabbitMQ message broker.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published