Patrón Modern Web App para .NET
En este artículo se muestra cómo implementar el patrón de aplicación web moderna. El patrón Modern Web App define cómo modernizar las aplicaciones web en la nube e introducir una arquitectura orientada a servicios. El patrón Modern Web App proporciona instrucciones de configuración, código y arquitectura prescriptivas que se alinean con los principios del marco de trabajo de Azure Well-Architected y se basa en el patrón Reliable Web App.
¿Por qué utilizar el patrón Modern Web App?
El patrón Modern Web App puede ayudarle a optimizar las áreas de alta demanda de una aplicación web. Proporciona instrucciones detalladas para desacoplar estas áreas, lo que permite el escalado independiente para la optimización de costos. Este enfoque permite asignar recursos dedicados a componentes críticos, lo que mejora el rendimiento general. Desacoplar servicios separables puede mejorar la fiabilidad al evitar que las ralentizaciones en una parte de la aplicación afecten a otras. La desacoplamiento también le permite versionar componentes individuales de la aplicación de forma independiente.
Cómo implementar el patrón Modern Web App
Este artículo contiene instrucciones de arquitectura, código y configuración para implementar el patrón Modern Web App. Use los vínculos siguientes para ir a las instrucciones que necesita:
- Guía de arquitectura. Obtenga información sobre cómo modularizar los componentes de la aplicación web y seleccionar las soluciones de plataforma como servicio (PaaS) adecuadas.
- Guía de código. Implemente cuatro patrones de diseño para optimizar los componentes desacoplados: Strangler Fig, Queue-Based Load Leveling, Competing Consumers y Health Endpoint Monitoring.
- Guía de configuración. Configure la autenticación, la autorización, el escalado automático y la contenedorización para los componentes desacoplados.
Sugerencia
Existe una implementación de referencia (aplicación de ejemplo) del patrón Modern Web App. Representa el estado final de la implementación de Modern Web App. Se trata de una aplicación web de producción que incluye todas las actualizaciones de código, arquitectura y configuración comentadas en este artículo. implemente y utilice la implementación de referencia para guiar su implementación del patrón Modern Web App.
Guía de arquitectura
El patrón Modern Web App se basa en el patrón Reliable Web App. Requiere algunos componentes arquitectónicos adicionales para implementar. Necesita una cola de mensajes, una plataforma de contenedor, un almacén de datos de servicio desacoplado y un registro de contenedor. En el diagrama siguiente se muestra la arquitectura de línea base.
Para un objetivo de nivel de servicio (SLO) superior, puede añadir una segunda región a la arquitectura de su aplicación web. Si agrega una segunda región, debe configurar el equilibrador de carga para enrutar el tráfico a esa región para admitir una configuración activa-activa o activa-pasiva. Use una topología de red en estrella tipo hub-and-spoke para centralizar y compartir recursos, como un firewall de red. Acceda al repositorio de contenedores a través de la red virtual hub. Si tiene máquinas virtuales, agregue un host bastión a la red virtual del centro para administrarlas con seguridad mejorada. En el diagrama siguiente se muestra esta arquitectura.
Desacoplar la arquitectura
Para implementar el patrón Modern Web App, es necesario desacoplar la arquitectura de la aplicación web existente. La desacoplamiento de la arquitectura implica dividir una aplicación monolítica en servicios independientes más pequeños que son cada uno de los responsables de una característica o funcionalidad específica. Este proceso implica evaluar la aplicación web actual, modificar la arquitectura y, por último, extraer el código de la aplicación web a una plataforma de contenedores. El objetivo es identificar y extraer sistemáticamente los servicios de aplicación que se benefician de la mayor parte de la desacoplación. Para desacoplar su arquitectura, siga estas recomendaciones:
Identifique los límites del servicio. Aplique principios de diseño controlados por dominio para identificar contextos enlazados dentro de la aplicación monolítica. Cada contexto delimitado representa un límite lógico y puede ser un candidato para un servicio independiente. Los servicios que representan funciones empresariales distintas y tienen menos dependencias son buenos candidatos para el desacoplamiento.
Evalúe los beneficios del servicio. Céntrese en los servicios que más se benefician del escalado independiente. Desacoplamiento de estos servicios y conversión de tareas de procesamiento de operaciones sincrónicas a asincrónicas permite la administración de recursos más eficaz, admite implementaciones independientes y reduce el riesgo de afectar a otras partes de la aplicación durante las actualizaciones o los cambios. Por ejemplo, puede separar la desprotección del pedido del procesamiento de pedidos.
Evaluar la viabilidad técnica. Examine la arquitectura actual para identificar las limitaciones y dependencias técnicas que podrían afectar al proceso de desacoplamiento. Planificar cómo se administran y comparten los datos entre servicios. Los servicios desacoplados deben administrar sus propios datos y minimizar el acceso directo a la base de datos a través de los límites del servicio.
Implementar servicios de Azure. Seleccione e implemente los servicios de Azure que necesita para admitir el servicio de aplicaciones web que pretende extraer. Para obtener instrucciones, consulte Selección de los servicios de Azure adecuados.
Desacoplar los servicios de aplicaciones web. Defina interfaces y API claras para permitir que los servicios de aplicaciones web recién extraídos interactúen con otras partes del sistema. Diseñe una estrategia de administración de datos que permita a cada servicio administrar sus propios datos a la vez que garantiza la coherencia y la integridad. Para obtener estrategias de implementación específicas y patrones de diseño que se usarán durante este proceso de extracción, consulte la sección Guía de código de este artículo.
Utilice almacenamiento independiente para los servicios desacoplados. Cada servicio desacoplado debe tener su propio almacén de datos aislado para facilitar el control de versiones, la implementación y la escalabilidad independientes y mantener la integridad de los datos. Por ejemplo, la implementación de referencia separa el servicio de representación de vales de la API web y elimina la necesidad de que el servicio acceda a la base de datos de la API. En su lugar, el servicio pasa la dirección URL en la que las imágenes de vale se generaron de nuevo a la API web a través de un mensaje de Azure Service Bus y la API conserva la ruta de acceso a su base de datos.
Implemente pipelines de implementación independientes para cada servicio desacoplado. Las pipelines de implementación separadas permiten que cada servicio se actualice a su propio ritmo. Si distintos equipos u organizaciones de su empresa son propietarios de distintos servicios, disponer de pipelines de implementación independientes permite a cada equipo controlar sus propias implementaciones. Use herramientas de integración continua y entrega continua (CI/CD), como Jenkins, Acciones de GitHub o Azure Pipelines para configurar estas canalizaciones.
Revise los controles de seguridad. Asegúrese de que sus controles de seguridad están actualizados para tener en cuenta la nueva arquitectura, incluidas las reglas de firewall y los controles de acceso.
Seleccione los servicios Azure adecuados
Para cada servicio Azure de su arquitectura, consulte la guía de servicios Azure correspondiente en el Marco de trabajo bien diseñado. Para el patrón Modern Web App, necesita un sistema de mensajería que admita la mensajería asíncrona, una plataforma de aplicaciones que admita la contenedorización y un repositorio de imágenes de contenedor.
Elija una cola de mensajes. Una cola de mensajes es un componente importante de las arquitecturas orientadas a servicios. Desacopla los emisores y receptores de mensajes para permitir la mensajería asíncrona. Utilice la guía para elegir un servicio de mensajería de Azure para elegir un sistema de mensajería de Azure que satisfaga sus necesidades de diseño. Azure tiene tres servicios de mensajería: Azure Event Grid, Azure Event Hubs y Azure Service Bus. Comience con Service Bus como opción predeterminada y use las otras dos opciones si Service Bus no satisface sus necesidades.
Servicio Caso de uso Bus de Servicio Elija Service Bus para la entrega confiable, ordenada y posiblemente transaccional de mensajes de alto valor en aplicaciones empresariales. Cuadrícula de Eventos Elija Event Grid cuando necesite controlar un gran número de eventos discretos de forma eficaz. Event Grid es escalable para aplicaciones controladas por eventos en las que es necesario enrutar muchos eventos pequeños independientes (como cambios de estado de recursos) a los suscriptores en un modelo de publicación-suscripción de baja latencia. Event Hubs Elija Event Hubs para la ingesta masiva de datos de alto rendimiento, como telemetría, registros o análisis en tiempo real. Event Hubs está optimizado para escenarios de streaming en los que los datos masivos deben ingerirse y procesarse continuamente. Implemente un servicio de contenedor. Para los componentes de la aplicación que desea incluir en contenedores, necesita una plataforma de aplicaciones que admita contenedores. La guía Elegir un servicio de contenedor de Azure puede ayudarle a tomar la decisión. Azure tiene tres servicios de contenedor principales: Azure Container Apps, Azure Kubernetes Service (AKS) y App de Azure Service. Comience con Container Apps como opción predeterminada y use las otras dos opciones si Container Apps no satisface sus necesidades.
Servicio Caso de uso Aplicaciones de contenedor Elija Container Apps si necesita una plataforma sin servidor que escale y administre automáticamente los contenedores en aplicaciones controladas por eventos. AKS (Azure Kubernetes Service) Elija AKS si necesita un control detallado de las configuraciones de Kubernetes y funciones avanzadas de escalado, redes y seguridad. Aplicaciones web para contenedores Elija Aplicación web para contenedores en App Service para obtener la experiencia paaS más sencilla. Implemente un repositorio de contenedores. Al usar cualquier servicio de proceso basado en contenedores, debe tener un repositorio para almacenar las imágenes de contenedor. Puede utilizar un registro de contenedores público como Docker Hub o un registro administrado como Azure Container Registry. La introducción a los registros de contenedor en la guía de Azure puede ayudarle a tomar la decisión.
Guía de código
Para desacoplar y extraer con éxito un servicio independiente, debe actualizar el código de su aplicación web con los siguientes patrones de diseño: el patrón Strangler Fig, el patrón Queue-Based Load Leveling, el patrón Competing Consumers, el patrón Health Endpoint Monitoring y el patrón Retry. Los roles de estos patrones se ilustran aquí:
Patrón Strangler Fig: El patrón Strangler Fig migra de forma incremental la funcionalidad de una aplicación monolítica al servicio desacoplado. Implemente este patrón en la aplicación web principal para migrar gradualmente la funcionalidad a servicios independientes dirigiendo el tráfico en función de los puntos de conexión.
Patrón de nivelación de carga basado en cola: el patrón de nivelación de carga basado en cola administra el flujo de mensajes entre el productor y el consumidor mediante una cola como búfer. Implemente este patrón en el código base que genera mensajes para la cola. A continuación, el servicio desacoplado consume estos mensajes de la cola de forma asincrónica.
Patrón de consumidores en competencia: El patrón Consumidores en competencia permite que varias instancias del servicio desacoplado lean independientemente de la misma cola de mensajes y compitan por procesar los mensajes. Implemente este patrón en el servicio desacoplado para distribuir tareas entre varias instancias.
Patrón Health Endpoint Monitoring: El patrón Health Endpoint Monitoring expone puntos de conexión para supervisar el estado y la salud de diferentes partes de la aplicación web. (4a) Implementar este patrón en la aplicación web principal. (4b) Impleméntalo también en el servicio desacoplado para controlar el estado de los puntos de conexión.
Patrón de reintento: El patrón de reintento administra los fallos transitorios reintentando las operaciones que pueden fallar de forma intermitente. (5a) Implemente este patrón en todas las llamadas salientes a otros servicios de Azure de la aplicación web principal, como las llamadas a la cola de mensajes y los puntos de conexión privados. (5b) Implementar también este patrón en el servicio desacoplado para administrar fallos transitorios en las llamadas a los puntos de conexión privados.
Cada patrón de diseño proporciona ventajas que se alinean con uno o varios pilares del marco de Well-Architected. Consulte la siguiente tabla para obtener información.
Modelo de diseño | Lugar de implementación | Fiabilidad (RE) | Seguridad (SE) | Optimización de costes (OC) | Excelencia operativa (OE) | Eficiencia del rendimiento (PE) | Apoyo a principios de marco bien diseñados |
---|---|---|---|---|---|---|---|
Patrón Fig Strangler | Aplicación web principal | ✔ | ✔ | ✔ |
RE:08 CO:07 CO:08 OE:06 OE:11 |
||
Patrón Queue-based Load Leveling | Aplicación web principal (productor de mensajes) | ✔ | ✔ | ✔ |
RE:07 RE:07 CO:12 PE:05 |
||
Patrón de consumidores simultáneos | Servicio desacoplado | ✔ | ✔ | ✔ |
RE:05 RE:07 CO:05 CO:07 PE:05 PE:07 |
||
Patrón de supervisión de puntos de conexión de mantenimiento | Aplicación web principal y servicio desacoplado | ✔ | ✔ | ✔ |
RE:07 OBJETO 10 OE:07 PE:05 |
||
Patrón de reintento | Aplicación web principal y servicio desacoplado | ✔ | RE:07 |
Implementación del patrón Strangler Fig
Use el patrón Strangler Fig para migrar gradualmente la funcionalidad de la base de código monolítica a nuevos servicios independientes. Extraiga nuevos servicios de la base de código monolítica existente y modernice lentamente las partes críticas de la aplicación web. Para aplicar el patrón Strangler Fig, siga estas recomendaciones:
Configure una capa de enrutamiento. En la base de código de aplicación web monolítica, implemente una capa de enrutamiento que dirija el tráfico en función de los puntos de conexión. Utilice lógica de enrutamiento personalizada según sea necesario para administrar reglas de negocio específicas para dirigir el tráfico. Por ejemplo, si tiene un punto de conexión de
/users
en la aplicación monolítica y mueve esa funcionalidad al servicio desacoplado, la capa de enrutamiento dirige todas las solicitudes a/users
al nuevo servicio.Administrar el lanzamiento de características. Use bibliotecas de administración de características de .NET para implementar marcas de características y lanzamiento preconfigurado para implementar gradualmente los servicios desacoplados. El enrutamiento de la aplicación monolítica existente debería controlar cuántas solicitudes reciben los servicios desacoplados. Comience con un pequeño porcentaje de solicitudes y aumente el uso a lo largo del tiempo a medida que obtenga confianza en la estabilidad y el rendimiento del nuevo servicio. Por ejemplo, la implementación de referencia extrae la funcionalidad de representación de vales en un servicio independiente, que se puede introducir gradualmente para controlar una parte mayor de las solicitudes de representación de vales. A medida que el nuevo servicio demuestra su confiabilidad y rendimiento, finalmente puede asumir toda la funcionalidad de representación de vales del monolito, completando la transición.
Utilizar un servicio de fachada (si es necesario). Un servicio de fachada es útil cuando una única solicitud debe interactuar con varios servicios o cuando se desea ocultar al cliente la complejidad del sistema subyacente. Sin embargo, si el servicio desacoplado no tiene ninguna API de acceso público, es posible que no sea necesario un servicio de fachada. En la base de código de la aplicación web monolítica, implemente un servicio de fachada para enrutar las solicitudes al back-end adecuado (monolito o microservicio). En el nuevo servicio desacoplado, asegúrese de que el nuevo servicio pueda controlar las solicitudes de forma independiente cuando se accede a través de la fachada.
Implementar el patrón de Queue-Based Load Leveling
Implemente el patrón de nivelación de carga basado en cola en la parte del productor del servicio desacoplado para controlar de forma asincrónica las tareas que no necesitan respuestas inmediatas. Este patrón mejora la capacidad de respuesta y la escalabilidad general del sistema mediante el uso de una cola para administrar la distribución de la carga de trabajo. Permite que el servicio desacoplado procese las solicitudes a un ritmo constante. Para implementar este patrón de forma eficaz, siga estas recomendaciones:
Use colas de mensajes no bloqueantes. Asegúrese de que el proceso que envía mensajes a la cola no bloquea otros procesos mientras espera a que el servicio desacoplado administre los mensajes de la cola. Si el proceso requiere el resultado de la operación del servicio desacoplado, tenga una forma alternativa de manejar la situación mientras espera a que se complete la operación en cola. Por ejemplo, la implementación de referencia usa Service Bus y la
await
palabra clave conmessageSender.PublishAsync()
para publicar mensajes de forma asincrónica en la cola sin bloquear el subproceso que ejecuta este código:// Asynchronously publish a message without blocking the calling thread. await messageSender.PublishAsync(new TicketRenderRequestMessage(Guid.NewGuid(), ticket, null, DateTime.Now), CancellationToken.None);
Este enfoque garantiza que la aplicación principal siga respondiendo y pueda administrar otras tareas simultáneamente, mientras que el servicio desacoplado procesa las solicitudes en cola a un ritmo manejable.
Implementar el reintento y la eliminación de mensajes. Implemente un mecanismo para reintentar el procesamiento de mensajes en cola que no puedan procesarse con éxito. Si los fallos persisten, estos mensajes deben eliminarse de la cola. Por ejemplo, Service Bus tiene características integradas de cola de reintentos y mensajes fallidos.
Configure el procesamiento de mensajes idempotente. La lógica que procesa los mensajes de la cola debe ser idempotente para controlar los casos en los que un mensaje puede procesarse más de una vez. Por ejemplo, la implementación de referencia usa
ServiceBusClient.CreateProcessor
conAutoCompleteMessages = true
yReceiveMode = ServiceBusReceiveMode.PeekLock
para asegurarse de que los mensajes solo se procesan una vez y se pueden volver a procesar en caso de error. En el código siguiente se muestra esta lógica.// Create a processor for idempotent message processing. var processor = serviceBusClient.CreateProcessor(path, new ServiceBusProcessorOptions { // Allow the messages to be auto-completed // if processing finishes without failure. AutoCompleteMessages = true, // PeekLock mode provides reliability in that unsettled messages // will be redelivered on failure. ReceiveMode = ServiceBusReceiveMode.PeekLock, // Containerized processors can scale at the container level // and need not scale via the processor options. MaxConcurrentCalls = 1, PrefetchCount = 0 });
Gestionar los cambios en la experiencia. El procesamiento asíncrono puede hacer que las tareas no se completen inmediatamente. Los usuarios deben saber cuándo se está procesando su tarea para establecer expectativas correctas y evitar confusiones. Utilice indicaciones visuales o mensajes para indicar que una tarea está en curso. Ofrezca a los usuarios la opción de recibir notificaciones cuando su tarea esté terminada, como un correo electrónico o una notificación push.
Implementar el patrón de Competing Consumers
Implemente el patrón Competing Consumers en los servicios desacoplados para administrar las tareas entrantes desde la cola de mensajes. Este patrón consiste en distribuir las tareas entre varias instancias de servicios desacoplados. Estos servicios procesan mensajes de la cola, lo que mejora el equilibrio de carga y aumenta la capacidad del sistema para controlar las solicitudes simultáneas. El patrón Competing Consumers es eficaz cuando:
- La secuencia de procesamiento de los mensajes no es crucial.
- La cola no se ve afectada por mensajes con formato incorrecto.
- La operación de procesamiento es idempotente, lo que significa que puede aplicarse varias veces sin cambiar el resultado más allá de la aplicación inicial.
Para implementar el patrón Competing Consumers, siga estas recomendaciones:
Controlar mensajes simultáneos. Cuando el sistema recibe mensajes de una cola, asegúrese de que el sistema está diseñado para controlar varios mensajes simultáneamente. Establezca el número máximo de llamadas simultáneas en 1 para que cada mensaje sea administrado por un consumidor independiente.
Desactive la precarga. Deshabilite la captura previa de mensajes para que los consumidores capturen los mensajes solo cuando estén listos.
Use modos de procesamiento de mensajes fiables. Use un modo de procesamiento fiable, como PeekLock (o su equivalente), que reintente automáticamente los mensajes que no se procesen correctamente. Este modo proporciona más confiabilidad que los métodos de eliminación inicial. Si un trabajador no puede administrar un mensaje, otro debe ser capaz de procesarlo sin errores, incluso si el mensaje se procesa varias veces.
Implementar tratamiento de errores. Enrutar mensajes con formato incorrecto o no procesados a una cola de mensajes fallidos independiente. Este diseño evita el procesamiento repetitivo. Por ejemplo, puede detectar excepciones durante el procesamiento del mensaje y mover el mensaje problemático a la cola separada.
Controle los mensajes desordenados. Diseñe consumidores que procesen mensajes que llegan fuera de secuencia. Si tiene varios consumidores paralelos, podrían procesar mensajes desordenados.
Escala basada en la longitud de la cola. Los servicios que consumen mensajes de una cola deben considerar el escalado automático en función de la longitud de la cola o usar criterios de escalado adicionales para mejorar los picos de los mensajes entrantes.
Use una cola de respuesta de mensajes. Si el sistema requiere notificaciones para el procesamiento posterior al mensaje, configure una cola de respuesta o respuesta dedicada. Esta configuración divide la mensajería operativa de los procesos de notificación.
Usar servicios sin estado. Considere la posibilidad de utilizar servicios sin estado para procesar las solicitudes de una cola. Estos servicios permiten un escalado sencillo y un uso eficaz de los recursos.
Configure el registro. Integre el registro y el control de excepciones específicos en el flujo de trabajo de procesamiento de mensajes. Céntrese en capturar errores de serialización y dirigir estos mensajes problemáticos a un mecanismo de mensajes fallidos. Estos registros proporcionan información valiosa para la solución de problemas.
Por ejemplo, la implementación de referencia usa el patrón De consumidores competidores en un servicio sin estado que se ejecuta en Container Apps para procesar solicitudes de representación de vales desde una cola de Service Bus. Configura un procesador de cola con:
-
AutoCompleteMessages
. Completa automáticamente los mensajes si se procesan sin errores. -
ReceiveMode
. Usa el modo PeekLock y los mensajes de redelivers si no se resuelven. -
MaxConcurrentCalls
. Establezca en 1 para controlar un mensaje a la vez. -
PrefetchCount
. Establézcalo en 0 para evitar la captura previa de mensajes.
El procesador registra los detalles de procesamiento de mensajes, lo que puede ayudar con la solución de problemas y la supervisión. Captura errores de deserialización y enruta mensajes no válidos a una cola de mensajes fallidos para evitar el procesamiento repetitivo de mensajes defectuosos. El servicio se escala en el nivel de contenedor, lo que permite un control eficaz de los picos de mensajes en función de la longitud de la cola.
// Create a processor for the given queue that will process
// incoming messages.
var processor = serviceBusClient.CreateProcessor(path, new ServiceBusProcessorOptions
{
// Allow the messages to be auto-completed
// if processing finishes without failure.
AutoCompleteMessages = true,
// PeekLock mode provides reliability in that unsettled messages
// are redelivered on failure.
ReceiveMode = ServiceBusReceiveMode.PeekLock,
// Containerized processors can scale at the container level
// and need not scale via the processor options.
MaxConcurrentCalls = 1,
PrefetchCount = 0
});
// Called for each message received by the processor.
processor.ProcessMessageAsync += async args =>
{
logger.LogInformation("Processing message {MessageId} from {ServiceBusNamespace}/{Path}", args.Message.MessageId, args.FullyQualifiedNamespace, args.EntityPath);
// Unhandled exceptions in the handler will be caught by
// the processor and result in abandoning and dead-lettering the message.
try
{
var message = args.Message.Body.ToObjectFromJson<T>();
await messageHandler(message, args.CancellationToken);
logger.LogInformation("Successfully processed message {MessageId} from {ServiceBusNamespace}/{Path}",args.Message.MessageId, args.FullyQualifiedNamespace, args.EntityPath);
}
catch (JsonException)
{
logger.LogError("Invalid message body; could not be deserialized to {Type}", typeof(T));
await args.DeadLetterMessageAsync(args.Message, $"Invalid message body; could not be deserialized to {typeof(T)}",cancellationToken: args.CancellationToken);
}
};
Implantación del patrón Health Endpoint Monitoring
Implemente el patrón Health Endpoint Monitoring en el código de la aplicación principal y en el código del servicio desacoplado para realizar un seguimiento del estado de los puntos de conexión de la aplicación. Los orquestadores como AKS o Container Apps pueden sondear estos puntos de conexión para comprobar el estado del servicio y reiniciar las instancias incorrectas. Las aplicaciones ASP.NET Core pueden añadir middleware de comprobación de salud dedicado para servir eficientemente los datos de salud del punto de conexión y las dependencias clave. Para implementar el patrón Health Endpoint Monitoring, siga estas recomendaciones:
Implemente comprobaciones de salud. Utilice el middleware de comprobaciones de salud de ASP.NET Core para proporcionar puntos de conexión de comprobación de salud.
Valide las dependencias. Asegúrese de que sus comprobaciones de estado validan la disponibilidad de las dependencias clave, como la base de datos, el almacenamiento y el sistema de mensajería. El paquete que no es de Microsoft AspNetCore.Diagnostics.HealthChecks puede implementar comprobaciones de dependencias de comprobación de estado para muchas dependencias comunes de la aplicación.
Por ejemplo, la implementación de referencia usa ASP.NET middleware de comprobación de estado principal para exponer puntos de conexión de comprobación de estado. Usa el
AddHealthChecks()
método en elbuilder.Services
objeto . El código valida la disponibilidad de las dependencias clave, Azure Blob Storage y la cola de Service Bus mediante losAddAzureBlobStorage()
métodos yAddAzureServiceBusQueue()
, que forman parte delAspNetCore.Diagnostics.HealthChecks
paquete. Container Apps permite configurar sondeos de estado que se supervisan para medir si las aplicaciones son correctas o necesitan reciclaje.// Add health checks, including health checks for Azure services // that are used by this service. // The Blob Storage and Service Bus health checks are provided by // AspNetCore.Diagnostics.HealthChecks // (a popular open source project) rather than by Microsoft. // https://github.com/Xabaril/AspNetCore.Diagnostics.HealthChecks builder.Services.AddHealthChecks() .AddAzureBlobStorage(options => { // AddAzureBlobStorage will use the BlobServiceClient registered in DI. // We just need to specify the container name. options.ContainerName = builder.Configuration.GetRequiredConfigurationValue("App:StorageAccount:Container"); }) .AddAzureServiceBusQueue( builder.Configuration.GetRequiredConfigurationValue("App:ServiceBus:Host"), builder.Configuration.GetRequiredConfigurationValue("App:ServiceBus:RenderRequestQueueName"), azureCredentials); // Further app configuration omitted for brevity. app.MapHealthChecks("/health");
Configuración de recursos de Azure. Configure los recursos de Azure para usar las direcciones URL de comprobación de estado de la aplicación para confirmar la vida y la preparación. Por ejemplo, la implementación de referencia utiliza Bicep para configurar las URL de comprobación de estado a fin de confirmar la vitalidad y disponibilidad del recurso Azure. Un sondeo de ejecución alcanza el
/health
punto de conexión cada 10 segundos después de un retraso inicial de 2 segundos.probes: [ { type: 'liveness' httpGet: { path: '/health' port: 8080 } initialDelaySeconds: 2 periodSeconds: 10 } ]
Implementar el patrón Retry
El patrón Retry permite a las aplicaciones recuperarse de fallos transitorios. El patrón Retry es fundamental para el patrón Reliable Web App, por lo que su aplicación web ya debería usar el patrón Retry. Aplique el patrón Retry a las solicitudes a los sistemas de mensajería y las solicitudes emitidas por los servicios desacoplados que extraiga de la aplicación web. Para aplicar el patrón Retry, siga estas recomendaciones:
Configure las opciones de reintento. Al integrar con una cola de mensajes, asegúrese de configurar el cliente responsable de las interacciones con la cola con la configuración de reintento adecuada. Especifique parámetros como el número máximo de reintentos, el retraso entre los reintentos y el retraso máximo.
Utilice el retardo exponencial. Implemente una estrategia de retroceso exponencial para los reintentos. Esta estrategia implica aumentar el tiempo entre cada reintento exponencialmente, lo que ayuda a reducir la carga en el sistema durante períodos de altas tasas de error.
Use la funcionalidad de reintento del SDK. Para los servicios que tienen SDK especializados, como Service Bus o Blob Storage, use los mecanismos de reintento integrados. Los mecanismos de reintento integrados están optimizados para los casos de uso típicos del servicio y pueden controlar reintentos de forma más eficaz con menos configuración necesaria. Por ejemplo, la implementación de referencia usa la funcionalidad de reintento integrada del SDK de Service Bus (
ServiceBusClient
yServiceBusRetryOptions
). ElServiceBusRetryOptions
objeto captura la configuración de para configurar opciones deMessageBusOptions
reintento comoMaxRetries
,Delay
,MaxDelay
yTryTimeout
.// ServiceBusClient is thread-safe and can be reused for the lifetime // of the application. services.AddSingleton(sp => { var options = sp.GetRequiredService<IOptions<MessageBusOptions>>().Value; var clientOptions = new ServiceBusClientOptions { RetryOptions = new ServiceBusRetryOptions { Mode = ServiceBusRetryMode.Exponential, MaxRetries = options.MaxRetries, Delay = TimeSpan.FromSeconds(options.BaseDelaySecondsBetweenRetries), MaxDelay = TimeSpan.FromSeconds(options.MaxDelaySeconds), TryTimeout = TimeSpan.FromSeconds(options.TryTimeoutSeconds) } }; return new ServiceBusClient(options.Host, azureCredential ?? new DefaultAzureCredential(), clientOptions); });
Adoptar bibliotecas de resistencia estándar para clientes HTTP. Para las comunicaciones HTTP, integre una biblioteca de resistencia estándar como Polly o
Microsoft.Extensions.Http.Resilience
. Estas bibliotecas ofrecen mecanismos de reintento completos que son cruciales para administrar las comunicaciones con servicios web externos.Gestione el bloqueo de mensajes. En el caso de los sistemas basados en mensajes, implemente estrategias de control de mensajes que admitan reintentos sin pérdida de datos, como el uso de modos de "inspección y bloqueo" cuando estén disponibles. Asegúrese de que los mensajes fallidos se reintentan de forma efectiva y se mueven a una cola de espera después de repetidos fallos.
Implantar el rastreo distribuido
A medida que las aplicaciones se orientan más a los servicios y sus componentes se desacoplan, es crucial supervisar el flujo de ejecución entre los servicios. El patrón Modern Web App usa Application Insights y Azure Monitor para obtener visibilidad sobre el estado y el rendimiento de las aplicaciones a través de las API de OpenTelemetry, que admiten el seguimiento distribuido.
El seguimiento distribuido rastrea una solicitud de usuario a medida que atraviesa múltiples servicios. Cuando se recibe una solicitud, se etiqueta con un identificador de seguimiento, que se pasa a otros componentes a través de encabezados HTTP y propiedades de Service Bus durante la invocación de dependencias. Los seguimientos y los registros incluyen entonces tanto el identificador de seguimiento como un identificador de actividad (o identificador span), que corresponde al componente específico y a su actividad principal. Las herramientas de supervisión como Application Insights usan esta información para mostrar un árbol de actividades y registros en distintos servicios, lo que es fundamental para supervisar aplicaciones distribuidas.
Instale las bibliotecas de OpenTelemetry. Use bibliotecas de instrumentación para habilitar el seguimiento y las métricas de componentes comunes. Añada instrumentación personalizada con
System.Diagnostics.ActivitySource
ySystem.Diagnostics.Activity
si es necesario. Use bibliotecas exportadoras para escuchar diagnósticos de OpenTelemetry y registrarlos en almacenes persistentes. Use exportadores existentes o cree su propio medianteSystem.Diagnostics.ActivityListener
.Configure OpenTelemetry. Utilice la distribución Azure Monitor de OpenTelemetry (
Azure.Monitor.OpenTelemetry.AspNetCore
). Asegúrese de que exporta diagnósticos a Application Insights e incluye instrumentación integrada para métricas comunes, seguimientos, registros y excepciones desde el entorno de ejecución de .NET y ASP.NET Core. Incluya otros paquetes de instrumentación de OpenTelemetry para los clientes de SQL, Redis y Azure SDK.Supervise y analice. Después de configurar el seguimiento, asegúrese de que los registros, seguimientos, métricas y excepciones se capturan y envían a Application Insights. Compruebe que se incluyen los identificadores de seguimiento, actividad y actividad primaria. Estos identificadores permiten a Application Insights proporcionar visibilidad de seguimiento de un extremo a otro en los límites de HTTP y Service Bus. Use esta configuración para supervisar y analizar las actividades de la aplicación en todos los servicios.
El ejemplo de Modern Web App utiliza la distribución Azure Monitor de OpenTelemetry (Azure.Monitor.OpenTelemetry.AspNetCore
). Se usan más paquetes de instrumentación para clientes sql, Redis y Azure SDK. OpenTelemetry está configurado en el servicio de representación de vales de ejemplo de Aplicación web moderna como este:
builder.Logging.AddOpenTelemetry(o =>
{
o.IncludeFormattedMessage = true;
o.IncludeScopes = true;
});
builder.Services.AddOpenTelemetry()
.UseAzureMonitor(o => o.ConnectionString = appInsightsConnectionString)
.WithMetrics(metrics =>
{
metrics.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddRuntimeInstrumentation();
})
.WithTracing(tracing =>
{
tracing.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddSource("Azure.*");
});
El builder.Logging.AddOpenTelemetry
método enruta todo el registro a través de OpenTelemetry para garantizar un seguimiento y un registro coherentes en toda la aplicación. Dado que los servicios OpenTelemetry se registran con builder.Services.AddOpenTelemetry
, la aplicación está configurada para recopilar y exportar diagnósticos, que luego se envían a Application Insights a través UseAzureMonitor
de . Además, la instrumentación de cliente para componentes como Service Bus y clientes HTTP se configura mediante WithMetrics
y WithTracing
, lo que permite la recopilación automática de métricas y seguimientos sin necesidad de cambios en el uso del cliente existente. Solo se requiere una actualización de la configuración.
Guía de configuración
Las siguientes secciones proporcionan orientación sobre la implementación de las actualizaciones de configuración. Cada sección se ajusta a uno o varios pilares del marco de trabajo bien diseñado.
Configuración | Fiabilidad (RE) | Seguridad (SE) | Optimización de costes (OC) | Excelencia operativa (OE) | Eficiencia del rendimiento (PE) | Apoyo a principios de marco bien diseñados |
---|---|---|---|---|---|---|
Configuración de la autenticación y la autorización | ✔ | ✔ |
SE:05 OE:10 |
|||
Implementar autoescalado independiente | ✔ | ✔ | ✔ |
RE:06 CO:12 PE:05 |
||
Implementación de servicios de contenedorización | ✔ | ✔ |
CO:13 PE:09 PE:03 |
Configuración de la autenticación y la autorización
Para configurar la autenticación y autorización en los nuevos servicios de Azure (identidades de carga de trabajo) que agregue a la aplicación web, siga estas recomendaciones:
Uso de identidades administradas para cada nuevo servicio. Cada servicio independiente debe tener su propia identidad y utilizar identidades administradas para la autenticación de servicio a servicio. Las identidades administradas eliminan la necesidad de administrar credenciales en el código y reducen el riesgo de fuga de credenciales. Le ayudan a evitar poner información sensible como cadenas de conexión en su código o archivos de configuración.
Conceda los mínimos privilegios a cada nuevo servicio. Asigne solo los permisos necesarios a cada nueva identidad de servicio. Por ejemplo, si una identidad solo necesita insertar en un registro de contenedor, no conceda permisos de extracción. Revise estos permisos con regularidad y ajustelos según sea necesario. Use diferentes identidades para diferentes funciones, como la implementación y la aplicación. Esto limita el daño potencial si una identidad se ve comprometida.
Adopte la infraestructura como código (IaC). Use herramientas de Bicep o iaC similares para definir y administrar los recursos en la nube. IaC garantiza una aplicación coherente de las configuraciones de seguridad en sus implantaciones y le permite controlar las versiones de la configuración de su infraestructura.
Para configurar la autenticación y autorización de usuarios (identidades de usuario), siga estas recomendaciones:
Concede los mínimos privilegios a los usuarios. Al igual que con los servicios, asegúrese de que a los usuarios solo se les conceden los permisos que necesitan para realizar sus tareas. Revise y ajuste periódicamente estos permisos.
Realice auditorías de seguridad periódicas. Revise y audite periódicamente su configuración de seguridad. Busque cualquier error de configuración o permisos innecesarios y rectifíquelos inmediatamente.
La implementación de referencia utiliza IaC para asignar identidades administradas a servicios añadidos y roles específicos a cada identidad. Define los roles y el acceso a permisos para la implementación (containerRegistryPushRoleId
), el propietario de la aplicación (containerRegistryPushRoleId
) y la aplicación Container Apps (containerRegistryPullRoleId
). En el ejemplo siguiente se muestra el código.
roleAssignments: \[
{
principalId: deploymentSettings.principalId
principalType: deploymentSettings.principalType
roleDefinitionIdOrName: containerRegistryPushRoleId
}
{
principalId: ownerManagedIdentity.outputs.principal_id
principalType: 'ServicePrincipal'
roleDefinitionIdOrName: containerRegistryPushRoleId
}
{
principalId: appManagedIdentity.outputs.principal_id
principalType: 'ServicePrincipal'
roleDefinitionIdOrName: containerRegistryPullRoleId
}
\]
La implementación de referencia asigna la identidad administrada como la nueva identidad de Container Apps en la implementación:
module renderingServiceContainerApp 'br/public:avm/res/app/container-app:0.1.0' = {
name: 'application-rendering-service-container-app'
scope: resourceGroup()
params: {
// Other parameters omitted for brevity.
managedIdentities: {
userAssignedResourceIds: [
managedIdentity.id
]
}
}
}
Configurar el autoescalado independiente
El patrón Modern Web App comienza a romper la arquitectura monolítica e introduce el desacoplamiento de servicios. Cuando desacopla una arquitectura de aplicación web, puede escalar los servicios desacoplados de forma independiente. Al escalar los servicios Azure para dar soporte a un servicio de aplicación web independiente, en lugar de a una aplicación web completa, se optimizan los costes de escalado al tiempo que se satisfacen las demandas. Para autoescalar contenedores, siga estas recomendaciones:
Usar servicios sin estado. Asegúrese de que los servicios no tienen estado. Si la aplicación .NET contiene el estado de sesión en proceso, externalícelo a una caché distribuida como Redis o una base de datos como SQL Server.
Configure reglas de autoescalado. Utilice las configuraciones de autoescalado que proporcionen el control más rentable sobre sus servicios. En el caso de los servicios en contenedores, el escalado basado en eventos, como Kubernetes Event-Driven Autoscaler (KEDA), a menudo proporciona un control pormenorizado que permite escalar en función de las métricas de eventos. Container Apps y AKS admiten KEDA. En el caso de los servicios que no admiten KEDA, como App Service, use las características de escalado automático que proporciona la plataforma. Estas características suelen incluir el escalado basado en reglas basadas en métricas o el tráfico HTTP.
Configure réplicas mínimas. Para evitar un arranque en frío, configure los ajustes de autoescalado para mantener un mínimo de una réplica. Un inicio en frío se produce cuando se inicializa un servicio desde un estado detenido, que a menudo crea una respuesta retrasada. Si minimizar los costos es una prioridad y puede tolerar retrasos en el arranque en frío, establezca el número mínimo de réplicas en 0 al configurar el escalado automático.
Configure un periodo de enfriamiento. Aplique un periodo de enfriamiento adecuado para introducir un retardo entre los eventos de escalado. El objetivo es evitar actividades de escalado excesivas desencadenadas por picos de carga temporales.
Configure el escalado basado en colas. Si la aplicación usa una cola de mensajes como Service Bus, configure las opciones de escalado automático para escalar según la longitud de la cola con mensajes de solicitud. El escalador tiene como objetivo mantener una réplica del servicio para cada N mensajes de la cola (redondeado hacia arriba).
Por ejemplo, la implementación de referencia usa el escalador KEDA de Service Bus para escalar la aplicación contenedora en función de la longitud de la cola. Escala service-bus-queue-length-rule
el servicio en función de la longitud de una cola de Service Bus especificada. El parámetro messageCount
se establece en 10, por lo que el escalador tiene una réplica del servicio por cada 10 mensajes en la cola. Los parámetros scaleMaxReplicas
y scaleMinReplicas
establecen el número máximo y mínimo de réplicas del servicio. El queue-connection-string
secreto, que contiene el cadena de conexión de la cola de Service Bus, se recupera de Azure Key Vault. Este secreto se utiliza para autenticar el escalador en el Bus de Servicios.
scaleRules: [
{
name: 'service-bus-queue-length-rule'
custom: {
type: 'azure-servicebus'
metadata: {
messageCount: '10'
namespace: renderRequestServiceBusNamespace
queueName: renderRequestServiceBusQueueName
}
auth: [
{
secretRef: 'render-request-queue-connection-string'
triggerParameter: 'connection'
}
]
}
}
]
scaleMaxReplicas: 5
scaleMinReplicas: 0
Implementación de servicios de contenedorización
En una implementación en contenedores, todas las dependencias requeridas por la aplicación se encapsulan en una imagen ligera que se puede implementar de forma confiable en una amplia gama de hosts. Para desplegar en contenedores, siga estas recomendaciones:
Identifique los límites del dominio. Empiece por identificar los límites del dominio en la aplicación monolítica. Esto le ayuda a determinar qué partes de la aplicación se pueden extraer en servicios independientes.
Cree imágenes de Docker. Al crear imágenes de Docker para los servicios .NET, use imágenes base chiseled. Estas imágenes solo contienen el conjunto mínimo de paquetes necesarios para que .NET se ejecute, lo que minimiza tanto el tamaño del paquete como el área expuesta a ataques.
Utilice Dockerfiles multietapa. Implemente Dockerfiles multietapa para separar los activos en tiempo de compilación de la imagen del contenedor en tiempo de ejecución. El uso de este tipo de archivo ayuda a mantener pequeñas y seguras las imágenes de producción.
Ejecute como usuario que no sea de raíz. Ejecute los contenedores de .NET como un usuario que no sea raíz (a través de nombre de usuario o UID $APP_UID) para alinearse con el principio de privilegios mínimos. Al hacerlo, se limitan los posibles efectos de un contenedor en peligro.
Escucha en el puerto 8080. Al ejecutar contenedores como usuario que no es de raíz, configure la aplicación para que escuche en el puerto 8080. Se trata de una convención común para los usuarios que no son raíz.
Encapsular dependencias. Asegúrese de que todas las dependencias de la aplicación se encapsulan en la imagen de contenedor de Docker. La encapsulación permite implementar la aplicación de forma confiable en una amplia gama de hosts.
Elija las imágenes base adecuadas. La imagen base que elija dependerá de su entorno de implementación. Si va a implementar en Container Apps, por ejemplo, debe usar imágenes de Docker de Linux.
Por ejemplo, la implementación de referencia utiliza un proceso de compilación de multi-pila. Las etapas iniciales compilan y crean la aplicación utilizando una imagen completa del SDK (mcr.microsoft.com/dotnet/sdk:8.0-jammy
). La imagen de ejecución final se crea a partir de la imagen base chiseled
, que excluye el SDK y los artefactos de compilación. El servicio se ejecuta como usuario no root (USER $APP_UID
) y expone el puerto 8080. Las dependencias necesarias para que la aplicación funcione se incluyen en la imagen de Docker, como se evidencia en los comandos para copiar archivos de proyecto y restaurar paquetes. El uso de imágenes basadas en Linux (mcr.microsoft.com/dotnet/aspnet:8.0-jammy-chiseled
) garantiza la compatibilidad con Container Apps, que requiere contenedores de Linux para la implementación.
# Build in a separate stage to avoid copying the SDK into the final image.
FROM mcr.microsoft.com/dotnet/sdk:8.0-jammy AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
# Restore packages.
COPY ["Relecloud.TicketRenderer/Relecloud.TicketRenderer.csproj", "Relecloud.TicketRenderer/"]
COPY ["Relecloud.Messaging/Relecloud.Messaging.csproj", "Relecloud.Messaging/"]
COPY ["Relecloud.Models/Relecloud.Models.csproj", "Relecloud.Models/"]
RUN dotnet restore "./Relecloud.TicketRenderer/Relecloud.TicketRenderer.csproj"
# Build and publish.
COPY . .
WORKDIR "/src/Relecloud.TicketRenderer"
RUN dotnet publish "./Relecloud.TicketRenderer.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
# Chiseled images contain only the minimal set of packages needed for .NET 8.0.
FROM mcr.microsoft.com/dotnet/aspnet:8.0-jammy-chiseled AS final
WORKDIR /app
EXPOSE 8080
# Copy the published app from the build stage.
COPY --from=build /app/publish .
# Run as nonroot user.
USER $APP_UID
ENTRYPOINT ["dotnet", "./Relecloud.TicketRenderer.dll"]
Realice la implementación de referencia
Implemente la implementación de referencia del patrón Modern Web App for .NET. En el repositorio hay instrucciones tanto para la implantación de desarrollo como de producción. Después de implementar la implementación, puede simular y observar patrones de diseño.
En el diagrama siguiente se muestra la arquitectura de la implementación de referencia: