In the interests of full disclosure, I know very little (very little indeed!) about .NET. But I do enjoy figuring things out. In this post I’ve documented what I learned when trying to connect a simple .NET application to MySQL using Docker Compose.
We’re going to try to do this using Docker as far as possible, which will allow me to avoid having to set up .NET on my local machine.
📢 I’m not going to be remotely concerned about security. I just want something that works.
Create a Console Application
Launch a BASH shell in a .NET SDK container.
docker run -it -v $(pwd):/app mcr.microsoft.com/dotnet/sdk:8.0 /bin/bash
Create a new console application.
dotnet new console -n docker-mysql
That will set up a docker-mysql
folder with the required project structure.
Add the MySql.Data
package.
dotnet add package MySql.Data
If you take a look at the resulting docker-mysql.csproj
file you’ll see that it includes MySql.Data
.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>docker_mysql</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MySql.Data" Version="8.2.0" />
</ItemGroup>
</Project>
Create a simple application in Program.cs
.
using System;
using MySql.Data.MySqlClient;
namespace DockerMySQL {
class Program {
static void Main(string[] args) {
string connectionString = "server=database;user=root;password=test123;database=mysql";
System.Threading.Thread.Sleep(5000);
try {
using (MySqlConnection connection = new MySqlConnection(connectionString)) {
connection.Open();
connection.Close();
}
} catch (Exception ex) {
Console.WriteLine(ex.ToString());
Environment.Exit(1);
}
Console.WriteLine("Connected to MySQL!");
}
}
}
Docker Image
We’re going to build the .NET application in a Docker image. Here’s the Dockerfile
.
# Stage 1: Build application.
#
FROM mcr.microsoft.com/dotnet/sdk:latest AS build
WORKDIR /app
COPY docker-mysql /app
RUN dotnet restore docker-mysql.csproj
RUN dotnet build docker-mysql.csproj -c Release -o /app/build
FROM build AS publish
RUN dotnet publish docker-mysql.csproj -c Release -o /app/publish
# Stage 2: Create runtime image.
#
FROM mcr.microsoft.com/dotnet/runtime:latest AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "docker-mysql.dll"]
Now build the image.
docker build -t docker-mysql .
Docker Compose
Finally we need to set up Docker Compose so that it launches MySQL and our .NET app.
services:
client:
image: docker-mysql
container_name: client
build:
context: .
dockerfile: Dockerfile
depends_on:
- database
restart: always
database:
image: mysql
container_name: mysql
volumes:
- db-volume:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: test123
volumes:
db-volume:
We created a Docker volume, db-volume
, which is where the database would persist any content.
The MySQL service is labelled database
, so this is the name that’s used for server
in the .NET connection string. The connection string also specifies that the application will connect to the database as the root
user and gives the same password provided in docker-compose.yml
.
Despite the fact that the client
service depends on the database
service, this does not guarantee that the database will be ready to accept connections when the client application starts. Ideally you should build the application so that it retries connecting to the database. Since I’m lazy I just put in a brief pause before it tries to connect.
With all of that in place we can bring up Docker Compose.
docker-compose up
That generates the following (aggressively redacted) output.
Creating mysql ... done
Creating client ... done
Attaching to mysql, client
mysql | 2024-01-15 07:25:46+00:00 Entrypoint script for MySQL Server 8.2.0-1.el8 started.
mysql | 2024-01-15 07:25:46+00:00 Switching to dedicated user 'mysql'
mysql | 2024-01-15 07:25:46+00:00 Entrypoint script for MySQL Server 8.2.0-1.el8 started.
mysql | 2024-01-15T07:25:46.69624 MySQL Server - start.
mysql | 2024-01-15T07:25:46.88333 /usr/sbin/mysqld (mysqld 8.2.0) starting as process 1
mysql | 2024-01-15T07:25:46.88711 InnoDB initialization has started.
mysql | 2024-01-15T07:25:46.96072 InnoDB initialization has ended.
mysql | 2024-01-15T07:25:47.08986 CA certificate ca.pem is self signed.
mysql | 2024-01-15T07:25:47.10250 /usr/sbin/mysqld: ready for connections.
mysql | 2024-01-15T07:25:47.10250 X Plugin ready for connections.
client | Connected to MySQL!
Stopping client ... done
Stopping mysql ... done
As you can see from the single client
log, the application successfully connects to the database.