Cómo dockerizar una aplicación .NET en capas con base de datos SQL Server
Aprende a levantar tu API con Base de datos y todo con Docker, esta es la guía definitiva 🤝

Este artículo es muy práctico, aquí te lanzo los hechos, como diría la chaviza, aquí van los factos:

  • Tienes una aplicación de tipo API REST con arquitectura Layered
  • Esta aplicación utiliza Entity Framework Core con SQL Server
  • Quieres dockerizarla pero no sabes cómo o no te funcionan los mil tutoriales de internet...

Primeros pasos

Clona la aplicación que usaré de ejemplo, aquí está el repo.

Esta es la estructura que tiene el proyecto:

Ojo que el proyecto es funcional pero no tiene aplicado en su totalidad la arquitectura layered, faltan capas, y no tiene aplicadas las mejores prácticas, será utilizado para efectos prácticos y aprender Docker ya que tiene varias capas y es perfecto para aprender docker, no para aprender arquitectura de software.

Te explicaré la parte del proyecto que es relevante para docker:

He creado un appsettings más, lo llamé appsettings.Docker.json para que sea utilizado como archivo de configuración cuando trabajas en el ambiente llamado Docker, es recomendable que además del ambiente Development y Production, manejes uno para docker.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "ConnectionStrings": {
    "defaultConnection": "server=simpledb,1433;database=MySimpleLayeredDb;user id=sa;password=YourStrongPwd1;trust server certificate=true"
  }
}

La línea que he resaltado contiene parámetros que luego configuraremos en docker.

Añadiendo archivos docker

Los archivos de docker que usarás casi siempre son 3:

  • Dockerfile
  • docker-compose.yaml
  • .dockerignore

Estos archivos van distribuidos en el proyecto de la siguiente manera:

Tu puedes poner tus archivos docker donde quieras, por ejemplo los 3 juntos en una carpeta llamada docker en la raíz del proyecto, eso está bien, sólo ten en cuenta que deberás cambiar algunas rutas en el Dockerfile.

.dockerignore

Sirve para ignorar archivos y carpetas que no necesitas en tu imagen de Docker.

Este archivo va en el root o carpeta raíz del proyecto, en este caso la carpeta es MySimpleLayeredApi

**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md

Dockerfile

Define cómo se crea la imagen de tu aplicación, con instrucciones como qué base usar, qué copiar, y qué ejecutar.

# Imagen base para el runtime
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

# Imagen base para construir el proyecto
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src

# Copia de archivos de proyecto
COPY ["src/MySimpleLayeredApi.Api/MySimpleLayeredApi.Api.csproj", "MySimpleLayeredApi.Api/"]
COPY ["src/MySimpleLayeredApi.Entities/MySimpleLayeredApi.Entities.csproj", "MySimpleLayeredApi.Entities/"]
COPY ["src/MySimpleLayeredApi.Persistence/MySimpleLayeredApi.Persistence.csproj", "MySimpleLayeredApi.Persistence/"]

# Restauración de dependencias
RUN dotnet restore "MySimpleLayeredApi.Api/MySimpleLayeredApi.Api.csproj"

# Copia del resto del código
COPY ./src .

# Build del proyecto
WORKDIR "/src/MySimpleLayeredApi.Api"
RUN dotnet build "MySimpleLayeredApi.Api.csproj" -c Release -o /app/build

# Publicar el resultado
FROM build AS publish
RUN dotnet publish "MySimpleLayeredApi.Api.csproj" -c Release -o /app/publish /p:UseAppHost=false

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

He resaltado las líneas a las que deberás prestar especial atención ya que van a depender de la estructura de carpetas que tenga tu proyecto. Por ejemplo la línea que copia el código COPY si no está bien configurada dará errores del tipo "Class does not have a main method" entre otros problemas.

docker-compose.yaml

Configura y ejecuta varios contenedores a la vez, como si fuera un plan para levantar servicios juntos.

services:
  db:
    image: mcr.microsoft.com/mssql/server:2022-latest
    container_name: simpledb
    user: '0:0'
    ports:
      - 2500:1433
    environment:
      - SA_PASSWORD=YourStrongPwd1
      - ACCEPT_EULA=Y
      - MSSQL_PID=Developer
    volumes:
      - sqldata:/var/opt/mssql
    healthcheck:
      test: ["CMD-SHELL", "if [ -f /opt/mssql-tools18/bin/sqlcmd ]; then /opt/mssql-tools18/bin/sqlcmd -S localhost -U SA -P YourStrongPwd1 -Q 'SELECT 1' -C; elif [ -f /opt/mssql-tools/bin/sqlcmd ]; then /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P YourStrongPwd1 -Q 'SELECT 1'; else exit 1; fi"]
      interval: 10s         # Intervalo entre cada intento de healthcheck
      timeout: 10s          # Tiempo máximo para la respuesta de la BD
      retries: 3            # Número de intentos antes de marcarlo como no saludable
      start_period: 30s     # Tiempo antes de comenzar a chequear la salud
    restart: unless-stopped

  app:
    build:
      dockerfile: "src/MySimpleLayeredApi.Api/Dockerfile"
    container_name: simpleapi
    environment:
      - ASPNETCORE_KESTREL__ENDPOINTS__HTTP__URL=http://+:80
      - ASPNETCORE_ENVIRONMENT=Docker      
    volumes:
      - appdata:/app
    ports:
      - 8080:80
    depends_on:
      db:
        condition: service_healthy  # Espera hasta que el servicio de la base de datos esté saludable
    restart: unless-stopped

volumes:
  appdata:
    driver: local
  sqldata:
    driver: local
    

Nota cómo el docker tiene buenas prácticas, añadí un healthcheck al servicio de base de datos para verificar y validar su estado antes de iniciar el api.

Levantando contenedores

Ahora te dirijes a la carpeta donde está tu docker-compose, abres un terminal y ejecutas lo siguiente:

docker compose up -d

Tu vas a ver más información en la consola, yo aquí ya ejecuté varias veces, por eso me dale directo 😉

Tendrás que esperar un minuto mas o menos, hasta que culmine la ejecución de dicho comando.

Si te surge algún problema durante la ejecución de los comandos docker puedes eliminar los contenedores, y volúmenes y volver a ejecutar el comando.

Ahora si abres tu docker desktop tendrás esto:

Ejecución y pruebas

Abre tu API, en este caso está en el puerto 8080

Todo debería estar funcionando incluyendo la base de datos.

Si quieres ingresar a tu base de datos con SSMS simplmente usa los datos que pusiste en tu docker compose, como habrás visto utilizamos el puerto 2500:

Y ahí lo tienes:

Y eso es todo estimado dev! a probar y adaptarlo a tus proyectos! 🦊🐿️🥳

Si esta entrada te ha encantado, y estoy seguro que sí, compártela con toda tu red! 😉

Créditos de imagen de portada: Foto de Bernd 📷 Dittrich en Unsplash

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *