Usar contenedores para el desarrollo con .NET
Requisitos previos
Completa Containerizar una aplicación .NET.
Resumen
En esta sección, aprenderás cómo configurar un entorno de desarrollo para tu aplicación containerizada. Esto incluye:
- Agregar una base de datos local y persistir los datos
- Configurar Compose para actualizar automáticamente los servicios de Compose en ejecución a medida que editas y guardas tu código
- Crear un contenedor de desarrollo que contenga las herramientas y dependencias del SDK de .NET Core
Actualizar la aplicación
Esta sección utiliza una rama diferente del repositorio docker-dotnet-sample que contiene una aplicación .NET actualizada. La aplicación actualizada está en la rama add-db del repositorio que clonaste en Containerizar una aplicación .NET.
Para obtener el código actualizado, debes cambiar a la rama add-db (checkout). Puedes guardar en el depósito (stash) los cambios que realizaste en Containerizar una aplicación .NET para esta sección. En una terminal, ejecuta los siguientes comandos en el directorio docker-dotnet-sample.
Guarda cualquier cambio anterior.
$ git stash -uCambia a la nueva rama con la aplicación actualizada.
$ git checkout add-db
En la rama add-db, solo se ha actualizado la aplicación .NET. Ninguno de los recursos de Docker se ha actualizado aún.
Ahora deberías tener lo siguiente en tu directorio docker-dotnet-sample.
├── docker-dotnet-sample/
│ ├── .git/
│ ├── src/
│ │ ├── Data/
│ │ ├── Models/
│ │ ├── Pages/
│ │ ├── Properties/
│ │ ├── wwwroot/
│ │ ├── appsettings.Development.json
│ │ ├── appsettings.json
│ │ ├── myWebApp.csproj
│ │ └── Program.cs
│ ├── tests/
│ │ ├── tests.csproj
│ │ ├── UnitTest1.cs
│ │ └── Usings.cs
│ ├── .dockerignore
│ ├── .gitignore
│ ├── compose.yaml
│ ├── Dockerfile
│ ├── README.Docker.md
│ └── README.mdAgregar una base de datos local y persistir datos
Puedes utilizar contenedores para configurar servicios locales, como una base de datos. En esta sección, actualizarás el archivo compose.yaml para definir un servicio de base de datos y un volumen para persistir los datos.
Abre el archivo compose.yaml en un IDE o editor de texto. Notarás que ya contiene instrucciones comentadas para una base de datos PostgreSQL y un volumen.
Abre docker-dotnet-sample/src/appsettings.json in un IDE o editor de texto. Notarás la cadena de conexión con toda la información de la base de datos. El archivo compose.yaml ya contiene esta información, pero está comentada. Descomenta las instrucciones de la base de datos en el archivo compose.yaml.
A continuación se muestra el archivo compose.yaml actualizado.
services:
server:
build:
context: .
target: final
ports:
- 8080:8080
depends_on:
db:
condition: service_healthy
db:
image: postgres:18
restart: always
user: postgres
secrets:
- db-password
volumes:
- db-data:/var/lib/postgresql
environment:
- POSTGRES_DB=example
- POSTGRES_PASSWORD_FILE=/run/secrets/db-password
expose:
- 5432
healthcheck:
test: ["CMD", "pg_isready"]
interval: 10s
timeout: 5s
retries: 5
volumes:
db-data:
secrets:
db-password:
file: db/password.txtNotePara obtener más información sobre las instrucciones en el archivo de Compose, consulta la referencia del archivo de Compose.
Antes de ejecutar la aplicación con Compose, ten en cuenta que este archivo de Compose utiliza secrets y especifica un archivo password.txt para almacenar la contraseña de la base de datos. Debes crear este archivo, ya que no está incluido en el repositorio de origen.
En el directorio docker-dotnet-sample, crea un nuevo directorio llamado db y, dentro de ese directorio, crea un archivo llamado password.txt. Abre password.txt en un IDE o editor de texto y agrega la siguiente contraseña. La contraseña debe estar en una sola línea, sin líneas adicionales en el archivo.
exampleGuarda y cierra el archivo password.txt.
Ahora deberías tener lo siguiente en tu directorio docker-dotnet-sample.
├── docker-dotnet-sample/
│ ├── .git/
│ ├── db/
│ │ └── password.txt
│ ├── src/
│ ├── tests/
│ ├── .dockerignore
│ ├── .gitignore
│ ├── compose.yaml
│ ├── Dockerfile
│ ├── README.Docker.md
│ └── README.mdEjecuta el siguiente comando para iniciar tu aplicación.
$ docker compose up --build
Abre un navegador y visualiza la aplicación en http://localhost:8080. Deberías ver una aplicación web sencilla con el texto Student name is.
La aplicación no muestra ningún nombre porque la base de datos está vacía. Para esta aplicación, debes acceder a la base de datos y luego agregar registros.
Agregar registros a la base de datos
Para la aplicación de ejemplo, debes acceder directamente a la base de datos para crear registros de prueba.
Puedes ejecutar comandos dentro del contenedor de la base de datos utilizando el comando docker exec. Antes de ejecutar ese comando, debes obtener el ID del contenedor de la base de datos. Abre una nueva ventana de terminal y ejecuta el siguiente comando para listar todos los contenedores en ejecución.
$ docker container ls
Deberías ver una salida como la siguiente.
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cb36e310aa7e docker-dotnet-sample-server "dotnet myWebApp.dll" About a minute ago Up About a minute 0.0.0.0:8080->8080/tcp docker-dotnet-sample-server-1
39fdcf0aff7b postgres:18 "docker-entrypoint.s…" About a minute ago Up About a minute (healthy) 5432/tcp docker-dotnet-sample-db-1
En el ejemplo anterior, el ID del contenedor es 39fdcf0aff7b. Ejecuta el siguiente comando para conectarte a la base de datos postgres en el contenedor. Reemplaza el ID del contenedor por tu propio ID de contenedor.
$ docker exec -it 39fdcf0aff7b psql -d example -U postgres
Y finalmente, inserta un registro en la base de datos.
example=# INSERT INTO "Students" ("ID", "LastName", "FirstMidName", "EnrollmentDate") VALUES (DEFAULT, 'Whale', 'Moby', '2013-03-20');
Deberías ver una salida como la siguiente.
INSERT 0 1
Cierra la conexión a la base de datos y sal del shell del contenedor ejecutando exit.
example=# exit
Verificar que los datos persisten en la base de datos
Abre un navegador y visualiza la aplicación en http://localhost:8080. Deberías ver una aplicación web sencilla con el texto Student name is Moby Whale.
Presiona ctrl+c en la terminal to detener tu aplicación.
En la terminal, ejecuta docker compose rm para eliminar tus contenedores y luego ejecuta docker compose up para ejecutar tu aplicación de nuevo.
$ docker compose rm
$ docker compose up --build
Actualiza http://localhost:8080 en tu navegador y verifica que el nombre del estudiante se mantuvo persistente, incluso después de que los contenedores fueron eliminados y ejecutados nuevamente.
Presiona ctrl+c en la terminal para detener tu aplicación.
Actualizar servicios automáticamente
Usa Compose Watch para actualizar automáticamente tus servicios de Compose en ejecución a medida que editas y guardas tu código. Para obtener más detalles sobre Compose Watch, consulta Usar Compose Watch.
Abre tu archivo compose.yaml en un IDE o editor de texto y luego agrega las instrucciones de Compose Watch. A continuación se muestra el archivo compose.yaml actualizado.
services:
server:
build:
context: .
target: final
ports:
- 8080:8080
depends_on:
db:
condition: service_healthy
develop:
watch:
- action: rebuild
path: .
db:
image: postgres:18
restart: always
user: postgres
secrets:
- db-password
volumes:
- db-data:/var/lib/postgresql
environment:
- POSTGRES_DB=example
- POSTGRES_PASSWORD_FILE=/run/secrets/db-password
expose:
- 5432
healthcheck:
test: ["CMD", "pg_isready"]
interval: 10s
timeout: 5s
retries: 5
volumes:
db-data:
secrets:
db-password:
file: db/password.txtEjecuta el siguiente comando para ejecutar tu aplicación con Compose Watch.
$ docker compose watch
Abre un navegador y verifica que la aplicación se está ejecutando en http://localhost:8080.
Cualquier cambio en los archivos fuente de la aplicación en tu máquina local se reflejará inmediatamente en el contenedor en ejecución.
Abre docker-dotnet-sample/src/Pages/Index.cshtml en un IDE o editor de texto y actualiza el texto del nombre del estudiante en la línea 13 de Student name is a Student name:.
- <p>Student name is @Model.StudentName</p>
+ <p>Student name: @Model.StudentName</p>
Guarda los cambios en Index.cshtml y luego espera unos segundos a que la aplicación se vuelva a compilar. Actualiza http://localhost:8080 en tu navegador y verifica que aparezca el texto actualizado.
Presiona ctrl+c en la terminal para detener tu aplicación.
Crear un contenedor de desarrollo
En este punto, cuando ejecutas tu aplicación containerizada, esta utiliza la imagen del tiempo de ejecución (runtime) de .NET. Si bien esta imagen pequeña es adecuada para producción, carece de las herramientas y dependencias del SDK que puedes necesitar al desarrollar. Además, durante el desarrollo, es posible que no necesites ejecutar dotnet publish. Puedes usar compilaciones multietapa (multi-stage builds) para construir etapas tanto de desarrollo como de producción en el mismo Dockerfile. Para más detalles, consulta
Compilaciones multietapa.
Agrega una nueva etapa de desarrollo a tu Dockerfile y actualiza tu archivo compose.yaml para usar esta etapa en el desarrollo local.
A continuación se muestra el Dockerfile actualizado.
# syntax=docker/dockerfile:1
FROM --platform=$BUILDPLATFORM dhi.io/dotnet:10-sdk AS build
ARG TARGETARCH
COPY . /source
WORKDIR /source/src
RUN --mount=type=cache,id=nuget,target=/root/.nuget/packages \
dotnet publish -a ${TARGETARCH/amd64/x64} --use-current-runtime --self-contained false -o /app
FROM dhi.io/dotnet:10-sdk AS development
COPY . /source
WORKDIR /source/src
CMD dotnet run --no-launch-profile
FROM dhi.io/aspnetcore:10 AS final
WORKDIR /app
COPY --from=build /app .
ENTRYPOINT ["dotnet", "myWebApp.dll"]# syntax=docker/dockerfile:1
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build
ARG TARGETARCH
COPY . /source
WORKDIR /source/src
RUN --mount=type=cache,id=nuget,target=/root/.nuget/packages \
dotnet publish -a ${TARGETARCH/amd64/x64} --use-current-runtime --self-contained false -o /app
FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS development
COPY . /source
WORKDIR /source/src
CMD dotnet run --no-launch-profile
FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine AS final
WORKDIR /app
COPY --from=build /app .
ARG UID=10001
RUN adduser \
--disabled-password \
--gecos "" \
--home "/nonexistent" \
--shell "/sbin/nologin" \
--no-create-home \
--uid "${UID}" \
appuser
USER appuser
ENTRYPOINT ["dotnet", "myWebApp.dll"]A continuación se muestra el archivo compose.yaml actualizado.
services:
server:
build:
context: .
target: development
ports:
- 8080:8080
depends_on:
db:
condition: service_healthy
develop:
watch:
- action: rebuild
path: .
environment:
- ASPNETCORE_ENVIRONMENT=Development
db:
image: postgres:18
restart: always
user: postgres
secrets:
- db-password
volumes:
- db-data:/var/lib/postgresql
environment:
- POSTGRES_DB=example
- POSTGRES_PASSWORD_FILE=/run/secrets/db-password
expose:
- 5432
healthcheck:
test: ["CMD", "pg_isready"]
interval: 10s
timeout: 5s
retries: 5
volumes:
db-data:
secrets:
db-password:
file: db/password.txtTu aplicación containerizada ahora utilizará la imagen del SDK (ya sea dhi.io/dotnet:10-sdk para DHI o mcr.microsoft.com/dotnet/sdk:10.0-alpine para las imágenes oficiales), que incluye herramientas de desarrollo como dotnet test. Continúa con la siguiente sección para aprender cómo puedes ejecutar dotnet test.
Resumen
En esta sección, analizaste la configuración de tu archivo de Compose para agregar una base de datos local y persistir datos. También aprendiste cómo usar Compose Watch para reconstruir y ejecutar automáticamente tu contenedor cuando actualizas tu código. Y finalmente, aprendiste cómo crear un contenedor de desarrollo que contiene las herramientas y dependencias del SDK necesarias para el desarrollo.
Información relacionada:
- Referencia del archivo de Compose
- Supervisión de archivos en Compose (Compose Watch)
- Compilaciones multietapa
Próximos pasos
En la siguiente sección, aprenderás cómo ejecutar pruebas unitarias usando Docker.