Table of ContentsMicroservicios de .NET: Arquitectura para aplicaciones .NET en contenedor Introducción a los contenedores y Docker ¿Qué es Docker? Terminología de Docker Contenedores, imágenes y registros de Docker Selección entre .NET Core y .NET Framework para contenedores de Docker Orientación general Cuándo elegir .NET Core para contenedores de Docker Cuándo elegir .NET Framework para contenedores de Docker Tabla de decisiones: versiones de .NET Framework para su uso con Docker Selección del sistema operativo de destino con contenedores de .NET Imágenes de Docker de .NET oficiales Diseñar la arquitectura de aplicaciones basadas en contenedor y microservicio Incluir en un contenedor aplicaciones monolíticas Estado y datos en aplicaciones de Docker Arquitectura orientada a servicios Arquitectura de microservicios Propiedad de los datos por microservicio Arquitectura lógica frente a arquitectura física Desafíos y soluciones de la administración de datos distribuidos Identificar los límites del modelo de dominio para cada microservicio Comunicación entre microservicios Comunicación asincrónica basada en mensajes Crear, desarrollar y controlar las versiones de los contratos y las API de microservicio Direccionabilidad de microservicios y el Registro de servicios Crear una interfaz de usuario compuesta en función de los microservicios, incluidos la forma y el diseño visual de la interfaz de usuario generados por varios microservicios Resistencia y la alta disponibilidad en microservicios Orquestar microservicios y aplicaciones de varios contenedores para una alta escalabilidad y disponibilidad Uso de Azure Service Fabric Proceso de desarrollo de aplicaciones basadas en Docker Flujo de trabajo de desarrollo para aplicaciones de Docker Implementar aplicaciones web de .NET Core basadas en un solo contenedor en hosts de Linux o Windows Nano Server Migrar aplicaciones de .NET Framework monolíticas heredadas a contenedores de Windows Diseñar y desarrollar aplicaciones .NET basadas en varios contenedores y microservicios Diseñar una aplicación orientada a microservicios Crear un microservicio CRUD sencillo controlado por datos Definir una aplicación de varios contenedores con docker-compose.yml Usar un servidor de bases de datos que se ejecuta como un contenedor Implementar la comunicación basada en eventos entre microservicios (eventos de integración) Implementar un bus de eventos con RabbitMQ para el entorno de desarrollo o de prueba Suscribirse a eventos Probar aplicaciones web y servicios ASP.NET Core Abordar la complejidad empresarial en un microservicio con patrones DDD y CQRS Aplicar los patrones CQRS y DDD simplificados en un microservicio Aplicar enfoques CQRS y CQS en un microservicio DDD en eShopOnContainers Implementar lecturas/consultas en un microservicio CQRS Diseñar un microservicio orientado a DDD Diseñar un modelo de dominio de microservicio Implementar un modelo de dominio de microservicio con .NET Core Seedwork (clases base e interfaces reutilizables para el modelo de dominio) Implementar objetos de valor Usar las clases de enumeración en lugar de tipos de enumeración Diseñar las validaciones en el nivel de modelo de dominio Validación del lado cliente (validación de los niveles de presentación) Eventos de dominio: diseño e implementación Diseñar el nivel de persistencia de infraestructura Implementar el nivel de persistencia de infraestructura con Entity Framework Core Usar bases de datos NoSQL como una infraestructura de persistencia Diseñar el nivel de aplicación de microservicios y API web Implementar el nivel de aplicación de microservicios mediante la API web Implementar aplicaciones resistentes Controlar errores parciales Estrategias para controlar errores parciales Implementar reintentos con retroceso exponencial Implementar conexiones SQL resistentes de Entity Framework Core Implementar reintentos de llamada HTTP personalizados con retroceso exponencial Implementar reintentos de llamada HTTP con retroceso exponencial con Polly Implementar el patrón de interruptor Supervisión de matenimiento Proteger microservicios y aplicaciones web de .NET Acerca de la autorización en microservicios y aplicaciones web de .NET Almacenar secretos de aplicación de forma segura durante el desarrollo Usar Azure Key Vault para proteger secretos en tiempo de producción Puntos clave 03/10/2017 • 6 min to read • Edit Online Microservicios de .NET. Arquitectura para aplicaciones .NET en contenedor DESCARGA disponible en: https://aka.ms/microservicesebook PUBLICADO POR Equipos de producto de la División de desarrolladores de Microsoft, .NET y Visual Studio División de Microsoft Corporation One Microsoft Way Redmond, Washington 98052-6399 Copyright © 2017 de Microsoft Corporation Todos los derechos reservados. No se puede reproducir ni transmitir de ninguna forma ni por ningún medio ninguna parte del contenido de este libro sin la autorización por escrito del publicador. Este libro se proporciona “tal cual” y expresa las opiniones del autor. Las opiniones y la información expresados en este libro, incluidas las direcciones URL y otras referencias a sitios web de Internet, pueden cambiar sin previo aviso. Algunos ejemplos descritos aquí se proporcionan únicamente con fines ilustrativos y son ficticios. No debe deducirse ninguna asociación ni conexión reales. Microsoft y las marcas comerciales indicadas en http://www.microsoft.com en la página web “Marcas comerciales” pertenecen al grupo de empresas de Microsoft. Mac y macOS son marcas comerciales de Apple Inc. El logotipo de la ballena de Docker es una marca registrada de Docker, Inc. Se usa con permiso. El resto de marcas y logotipos pertenece a sus respectivos propietarios. Coautores: Cesar de la Torre, administrador de programas sénior del equipo del producto de .NET, Microsoft Corp. Bill Wagner, desarrollador de contenido sénior de C+E, Microsoft Corp. Mike Rousos, ingeniero de software principal del equipo de CAT de la división de desarrollo, Microsoft Editores: Mike Pope Steve Hoag Participantes y revisores: Jeffrey Ritcher, ingeniero de software asociado del equipo de Azure, Microsoft Jimmy Bogard, arquitecto jefe de Headspring Udi Dahan, fundador y director general de Particular Software Jimmy Nilsson, cofundador y director general de Factor10 Glenn Condron, director de programas sénior del equipo de ASP.NET Mark Fussell, responsable principal de administración de programas del equipo de Azure Service Fabric, Microsoft Diego Vega, responsable de administración de programas del equipo de Entity Framework, Microsoft Barry Dorrans, administrador de programas de seguridad sénior Rowan Miller, administrador de programas sénior, Microsoft Ankit Asthana, director principal de administración de programas del equipo de .NET, Microsoft Scott Hunter, director asociado de administración de programas del equipo de .NET, Microsoft Dylan Reisenberger, arquitecto y responsable de desarrollo de Polly Steve Smith, artesano e instructor de software de ASPSmith Ltd. Cooper Ian, arquitecto de codificación de Brighter Unai Zorrilla, arquitecto y responsable de desarrollo de Plain Concepts Eduard Tomas, responsable de desarrollo de Plain Concepts Ramon Tomas, desarrollador de Plain Concepts David Sanz, desarrollador de Plain Concepts Javier Valero, director de operaciones de Grupo Solutio Pierre Millet, asesor sénior de Microsoft Michael Friis, administrador de productos de Docker Inc. Charles Lowell, ingeniero de software del equipo de CAT de VS, Microsoft Introducción Las empresas cada vez ahorran más costos, resuelven más problemas de implementación y mejoran más las operaciones de DevOps y producción mediante el uso de contenedores. Microsoft ha lanzado recientemente innovaciones en los contenedores de Windows y Linux con la creación de productos como Azure Container Service y Azure Service Fabric, contando además con la colaboración de líderes del sector como Docker, Mesosphere y Kubernetes. Estos productos ofrecen soluciones de contenedores que ayudan a las empresas a compilar e implementar aplicaciones a velocidad y escala de nube, sea cual sea la plataforma o las herramientas que hayan elegido. Docker se está convirtiendo en el estándar de facto del sector de los contenedores, ya que es compatible con los proveedores más importantes del ecosistema de Windows y Linux. (Microsoft es uno de los principales proveedores de nube que admite Docker). En el futuro, Docker probablemente estará omnipresente en todos los centros de datos en la nube o locales. Además, la arquitectura de microservicios se está convirtiendo en un enfoque fundamental para las aplicaciones críticas distribuidas. En una arquitectura basada en microservicios, la aplicación se basa en una colección de servicios que se pueden desarrollar, probar, implementar y versionar por separado. Acerca de esta guía Esta guía es una introducción al desarrollo de aplicaciones basadas en microservicios y a su administración mediante contenedores. En ella se trata el diseño de la arquitectura y los métodos de implementación con .NET Core y contenedores de Docker. Para que sea más fácil empezar a trabajar con contenedores y microservicios, la guía se centra en una aplicación de referencia en contenedor y basada en microservicios que puede explorar. Esta misma aplicación de ejemplo está disponible en el repositorio de GitHub eShopOnContainers. En esta guía se proporciona el desarrollo fundamental y una guía de arquitectura principalmente en el nivel del entorno de desarrollo con especial hincapié en dos tecnologías: Docker y .NET Core. Nuestra intención es que lea esta guía cuando reflexione sobre el diseño de las aplicaciones sin centrarse en la infraestructura (en la nube o local) de su entorno de producción. Tomará decisiones sobre la infraestructura más adelante, cuando cree aplicaciones listas para la producción. Por lo tanto, esta guía está diseñada para ser independiente de la infraestructura y centrarse en el desarrollo y el entorno. Una vez que haya estudiado esta guía, el siguiente paso que debería dar es obtener información sobre los microservicios listos para la producción en Microsoft Azure. Aspectos no tratados en esta guía Esta guía no se centra en el ciclo de vida de la aplicación, DevOps, las canalizaciones CI/CD ni el trabajo de equipo. La guía complementaria Containerized Docker Application Lifecycle with Microsoft Platform and Tools (Ciclo de vida de aplicaciones de Docker en contenedor con la plataforma y herramientas de Microsoft) se centra en esas cuestiones. La guía actual tampoco proporciona detalles de implementación de la infraestructura de Azure, como información sobre orquestadores específicos. Recursos adicionales Containerized Docker Application Lifecycle with Microsoft Platform and Tools (Ciclo de vida de aplicaciones de Docker en contenedor con la plataforma y herramientas de Microsoft) (libro electrónico descargable) https://aka.ms/dockerlifecycleebook Destinatarios de esta guía Esta guía se ha escrito para desarrolladores y arquitectos de soluciones que no están familiarizados con el desarrollo de aplicaciones basado en Docker y la arquitectura basada en microservicios. Esta guía será de su interés si quiere obtener información sobre cómo crear arquitecturas, diseñar e implementar aplicaciones de prueba de concepto con tecnologías de desarrollo de Microsoft (con un hincapié especial en .NET Core) y con contenedores de Docker. También le resultará útil si es el responsable de tomar decisiones técnicas (por ejemplo, un arquitecto empresarial) y necesita una descripción de la arquitectura y la tecnología antes de decidir qué enfoque tomar para el diseño de aplicaciones distribuidas tanto nuevas como modernas. Cómo usar esta guía La primera parte de esta guía presenta los contenedores de Docker, describe cómo elegir entre .NET Core y .NET Framework como marco de desarrollo y proporciona una visión general de los microservicios. Este contenido está destinado a arquitectos y responsables de la toma de decisiones técnicas que quieren obtener información general pero que no necesitan centrarse en los detalles de la implementación de código. La segunda parte de la guía comienza con la sección Proceso de desarrollo de aplicaciones basadas en Docker. Se centra en los patrones de desarrollo y microservicios usados en la implementación de las aplicaciones que utilizan .NET Core y Docker. Esta sección será de gran interés para los desarrolladores y los arquitectos que quieran centrarse en el código, en los patrones y los detalles de implementación. Aplicación de referencia relacionada de microservicios y basada en contenedor: eShopOnContainers La aplicación eShopOnContainers es una aplicación de referencia para .NET Core y microservicios que está diseñada para implementarse mediante contenedores de Docker. La aplicación consta de varios subsistemas, incluidos varios front-end de interfaz de usuario de almacén electrónico (una aplicación web y una aplicación móvil nativa). También incluye microservicios y contenedores de back-end para todas las operaciones del lado servidor necesarias. El código fuente de la aplicación de microservicios basada en contenedor es código abierto y está disponible en el repositorio de GitHub eShopOnContainers. Envíenos sus comentarios. Hemos creado esta guía para ayudarle a entender la arquitectura de aplicaciones y microservicios en contenedor en .NET. La guía y la aplicación de referencia relacionada irán evolucionando, por lo que le agradecemos sus comentarios. Si tiene comentarios sobre cómo se puede mejorar esta guía, envíelos a:
[email protected] S IG U IE N T E Introducción a Containers y Docker 03/10/2017 • 1 min to read • Edit Online La inclusión en contenedores es un enfoque de desarrollo de software en el que una aplicación o un servicio, sus dependencias y su configuración (extraídos como archivos de manifiesto de implementación) se empaquetan como una imagen de contenedor. La aplicación en contenedor puede probarse como una unidad e implementarse como una instancia de imagen de contenedor en el sistema operativo (SO) host. Del mismo modo que los contenedores de mercancías permiten su transporte por barco, tren o camión independientemente de la carga de su interior, los contenedores de software actúan como una unidad estándar de software que puede contener diferentes dependencias y código. De esta manera, la inclusión del software en contenedor permite a los desarrolladores y los profesionales de TI implementarlo en entornos con pocas modificaciones o ninguna en absoluto. Los contenedores también aíslan las aplicaciones entre sí en un sistema operativo compartido. Las aplicaciones en contenedor se ejecutan sobre un host de contenedor que a su vez se ejecuta en el sistema operativo (Linux o Windows). Por lo tanto, los contenedores tienen una superficie significativamente menor que las imágenes de máquina virtual (VM). Cada contenedor puede ejecutar una aplicación web o un servicio al completo, como se muestra en la figura 2-1. En este ejemplo, el host de Docker es un host de contenedor, y App 1, App 2, Svc 1 y Svc 2 son aplicaciones o servicios en contenedor. Figura 2-1. Varios contenedores ejecutándose en un host de contenedor. Otra ventaja de la inclusión en contenedores es la escalabilidad. La creación de contenedores para tareas a corto plazo permite escalar horizontalmente con gran rapidez. Desde el punto de vista de la aplicación, la creación de instancias de una imagen (la creación de un contenedor) es similar a la creación de instancias de un proceso como un servicio o una aplicación web. Pero con fines de confiabilidad, cuando ejecute varias instancias de la misma imagen en varios servidores host, seguramente le interesará que cada contenedor (instancia de imagen) se ejecute en un servidor host o máquina virtual diferente en dominios de error distintos. En resumen, los contenedores ofrecen las ventajas del aislamiento, la portabilidad, la agilidad, la escalabilidad y el control a lo largo de todo el flujo de trabajo del ciclo de vida de la aplicación. La ventaja más importante es el aislamiento que se proporciona entre el desarrollo y las operaciones. A N T E R IO R S IG U IE N T E Selección entre .NET Core y .NET Framework para contenedores de Docker 03/10/2017 • 1 min to read • Edit Online Se admiten dos implementaciones para compilar aplicaciones de Docker en contenedor del lado del servidor con .NET: .NET Framework y .NET Core. Ambas comparten muchos de los componentes de .NET Standard y es posible compartir código entre ellas. Aun así, presentan diferencias fundamentales, y la elección de la implementación dependerá de lo que quiera realizar. En esta sección se proporciona orientación sobre cuándo se debe elegir cada implementación. A N T E R IO R S IG U IE N T E Diseñar la arquitectura de aplicaciones basadas en contenedores y microservicios 03/10/2017 • 2 min to read • Edit Online Los microservicios ofrecen grandes ventajas, pero también generan nuevos desafíos enormes. Los patrones de arquitectura de microservicios son los pilares fundamentales a la hora de crear una aplicación basada en microservicios. Previamente en esta guía, ha aprendido los conceptos básicos sobre los contenedores y Docker. Con esa información mínima, ya puede empezar a trabajar con contenedores. Aunque los contenedores son habilitadores y se consideran una gran elección para microservicios, no son obligatorios para una arquitectura de microservicios, y muchos conceptos arquitectónicos de esta sección sobre la arquitectura también se podrían aplicar sin contenedores. A pesar de ello, esta guía se centra en la intersección de ambos debido a la importancia actual de los contenedores. Las aplicaciones empresariales pueden ser complejas y, a menudo, se componen de varios servicios en lugar de una sola aplicación basada en servicios. En esos casos, debe comprender otros enfoques de arquitectura, como son los microservicios y determinados patrones de diseño guiado por el dominio (DDD), además de conceptos de orquestación de contenedores. Tenga en cuenta que en este capítulo no solo se describen los microservicios en contenedor, sino cualquier aplicación en contenedor. Principios de diseño de contenedores En el modelo de contenedor, una instancia de imagen de contenedor representa un único proceso. Al definir una imagen de contenedor como un límite de proceso, puede crear primitivas que se usen para escalar el proceso o para procesarlo por lotes. Cuando diseñe una imagen de contenedor, verá una definición ENTRYPOINT en el archivo Dockerfile. Esto define el proceso cuya duración controla la duración del contenedor. Cuando se completa el proceso, finaliza el ciclo de vida del contenedor. Los contenedores pueden representar procesos de ejecución prolongada como servidores web, pero también pueden representar procesos de corta duración, como trabajos por lotes, que anteriormente se implementarían como WebJobs de Azure. Si se produce un error en el proceso, el contenedor finaliza y lo sustituye el orquestador. Si el orquestador está configurado para mantener cinco instancias en ejecución y se produce un error en una de ellas, el orquestador creará otra instancia del contenedor para reemplazar al proceso con error. En un trabajo por lotes, el proceso se inicia con parámetros. Cuando el proceso finalice, el trabajo se habrá completado. Es posible que en algún momento le interese que varios procesos se ejecuten en un solo contenedor. En ese caso, dado que solo puede haber un punto de entrada por contenedor, puede ejecutar dentro del contenedor un script que inicie todos los programas que sean necesarios. Por ejemplo, puede usar Supervisor o una herramienta similar para que se encargue de iniciar varios procesos dentro de un único contenedor. Este método no es muy habitual, aunque existan arquitecturas que contengan varios procesos por contenedor. A N T E R IO R S IG U IE N T E 1 min to read • Edit O nline Proceso de desarrollo de aplicaciones basadas en Docker 03/10/2017 • 2 min to read • Edit Online Desarrolle aplicaciones .NET en contenedor de la forma que prefiera, ya sea centradas en el IDE con Visual Studio y Visual Studio Tools para Docker o bien centradas en la CLI o el editor con la CLI de Docker y Visual Studio Code. Entorno de desarrollo para aplicaciones de Docker Opciones de herramientas de desarrollo: IDE o editor Tanto si quiere un IDE eficaz y completo como si prefiere un editor ligero y ágil, Microsoft dispone de herramientas que puede usar para desarrollar aplicaciones de Docker. Visual Studio Tools para Docker. Si usa Visual Studio 2015, puede instalar el complemento Visual Studio Tools para Docker. Si usa Visual Studio 2017, las herramientas para Docker ya están integradas. En cualquier caso, las herramientas para Docker permiten desarrollar, ejecutar y validar las aplicaciones directamente en el entorno de Docker de destino. Puede presionar F5 para ejecutar y depurar la aplicación (de un solo contenedor o de varios contenedores) directamente en un host de Docker, o bien presionar CTRL+F5 para editar y actualizar la aplicación sin tener que volver a compilar el contenedor. Esta es la opción más sencilla y más eficaz para los desarrolladores de Windows que tienen como destino contenedores de Docker para Linux o Windows. Visual Studio Code y la CLI de Docker. Si prefiere un editor ligero multiplataforma que admita todos los lenguajes de programación, puede usar Microsoft Visual Studio Code (VS Code) y la CLI de Docker. Se trata de un enfoque de desarrollo multiplataforma para Mac, Linux y Windows. Estos productos proporcionan una experiencia sencilla pero sólida que agiliza el flujo de trabajo del desarrollador. Mediante la instalación de las herramientas de Docker Community Edition (CE), puede usar una sola CLI de Docker para compilar aplicaciones para Windows y Linux. Además, Visual Studio Code admite extensiones para Docker como IntelliSense para Dockerfiles y tareas de acceso directo para ejecutar comandos de Docker desde el editor. Recursos adicionales Visual Studio Tools para Docker https://visualstudiogallery.msdn.microsoft.com/0f5b2caa-ea00-41c8- b8a2-058c7da0b3e4 Visual Studio Code. Sitio oficial. https://code.visualstudio.com/download Docker Community Edition (CE) para Mac y Windows https://www.docker.com/community-editions Lenguajes y marcos de .NET para contenedores de Docker Como se ha mencionado en secciones anteriores de esta guía, puede usar .NET Framework, .NET Core o el proyecto Mono de código abierto para desarrollar aplicaciones .NET en contenedor de Docker. Puede desarrollar en C#, F# o Visual Basic cuando tenga como destino contenedores de Windows o Linux, en función de qué versión de .NET Framework esté en uso. Para obtener más información sobre lenguajes de .NET, vea la entrada de blog The .NET Language Strategy (Estrategia de lenguaje de .NET). A N T E R IO R S IG U IE N T E Implementar aplicaciones web de .NET Core basadas en un solo contenedor en hosts de Linux o Windows Nano Server 03/10/2017 • 9 min to read • Edit Online Puede usar contenedores de Docker para la implementación monolítica de aplicaciones web más sencillas. Esto mejora la integración continua y las canalizaciones de implementación continua y ayuda a llevar a cabo correctamente el proceso desde la implementación hasta la producción. Ya no se volverá a preguntar: "Funciona en mi equipo, ¿por qué no funciona en producción?". Una arquitectura basada en microservicios tiene muchas ventajas, pero a costa de una mayor complejidad. En algunos casos, los costos superan a las ventajas y podría resultarle más útil una aplicación de implementación monolítica que se ejecute en un solo contenedor o en unos pocos contenedores. Es posible que una aplicación monolítica no se pueda descomponer fácilmente en microservicios bien separados. Ya ha aprendido que estos se deben particionar por función: los microservicios deberían funcionar de manera independiente para proporcionar una aplicación más resistente. Si no puede proporcionar sectores de características de la aplicación, el hecho de separarla solo agrega complejidad. Una aplicación podría no necesitar inicialmente el escalado de las características por separado. Supongamos que en una fase temprana del ciclo de vida de la aplicación de referencia eShopOnContainers, el tráfico no justificaba separar las características en diferentes microservicios. El tráfico era bastante reducido y el hecho de agregar recursos a un servicio normalmente suponía agregar recursos a todos los servicios. El trabajo adicional que suponía separar la aplicación en servicios diferenciados apenas proporcionaba beneficio. Además, en las fases tempranas del desarrollo de una aplicación, es posible que no se tenga una idea clara de dónde están los límites funcionales naturales. Mientras desarrolla un producto mínimamente viable, puede que todavía no sea evidente la separación natural. Algunas de estas condiciones pueden ser temporales. Podría empezar creando una aplicación monolítica y, más adelante, separar algunas características para desarrollarlas e implementarlas como microservicios. Otras condiciones podrían ser básicas para el espacio de problemas de la aplicación y, en consecuencia, tal vez no se pueda dividir nunca en varios microservicios. La separación de una aplicación en varios procesos diferenciados también introduce una sobrecarga. La separación de funciones en procesos diferentes conlleva una mayor complejidad. Los protocolos de comunicación se vuelven más complejos. En lugar de llamadas a métodos, debe usar comunicaciones asincrónicas entre servicios. Cuando cambie a una arquitectura de microservicios, deberá agregar muchos de los bloques de creación que se implementan en la versión de microservicios de la aplicación eShopOnContainers: control de bus de eventos, reintentos y resistencia de mensajes, coherencia eventual y mucho más. Una versión muy simplificada de eShopOnContainers (denominada eShopWeb e incluida en el mismo repositorio de GitHub) se ejecuta como una aplicación de MVC monolítica y, tal como se ha indicado, esta opción de diseño ofrece una serie de ventajas. Puede descargar de GitHub el código fuente para esta aplicación y ejecutarlo de forma local. Incluso esta aplicación monolítica se beneficia de la implementación en un entorno de contenedor. Ante todo, la implementación en contenedor implica que cada instancia de la aplicación se ejecuta en el mismo entorno. Esto incluye el entorno de desarrollo donde tienen lugar las pruebas y el desarrollo iniciales. El equipo de desarrollo puede ejecutar la aplicación en un entorno en contenedor que coincida con el entorno de producción. Además, las aplicaciones en contenedor se escalan horizontalmente con un costo menor. Como ya ha visto, el entorno en contenedor permite un mayor uso compartido de recursos que los entornos de máquina virtual tradicionales. Por último, el hecho de incluir la aplicación en un contenedor fuerza la separación entre la lógica de negocios y el servidor de almacenamiento. Cuando la aplicación se escala horizontalmente, los diversos contenedores se basan en un único medio de almacenamiento físico. Normalmente, se trata de un servidor de alta disponibilidad que ejecuta una base de datos de SQL Server. Paseo por la aplicación La aplicación eShopWeb representa una parte de la aplicación eShopOnContainers que se ejecuta como una aplicación monolítica (una aplicación basada en MVC de ASP.NET Core que se ejecuta en .NET Core). Básicamente, proporciona las funciones de exploración del catálogo que se han descrito en secciones anteriores. La aplicación usa una base de datos de SQL Server para el almacenamiento del catálogo. En las implementaciones basadas en contenedor, esta aplicación monolítica puede tener acceso al mismo almacén de datos que la aplicación basada en microservicios. La aplicación está configurada para ejecutar SQL Server en un contenedor junto con la aplicación monolítica. En un entorno de producción, SQL Server se ejecutaría en una máquina con alta disponibilidad, fuera del host de Docker. Para mayor comodidad en un entorno de desarrollo o de pruebas, se recomienda ejecutar SQL Server en su propio contenedor. El conjunto de características iniciales solo permite examinar el catálogo. Mediante las actualizaciones se habilitará el conjunto completo de características de la aplicación en contenedor. En el libro electrónico ASP.NET Web Application architecture practices (Prácticas de la arquitectura de aplicaciones web ASP.NET) y en la aplicación de ejemplo eShopOnWeb relacionada se describe una arquitectura de aplicaciones web monolítica más avanzada, aunque en este caso no se ejecuta en contenedores de Docker, ya que el escenario se centra en el desarrollo web simple con ASP.NET Core. Aun así, la versión simplificada disponible en eShopOnContainers (eShopWeb) se ejecuta en un contenedor de Docker. Compatibilidad con Docker El proyecto eShopOnWeb se ejecuta en .NET Core. Por lo tanto, se puede ejecutar en contenedores basados en Linux o en Windows. Tenga en cuenta que, para la implementación de Docker, le interesa usar el mismo tipo de host para SQL Server. Los contenedores basados en Linux permiten una superficie menor y son preferibles. Visual Studio proporciona una plantilla de proyecto que agrega compatibilidad con Docker a una solución. Para ello, haga clic con el botón derecho en el proyecto y haga clic en Agregar y en Compatibilidad con Docker. La plantilla agrega un archivo Dockerfile al proyecto y un nuevo proyecto docker-compose que proporciona un archivo de inicio docker-compose.yml. Este paso ya se ha llevado a cabo en el proyecto eShopOnWeb descargado de GitHub. Verá que la solución contiene el proyecto eShopOnWeb y el proyecto docker-compose tal como se muestra en la figura 6-1. Figura 6-1. Proyecto docker-compose en una aplicación web de un solo contenedor. Estos archivos son archivos estándar docker-compose, coherentes con cualquier proyecto de Docker. Puede usarlos con Visual Studio o desde la línea de comandos. Esta aplicación se ejecuta en .NET Core y usa contenedores de Linux, por lo que también puede codificar, compilar y ejecutar en un equipo Mac o en un equipo Linux. El archivo docker-compose.yml contiene información sobre qué imágenes se van a compilar y qué contenedores se van a iniciar. Las plantillas especifican cómo se compila la imagen eshopweb y cómo se inician los contenedores de la aplicación. Debe agregar la dependencia en SQL Server. Para hacerlo, incluya una imagen para ella (por ejemplo, mssql-server-linux) y un servicio para la imagen sql.data de modo que Docker compile e inicie ese contenedor. Esta configuración se muestra en el ejemplo siguiente: version: '2' services: eshopweb: image: eshop/web build: context: ./eShopWeb dockerfile: Dockerfile depends_on: - sql.data sql.data: image: microsoft/mssql-server-linux La directiva depends_on le indica a Docker que la imagen eShopWeb depende de la imagen sql.data. Unas líneas por debajo se encuentran las instrucciones para compilar una imagen etiquetada como sql.data mediante la imagen microsoft/mssql-server-linux. En el proyecto docker-compose se muestran los demás archivos docker-compose bajo el nodo principal docker- compose.yml. Esto ayuda a señalar visualmente la relación entre estos archivos. El archivo docker-compose- override.yml contiene los valores de configuración de ambos servicios, como las cadenas de conexión y otros valores para las aplicaciones. En el ejemplo siguiente se muestra el archivo docker-compose.vs.debug.yml, que contiene los valores usados para la depuración en Visual Studio. En ese archivo, la imagen eshopweb tiene anexada la etiqueta de desarrollo. Esto ayuda a separar las imágenes de depuración de las imágenes de lanzamiento, para que no se implemente accidentalmente la información de depuración en un entorno de producción: version: '2' services: eshopweb: image: eshop/web:dev build: args: source: ${DOCKER_BUILD_SOURCE} environment: - DOTNET_USE_POLLING_FILE_WATCHER=1 volumes: - ./eShopWeb:/app - ~/.nuget/packages:/root/.nuget/packages:ro - ~/clrdbg:/clrdbg:ro entrypoint: tail -f /dev/null labels: - "com.microsoft.visualstudio.targetoperatingsystem=linux" El último archivo agregado es docker-compose.ci.build.yml, que se usaría desde la línea de comandos para compilar el proyecto desde un servidor CI. Este archivo compuesto inicia un contenedor de Docker que compila las imágenes necesarias para la aplicación. En el ejemplo siguiente se muestra el contenido del archivo docker- compose.ci.build.yml. version: '2' services: ci-build: image: microsoft/aspnetcore-build:1.0-1.1 volumes: - .:/src working_dir: /src command: /bin/bash -c "dotnet restore ./eShopWeb.sln && dotnet publish ./eShopWeb.sln -c Release -o ./obj/Docker/publish" Tenga en cuenta que la imagen es una imagen de compilación de ASP.NET Core. Esta imagen incluye las herramientas de compilación y el SDK para compilar la aplicación y crear las imágenes necesarias. Al ejecutar el proyecto docker-compose con este archivo, se inicia el contenedor de compilación desde la imagen y se compila la imagen de la aplicación en ese contenedor. Debe especificar ese archivo docker-compose como parte de la línea de comandos para compilar la aplicación en un contenedor de Docker y, después, iniciarlo. En Visual Studio, puede ejecutar la aplicación en contenedores de Docker. Para ello, seleccione el proyecto docker- compose como proyecto de inicio y presione Ctrl+F5 (F5 para depurar), como haría con cualquier otra aplicación. Cuando se inicia el proyecto docker-compose, Visual Studio ejecuta docker-compose mediante el archivo docker-compose.yml, el archivo docker-compose.override.yml y uno de los archivos docker-compose.vs.*. Una vez que se ha iniciado la aplicación, Visual Studio abre automáticamente el explorador. Si inicia la aplicación en el depurador, Visual Studio se asociará a la aplicación en ejecución en Docker. Solución de problemas En esta sección se describen algunos de los problemas que pueden surgir cuando se ejecutan contenedores de manera local y se sugieren diversas correcciones. Detener contenedores de Docker Después de iniciar la aplicación en contenedor, los contenedores siguen ejecutándose incluso después de que se haya detenido la depuración. Puede ejecutar el comando docker ps desde la línea de comandos para ver qué contenedores se están ejecutando. El comando docker stop detiene un contenedor en ejecución, tal como se muestra en la figura 6-2. Figura 6-2. Mostrar y detener contenedores con los comandos docker ps y docker stop de la CLI Es posible que necesite detener procesos en ejecución cuando cambie entre diferentes configuraciones. En caso contrario, el contenedor que ejecuta la aplicación web usará el puerto para la aplicación (5106 en este ejemplo). Agregar Docker a los proyectos El asistente que agrega compatibilidad con Docker se comunica con el proceso de Docker que se está ejecutando. El asistente no se ejecutará correctamente si Docker no está funcionando cuando se inicie el asistente. Además, el asistente examinará el contenedor que ha elegido actualmente para agregar la compatibilidad correcta con Docker. Si quiere agregar compatibilidad con contenedores de Windows, debe ejecutar el asistente mientras Docker se ejecuta con contenedores de Windows configurados. Si quiere agregar compatibilidad con contenedores de Linux, ejecute el asistente mientras Docker se ejecuta con contenedores de Linux configurados. A N T E R IO R S IG U IE N T E Migrar aplicaciones de .NET Framework monolíticas heredadas a contenedores de Windows 03/10/2017 • 13 min to read • Edit Online Los contenedores de Windows pueden usarse como una manera para mejorar los entornos de desarrollo y pruebas y para implementar aplicaciones que se basan en tecnologías de .NET Framework heredadas, como formularios Web Forms. El uso de contenedores para aplicaciones heredadas de esta manera se conoce como “migración mediante lift-and-shift”. Las secciones anteriores de esta guía han abogado por el uso de una arquitectura de microservicios en la que las aplicaciones empresariales se distribuyen entre diferentes contenedores, cada uno de los cuales ejecuta un pequeño servicio especializado. Este objetivo tiene numerosas ventajas. En el desarrollo nuevo, se recomienda encarecidamente este enfoque. Las aplicaciones fundamentales para la empresa también se verán lo bastante beneficiadas como para justificar el costo que supone el cambio de arquitectura y la reimplementación. Pero no todas las aplicaciones se beneficiarán lo suficiente como para justificar dicho costo. Esto no significa que esas aplicaciones no se puedan usar en escenarios de contenedor. En esta sección, exploraremos una aplicación para eShopOnContainers, mostrada en la figura 7-1. Los usuarios de esta aplicación serían los miembros del equipo empresarial de eShopOnContainers, que la utilizarían para ver y editar el catálogo de productos. Figura 7-1. Aplicación de formularios Web Forms ASP.NET (tecnología heredada) en un contenedor de Windows. Se trata de una aplicación de formularios Web Forms que se usa para examinar y modificar las entradas del catálogo. La dependencia de formularios Web Forms significa que esta aplicación no se ejecutará en .NET Core, a menos que se reescriba sin formularios Web Forms y use en su lugar MVC de ASP.NET Core. Verá cómo puede ejecutar aplicaciones como estas en contenedores sin ningún cambio. También verá cómo puede realizar cambios mínimos para trabajar en un modo híbrido en el que alguna función se ha movido a un microservicio independiente, pero la mayoría de las funciones permanece en la aplicación monolítica. Ventajas de incluir en un contenedor una aplicación monolítica La aplicación Catalog.WebForms está disponible en el repositorio de GitHub eShopOnContainers (https://github.com/dotnet/eShopOnContainers). Se trata de una aplicación web independiente que tiene acceso a un almacén de datos de alta disponibilidad. Aun así, resulta beneficioso ejecutar la aplicación en un contenedor. Cree una imagen de la aplicación. A partir de ese momento, todas las implementaciones se ejecutarán en el mismo entorno. Cada contenedor usa la misma versión de sistema operativo, tiene instalada la misma versión de dependencias, usa el mismo marco de trabajo y se compila con el mismo proceso. En la figura 7-2 verá la aplicación cargada en Visual Studio 2017. Figura 7-2. Aplicación de formularios Web Forms de administración de catálogos en Visual Studio de 2017. Además, todos los desarrolladores pueden ejecutar la aplicación en este entorno coherente. Los problemas que solo se manifiestan con determinadas versiones aparecerán de inmediato para los desarrolladores, en lugar de surgir en un entorno de ensayo o de producción. Las diferencias entre los entornos de desarrollo dejan de ser tan importantes para el equipo de desarrollo una vez que las aplicaciones se ejecutan en contenedores. Por último, las aplicaciones en contenedor tienen una curva de escalado horizontal más plana. Ya ha aprendido que las aplicaciones en contenedor permiten la existencia de más contenedores en una máquina virtual o más contenedores en una máquina física. Esto supone una mayor densidad y requiere menos recursos. Por todas estas razones, considere la posibilidad de ejecutar aplicaciones monolíticas heredadas en un contenedor de Docker mediante una operación de “migración mediante lift-and-shift”. La expresión “lift-and-shift” describe el ámbito de la tarea: extrae (lift) toda la aplicación de una máquina física o virtual y la traslada (shift) a un contenedor. En situaciones ideales, no es necesario realizar ningún cambio en el código de la aplicación para que se ejecute en un contenedor. Posibles procedimientos de migración La aplicación monolítica Catalog.Webforms es una aplicación web que contiene todo el código, incluidas las bibliotecas de acceso a datos. La base de datos se ejecuta en un equipo independiente de alta disponibilidad. Esta configuración se simula en el código de ejemplo mediante el uso de un servicio de catálogo ficticio. Esto significa que puede ejecutar la aplicación Catalog.WebForms con esos datos falsos para simular un escenario puro de migración mediante lift-and-shift. Esto muestra el procedimiento de migración más sencillo, según el cual se mueven los activos existentes para ejecutarlos en un contenedor sin realizar ningún cambio en el código. Este procedimiento es adecuado para las aplicaciones que están completas y que tienen una interacción mínima con las funciones que se van a mover a microservicios. Pero el sitio web de eShopOnContainers ya tiene acceso al almacenamiento de datos mediante microservicios para diferentes escenarios. Se pueden realizar algunos pequeños cambios adicionales en el editor del catálogo para aprovechar el microservicio de catálogo, en lugar de tener acceso directamente al almacenamiento de datos del catálogo. Estos cambios muestran el continuo de sus propias aplicaciones. Puede hacer de todo, desde mover a contenedores una aplicación existente sin cambiarla en absoluto, hasta realizar pequeños cambios que permiten que las aplicaciones existentes tengan acceso a algunos de los nuevos microservicios, pasando por reescribir completamente una aplicación para participar de lleno en una nueva arquitectura basada en microservicios. El procedimiento que elija dependerá del costo y de las ventajas de la migración. Paseo por la aplicación Puede cargar la solución Catalog.WebForms y ejecutar la aplicación como una aplicación independiente. En esta configuración, en lugar de una base de datos de almacenamiento persistente, la aplicación usa un servicio falso para devolver los datos. La aplicación usa Autofac (https://autofac.org/) como contenedor de inversión de control (IOC). Mediante la inserción de dependencias (DI), puede configurar la aplicación para que use los datos falsos o el servicio de datos de catálogo activo. (Más adelante explicaremos con más detalle la inserción de dependencias). El código de inicio lee un valor useFake en los archivos web.config y configura el contenedor Autofac para insertar el servicio de datos falsos o el servicio de catálogo activo. Si ejecuta la aplicación con useFake establecido en false en el archivo web.config, verá que la aplicación de formularios Web Forms muestra los datos del catálogo. La mayoría de las técnicas usadas en esta aplicación le resultarán conocidas a cualquier persona que haya usado formularios Web Forms. Aun así, el microservicio de catálogo introduce dos técnicas con las que probablemente no esté familiarizado: la inserción de dependencias (DI) mencionada anteriormente y el trabajo con almacenes de datos asincrónicos en formularios Web Forms. DI invierte la estrategia típica orientada a objetos consistente en escribir clases que asignan todos los recursos necesarios. En su lugar, las clases solicitan sus dependencias a un contenedor de servicios. La ventaja de DI es que puede reemplazar a los servicios externos por emulaciones (objetos ficticios) para admitir entornos de pruebas o de otro tipo. El contenedor de DI usa la configuración appSettings de web.config para controlar si se van a usar los datos falsos del catálogo o los datos activos del servicio en ejecución. La aplicación registra un objeto HttpModule que crea el contenedor y registra un controlador de solicitud previa para insertar las dependencias. Puede ver ese código en el archivo Modules/AutoFacHttpModule.cs, que es similar al ejemplo siguiente: private static IContainer CreateContainer() { // Configure AutoFac: // Register Containers: var settings = WebConfigurationManager.AppSettings; var useFake = settings["usefake"]; bool fake = useFake == "true"; var builder = new ContainerBuilder(); if (fake) { builder.RegisterType<CatalogMockService>() .As<ICatalogService>(); } else { builder.RegisterType<CatalogService>() .As<ICatalogService>(); builder.RegisterType<RequestProvider>() .As<IRequestProvider>(); } var container = builder.Build(); return container; } private void InjectDependencies() { if (HttpContext.Current.CurrentHandler is Page page) { // Get the code-behind class that we may have written var pageType = page.GetType().BaseType; // Determine if there is a constructor to inject, and grab it var ctor = (from c in pageType.GetConstructors() where c.GetParameters().Length > 0 select c).FirstOrDefault(); if (ctor != null) { // Resolve the parameters for the constructor var args = (from parm in ctor.GetParameters() select Container.Resolve(parm.ParameterType)) .ToArray(); // Execute the constructor method with the arguments resolved ctor.Invoke(page, args); } // Use the Autofac method to inject any // properties that can be filled by Autofac Container.InjectProperties(page); } } Las páginas de la aplicación (Default.aspx.cs y EditPage.aspx.cs) definen constructores que toman estas dependencias. Observe que el constructor predeterminado sigue estando presente y es accesible. La infraestructura necesita el código siguiente. protected _Default() { } public _Default(ICatalogService catalog) => this.catalog = catalog; Todas las API de catálogo son métodos asincrónicos. Los formularios Web Forms ahora las admiten para todos los controles de datos. La aplicación Catalog.WebForms usa el enlace de modelos para las páginas de lista y edición; los controles de las páginas definen las propiedades SelectMethod, UpdateMethod, InsertMethod y DeleteMethod que especifican las operaciones asincrónicas de devolución de tarea. Los controles de los formularios Web Forms entienden cuándo son asincrónicos los métodos enlazados a un control. La única restricción con la que se encontrará al usar determinados métodos asincrónicos es que no se admite la paginación. La firma de paginación requiere un parámetro out y los métodos asincrónicos no pueden tener parámetros out. Esta misma técnica se usa en otras páginas que requieren datos del servicio de catálogo. La configuración predeterminada de la aplicación de catálogo de formularios Web Forms usa una implementación ficticia del servicio catalog.api. Dicha implementación ficticia usa un conjunto de datos codificados de forma rígida para sus datos, lo que simplifica algunas tareas al quitar la dependencia del servicio catalog.api en entornos de desarrollo. Migración mediante lift-and-shift Visual Studio proporciona una excelente compatibilidad para incluir una aplicación en un contenedor. Haga clic con el botón derecho en el nodo del proyecto y seleccione Agregar y Compatibilidad con Docker. La plantilla de proyecto de Docker agrega un nuevo proyecto a la solución denominado docker-compose. El proyecto contiene los activos de Docker que componen (o compilan) las imágenes que necesita y empieza a ejecutar los contenedores necesarios, tal como se muestra en la figura 7-3. En los escenarios más simples de migración mediante lift-and-shift, la aplicación será el único servicio que usará para la aplicación de formularios Web Forms. La plantilla también cambia el proyecto de inicio para que apunte al proyecto docker-compose. Ahora, al presionar CTRL+F5 o F5, creará la imagen de Docker e iniciará el contenedor de Docker. Figura 7-3. El proyecto docker-compose en la solución de formularios Web Forms. Antes de ejecutar la solución, debe asegurarse de que ha configurado Docker para usar contenedores de Windows. Para ello, haga clic con el botón derecho en el icono de la barra de tareas de Docker en Windows y seleccione Switch to Windows Containers (Cambiar a contenedores de Windows), como se muestra en la figura 7-4. Figura 7-4. Cambiar a contenedores de Windows desde el icono de la barra de tareas de Docker en Windows. Si el elemento de menú indica Switch to Linux containers (Cambiar a contenedores de Linux), ya está ejecutando Docker con contenedores de Windows. Al ejecutar la solución, se reinicia el host de Docker. Al compilar, la aplicación y la imagen de Docker se compilan para el proyecto de formularios Web Forms. La primera vez que lo haga, tardará bastante. Esto se debe a que el proceso de compilación extrae la imagen base de Windows Server y la imagen adicional para ASP.NET. Los ciclos posteriores de compilación y ejecución serán mucho más rápidos. Examinemos con detalle los archivos que ha agregado la plantilla de proyecto de Docker. Ha creado varios archivos que Visual Studio usa para crear la imagen de Docker e iniciar un contenedor. Puede usar los mismos archivos desde la CLI para ejecutar comandos de Docker manualmente. En el siguiente ejemplo de Dockerfile se muestra la configuración básica para compilar una imagen de Docker basada en la imagen de ASP.NET de Windows que ejecuta un sitio de ASP.NET. FROM microsoft/aspnet ARG source WORKDIR /inetpub/wwwroot COPY ${source:-obj/Docker/publish} . Este archivo Dockerfile tendrá un aspecto muy similar a los creados para ejecutar una aplicación de ASP.NET Core en contenedores de Linux. Aun así, hay algunas diferencias importantes. La más importante es que la imagen base es microsoft/aspnet, que es la imagen actual de Windows Server que incluye .NET Framework. Otra diferencia es que los directorios copiados del directorio de origen son diferentes. Los demás archivos del proyecto docker-compose son los activos de Docker necesarios para compilar y configurar los contenedores. Visual Studio coloca los diversos archivos docker-compose.yml en un nodo para resaltar cómo se usan. El archivo base docker-compose contiene las directivas que son comunes a todas las configuraciones. El archivo docker-compose.override.yml contiene variables de entorno y los reemplazos relacionados para una configuración de desarrollador. Las variantes con .vs.debug y .vs.release proporcionan una configuración de entorno que permite a Visual Studio adjuntar elementos al contenedor en ejecución y administrarlo. Aunque la integración de Visual Studio forma parte de la compatibilidad con la adición de Docker a la solución, también puede compilar y ejecutar desde la línea de comandos mediante el comando docker-compose up, como ha visto en secciones anteriores. Obtener datos del microservicio de catálogo existente de .NET Core Puede configurar la aplicación de formularios Web Forms para usar el microservicio de catálogo eShopOnContainers para obtener datos en lugar de usar datos falsos. Para ello, edite el archivo web.config y establezca el valor de la clave useFake en false. El contenedor de inserción de dependencias usará la clase que tiene acceso al microservicio de catálogo activo en lugar de la clase que devuelve los datos codificados de forma rígida. No se necesita ningún otro cambio de código. Para obtener acceso al servicio de catálogo activo tiene que actualizar el proyecto docker-compose para compilar la imagen del servicio de catálogo e iniciar el contenedor de servicio de catálogo. Docker CE para Windows es compatible con contenedores de Linux y con contenedores de Windows, pero no al mismo tiempo. Para ejecutar el microservicio de catálogo, debe compilar una imagen que ejecute el microservicio de catálogo sobre un contenedor de Windows. Este enfoque requiere para el proyecto de microservicios un archivo Dockerfile diferente del que ha visto en secciones anteriores. El archivo Dockerfile.windows contiene los valores de configuración necesarios para compilar la imagen de contenedor de la API de catálogo de modo que se ejecute en un contenedor de Windows, por ejemplo, para usar una imagen de Docker de Windows Nano. El catálogo de microservicio se fundamenta en la base de datos de SQL Server. Por lo tanto, debe usar también una imagen de Docker de SQL Server basada en Windows. Después de estos cambios, el proyecto docker-compose lleva a cabo más acciones para iniciar la aplicación. El proyecto inicia ahora SQL Server con la imagen de SQL Server basada en Windows, inicia el microservicio de catálogo en un contenedor de Windows e inicia el contenedor del editor de catálogo de formularios Web Forms, también en un contenedor de Windows. Si es necesario compilar alguna de las imágenes, se crean en primer lugar. Entornos de desarrollo y producción Hay un par de diferencias entre la configuración de desarrollo y la configuración de producción. En el entorno de desarrollo, puede ejecutar la aplicación de formularios Web Forms, el microservicio de catálogo y SQL Server en contenedores de Windows, como parte del mismo host de Docker. En las secciones anteriores, hemos mencionado imágenes de SQL Server implementadas en el mismo host de Docker que los demás servicios basados en .NET Core en un host de Docker basado en Linux. La ventaja de ejecutar los diversos microservicios en el mismo host de Docker (o clúster) es que hay menos comunicación de red y la comunicación entre los contenedores tiene una latencia inferior. En el entorno de desarrollo, debe ejecutar todos los contenedores en el mismo sistema operativo. Docker CE para Windows no admite la ejecución simultánea de contenedores basados en Windows y en Linux. En producción, puede decidir si quiere ejecutar el microservicio de catálogo en un contenedor de Windows en un solo host de Docker (o clúster), o bien si prefiere que la aplicación de formularios Web Forms se comunique con una instancia del microservicio de catálogo que se ejecuta en un contenedor de Linux en un host de Docker diferente. Depende de cómo quiera optimizar la latencia de red. En la mayoría de los casos, le interesará que los microservicios de los que dependen sus aplicaciones se ejecuten en el mismo host de Docker (o conjunto) para facilitar la implementación y para que la latencia de comunicación sea inferior. En estas configuraciones, la única comunicación costosa es la que se establece entre las instancias de microservicios y los servidores de alta disponibilidad para el almacenamiento de datos persistentes. A N T E R IO R S IG U IE N T E Diseñar y desarrollar aplicaciones .NET basadas en varios contenedores y microservicios 03/10/2017 • 1 min to read • Edit Online Si desarrolla aplicaciones de microservicios en contenedor significa que está compilando aplicaciones de varios contenedores. Pero una aplicación de varios contenedores también podría ser más sencilla (por ejemplo, una aplicación de tres niveles) y podría no compilarse con una arquitectura de microservicios. Previamente planteamos la pregunta “¿Se necesita Docker para compilar una arquitectura de microservicios?”. La respuesta es un no rotundo. Docker es un habilitador y puede proporcionar grandes ventajas, pero los contenedores y Docker no son un requisito imprescindible para los microservicios. Por ejemplo, podría crear una aplicación basada en microservicios con o sin Docker al usar Azure Service Fabric, que es compatible con los microservicios que se ejecutan como procesos simples o como contenedores de Docker. Pero si sabe cómo diseñar y desarrollar una aplicación basada en microservicios que también se base en contenedores de Docker, podrá diseñar y desarrollar cualquier modelo de aplicación más sencillo. Por ejemplo, podría diseñar una aplicación de tres niveles que también requiera un enfoque de varios contenedores. Debido a eso, y dado que las arquitecturas de microservicios son una tendencia importante en el mundo de los contenedores, esta sección se centra en la implementación de una arquitectura de microservicios con contenedores de Docker. A N T E R IO R S IG U IE N T E Abordar la complejidad empresarial en un microservicio con patrones DDD y CQRS 03/10/2017 • 2 min to read • Edit Online Diseñe un modelo de dominio para cada microservicio o contexto limitado que refleje un conocimiento del ámbito empresarial. Esta sección se centra en microservicios más avanzados que se implementan cuando se deben abordar subsistemas complejos y en microservicios derivados de los conocimientos de expertos en el dominio con reglas de negocios cambiantes. Los patrones de arquitectura que se usan en esta sección se basan en enfoques de diseño guiado por el dominio (DDD) y separación de la responsabilidad de comandos y consultas (CQRS), como se muestra en la figura 9-1. Figura 9-1. Arquitectura de microservicios externa frente a patrones de arquitectura interna para cada microservicio. Pero la mayoría de las técnicas para microservicios controlados por datos (por ejemplo, cómo implementar un servicio ASP.NET Core Web API o cómo exponer metadatos de Swagger con Swashbuckle) también son aplicables a los microservicios más avanzados que se implementan internamente con patrones DDD. Esta sección es una ampliación de las secciones anteriores, ya que la mayoría de las prácticas explicadas anteriormente también se aplican aquí o a cualquier tipo de microservicio. Esta sección proporciona en primer lugar detalles sobre los patrones CQRS simplificados que se usan en la aplicación de referencia eShopOnContainers. Más adelante, obtendrá información general sobre las técnicas DDD que le permiten encontrar patrones comunes que puede volver a usar en sus aplicaciones. DDD es un tema amplio con numerosos recursos para obtener más información. Puede empezar con libros como Domain-Driven Design (Diseño guiado por el dominio), de Eric Evans, y materiales adicionales de Vaughn Vernon, Jimmy Nilsson, Greg Young, Udi Dahan, Jimmy Bogard y muchos otros expertos en DDD y CQRS. Pero, sobre todo, para aprender a aplicar técnicas DDD, debe recurrir a conversaciones, pizarras interactivas y sesiones de modelado de dominio con expertos de su ámbito empresarial específico. Recursos adicionales D D D (d i se ñ o g u i a d o p o r e l d o m i n i o ) Eric Evans. Sitio web Domain Language http://domainlanguage.com/ Martin Fowler. Artículos con la etiqueta "domain-driven design" (diseño guiado por el dominio) http://martinfowler.com/tags/domain%20driven%20design.html Jimmy Bogard. Strengthening your domain: a primer (Manual sobre cómo fortalecer el dominio) https://lostechies.com/jimmybogard/2010/02/04/strengthening-your-domain-a-primer/ L i b r o s so b r e D D D Eric Evans. Domain-Driven Design: Tackling Complexity in the Heart of Software https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215/ Eric Evans. Domain-Driven Design Reference: Definitions and Pattern Summaries https://www.amazon.com/Domain-Driven-Design-Reference-Definitions-2014-09-22/dp/B01N8YB4ZO/ Vaughn Vernon. Implementing Domain-Driven Design https://www.amazon.com/Implementing- Domain-Driven-Design-Vaughn-Vernon/dp/0321834577/ Vaughn Vernon. Domain-Driven Design Distilled https://www.amazon.com/Domain-Driven-Design- Distilled-Vaughn-Vernon/dp/0134434420/ Jimmy Nilsson. Applying Domain-Driven Design and Patterns https://www.amazon.com/Applying- Domain-Driven-Design-Patterns-Examples/dp/0321268202/ Cesar de la Torre. N-Layered Domain-Oriented Architecture Guide with .NET https://www.amazon.com/N-Layered-Domain-Oriented-Architecture-Guide-NET/dp/8493903612/ Abel Avram y Floyd Marinescu. Domain-Driven Design Quickly https://www.amazon.com/Domain- Driven-Design-Quickly-Abel-Avram/dp/1411609255/ Aprendizaje de DDD Julie Lerman y Steve Smith. Curso "Domain-Driven Design Fundamentals" (Conceptos fundamentales sobre el diseño guiado por el dominio) http://bit.ly/PS-DDD A N T E R IO R S IG U IE N T E Implementar aplicaciones resistentes 03/10/2017 • 1 min to read • Edit Online Sus aplicaciones basadas en microservicios y en la nube deben estar preparadas para los errores parciales que seguramente se acabarán produciendo en algún momento. Debe diseñar su aplicación de modo que sea resistente a estos errores parciales. La resistencia es la capacidad de recuperarse de errores y seguir funcionando. No se trata de evitar los errores, sino de aceptar el hecho de que se producirán errores y responder a ellos de forma que se evite el tiempo de inactividad o la pérdida de datos. El objetivo de la resistencia consiste en que la aplicación vuelva a un estado totalmente operativo después de un error. Es todo un desafío diseñar e implementar una aplicación basada en microservicios. Pero también necesita mantener la aplicación en ejecución en un entorno en el que con seguridad se producirá algún tipo de error. Por lo tanto, la aplicación debe ser resistente. Debe estar diseñada para hacer frente a errores parciales, como las interrupciones de red o el bloqueo de nodos o máquinas virtuales en la nube. Incluso los microservicios (contenedores) que se mueven a otro nodo dentro de un clúster pueden causar breves errores intermitentes dentro de la aplicación. Los numerosos componentes individuales de la aplicación también deberían incorporar características de seguimiento de estado. Mediante las directrices descritas en este capítulo, podrá crear una aplicación que funcione sin problemas aunque se produzcan tiempos de inactividad transitorios o las interrupciones típicas de las implementaciones complejas y basadas en la nube. A N T E R IO R S IG U IE N T E Proteger microservicios y aplicaciones web de .NET 03/10/2017 • 11 min to read • Edit Online A menudo es necesario que los recursos y las API expuestas por un servicio se limiten a determinados usuarios o clientes de confianza. El primer paso para tomar este tipo de decisiones de confianza en el nivel de API es la autenticación. La autenticación es el proceso de determinar de forma fiable la identidad de un usuario. En escenarios de microservicios, la autenticación suele controlarse de manera centralizada. Si usa una puerta de enlace de API, dicha puerta es un buen lugar para realizar la autenticación, como se muestra en la figura 11-1. Si emplea este método, asegúrese de que no es posible ponerse en contacto directamente con los microservicios individuales (sin la puerta de enlace de API), a menos que haya aplicado seguridad adicional para autenticar si los mensajes provienen de la puerta de enlace. Figura 11-1. Autenticación centralizada con una puerta de enlace de API. Si se puede tener acceso directamente a los servicios, entonces para la autenticación de los usuarios se puede usar un servicio de autenticación como Azure Active Directory o un microservicio de autenticación dedicado que actúe como un servicio de token de seguridad (STS). Las decisiones de confianza se comparten entre los servicios con tokens de seguridad o cookies. (Si es necesario, estos se pueden compartir entre aplicaciones en ASP.NET Core con servicios de protección de datos). Este patrón se ilustra en la figura 11-2. Figura 11-2. Autenticación realizada por un microservicio de identidad; la confianza se comparte mediante un token de autorización. Autenticación mediante ASP.NET Core Identity El principal mecanismo de ASP.NET Core para identificar a los usuarios de una aplicación es el sistema de pertenencia ASP.NET Core Identity. ASP.NET Core Identity almacena la información del usuario (incluida la información de inicio de sesión, los roles y las notificaciones) en un almacén de datos configurado por el desarrollador. Normalmente, el almacén de datos de ASP.NET Core Identity es un almacén de Entity Framework incluido en el paquete Microsoft.AspNetCore.Identity.EntityFrameworkCore. Aun así, se pueden usar almacenes personalizados u otros paquetes de terceros para almacenar información de identidad en Table Storage de Azure, DocumentDB u otras ubicaciones. El código siguiente procede de la plantilla de proyecto de aplicación web de ASP.NET Core con la autenticación de cuentas de usuario individuales seleccionada. Muestra cómo configurar ASP.NET Core Identity mediante EntityFramework.Core en el método Startup.ConfigureServices. services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); services.AddIdentity<ApplicationUser, IdentityRole>() .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders(); Una vez configurado ASP.NET Core Identity, debe habilitarlo. Para ello, llame a app.UseIdentity en el método Startup.Configure del servicio. El uso de ASP.NET Core Identity permite varios escenarios: Crear información de usuario con el tipo UserManager (userManager.CreateAsync). Autenticar a los usuarios con el tipo SignInManager. Puede usar signInManager.SignInAsync para iniciar sesión directamente, o bien signInManager.PasswordSignInAsync para confirmar que la contraseña del usuario es correcta y, después, iniciar sesión. Identificar a un usuario en función de la información almacenada en una cookie (que se lee mediante el software intermedio de ASP.NET Core Identity), de modo que las solicitudes posteriores desde un explorador incluyan la identidad y las notificaciones del usuario que ha iniciado sesión. ASP.NET Core Identity también es compatible con la autenticación en dos fases. ASP.NET Core Identity es una solución recomendada para los escenarios de autenticación que usan un almacén de datos de usuario local y que conservan la identidad entre las solicitudes mediante el uso de cookies (como es habitual en las aplicaciones web MVC). Autenticar mediante proveedores externos ASP.NET Core también admite el uso de proveedores de autenticación externos para permitir que los usuarios inicien sesión a través de flujos OAuth 2.0. Esto significa que los usuarios pueden iniciar sesión mediante los procesos de autenticación existentes de proveedores como Microsoft, Google, Facebook o Twitter y asociar las identidades con una identidad de ASP.NET Core en la aplicación. Para usar la autenticación externa, incluya el software intermedio de autenticación adecuado en la canalización de procesamiento de solicitudes HTTP de la aplicación. Este software intermedio es responsable de controlar las solicitudes para devolver las rutas URI desde el proveedor de autenticación, capturar información de identidad y hacer que esté disponible mediante el método SignInManager.GetExternalLoginInfo. A continuación se muestran proveedores de autenticación externos populares y sus paquetes NuGet asociados. Microsoft: Microsoft.AspNetCore.Authentication.MicrosoftAccount Google: Microsoft.AspNetCore.Authentication.Google Facebook: Microsoft.AspNetCore.Authentication.Facebook Twitter: Microsoft.AspNetCore.Authentication.Twitter En todos los casos, el software intermedio se registra con una llamada a un método de registro similar a app.Use{ExternalProvider}Authentication en Startup.Configure. Estos métodos de registro toman un objeto de opciones que contiene un identificador de aplicación e información secreta (una contraseña, por ejemplo), según requiera el proveedor. Los proveedores de autenticación externos requieren que la aplicación se registre (como se explica en la documentación de ASP.NET Core) para que puedan informar al usuario sobre qué aplicación solicita acceso a su identidad. Una vez que se haya registrado el software intermedio en Startup.Configure, podrá pedirles a los usuarios que inicien sesión desde cualquier acción de controlador. Para ello, cree un objeto AuthenticationProperties que incluya el nombre del proveedor de autenticación y una dirección URL de redireccionamiento. Después, devuelva una respuesta Challenge que pase el objeto AuthenticationProperties. El código siguiente muestra un ejemplo de esto. var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl); return Challenge(properties, provider); El parámetro redirectUrl incluye la dirección URL a la que el proveedor externo debe redirigir una vez que se ha autenticado el usuario. La dirección URL debe representar una acción que iniciará la sesión del usuario en función de información de identidad externa, como en el siguiente ejemplo simplificado: // Sign in the user with this external login provider if the user // already has a login. var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false); if (result.Succeeded) { return RedirectToLocal(returnUrl); } else { ApplicationUser newUser = new ApplicationUser { // The user object can be constructed with claims from the // external authentication provider, combined with information // supplied by the user after they have authenticated with // the external provider. UserName = info.Principal.FindFirstValue(ClaimTypes.Name), Email = info.Principal.FindFirstValue(ClaimTypes.Email) }; var identityResult = await _userManager.CreateAsync(newUser); if (identityResult.Succeeded) { identityResult = await _userManager.AddLoginAsync(newUser, info); if (identityResult.Succeeded) { await _signInManager.SignInAsync(newUser, isPersistent: false); } return RedirectToLocal(returnUrl); } } Si elige la opción de autenticación Individual User Account (Cuenta de usuario individual) al crear el proyecto de aplicación web de ASP.NET Core en Visual Studio, todo el código necesario para iniciar sesión con un proveedor externo ya está en el proyecto, como se muestra en la figura 11-3. Figura 11-3. Proceso de selección de una opción para usar la autenticación externa al crear un proyecto de aplicación web. Además de los proveedores de autenticación externa mencionados anteriormente, hay disponibles paquetes de terceros que proporcionan software intermedio para el uso de muchos otros proveedores de autenticación externos. Para obtener una lista, vea el repositorio AspNet.Security.OAuth.Providers en GitHub. Por supuesto, también puede crear su propio software intermedio de autenticación externo. Autenticar mediante tokens de portador La autenticación con ASP.NET Core Identity (o con Identity y proveedores de autenticación externos) funciona bien en muchos escenarios de aplicación web en los que es adecuado almacenar información de usuario en una cookie. En cambio, en otros escenarios las cookies no son una manera natural de conservar y transmitir datos. Por ejemplo, en una Web API de ASP.NET Core que expone puntos de conexión RESTful a los que podrían tener acceso aplicaciones de una sola página (SPA), clientes nativos o incluso otras Web API, normalmente le interesa usar la autenticación mediante token de portador. Estos tipos de aplicaciones no funcionan con cookies, pero pueden recuperar fácilmente un token de portador e incluirlo en el encabezado de autorización de las solicitudes posteriores. Con objeto de habilitar la autenticación mediante token, ASP.NET Core admite varias opciones para el uso de OAuth 2.0 y OpenID Connect. Autenticar con un proveedor de identidad OpenID Connect u OAuth 2.0 Si la información de usuario se almacena en Azure Active Directory u otra solución de identidad compatible con OpenID Connect u OAuth 2.0, puede usar el paquete Microsoft.AspNetCore.Authentication.OpenIdConnect para autenticarse con el flujo de trabajo de OpenID Connect. Por ejemplo, para autenticarse con Azure Active Directory, una aplicación web de ASP.NET Core puede usar el software intermedio de ese paquete tal como se muestra en el ejemplo siguiente: // Configure the OWIN pipeline to use OpenID Connect auth app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions { ClientId = Configuration["AzureAD:ClientId"], Authority = String.Format(Configuration["AzureAd:AadInstance"], Configuration["AzureAd:Tenant"]), ResponseType = OpenIdConnectResponseType.IdToken, PostLogoutRedirectUri = Configuration["AzureAd:PostLogoutRedirectUri"] }); Los valores de configuración son los valores de Azure Active Directory que se crean cuando la aplicación se registra como cliente de Azure AD. Es posible compartir un identificador de cliente único entre varios microservicios de una aplicación si todos necesitan autenticar a usuarios autenticados mediante Azure Active Directory. Tenga en cuenta que, cuando se usa este flujo de trabajo, el software intermedio de ASP.NET Core Identity no es necesario, porque Azure Active Directory controla el almacenamiento y la autenticación de la información del usuario. Emitir tokens de seguridad desde un servicio de ASP.NET Core Si prefiere emitir tokens de seguridad para los usuarios locales de ASP.NET Core Identity en lugar de usar un proveedor de identidades externo, puede aprovechar algunas buenas bibliotecas de terceros. IdentityServer4 y OpenIddict son proveedores de OpenID Connect que se integran fácilmente con ASP.NET Core Identity y le permiten emitir tokens de seguridad desde un servicio de ASP.NET Core. En la documentación de IdentityServer4 encontrará instrucciones detalladas para usar la biblioteca, pero los pasos básicos para emitir tokens con IdentityServer4 son los que se indican a continuación. 1. Llame a app.UseIdentityServer en el método Startup.Configure para agregar IdentityServer4 a la canalización de procesamiento de solicitudes HTTP de la aplicación. Esto permite a la biblioteca atender las solicitudes a los puntos de conexión de OpenID Connect y OAuth2 como /connect/token. 2. Configure IdentityServer4 en Startup.ConfigureServices mediante una llamada a services.AddIdentityServer. 3. Para configurar el servidor de identidades, proporcione los datos siguientes: Las credenciales que se van a usar para la firma. Los recursos de identidad y de API a los que los usuarios podrían solicitar acceso: Los recursos de API representan funciones o datos protegidos a los que los usuarios pueden tener acceso con un token de acceso. Un ejemplo de un recurso de API sería una API web (o un conjunto de API) que requiere autorización. Los recursos de identidad representan información (notificaciones) que se entregan a un cliente para identificar a un usuario. Las notificaciones pueden incluir el nombre de usuario, la dirección de correo electrónico, etc. Los clientes que se conectarán para solicitar tokens. El mecanismo de almacenamiento de la información de usuario, como ASP.NET Core Identity u otra alternativa. Al especificar los clientes y los recursos que usará IdentityServer4, puede pasar una colección IEnumerable<T> del tipo adecuado a los métodos que toman almacenes de recursos o cliente en memoria. En escenarios más complejos, puede proporcionar tipos de proveedor de recursos o cliente mediante la inserción de dependencias. En el ejemplo siguiente se muestra el aspecto que podría tener una configuración para que IdentityServer4 use clientes y recursos en memoria proporcionados por un tipo IClientStore personalizado: // Add IdentityServer services services.AddSingleton<IClientStore, CustomClientStore>(); services.AddIdentityServer() .AddSigningCredential("CN=sts") .AddInMemoryApiResources(MyApiResourceProvider.GetAllResources()) .AddAspNetIdentity<ApplicationUser>(); Consumir tokens de seguridad La autenticación con un punto de conexión de OpenID Connect o mediante la emisión de tokens de seguridad propios se aplica a diversos escenarios. Pero ¿qué sucede si un servicio solo necesita limitar el acceso a los usuarios que tienen tokens de seguridad válidos proporcionados por otro servicio? Para este escenario, en el paquete Microsoft.AspNetCore.Authentication.JwtBearer está disponible el software intermedio de autenticación que controla los tokens JWT. JWT es el acrónimo de "JSON Web Token" y es un formato común de token de seguridad (definido en RFC 7519) para la comunicación de notificaciones de seguridad. Un ejemplo sencillo de cómo emplear el software intermedio para usar esos tokens tendría un aspecto similar al siguiente. Este código debe preceder a las llamadas al software intermedio MVC de ASP.NET Core (app.UseMvc). app.UseJwtBearerAuthentication(new JwtBearerOptions() { Audience = "http://localhost:5001/", Authority = "http://localhost:5000/", AutomaticAuthenticate = true }); Los parámetros de este uso son los siguientes: Audience representa el receptor del token entrante o el recurso al que el token concede acceso. Si el valor especificado en este parámetro no coincide con el parámetro aud del token, se rechazará el token. Authority es la dirección del servidor de autenticación de emisión de tokens. El software intermedio de autenticación del portador de JWT usa este URI para obtener la clave pública que puede usarse para validar la firma del token. El software intermedio también confirma que el parámetro iss del token coincide con este URI. AutomaticAuthenticate es un valor booleano que indica si la sesión del usuario definido por el token debe iniciarse automáticamente. En este ejemplo no se usa el parámetro RequireHttpsMetadata. Resulta útil para la realización de pruebas; establézcalo en false para efectuar pruebas en los entornos en los que no tiene certificados. En implementaciones reales, los tokens de portador de JWT siempre se deben pasar a través de HTTPS exclusivamente. Con este software intermedio, los tokens JWT se extraen automáticamente de los encabezados de autorización. Después, se deserializan, se validan (mediante los valores de los parámetros Audience y Authority) y se almacenan como información del usuario a la que se hará referencia más adelante a través de acciones de MVC o filtros de autorización. El software intermedio de autenticación del portador de JWT también puede admitir escenarios más avanzados, como el uso de un certificado local para validar un token si la entidad no está disponible. En este escenario, puede especificar un objeto TokenValidationParameters en el objeto JwtBearerOptions. Recursos adicionales Sharing cookies between applications (Compartir cookies entre aplicaciones) https://docs.microsoft.com/aspnet/core/security/data-protection/compatibility/cookie-sharing#sharing- authentication-cookies-between-applications Introduction to Identity (Introducción a la identidad) https://docs.microsoft.com/aspnet/core/security/authentication/identity Rick Anderson. Two-factor authentication with SMS (Autenticación en dos fases con SMS) https://docs.microsoft.com/aspnet/core/security/authentication/2fa Enabling authentication using Facebook, Google and other external providers (Habilitar la autenticación con Facebook, Google y otros proveedores externos) https://docs.microsoft.com/aspnet/core/security/authentication/social/ Michell Anicas. An Introduction to OAuth 2 (Introducción a OAuth 2) https://www.digitalocean.com/community/tutorials/an-introduction-to-oauth-2 AspNet.Security.OAuth.Providers (repositorio de GitHub para proveedores de OAuth de ASP.NET). https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers/tree/dev/src Danny Strockis. Integrating Azure AD into an ASP.NET Core web app (Integración de Azure AD en una aplicación web de ASP.NET Core) https://azure.microsoft.com/resources/samples/active-directory-dotnet- webapp-openidconnect-aspnetcore/ IdentityServer4. Documentación oficial https://identityserver4.readthedocs.io/en/release/ A N T E R IO R S IG U IE N T E