Compartir comentarios
Las respuestas se generan en base a la documentación.

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.

  1. Guarda cualquier cambio anterior.

    $ git stash -u
    
  2. Cambia 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.md

Agregar 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.txt
Note

Para 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.

example

Guarda 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.md

Ejecuta 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.txt

Ejecuta 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.txt

Tu 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:

Próximos pasos

En la siguiente sección, aprenderás cómo ejecutar pruebas unitarias usando Docker.