paint-brush
How to Create Multi-Container Applications With Docker Compose and C#by@ssukhpinder
158 reads

How to Create Multi-Container Applications With Docker Compose and C#

by Sukhpinder SinghFebruary 22nd, 2023
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Docker is a platform for developing, shipping, and running applications using containerization. Docker Compose is a tool for defining and running multi-container Docker applications. This article will walk us through the steps to create a multi- container application with Docker and C#.
featured image - How to Create Multi-Container Applications With Docker Compose and C#
Sukhpinder Singh HackerNoon profile picture

Creating a multi-container application can be daunting, but Docker Compose and C# can make it much more straightforward. This article will walk us through the steps to create a multi-container application with Docker Compose and C#.

Prerequisites

  • Basic knowledge of OOPS concepts.


  • Any programming language knowledge.


  • Basic knowledge of docker


So, to begin with, C#:


Introduction to C#


C# has been around for quite some period, and it continues to develop, obtaining more enhanced features.medium.com

Learning Objectives

  • How to create a multi-container application with Docker Compose and C#.

Getting Started

Step 1: Install Docker and Docker Compose

Before we begin, make sure that Docker and Docker Compose are installed on your machine. Docker is a platform for developing, shipping, and running applications using containerization, while Docker Compose is a tool for defining and running multi-container Docker applications.


You can download Docker and Docker Compose from their respective websites.

Step 2: Create a New Project Directory

Once Docker and Docker Compose are installed, please create a new directory for your project and navigate to it using your terminal. For this example, we will call our project "my-csharp-app".


mkdir my-csharp-app
cd my-csharp-app

Step 3: Create a Frontend Dockerfile

Next, we will create a Dockerfile for our frontend container. This container will serve as a basic HTML page that calls our backend API.


Create a new file called "Dockerfile.frontend" in your project directory and add the following code:

FROM nginx:alpine
COPY ./nginx.conf /etc/nginx/conf.d/default.conf
COPY ./wwwroot /usr/share/nginx/html


This Dockerfile uses the official nginx: alpine image as its base and copies our custom nginx configuration file and HTML files to the appropriate directories.

Step 4: Create a Backend Dockerfile

Next, we will create a Dockerfile for our backend container. This container will serve a simple C# API that returns a list of users in JSON format.


Create a new file called "Dockerfile.backend" in your project directory and add the following code:

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /app
COPY . .
RUN dotnet restore
RUN dotnet publish -c Release -o out

FROM mcr.microsoft.com/dotnet/aspnet:6.0
WORKDIR /app
COPY --from=build /app/out .
ENTRYPOINT ["dotnet", "MyCSharpApp.dll"]


This Dockerfile uses the official dotnet SDK as its base and copies our application code to the appropriate directory. It then restores dependencies, builds the application, and publishes it to an output directory.


Finally, it copies the output to the appropriate directory in the final container and sets the entry point to start the application.

Step 5: Create a Docker Compose File

Now that we have our Dockerfiles, we can create a Docker Compose file to define our multi-container application.


Create a new file called "docker-compose.yml" in your project directory and add the following code:

version: "3"
services:
  frontend:
    build:
      context: .
      dockerfile: Dockerfile.frontend
    ports:
      - "8080:80"
    depends_on:
      - backend
  backend:
    build:
      context: .
      dockerfile: Dockerfile.backend
    ports:
      - "5000:5000"


This Docker Compose file defines two services: "frontend" and "backend". The "frontend" service uses the Dockerfile.frontend file we created earlier to build its image. It exposes port 8080 on the host machine, which is mapped to port 80 in the container.


It also depends on the "backend" service, meaning it will not start until the "backend" service is running.


The "backend" service uses the Dockerfile.backend file we created earlier to build its image. It exposes port 5000 on both the host and container and has no dependencies on other services.

Step 6: Add Configuration Files

We need to add some configuration files that our containers will use to communicate.


Create a new directory called "config" in your project directory, and create two files inside it: "appsettings.json" and "nginx.conf".


In the "appsettings.json" file, add the following code:

{
  "BackendBaseUrl": "http://backend:5000"
}


This configuration file defines a variable called "BackendBaseUrl" used by our frontend container to make API calls to our backend container.


In the "nginx.conf" file, add the following code:

server {
    listen       80;
    server_name  localhost;
    location / {
        proxy_pass         http://frontend:80;
        proxy_redirect     off;
        proxy_set_header   Host $host;
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Host $server_name;
    }
}


This configuration file defines the NGINX reverse proxy settings, allowing our frontend container to communicate with our backend container.

Step 7: Write the Application Code

Now that we have our containers and configuration files, we can write the application code that will run inside them.


Create a new directory called "src" in your project directory, and create two files inside it: "Program.cs" and "UserController.cs".


In the "Program.cs" file, add the following code:

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;

namespace MyCSharpApp
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }
        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}


This code sets up the entry point for our backend container and configures it to use a "Startup" class that we will define in the next step.


In the "UserController.cs" file, add the following code:

using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;

namespace MyCSharpApp.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class UserController : ControllerBase
    {
        private static readonly List<string> Users = new List<string> { "Alice", "Bob", "Charlie" };
        [HttpGet]
        public ActionResult<IEnumerable<string>> Get()
        {
            return Ok(Users);
        }
    }
}


This code defines a simple controller that returns a list of users when an HTTP GET request is made to the "/user" route.

Step 8: Write the Startup Class

Create a new file called "Startup.cs" in your project directory, and add the following code:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace MyCSharpApp
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }
        public IConfiguration Configuration { get; }
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
        }
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.UseRouting();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}


This code sets up the middleware for our backend container and adds support for controllers.

Step 9: Build and Run the Application

Now that we have written our application code, we can build and run our containers using Docker Compose.


In your terminal, navigate to your project directory and run the following command:

docker-compose up


This command will build and start the containers defined in our "docker-compose.yml" file. Once the containers are running, you should be able to access the application by navigating to "http://localhost" in your web browser.


You should see a page that displays a list of users, which is being served by our backend container and demonstrated by our frontend container.

Step 10: Stop the Application

After testing your application, you can stop all the running containers by pressing "Ctrl-C" in your terminal.


This will stop and remove all the containers but will not delete any data or configuration files you created.

Conclusion

This article demonstrates how to create a multi-container application using Docker Compose and C#.


By breaking our application into smaller, more modular containers, we can improve the scalability and maintainability of our application and make it easier to develop and deploy.


Docker Compose provides a powerful toolset for managing complex container-based applications, and C# offers a robust and flexible platform for building web applications.


By combining these two technologies, we can create modern, cloud-native applications that are efficient, reliable, and easy to manage.

Follow Me On

C# Publication, LinkedIn, Instagram, Twitter, Dev.to, BuyMeACoffee


Also published here