· Tutorials  Â· 7 min read

Multi-container application with SQL Server

Build a multi-container application with SQL Server and ASP.NET Core using Docker Compose while using Docker Volume to persist the data and environment variables to form the connection string to connect to the database.

Build a multi-container application with SQL Server and ASP.NET Core using Docker Compose while using Docker Volume to persist the data and environment variables to form the connection string to connect to the database.

Introduction

In the previous beginner series for docker, we saw how to build a multi-container application with SQL Server and ASP.NET Core using Docker Compose. We built a really simple application and then ran two containers, one for the web application and one for the database. However, if you notice carefully, we actually didn’t connect the two containers together, we just ran them side by side. In real world, we need the containers to talk to each other.

So this time, we will be building a multi-container application with SQL Server and ASP.NET Core using Docker Compose while using Docker Volume to persist the data and use environment variables to form the connection string to connect to the database.

Prerequisites

Clone the starter code from this GitHub repository or apply the steps in this post to your existing project.

Setup Overview

In the starter code, you will have an ASP.NET Core Blazor Server App that works automatically, if you have SQL Server LocalDB installed already. If you don’t have SQL Server LocalDB installed, the app will fail to start for now. Nevertheless, we are going to reconfigure the app to use SQL Server in a container instead of LocalDB.

The app contains a simple set of CRUD operations to manage a list of departments and uses Entity Framework Core to interact with the database.

Blazor Server App

Objectives

The primary objectives of this post are as follows:

  • Use Docker Compose to run the app in a container and connect it to another container running SQL Server
  • Use Docker Volume to persist the data in the SQL Server container

The final architecture of the application will look like this:

Architecture

We should be able to use the application at http://localhost:8080

Game Plan

From the starter code, we will setup the following:

  • Create a Dockerfile for the ASP.NET Core Blazor Server App
  • Create a docker-compose.yml file for container orchestration
  • Add Services for the ASP.NET Core Blazor Server App and SQL Server
  • Create a Docker Volume to persist the data and configure the SQL Server container to use Docker Volume
  • Setup environment variables for the connection string

Setup Dockerfile for the ASP.NET Core Blazor Server App

Create a new file named Dockerfile in the src/SqlServerDockerExample folder and add the following content.

FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /src
COPY ["src/SqlServerDockerExample/SqlServerDockerExample.csproj", "SqlServerDockerExample/"]
RUN dotnet restore "SqlServerDockerExample/SqlServerDockerExample.csproj"
COPY . .
WORKDIR "/src/SqlServerDockerExample"
RUN dotnet build "SqlServerDockerExample.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "SqlServerDockerExample.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "SqlServerDockerExample.dll"]

Create a docker-compose.yml file for container orchestration

Create a new file named docker-compose.yml in the src folder and add the following content.

version: '3.4'

services:
    db:
        container_name: "SqlServerDatabase"
        image: mcr.microsoft.com/mssql/server:2022-latest
        volumes:
            - ./db-data:/var/opt/mssql/data
        environment:
            MSSQL_SA_PASSWORD: "Password123"
            ACCEPT_EULA: "Y"
            MSSQL_PID: "Developer" # Editions: Developer, Express, Standard, Enterprise
        ports:
            - "8082:1433"

    web:
        container_name: "BlazorServerApp"
        build:
            context: .
            dockerfile: SqlServerDockerExample/Dockerfile
        ports:
            - "8080:80"
        depends_on:
            - db
        environment:
            DBHOST: "db"
            DBPORT: 1433
            DBUSER: sa
            DBPASSWORD: Password123

The docker-compose.yml file contains two services - web and db, one for the ASP.NET Core Blazor Server App and one for SQL Server. The SQL Server db service is configured to use a Docker Volume to persist the data. We have also setup environment variables for the web service, which can be used internally to form the connection string to connect to the database.

Notice that the web service depends on the db service, which means that the db service will be started first and then the web service will be started. Also note that the web service is configured to talk to the db service using the port 1433, which is the internal port of the db service, instead of the external port 8082.

We will be exploring the entire working in detail, after running the application.

Update Program.cs to use environment variables

Open the Program.cs file in the src/SqlServerDockerExample folder.

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton<WeatherForecastService>();

var dbHost = Environment.GetEnvironmentVariable("DBHOST") ?? "localhost";
var dbPort = Environment.GetEnvironmentVariable("DBPORT") ?? "8082";
var dbUser = Environment.GetEnvironmentVariable("DBUSER") ?? "sa";
var dbPassword = Environment.GetEnvironmentVariable("DBPASSWORD") ?? "Your_password123";
var dbConnectionString = $"Server={dbHost},{dbPort};Database=SqlServerExample;User ID={dbUser};Password={dbPassword};Encrypt=False;Trusted_Connection=False;";

builder.Services.AddDbContext<AppDbContext>(options =>
{
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"));
    // options.UseSqlServer(dbConnectionString);
});
builder.Services.AddScoped<DepartmentService>();

Note that the necessary values for connection string are retrieved from the environment variables. Retrieve the values from the environment variables and assign them to the variables. In our code, if the environment variables are not set, the default values will be used.

Update the AddDbContext method to use the dbConnectionString variable instead of the DefaultConnection configuration.

var dbConnectionString = $"Server={dbHost},{dbPort};Database=SqlServerExample;User ID={dbUser};Password={dbPassword};Encrypt=False;Trusted_Connection=False;";

builder.Services.AddDbContext<AppDbContext>(options =>
{
    // options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"));
    options.UseSqlServer(dbConnectionString);
});

Build and run the app

Open a terminal and navigate to the root folder of the project. Run the following command to build the app.

docker-compose build

Once the build is successful, run the following command to start the app.

docker-compose up -t SqlServerDockerExample -d

The app should be running now. You can access the app at http://localhost:8080.

Blazor Server App

Access the SQL Server database

To access the SQL Server database, use the following details:

  • Server: localhost,8082
  • Username: sa
  • Password: Password123

Usually, SQL Server databases are managed using SQL Server Management Studio (SSMS). You can download and install SSMS from here.

For demo, I am using JetBrains Rider IDE to access the database. You can use any tool of your choice.

SQL Server Database

SQL Server Database

Since we have used a Docker Volume to persist the data, the data will be persisted even after the containers are stopped, destroyed and recreated.

How all this works?

When we run the docker-compose up command, the following happens:

  • First the docker-compose.yml file is parsed and the services are created. In our case, the db service is created first and then the web service is created.
  • The db service is created using the mcr.microsoft.com/mssql/server:2022-latest image. The db service is configured to use a Docker Volume to persist the data. The db service is also configured to use the port 8082 to expose the SQL Server database externally.
  • The Volume db-data is created and mounted to the /var/opt/mssql/data folder inside the container. This is where the SQL Server database files are stored. The physical location of the Volume is src/db-data.
  • The web service is created using the SqlServerDockerExample/Dockerfile file. Writing a Dockerfile is already covered in our beginner series here. The web service is configured to use the port 8080 to expose the ASP.NET Core Blazor Server App externally.
  • The web service is configured to talk to the db service using the port 1433, which is the internal port of the db service, instead of the external port 8082. This can be explained by the fact, containers that are part of the same network can talk to each other using the internal ports. We didn’t setup any network explicitly, hence Docker Compose creates a default network for us and adds all the services to that network. Thus we are using internal ports for communicating inside internal network. Networking in Docker is quite a separate topic and we may cover it in a separate article in the future.
  • The web service is configured to use the environment variables to form the connection string to connect to the database. The environment variables are set in the docker-compose.yml file, as this is where we configure and manage the web service itself. So we are delegating the responsibility of environment variables to the docker-compose.yml file. Then we are programmatically retrieving the values from the environment variables in the Program.cs file and using them to form the connection string.
  • The web service is configured to depend on the db service, which means that the db service will be started first and then the web service will be started.

After these steps, you’ll have a multi-container application running with SQL Server database.

Wrapping up

In this article, we learned how to create a multi-container application with SQL Server database using Docker Compose. We also learned how to use environment variables to configure the connection string for the database. We also learned how to use Docker Volumes to persist the data. In our upcoming articles, we’ll learn more in-depth about microservices and use the so far learned concepts to create a microservices based application. Thanks for reading and see you in the next article. Cheers!

Back to Blog

Related Posts

View All Posts »
Docker for beginners - Part 5 - Docker Compose

Docker for beginners - Part 5 - Docker Compose

Docker Compose is a tool for running multi-container applications. It defines the services that make up your app in docker-compose.yml so they can be run together in an isolated environment.

Docker for beginners - Part 3 - Docker CLI

Docker for beginners - Part 3 - Docker CLI

Docker CLI is the command-line interface for Docker. It allows you to interact with Docker daemon using commands. You can use Docker CLI to build, run, and manage Docker containers. In this tutorial, we will learn about some of the most commonly used Docker CLI commands.