# Usar contenedores para el desarrollo con .NET


## Requisitos previos

Completa [Containerizar una aplicación .NET](/guides/dotnet/develop/containerize/).

## 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](/guides/dotnet/develop/containerize/).

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](/guides/dotnet/develop/containerize/) para esta sección. En una terminal, ejecuta los siguientes comandos en el directorio `docker-dotnet-sample`.

1. Guarda cualquier cambio anterior.

   ```console
   $ git stash -u
   ```

2. Cambia a la nueva rama con la aplicación actualizada.

   ```console
   $ 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`.

```text
├── 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.

```yaml {hl_lines="8-33"}
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](/reference/compose-file/).

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.

```text
example
```

Guarda y cierra el archivo `password.txt`.

Ahora deberías tener lo siguiente en tu directorio `docker-dotnet-sample`.

```text
├── 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.

```console
$ docker compose up --build
```

Abre un navegador y visualiza la aplicación en [http://localhost:8080](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.

```console
$ docker container ls
```

Deberías ver una salida como la siguiente.

```console
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.

```console
$ docker exec -it 39fdcf0aff7b psql -d example -U postgres
```

Y finalmente, inserta un registro en la base de datos.

```console
example=# INSERT INTO "Students" ("ID", "LastName", "FirstMidName", "EnrollmentDate") VALUES (DEFAULT, 'Whale', 'Moby', '2013-03-20');
```

Deberías ver una salida como la siguiente.

```console
INSERT 0 1
```

Cierra la conexión a la base de datos y sal del shell del contenedor ejecutando `exit`.

```console
example=# exit
```

## Verificar que los datos persisten en la base de datos

Abre un navegador y visualiza la aplicación en [http://localhost:8080](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.

```console
$ docker compose rm
$ docker compose up --build
```

Actualiza [http://localhost:8080](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](/compose/how-tos/file-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.

```yaml {hl_lines="11-14"}
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.

```console
$ docker compose watch
```

Abre un navegador y verifica que la aplicación se está ejecutando en [http://localhost:8080](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:`.

```diff
-    <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](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](/build/building/multi-stage/).

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.

**Usar Docker Hardened Images**



```Dockerfile {hl_lines="10-13"}
# 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"]
```

**Usar la imagen oficial de .NET 10**



```Dockerfile {hl_lines="10-13"}
# 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.

```yaml {hl_lines=[5,15,16]}
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:

- [Referencia del archivo de Compose](/reference/compose-file/)
- [Supervisión de archivos en Compose (Compose Watch)](/compose/how-tos/file-watch/)
- [Compilaciones multietapa](/build/building/multi-stage/)

## Próximos pasos

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

