S

super-dev — full-stack

@super-dev.app · Full-stack · ★ disponible para proyectos
12,8k suscriptores·47 vídeos·Activo desde 2017

Desarrollador full-stack .NET / Angular con perfil DevOps en Azure Cloud. También cómodo con Flutter + Firebase. Construyo productos, los despliego, los monitorizo.

Volver a los artículos
DEVOPS
DEVOPS

Imágenes Docker multi-stage para .NET + Angular

Artículo 5 de 6 — Azure DevOps desde cero
Prerender, Container Apps, secrets, CI/CD, Docker, observabilidad: desplegar en Azure sin servidor ni dolor.

Incluir el SDK de .NET y node_modules en la imagen que desplegamos en producción es enviar 800 MB de herramientas que nunca se usarán en la ejecución. El build multi-stage separa lo que compila de lo que se ejecuta: obtenemos una imagen final diminuta, que contiene solo lo estrictamente necesario para el runtime.

El principio: compilar y descartar

Un Dockerfile multi-stage declara varios FROM . Cada FROM abre un stage aislado; solo el último stage se convierte en la imagen entregada. Copiamos selectivamente los artefactos de un stage de build hacia un stage de runtime, y todo lo demás — SDK, fuentes, cachés — se descarta.

Bash
1# Stage 1 : build de l'API .NET
2FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
3WORKDIR /src
4COPY *.csproj ./
5RUN dotnet restore
6COPY . ./
7RUN dotnet publish -c Release -o /app/publish
8
9# Stage 2 : runtime seul (pas de SDK)
10FROM mcr.microsoft.com/dotnet/aspnet:9.0-noble-chiseled AS final
11WORKDIR /app
12COPY --from=build /app/publish ./
13ENV ASPNETCORE_URLS=http://+:8080
14EXPOSE 8080
15ENTRYPOINT ["dotnet", "Api.dll"]

Caché de layers: ordenar para no reconstruir todo

Docker pone en caché cada instrucción y la invalida en cuanto cambia una capa anterior. De ahí la regla de oro: copiar los archivos de dependencias antes que el código fuente . Al hacer COPY *.csproj y luego dotnet restore antes de copiar el resto, el restore solo se vuelve a ejecutar si el .csproj cambia — no en cada modificación de un archivo C#. La misma lógica en el lado de Angular con package.json y npm ci antes del COPY de las fuentes: un cambio de código nunca reinvalida la instalación de dependencias, lo que divide los tiempos de build por diez.

Una imagen final diminuta

La elección de la imagen base de runtime marca toda la diferencia. Las imágenes chiseled de Microsoft ( aspnet:9.0-noble-chiseled ) eliminan la shell, el gestor de paquetes y los binarios superfluos: superficie de ataque reducida, imagen frecuentemente por debajo de los 110 MB, ejecución como usuario no-root por defecto. Para servir el frontend de Angular, nginx alpine desempeña el papel de stage final.

Bash
1# Build Angular puis service par nginx
2FROM node:22-alpine AS web
3WORKDIR /app
4COPY package*.json ./
5RUN npm ci
6COPY . ./
7RUN npm run build
8
9FROM nginx:1.27-alpine AS final
10COPY --from=web /app/dist/browser /usr/share/nginx/html
11COPY nginx.conf /etc/nginx/conf.d/default.conf

El build de Angular solo produce archivos estáticos: no se necesita ningún runtime de Node en producción. Copiamos la carpeta dist/browser en la raíz de nginx y añadimos un try_files $uri /index.html en la configuración para el fallback SPA .

Orquestar en local con Compose

Para ejecutar la API y el frontend juntos durante el desarrollo, un docker-compose.yml conecta los dos servicios y su red:

YAML
1services:
2 api:
3 build: ./api
4 ports:
5 - "8080:8080"
6 web:
7 build: ./web
8 ports:
9 - "4200:80"
10 depends_on:
11 - api

La documentación de los builds multi-stage detalla los builds dirigidos ( --target build ) y la compartición de stages, útiles para aislar una etapa de test en el pipeline CI.

Una imagen de prod solo debería contener lo que se ejecuta. El multi-stage hace que esta disciplina sea gratuita: el SDK permanece en el stage de build, nunca en lo que desplegáis .
super-dev — portfolio.app
// Continuar en DEVOPS
>_
Un pipeline CI/CD de GitHub Actions → Azure, desde cero
8 min • 1,9k lecturas
Observabilidad .NET con OpenTelemetry
8 min • 1,6k lecturas