Compartir a través de


Tutorial: Creación de una aplicación de varios contenedores con Docker Compose

En este tutorial, aprenderá a administrar más de un contenedor y a comunicarse entre ellos al usar Herramientas de contenedor en Visual Studio. La administración de varios contenedores requiere la orquestación de contenedores y requiere un orquestador como Docker Compose o Service Fabric. Para estos procedimientos, se usa Docker Compose. Docker Compose es ideal para las pruebas y la depuración locales durante el ciclo de desarrollo.

El ejemplo completado que crea en este tutorial se puede encontrar en GitHub en https://github.com/MicrosoftDocs/vs-tutorial-samples en la carpeta docker/ComposeSample.

Prerrequisitos

  • Docker Desktop
  • Visual Studio 2022 con las cargas de trabajo Desarrollo web, Azure Tools o Desarrollo multiplataforma de .NET instaladas. Esta instalación incluye las herramientas de desarrollo de .NET 8.

Creación de un proyecto de aplicación web

En Visual Studio, cree un proyecto de ASP.NET Core Web App, nombrado WebFrontEnd, para crear una aplicación web con páginas Razor.

Captura de pantalla que muestra el proyecto Crear ASP.NET Core Web App.

No seleccione Habilitar compatibilidad con Docker. Agregue compatibilidad con Docker más adelante en el proceso.

Captura de pantalla de la pantalla Información adicional al crear un proyecto web. La opción Habilitar compatibilidad con Docker no está seleccionada.

No seleccione Habilitar compatibilidad con Docker. Agregue compatibilidad con Docker más adelante en el proceso.

Captura de pantalla de la pantalla Información adicional al crear un proyecto web. La opción Habilitar compatibilidad con Docker no está seleccionada.

Creación de un proyecto de API web

Agregue un proyecto a la misma solución y llámelo MyWebAPI. Seleccione API como tipo de proyecto y desmarque la casilla para Configuración HTTPS. En este diseño, solo se usa SSL para la comunicación con el cliente, no para la comunicación entre contenedores de la misma aplicación web. Solo WebFrontEnd necesita HTTPS y el código de los ejemplos supone que ha desactivado esa casilla. En general, los certificados de desarrollador de .NET usados por Visual Studio solo se admiten para las solicitudes externas a contenedor, no para las solicitudes de contenedor a contenedor.

Captura de pantalla de la creación del proyecto de API web.

  1. Agregue un proyecto a la misma solución y llámelo MyWebAPI. Seleccione API como tipo de proyecto y desactive la casilla de Configure for HTTPS (Configurar para HTTPS).

    Nota

    En este diseño, solo se usa HTTPS para la comunicación con el cliente, no para la comunicación entre contenedores de la misma aplicación web. Solo WebFrontEnd necesita HTTPS y el código de los ejemplos supone que ha desactivado esa casilla. En general, los certificados de desarrollador de .NET usados por Visual Studio solo se admiten para las solicitudes externas a contenedor, no para las solicitudes de contenedor a contenedor.

    Captura de pantalla de la creación del proyecto de API web.

  2. Agregue compatibilidad con Azure Cache for Redis. Agregue el paquete NuGet Microsoft.Extensions.Caching.StackExchangeRedis (no el StackExchange.Redis). En Program.cs, agregue las líneas siguientes, justo antes de var app = builder.Build():

    builder.Services.AddStackExchangeRedisCache(options =>
       {
          options.Configuration = "redis:6379"; // redis is the container name of the redis service. 6379 is the default port
          options.InstanceName = "SampleInstance";
       });
    
  3. Agregue directivas de uso en Program.cs para Microsoft.Extensions.Caching.Distributed y Microsoft.Extensions.Caching.StackExchangeRedis.

    using Microsoft.Extensions.Caching.Distributed;
    using Microsoft.Extensions.Caching.StackExchangeRedis;
    
  4. En el proyecto de Web API, elimine los archivos existentes WeatherForecast.cs y Controllers/WeatherForecastController.cs, y agregue un archivo en Controladores, CounterController.cs, con el siguiente contenido:

    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.Caching.Distributed;
    using StackExchange.Redis;
    
    namespace WebApi.Controllers
    {
        [ApiController]
        [Route("[controller]")]
        public class CounterController : ControllerBase
        {
            private readonly ILogger<CounterController> _logger;
            private readonly IDistributedCache _cache;
    
            public CounterController(ILogger<CounterController> logger, IDistributedCache cache)
            {
                _logger = logger;
                _cache = cache;
            }
    
            [HttpGet(Name = "GetCounter")]
            public string Get()
            {
                string key = "Counter";
                string? result = null;
                try
                {
                    var counterStr = _cache.GetString(key);
                    if (int.TryParse(counterStr, out int counter))
                    {
                        counter++;
                    }
                    else
                    {
                        counter = 0;
                    }
                    result = counter.ToString();
                    _cache.SetString(key, result);
                }
                catch(RedisConnectionException)
                {
                    result = "Redis cache is not found.";
                }
                return result;
            }
        }
    }
    

    El servicio incrementa un contador cada vez que se accede a la página y almacena el contador en la memoria caché.

Adición de código para llamar a la API web

  1. En el proyecto WebFrontEnd, abra el archivo Index.cshtml.cs y reemplace el método OnGet por el código siguiente.

     public async Task OnGet()
     {
        ViewData["Message"] = "Hello from webfrontend";
    
        using (var client = new System.Net.Http.HttpClient())
        {
           // Call *mywebapi*, and display its response in the page
           var request = new System.Net.Http.HttpRequestMessage();
           request.RequestUri = new Uri("http://mywebapi/WeatherForecast");
           // request.RequestUri = new Uri("http://mywebapi/api/values/1"); // For ASP.NET 2.x, comment out previous line and uncomment this line.
           var response = await client.SendAsync(request);
           ViewData["Message"] += " and " + await response.Content.ReadAsStringAsync();
        }
     }
    

    Nota

    En un entorno de programación real, no deberías eliminar HttpClient después de cada solicitud. Para conocer los procedimientos recomendados, consulte Usar HttpClientFactory para implementar solicitudes HTTP resistentes.

  2. En el archivo Index.cshtml, agregue una línea para mostrar ViewData["Message"] para que el archivo tenga un aspecto similar al código siguiente:

    @page
    @model IndexModel
    @{
       ViewData["Title"] = "Home page";
    }
    
    <div class="text-center">
       <h1 class="display-4">Welcome</h1>
       <p>Learn about <a href="/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
       <p>@ViewData["Message"]</p>
    </div>
    
  3. (solo ASP.NET 2.x) Ahora en el proyecto de API web, agregue código al controlador Values para personalizar el mensaje devuelto por la API para la llamada que agregó desde webfrontend.

    // GET api/values/5
    [HttpGet("{id}")]
    public ActionResult<string> Get(int id)
    {
       return "webapi (with value " + id + ")";
    }
    

    Nota

    En .NET Core 3.1 y versiones posteriores, puede usar la API WeatherForecast proporcionada en lugar de este código adicional. Sin embargo, debe marcar como comentario la llamada a UseHttpsRedirection en el proyecto de API web porque el código usa HTTP para realizar la llamada en lugar de HTTPS.

          //app.UseHttpsRedirection();
    

Adición de compatibilidad con Docker Compose

  1. En el proyecto WebFrontEnd, seleccione Agregar > Compatibilidad con el orquestador de contenedores. Aparece el cuadro de diálogo Opciones de soporte de Docker.

  2. Seleccione Docker Compose.

  3. Elija el sistema operativo de destino, por ejemplo, Linux.

    Captura de pantalla de selección del sistema operativo de destino.

    Visual Studio crea un archivo docker-compose.yml y un archivo .dockerignore en el nodo docker-compose de la solución, y ese proyecto se muestra en negrita, lo que indica que es el proyecto de inicio.

    Captura de pantalla del Explorador de soluciones con el proyecto docker Compose agregado.

    El docker-compose.yml aparece de la siguiente manera:

     services:
       webfrontend:
         image: ${DOCKER_REGISTRY-}webfrontend
         build:
           context: .
           dockerfile: WebFrontEnd/Dockerfile
    

    El version especificado en la primera línea es la versión del archivo Docker Compose. Normalmente no debería cambiarlo, ya que las herramientas lo usan para comprender cómo interpretar el archivo.

    El archivo .dockerignore contiene tipos de archivo y extensiones que no quiere que Docker incluya en el contenedor. Estos archivos suelen estar asociados con el entorno de desarrollo y el control de código fuente, no forman parte de la aplicación o el servicio que está desarrollando.

    Consulte la sección Container Tools del panel de salida para obtener detalles de los comandos que se están ejecutando. Puede ver que la herramienta de línea de comandos docker-compose se usa para configurar y crear los contenedores en tiempo de ejecución.

  4. En el proyecto de API web, vuelva a hacer clic con el botón derecho en el nodo del proyecto y seleccione Agregar>Compatibilidad con el orquestador de contenedores. Elija docker Composey, a continuación, seleccione el mismo sistema operativo de destino.

    Nota

    En este paso, Visual Studio ofrecerá crear un Dockerfile. Si lo hace en un proyecto que ya tiene compatibilidad con Docker, se le pedirá si desea sobrescribir el Dockerfile existente. Si ha realizado cambios en el Dockerfile que desea conservar, elija no.

    Visual Studio realiza algunos cambios en el archivo YML de Docker Compose. Ahora se incluyen ambos servicios.

    services:
      webfrontend:
        image: ${DOCKER_REGISTRY-}webfrontend
        build:
          context: .
          dockerfile: WebFrontEnd/Dockerfile
    
      mywebapi:
        image: ${DOCKER_REGISTRY-}mywebapi
        build:
          context: .
          dockerfile: MyWebAPI/Dockerfile
    
  5. El primer proyecto al que se añade la orquestación de contenedores está configurado para iniciarse al ejecutar o depurar. Puede configurar la acción de inicio en las Propiedades del Proyecto para el proyecto de Docker Compose. En el nodo del proyecto docker Compose, haga clic con el botón derecho para abrir el menú contextual y, a continuación, elija Propiedadeso use Alt+Entrar. En la captura de pantalla siguiente se muestran las propiedades que desea para la solución que se usa aquí. Por ejemplo, puede cambiar la página que se carga mediante la personalización de la propiedad URL de servicio.

    Captura de pantalla de las propiedades del proyecto de Docker Compose.

    Esto es lo que ve cuando se inicia (la versión de .NET Core 2.x):

    Captura de pantalla de la ejecución de la aplicación web.

    La aplicación web para .NET 3.1 muestra los datos meteorológicos en formato JSON.

  6. Ahora, supongamos que solo le interesa tener el depurador asociado a WebFrontEnd, no al proyecto de API web. En la barra de menús, puede usar la lista desplegable situada junto al botón inicio para abrir un menú de opciones de depuración; elija Administrar la configuración de inicio de Docker Compose.

    Captura de pantalla del elemento de menú Administrar la configuración de Compose de Depurar.

    Aparece el cuadro de diálogo Administrar la configuración de inicio de Docker Compose. Con este cuadro de diálogo, puede controlar qué subconjunto de servicios se inicia durante una sesión de depuración, que se inicia con o sin el depurador asociado, y el servicio de inicio y la dirección URL. Consulte Administración de perfiles de inicio para Docker Compose.

    Captura de pantalla del cuadro de diálogo Administrar configuración de inicio de Docker Compose.

    Elija Nuevo para crear un nuevo perfil y asígnelo el nombre Debug WebFrontEnd only. A continuación, establezca el proyecto de API web en Iniciar sin depurar, deje el proyecto WebFrontEnd establecido para comenzar con la depuración y elija Guardar.

    La nueva configuración se elige como valor predeterminado para el siguiente F5.

  7. Presione F5 para confirmar que funciona según lo previsto.

Enhorabuena, está ejecutando una aplicación de Docker Compose con un perfil personalizado de Docker Compose.

  1. En el proyecto WebFrontEnd, abra el archivo Index.cshtml.cs y reemplace el método OnGet por el código siguiente.

    public async Task OnGet()
    {
       // Call *mywebapi*, and display its response in the page
       using (var client = new System.Net.Http.HttpClient())
       {
          var request = new System.Net.Http.HttpRequestMessage();
    
          // A delay is a quick and dirty way to work around the fact that
          // the mywebapi service might not be immediately ready on startup.
          // See the text for some ideas on how you can improve this.
          // Uncomment if not using healthcheck (Visual Studio 17.13 or later)
          // await System.Threading.Tasks.Task.Delay(10000);
    
          // mywebapi is the service name, as listed in docker-compose.yml.
          // Docker Compose creates a default network with the services
          // listed in docker-compose.yml exposed as host names.
          // The port 8080 is exposed in the WebAPI Dockerfile.
          // If your WebAPI is exposed on port 80 (the default for HTTP, used
          // with earlier versions of the generated Dockerfile), change
          // or delete the port number here.
          request.RequestUri = new Uri("http://mywebapi:8080/Counter");
          var response = await client.SendAsync(request);
          string counter = await response.Content.ReadAsStringAsync();
          ViewData["Message"] = $"Counter value from cache :{counter}";
       }
    }
    

    Nota

    En el código del mundo real, no se debe desechar HttpClient después de cada solicitud. Para conocer los procedimientos recomendados, consulte Usar HttpClientFactory para implementar solicitudes HTTP resistentes.

    El URI especificado hace referencia a un nombre de servicio definido en el archivo docker-compose.yml. Docker Compose configura una red predeterminada para la comunicación entre contenedores mediante los nombres de servicio enumerados como hosts.

    El código que se muestra aquí funciona con .NET 8 y versiones posteriores, que configura una cuenta de usuario en el Dockerfile sin privilegios de administrador y expone el puerto 8080 porque el puerto predeterminado HTTP 80 no es accesible sin privilegios elevados.

  2. En el archivo Index.cshtml, agregue una línea para mostrar ViewData["Message"] para que el archivo tenga un aspecto similar al código siguiente:

    @page
    @model IndexModel
    @{
        ViewData["Title"] = "Home page";
    }
    
    <div class="text-center">
        <h1 class="display-4">Welcome</h1>
        <p>Learn about <a href="/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
        <p>@ViewData["Message"]</p>
    </div>
    

    Este código muestra el valor del contador devuelto desde el proyecto de API web. Se incrementa cada vez que el usuario accede o actualiza la página.

Adición de compatibilidad con Docker Compose

  1. En el proyecto WebFrontEnd, seleccione Agregar > Compatibilidad con el orquestador de contenedores. Aparece el cuadro de diálogo de Opciones de Soporte de Docker .

  2. Seleccione Docker Compose.

  3. Visual Studio 17.12 y versiones posteriores Elija las opciones de scaffolding para el proyecto WebFrontEnd.

    Captura de pantalla mostrando el cuadro de diálogo de opciones de andamiaje de contenedor para el proyecto WebFrontEnd.

    Visual Studio 17.11 y versiones anteriores Elegir el sistema operativo de destino, por ejemplo, Linux.

    Captura de pantalla de selección del sistema operativo de destino.

    Visual Studio crea un archivo docker-compose.yml y un archivo .dockerignore en el nodo docker-compose de la solución, y el proyecto se muestra en fuente en negrita, lo que indica que es el proyecto principal.

    Captura de pantalla del Explorador de soluciones con el proyecto docker Compose agregado.

    El docker-compose.yml aparece de la siguiente manera:

     services:
       webfrontend:
         image: ${DOCKER_REGISTRY-}webfrontend
         build:
           context: .
           dockerfile: WebFrontEnd/Dockerfile
    

    El archivo .dockerignore contiene tipos de archivo y extensiones que no quiere que Docker incluya en el contenedor. Estos archivos suelen estar asociados con el entorno de desarrollo y el control de código fuente, no forman parte de la aplicación o el servicio que está desarrollando.

    Consulte la sección Container Tools del panel de salida para obtener más información sobre los comandos que se ejecutan. Puede ver que la herramienta de línea de comandos docker-compose se usa para configurar y crear los contenedores en tiempo de ejecución.

  4. En el proyecto de API web, vuelva a hacer clic con el botón derecho en el nodo del proyecto y seleccione Agregar>Compatibilidad con el orquestador de contenedores. Elija docker Composey, a continuación, seleccione el mismo sistema operativo de destino.

    Nota

    En este paso, Visual Studio ofrecerá crear un Dockerfile. Si lo hace en un proyecto que ya tiene compatibilidad con Docker, se le pedirá si desea sobrescribir el Dockerfile existente. Si ha realizado cambios en el Dockerfile que desea conservar, elija no.

    Visual Studio realiza algunos cambios en el archivo docker-compose YML. Ahora se incluyen ambos servicios.

    services:
      webfrontend:
        image: ${DOCKER_REGISTRY-}webfrontend
        build:
          context: .
          dockerfile: WebFrontEnd/Dockerfile
    
      mywebapi:
        image: ${DOCKER_REGISTRY-}mywebapi
        build:
          context: .
          dockerfile: MyWebAPI/Dockerfile
    
  5. Agregue la memoria caché al archivo docker-compose.yml:

    redis:
       image: redis
    

    Asegúrese de que la sangría está en el mismo nivel que los otros dos servicios.

  6. (Visual Studio 17.13 o posterior) Los servicios dependientes muestran un problema común. La solicitud HTTP en la página principal del front-end podría ejecutarse inmediatamente en el inicio de la aplicación, antes de que el servicio mywebapi esté listo para recibir solicitudes web. Si usa Visual Studio 17.13 o posterior, puede usar las características de Docker Compose depends_on y healthcheck en docker-compose.yml para que los proyectos se inicien en la secuencia correcta y que estén listos para atender solicitudes cuando sea necesario. Consulte Docker Compose: orden de inicio.

    services:
      webfrontend:
         image: ${DOCKER_REGISTRY-}webfrontend
         depends_on:
            mywebapi:
              condition: service_healthy
         build:
            context: .
              dockerfile: WebFrontEnd/Dockerfile
    
      mywebapi:
         image: ${DOCKER_REGISTRY-}mywebapi
         depends_on:
            redis:
              condition: service_started
         healthcheck:
            test: curl --fail http://mywebapi:8080/ || exit 1
            interval: 20s
            timeout: 20s
            retries: 5
         build:
            context: .
              dockerfile: MyWebAPI/Dockerfile
    
      redis:
         image: redis
    

    En este ejemplo, la comprobación de estado usa curl para comprobar que el servicio está listo para procesar las solicitudes. Si la imagen que está usando no tiene curl instalado, agregue líneas a la fase base del Dockerfile MyWebAPI para instalarlo. Este paso requiere privilegios elevados, pero puede restaurar los privilegios de usuario normales después de instalarlos como se muestra aquí (para las imágenes de Debian usadas en este ejemplo):

    USER root
    RUN apt-get update && apt-get install -y curl
    USER $APP_UID
    

    Nota

    Si usa una distribución de Linux, como Alpine, que no admite apt-get, pruebe RUN apk --no-cache add curl en su lugar.

    Estas características de Docker Compose requieren una configuración de propiedad en el archivo de proyecto de Docker Compose (.dcproj). Establezca la propiedad DependencyAwareStart en true:

    <PropertyGroup>
       <!-- existing properties -->
       <DependencyAwareStart>true</DependencyAwareStart>
    </PropertyGroup>
    

    Esta propiedad activa una forma diferente de iniciar los contenedores para la depuración que admite las características de dependencia del servicio.

    Con estos cambios, el servicio webfrontend no se iniciará hasta que mywebapi se inicie y controle correctamente una solicitud web.

  7. El primer proyecto al que se añade la orquestación de contenedores está configurado para iniciarse al ejecutar o depurar. Puede configurar la acción de inicio en las Propiedades del proyecto del proyecto de Docker Compose. En el nodo del proyecto de Docker Compose, haga clic con el botón derecho para abrir el menú contextual y luego seleccione Propiedades o use Alt+Entrar. Por ejemplo, puede cambiar la página que se carga mediante la personalización de la propiedad URL de servicio.

    Captura de pantalla de las propiedades del proyecto de Docker Compose.

  8. Presione F5. Esto es lo que se ve cuando se inicia:

    Captura de pantalla de la ejecución de la aplicación web.

  9. Puede supervisar los contenedores mediante la ventana Contenedores. Si no ve la ventana, use el cuadro de búsqueda, presione Ctrl+K, Ctrl+Oo presione Ctrl+Q. En Búsqueda de características, busque containers y elija Ver>Otras ventanas>Contenedores en la lista.

  10. Expanda el nodo Contenedores de soluciones y elija el nodo del proyecto de Docker Compose para ver los registros combinados en la pestaña Registros de esta ventana.

    Captura de pantalla que muestra la pestaña Registros en la ventana Contenedores.

    También puede seleccionar el nodo de un contenedor individual para ver registros, variables de entorno, el sistema de archivos y otros detalles.

Configuración de perfiles de inicio

  1. Esta solución tiene una instancia de Azure Cache for Redis, pero no es eficiente reconstruir el contenedor de caché cada vez que inicie una sesión de depuración. Para evitar esa situación, puede configurar un par de perfiles de inicio. Cree un perfil para iniciar Azure Cache for Redis. Cree un segundo perfil para iniciar los demás servicios. El segundo perfil puede usar el contenedor de caché que ya está en ejecución. En la barra de menús, puede usar la lista desplegable junto al botón iniciar para abrir un menú con opciones de depuración. Seleccione Administrar la configuración de inicio de Docker Compose.

    Captura de pantalla del elemento de menú Administrar la configuración de Compose de Depurar.

    Aparece el cuadro de diálogo Administrar la configuración de inicio de Docker Compose. Con este cuadro de diálogo, puede controlar qué subconjunto de servicios se inicia durante una sesión de depuración, que se inicia con o sin el depurador asociado, y el servicio de inicio y la dirección URL. Consulte Administración de perfiles de inicio para Docker Compose.

    Captura de pantalla del cuadro de diálogo Administrar configuración de inicio de Docker Compose.

    Elija Nuevo para crear un nuevo perfil y asígnelo el nombre Start Redis. A continuación, establezca el contenedor de Redis en Iniciar sin depurar, deje el otro conjunto en No iniciar y elija Guardar.

    Captura de pantalla en la que se muestra cómo crear el perfil de Redis que inicia solo el servicio Redis.

    A continuación, cree otro perfil Start My Services que no inicie Redis, pero inicia los otros dos servicios.

    Captura de pantalla que muestra la creación del perfil de servicios que inicia los demás servicios.

    (Opcional) Cree un tercer perfil Start All para iniciar todo. Puede elegir Iniciar sin depurar para Redis.

  2. Elija Iniciar Redis en la lista desplegable de la barra de herramientas principal de Visual Studio. El contenedor de Redis se compila e inicia sin depurar. Puede usar la ventana Contenedores para ver que se está ejecutando. A continuación, elija Iniciar mis servicios en la lista desplegable y presione F5 para iniciarlos. Ahora puede mantener el contenedor de caché en ejecución en muchas sesiones de depuración posteriores. Cada vez que use Iniciar mis servicios, esos servicios usan el mismo contenedor de caché.

Enhorabuena, está ejecutando una aplicación de Docker Compose con un perfil personalizado de Docker Compose.

Pasos siguientes

Examine las opciones para implementar los contenedores de en Azure.

Consulte también

Docker Compose

Herramientas de contenedor