Introducción: ¿por qué nivel avanzado?
\nLa contenedorización de aplicaciones C# basadas en .NET ha dejado de ser una simple tendencia: es un estándar industrial. A nivel básico, basta con un Dockerfile simple con FROM mcr.microsoft.com/dotnet/aspnet. Sin embargo, en entornos de producción surgen desafíos que requieren una comprensión más profunda: minimización del tamaño de la imagen, seguridad, arquitectura multi-contenedor, gestión eficiente de la configuración y depuración.
Este artículo está dirigido a desarrolladores que ya conocen los fundamentos de Docker y C#, pero desean pasar al siguiente nivel. Analizaremos técnicas avanzadas que le permitirán crear imágenes ligeras, seguras y fácilmente escalables para sus aplicaciones .NET.
\n\n1. Compilación multi-etapa (Multi-stage build): el arte de las imágenes ligeras
\nEl problema clásico: las imágenes del SDK de .NET pesan más de 1.5 GB. Llevarlas a producción no tiene sentido y es peligroso. La compilación multi-etapa resuelve este problema separando la etapa de compilación de la etapa de ejecución.
\n\nDockerfile avanzado con multi-stage
\nEn lugar de simplemente copiar los archivos binarios, usamos las imágenes oficiales del SDK para compilar y luego copiamos el resultado en una imagen mínima aspnet.
# Etapa 1: Compilación\nFROM mcr.microsoft.com/dotnet/sdk:8.0 AS build\nWORKDIR /app\n\n# Copiamos solo los archivos del proyecto (optimización de caché)\nCOPY src/MyApp.csproj .\nRUN dotnet restore\n\n# Copiamos el resto del código\nCOPY src/ .\nRUN dotnet publish -c Release -o /app/publish\n\n# Etapa 2: Runtime\nFROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime\nWORKDIR /app\n\n# Agregamos un usuario no privilegiado\nRUN adduser --disabled-password --gecos '' appuser\nUSER appuser\n\nCOPY --from=build /app/publish .\n\nEXPOSE 8080\nENV ASPNETCORE_URLS=http://+:8080\nENTRYPOINT ["dotnet", "MyApp.dll"]\n¿Qué es importante aquí?
\n- \n
- Almacenamiento en caché de capas: primero se copia solo el
.csprojy se ejecutadotnet restore. Mientras las dependencias no cambien, Docker usará la caché para esta capa. \n - Usuario no privilegiado:
appuser— es crítico para la seguridad en producción. \n - Puerto 8080: en .NET 8 se usa por defecto el puerto 8080, lo que se alinea con los estándares modernos de la nube. \n
Truco: Compilación Trimmed y AOT
\nPara reducir aún más el tamaño, use Trimming (eliminación de código no utilizado) o Native AOT. Esto permite obtener imágenes de 10-30 MB.
\n# Para ReadyToRun (R2R) y Trimming\nRUN dotnet publish -c Release -r linux-x64 --self-contained true \\\n -p:PublishTrimmed=true \\\n -p:PublishSingleFile=true \\\n -o /app/publish\nImportante: AOT impone limitaciones en la reflexión y la carga dinámica de ensamblados. Antes de usarlo, asegúrese de que su código sea compatible.
\n\n2. Gestión de configuración y secretos
\nAlmacenar cadenas de conexión y claves API dentro de la imagen es un grave error. A nivel avanzado, usamos Docker Secrets, variables de entorno y la integración con appsettings.json.
Sobrescritura de appsettings mediante Docker
\n.NET tiene un mecanismo integrado de prioridades de configuración. Podemos pasar configuraciones a través de variables de entorno que se asignan automáticamente a las secciones de configuración.
\n// En código C#\nvar builder = WebApplication.CreateBuilder(args);\nbuilder.Configuration\n .AddJsonFile("appsettings.json", optional: false)\n .AddEnvironmentVariables(); // Sobrescribe los valores del JSON\nEjecutamos el contenedor:
\ndocker run -d -p 8080:8080 \\\n -e "ConnectionStrings__DefaultConnection=Server=db;Database=myapp;User Id=sa;Password=MyPass123!" \\\n -e "Logging__LogLevel__Default=Warning" \\\n