# Desarrolla tu aplicación Rust


## Prerrequisitos

- Has instalado la última versión de [Docker Desktop](/get-started/get-docker/).
- Has completado las guías en el [Centro de aprendizaje](/desktop/use-desktop/) de Docker Desktop para conocer los conceptos de Docker.
- Tienes un [cliente git](https://git-scm.com/downloads). Los ejemplos de esta sección utilizan un cliente git basado en la línea de comandos, pero puedes utilizar cualquier cliente.

## Resumen

En esta sección, aprenderás cómo utilizar volúmenes y redes en Docker. También utilizarás Docker para compilar tus imágenes y Docker Compose para hacer todo mucho más fácil.

Primero, veremos cómo ejecutar una base de datos en un contenedor y cómo utilizar volúmenes y redes para persistir tus datos y permitir que tu aplicación se comunique con la base de datos. Luego, lo reuniremos todo en un archivo Compose que te permitirá configurar y ejecutar un entorno de desarrollo local con un solo comando.

## Ejecuta una base de datos en un contenedor

En lugar de descargar PostgreSQL, instalarlo, configurarlo y luego ejecutar la base de datos PostgreSQL como un servicio, puedes utilizar la Imagen Oficial de Docker para PostgreSQL y ejecutarla en un contenedor.

Antes de ejecutar PostgreSQL en un contenedor, crea un volumen que Docker pueda administrar para almacenar tus datos y configuración persistentes. Utiliza la función de volúmenes con nombre (named volumes) que proporciona Docker en lugar de utilizar montajes de tipo bind (bind mounts).

Ejecuta el siguiente comando para crear tu volumen.

```console
$ docker volume create db-data
```

Ahora crea una red que tu aplicación y la base de datos utilizarán para comunicarse entre sí. La red se llama red puente definida por el usuario (user-defined bridge network) y te proporciona un servicio de resolución DNS útil que puedes utilizar al crear tu cadena de conexión.

```console
$ docker network create postgresnet
```

Ahora puedes ejecutar PostgreSQL en un contenedor y conectarlo al volumen y a la red que creaste anteriormente. Docker descargará la imagen desde Hub y la ejecutará localmente por ti.
En el siguiente comando, la opción `--mount` sirve para iniciar el contenedor con un volumen. Para obtener más información, consulta [Volúmenes de Docker](/engine/storage/volumes/).

```console
$ docker run --rm -d --mount \
  "type=volume,src=db-data,target=/var/lib/postgresql" \
  -p 5432:5432 \
  --network postgresnet \
  --name db \
  -e POSTGRES_PASSWORD=mysecretpassword \
  -e POSTGRES_DB=example \
  postgres:18
```

Ahora, asegúrate de que tu base de datos PostgreSQL esté en funcionamiento y de que puedas conectarte a ella. Conéctate a la base de datos PostgreSQL en ejecución dentro del contenedor.

```console
$ docker exec -it db psql -U postgres
```

Deberías ver una salida como la siguiente.

```console
psql (15.3 (Debian 15.3-1.pgdg110+1))
Type "help" for help.

postgres=#
```

En el comando anterior, iniciaste sesión en la base de datos PostgreSQL pasando el comando `psql` al contenedor `db`. Presiona `ctrl-d` para salir de la terminal interactiva de PostgreSQL.

## Obtén y ejecuta la aplicación de muestra

Para la aplicación de muestra, utilizarás una variante del backend de la aplicación react-rust-postgres de [Awesome Compose](https://github.com/docker/awesome-compose/tree/master/react-rust-postgres).

1. Clona el repositorio de la aplicación de muestra utilizando el siguiente comando.

   ```console
   $ git clone https://github.com/docker/docker-rust-postgres
   ```

2. En el directorio del repositorio clonado, ejecuta `docker init` para crear los archivos de Docker necesarios. Consulta el siguiente ejemplo para responder a las preguntas de `docker init`.

   ```console
   $ docker init
   Welcome to the Docker Init CLI!

   This utility will walk you through creating the following files with sensible defaults for your project:
     - .dockerignore
     - Dockerfile
     - compose.yaml
     - README.Docker.md

   Let's get started!

   ? What application platform does your project use? Rust
   ? What version of Rust do you want to use? 1.70.0
   ? What port does your server listen on? 8000
   ```

3. En el directorio del repositorio clonado, abre el `Dockerfile` en un IDE o editor de texto para actualizarlo.

   `docker init` se encargó de crear la mayoría de las instrucciones en el Dockerfile, pero tendrás que actualizarlo para tu aplicación específica. Además de un directorio `src`, esta aplicación incluye un directorio `migrations` para inicializar la base de datos. Agrega un montaje de tipo bind para el directorio `migrations` en la etapa de compilación en el Dockerfile. El siguiente es el Dockerfile actualizado.

   ```dockerfile {hl_lines="28"}
   # syntax=docker/dockerfile:1

   # Los comentarios se proporcionan a lo largo de este archivo para ayudarte a empezar.
   # Si necesitas más ayuda, visita la guía de referencia de Dockerfile en
   # https://docs-docker.esdocu.com/reference/dockerfile/

   ################################################################################
   # Crea una etapa para compilar la aplicación.

   ARG RUST_VERSION=1.70.0
   ARG APP_NAME=react-rust-postgres
   FROM rust:${RUST_VERSION}-slim-bullseye AS build
   ARG APP_NAME
   WORKDIR /app

   # Compila la aplicación.
   # Aprovecha un montaje de caché en /usr/local/cargo/registry/
   # para las dependencias descargadas y un montaje de caché en /app/target/ para
   # las dependencias compiladas, lo que acelerará las compilaciones posteriores.
   # Aprovecha un montaje de tipo bind al directorio src para evitar tener que copiar el
   # código fuente dentro del contenedor. Una vez compilado, copia el ejecutable a un
   # directorio de salida antes de que se desmonte el /app/target montado en caché.
   RUN --mount=type=bind,source=src,target=src \
       --mount=type=bind,source=Cargo.toml,target=Cargo.toml \
       --mount=type=bind,source=Cargo.lock,target=Cargo.lock \
       --mount=type=cache,target=/app/target/ \
       --mount=type=cache,target=/usr/local/cargo/registry/ \
       --mount=type=bind,source=migrations,target=migrations \
       <<EOF
   set -e
   cargo build --locked --release
   cp ./target/release/$APP_NAME /bin/server
   EOF

   ################################################################################
   # Crea una nueva etapa para ejecutar la aplicación que contenga las dependencias de
   # ejecución mínimas para la aplicación. A menudo se utiliza una imagen base diferente
   # de la etapa de compilación donde se copian los archivos necesarios desde la etapa de
   # compilación.
   #
   # El siguiente ejemplo utiliza la imagen debian bullseye como base para ejecutar la aplicación.
   # Al especificar la etiqueta "bullseye-slim", también utilizará la versión más reciente
   # de esa etiqueta cuando compiles tu Dockerfile. Si la reproducibilidad es importante,
   # considera utilizar un digest
   # (por ejemplo, debian@sha256:ac707220fbd7b67fc19b112cee8170b41a9e97f703f588b2cdbbcdcecdd8af57).
   FROM debian:bullseye-slim AS final

   # Crea un usuario sin privilegios bajo el cual se ejecutará la aplicación.
   # Consulta https://docs-docker.esdocu.com/develop/develop-images/dockerfile_best-practices/#user
   ARG UID=10001
   RUN adduser \
       --disabled-password \
       --gecos "" \
       --home "/nonexistent" \
       --shell "/sbin/nologin" \
       --no-create-home \
       --uid "${UID}" \
       appuser
   USER appuser

   # Copia el ejecutable desde la etapa de compilación "build".
   COPY --from=build /bin/server /bin/

   # Expone el puerto en el que escucha la aplicación.
   EXPOSE 8000

   # Lo que debe ejecutar el contenedor cuando se inicia.
   CMD ["/bin/server"]
   ```

4. En el directorio del repositorio clonado, ejecuta `docker build` para compilar la imagen.

   ```console
   $ docker build -t rust-backend-image .
   ```

5. Ejecuta `docker run` con las siguientes opciones para ejecutar la imagen como un contenedor en la misma red que la base de datos.

   ```console
   $ docker run \
     --rm -d \
     --network postgresnet \
     --name docker-develop-rust-container \
     -p 3001:8000 \
     -e PG_DBNAME=example \
     -e PG_HOST=db \
     -e PG_USER=postgres \
     -e PG_PASSWORD=mysecretpassword \
     -e ADDRESS=0.0.0.0:8000 \
     -e RUST_LOG=debug \
     rust-backend-image
   ```

6. Haz curl a la aplicación para verificar que se conecta a la base de datos.

   ```console
   $ curl http://localhost:3001/users
   ```

   Deberías obtener una respuesta como la siguiente.

   ```json
   [{ "id": 1, "login": "root" }]
   ```

## Usa Compose para desarrollar localmente

Cuando ejecutas `docker init`, además de un `Dockerfile`, también se crea un archivo `compose.yaml`.

Este archivo Compose es muy conveniente ya que no tienes que escribir todos los parámetros que se pasan al comando `docker run`. Puedes hacer eso de forma declarativa usando un archivo Compose.

En el directorio del repositorio clonado, abre el archivo `compose.yaml` en un IDE o editor de texto. `docker init` se encargó de crear la mayoría de las instrucciones, pero tendrás que actualizarlo para tu aplicación específica.

Debes actualizar los siguientes elementos en el archivo `compose.yaml`:

- Descomentar todas las instrucciones de la base de datos.
- Agregar las variables de entorno bajo el servicio server.

El siguiente es el archivo `compose.yaml` actualizado.

```yaml {hl_lines=["17-23","30-55"]}
# Los comentarios se proporcionan a lo largo de este archivo para ayudarte a empezar.
# Si necesitas más ayuda, visita la guía de referencia de Docker Compose en
# https://docs-docker.esdocu.com/reference/compose-file/

# Aquí las instrucciones definen tu aplicación como un servicio llamado "server".
# Este servicio se construye a partir del Dockerfile en el directorio actual.
# Puedes añadir otros servicios de los que dependa tu aplicación aquí, como una
# base de datos o un caché. Para ver ejemplos, consulta el repositorio Awesome Compose:
# https://github.com/docker/awesome-compose
services:
  server:
    build:
      context: .
      target: final
    ports:
      - 8000:8000
    environment:
      - PG_DBNAME=example
      - PG_HOST=db
      - PG_USER=postgres
      - PG_PASSWORD=mysecretpassword
      - ADDRESS=0.0.0.0:8000
      - RUST_LOG=debug
    # La siguiente sección comentada es un ejemplo de cómo definir una base de datos
    # PostgreSQL que tu aplicación puede utilizar. `depends_on` le dice a Docker Compose que
    # inicie la base de datos antes que tu aplicación. El volumen `db-data` persiste los
    # datos de la base de datos entre reinicios del contenedor. El secreto `db-password` se utiliza
    # para establecer la contraseña de la base de datos. Debes crear `db/password.txt` y añadir
    # la contraseña que elijas antes de ejecutar `docker compose up`.
    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
```

Ten en cuenta que el archivo no especifica una red para esos dos servicios. Cuando utilizas Compose, este crea automáticamente una red y conecta los servicios a ella. Para más información, consulta [Redes en Compose](/compose/how-tos/networking/).

Antes de ejecutar la aplicación con Compose, ten en cuenta que este archivo Compose especifica un archivo `password.txt` para contener la contraseña de la base de datos. Debes crear este archivo, ya que no está incluido en el repositorio fuente.

En el directorio del repositorio clonado, crea un nuevo directorio llamado `db` y dentro de ese directorio crea un archivo llamado `password.txt` que contenga la contraseña para la base de datos. Utilizando tu IDE o editor de texto favorito, añade el siguiente contenido al archivo `password.txt`.

```text
mysecretpassword
```

Si tienes algún otro contenedor ejecutándose de las secciones anteriores, [detenlos](/guides/rust/run-containers/#stop-start-and-name-containers) ahora.

Ahora, ejecuta el siguiente comando `docker compose up` para iniciar tu aplicación.

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

El comando pasa la bandera `--build` para que Docker compile tu imagen y luego inicie los contenedores.

Ahora prueba tu endpoint de la API. Abre una nueva terminal y realiza una solicitud al servidor utilizando los comandos curl:

```console
$ curl http://localhost:8000/users
```

Deberías recibir la siguiente respuesta:

```json
[{ "id": 1, "login": "root" }]
```

## Resumen

En esta sección, vimos cómo configurar tu archivo Compose para ejecutar tu aplicación Rust y la base de datos con un solo comando.

Información relacionada:

- [Volúmenes de Docker](/engine/storage/volumes/)
- [Resumen de Compose](/compose/)

## Pasos siguientes

En la siguiente sección, veremos cómo configurar un flujo de trabajo de CI/CD utilizando GitHub Actions.

