Escogiendo entre .NET Core y .NET Framework para contenedores Docker
Para construir aplicaciones en contenedores Docker con .NET se puede utilizar tanto .NET Framework como .NET Core. Estos comparten muchos componentes de la plataforma .NET y, en general, se puede compartir mucho código entre ambos. Sin embargo, hay diferencias fundamentales entre ellos y la decisión dependerá de lo que se quiera lograr. En esta sección se ofrece una guía para decidir.
En esta sección se presenta un resumen para decidir cuándo seleccionar .NET Core y .NET Framework. Mostraremos más detalles en las secciones siguientes.
Debería utilizar .NET Core, con Linux o contenedores Windows, cuando:
En resumen, debería considerar .NET Core como la opción preferida para crear aplicaciones .NET en contenedores, ya que esto aporta muchos beneficios y encaja mejor en la filosofía y el estilo de trabajo con contenedores.
Un beneficio adicional de utilizar .NET Core, es que pueden ejecutar aplicaciones con versiones diferentes de .NET en la misma máquina. Aunque, definitivamente, esto es más importante para servidores o máquinas virtuales que no utilizan contenedores, ya que los contenedores permiten aislar las aplicaciones por completo. (Siempre que, por supuesto, las aplicaciones sean compatibles con el sistema operativo.)
Por otro lado, debería utilizar .NET Framework para sus aplicaciones en contenedores Docker cuando:
Usar .NET Framework en Docker puede mejorar la experiencia de despliegue, al minimizar los problemas de asociados.
Esto permite manejar un escenario de migración rápida, “Lift and Shift”, que es importante para contenerizar, aplicaciones antiguas (legacy) que fueron desarrolladas originalmente con el .NET Framework tradicional, por ejemplo, aplicaciones ASP.NET WebForms, Web MVC o con servicios WCF (Windows Communication Foundation).
La naturaleza modular y liviana de .NET Core lo hace perfecto para trabajar en contenedores. Cuando se despliega y arranca un contenedor, su imagen es mucho menor con .NET Core que con .NET Framework. Porque para usar .NET Framework en un contenedor, se debe basar la imagen en el Windows Server Core, que es mucho más pesado que el Windows Nano Server o las imágenes de Linux que se utilizan para .NET Core.
Además, .NET Core es multiplataforma, así que puede desplegar sus aplicaciones tanto con imágenes Linux como Windows. Sin embargo, si está utilizando .NET Framework, sólo puede desplegar imágenes basadas en Windows.
A continuación, se presenta una explicación más detallada de por qué escoger .NET Core.
Evidentemente, si su meta es tener una aplicación (web o servicio) que pueda correr en múltiples plataformas soportadas por Docker (Linux y Windows), la opción correcta es .NET Core, porque .NET Framework sólo soporta Windows.
.NET Core también soporta macOS como plataforma de desarrollo. Sin embargo, cuando se despliegan contenedores a un host Docker, ese host debe estar basado en Linux o Windows (al menos hasta comienzos de 2018).
Visual Studio ofrece un entorno integrado de desarrollo (IDE por sus siglas en inglés) para Windows con soporte para desarrollo en Docker.
Visual Studio para Mac es un IDE, la evolución de Xamarin Studio, que corre en macOS y también soporta el desarrollo de aplicaciones basadas en Docker. Este debería ser el entorno preferido para desarrollar en Mac ya que es muy poderoso.
También puede usar Visual Studio Code (VS Code) en macOS, Linux y Windows. VS Code soporta .NET Core por completo, incluyendo IntelliSense y depuración. Como VS Code es un editor ligero, se puede utilizar para desarrollar aplicaciones contenerizadas en la Mac, junto con la interfaz de línea de comandos de Docker (Docker CLI) y la interfaz de línea de comandos de .NET Core. También se puede desarrollar para .NET Core con la mayoría de los editores de terceros como Sublime, Emacs, vi y el proyecto open source OmniSharp, que también ofrece soporte para IntelliSense.
Además de los IDEs y editores mencionados, también puede usar las herramientas de la CLI de .NET Core para todas las plataformas soportadas.
Los contenedores se utilizan frecuentemente con una arquitectura de microservicios, aunque también se puede utilizar para aplicaciones web o servicios que siguen cualquier patrón arquitectónico. Puede usar .NET Framework en contenedores Windows, pero la naturaleza modular y liviana de .NET Core lo hace perfecto para contenedores y arquitectura de microservicios. Cuando se crea y se despliega un contenedor con .NET Core, su imagen es mucho más pequeña que con .NET Framework.
Se podría utilizar el .NET Framework tradicional para desarrollar aplicaciones basadas en microservicios, corriendo como procesos simples en vez de contenedores. De esta forma, como el .NET Framework ya está instalado y compartido entre todos los procesos, éstos son livianos y arrancan rápido. Sin embargo, si está usando contenedores, como la imagen para el .NET Framework tradicional se basa en el Windows Server Core, es muy pesada para un enfoque basado en microservicios.
En cambio, .NET Core es la mejor opción si ha decidido trabajar orientado a microservicios basado en contenedores, porque .NET Core es muy liviano. Además, los contenedores correspondientes, tanto en Linux como en Windows Nano Server, son sencillos y livianos, haciendo que las imágenes sean livianas y arranquen rápido.
Un microservicio debe ser lo más pequeño posible, para ser ligero al arrancar, ocupar poco espacio, tener un Bounded Context pequeño (ver DDD, Domain-Driven Design), representar ámbito reducido de funciones y ser capaz de arrancar y terminar rápidamente. Para lograr esto es necesario utilizar imágenes pequeñas y rápidas de instanciar, como las que se consiguen con .NET Core.
Una arquitectura de microservicios también le permite combinar servicios con distintas tecnologías. Esto facilita la migración gradual a .NET Core para microservicios nuevos o crear otros, desarrollados con Node.js, Python, Java, Go u otras tecnologías.
Cuando se necesita la mejor densidad, granularidad y rendimiento posible, para una aplicación basada en contenedores, la mejor opción es usar .NET Core y ASP.NET Core. ASP.NET Core es hasta diez veces más rápido que ASP.NET en el .NET Framework tradicional y está de puntero, junto con otras tecnologías populares en microservicios, como Java Servlets, Go y Node.js.
Esto es especialmente importante para una arquitectura de microservicios, donde puede haber cientos de contenedores corriendo al mismo tiempo. Con las imágenes de ASP.NET Core (basadas en .NET Core) en Linux o Windows Nano, puede ejecutar su sistema con una cantidad mucho menor de servidores o máquinas virtuales, ahorrando en costes de infraestructura y hospedaje.
Aunque .NET Core ofrece beneficios importantes para nuevas aplicaciones y patrones, .NET Framework seguirá siendo una buena opción para muchos escenarios existentes
Podría ser interesante usar contenedores Docker simplemente para simplificar el despliegue, incluso si no está creando microservicios. Por ejemplo, quizás quiera mejorar su proceso DevOps con Docker (con los contenedores se puede conseguir un entorno aislado de pruebas y también eliminar problemas de despliegue causados por dependencias faltantes cuando se mueven al entorno de producción). En casos como este, incluso si está desplegando una aplicación monolítica, tiene sentido usar Docker y contenedores Windows para sus aplicaciones actuales en .NET Framework.
En la mayoría de los casos para este escenario, no necesitará migrar su aplicación a .NET Core, simplemente puede usar contenedores Docker que incluyan el .NET Framework tradicional. Sin embargo, se recomienda usar .NET Core en la medida que se vayan extender las aplicaciones existentes, por ejemplo, desarrollando un nuevo servicio en ASP.NET Core.
Las librerías de terceros están adoptando rápidamente el .NET Standard, que permite compartir código entre las distintas variantes de .NET, incluyendo .NET Core. Con la versión 2.0 o superior de .NET Standard se ha ampliado significativamente el área de compatibilidad entre los distintos frameworks .NET y las aplicaciones .NET Core 2 pueden hacer referencia directa a los binarios de librerías existentes, gracias a un adaptador de compatibilidad (compat shim).
Además, recientemente se presentó el Windows Compatibility Pack, que amplía significativamente el conjunto de APIs disponibles y permite que el código existente se pueda compilar casi sin modificaciones, para correr en Windows.
Sin embargo, aún con el avance excepcional desde .NET Standard 2.0 y .NET Core 2.0, podría haber casos donde ciertos paquetes NuGet necesitan Windows para funcionar y no sean soportados en .NET Core. Si esos paquetes son críticos para su aplicación, entonces deberá usar el .NET Framework en contenedores Windows.
Algunas tecnologías en .NET Framework no están disponibles en la versión actual de .NET Core (versión 2.0 al momento de este escribir esta guía). Algunas estarán disponibles en versiones posteriores de .NET Core, pero, en otros casos, cuando no se aplican los patrones hacia donde apunta .NET Core, puede que nunca estén disponibles.
A continuación, se muestra la lista de algunas tecnologías que no están disponibles en .NET Core 2:
Además de las tecnologías incluidas oficialmente en el .NET Core roadmap, puede que se incluyan otras características para ser migradas a .NET Core. Para ver la lista completa, busque los ítems marcados como port-to-core, en el repositorio GitHub de CoreFX. tenga en cuenta que está lista no representa un compromiso de Microsoft para migrar esos componentes a .NET Core, esa lista sólo registra las solicitudes de la comunidad. Si le interesa alguno de los componentes indicados, considere participar en las discusiones en GitHub, para poder aportar sus opiniones. Y si le parece que falta algo, por favor, registre una incidencia en el repositorio de CoreFX.
Algunas plataformas de Microsoft o de terceros no soportan .NET Core. Por ejemplo, algunos servicios de Azure tienen un SDK que todavía no está disponible en .NET Core. Esto es una situación temporal, porque todos los servicios de Azure eventualmente utilizarán .NET Core. Por ejemplo, el Azure DocumentDB SDK for .NET Core fue lanzado como una versión preliminar el 16 de noviembre de 2016, pero desde hace tiempo está disponible (GA – General Avalability) como una versión estable.
Mientras tanto, si alguna plataforma o servicio en Azure aún no es compatible con .NET Core a través de su API de cliente, se puede usar el API REST equivalente desde el servicio Azure o el SDK de cliente en .NET Framework.
En la siguiente tabla de decisiones se resume cuándo usar .NET Framework o .NET Core. Recuerde que, para los contenedores Linux, necesita hosts Docker basados en Linux (máquinas virtuales o servidores) y que, para los contenedores Windows, necesita hosts Docker basados en Windows Server (máquinas virtuales o servidores).
Dada la diversidad de sistemas operativos soportados por Docker y las diferencias entre .NET Framework y .NET Core, debe apuntar a un sistema operativo específico y versiones específicas según el framework que esté utilizando.
Para Windows, puede usar Windows Server Core o Windows Nano Server. Estas versiones de Windows proporcionan diferentes características (IIS en Windows Server Core frente a un servidor web self-hosted como Kestrel en Nano Server) que podrían ser necesarias para .NET Framework o .NET Core, respectivamente.
Para Linux, están disponibles múltiples distribuciones y están soportadas en imágenes oficiales de .NET en Docker (como Debian).
En la Figura 3-1, puede ver las versiones posibles del sistema operativo según la versión de .NET utilizada.
Figura 3-1. Sistemas operativos a desplegar dependiendo de la version de .NET
También puede crear su propia imagen de Docker en los casos en que desee utilizar una distribución de Linux diferente o cuando necesite una imagen con versiones no proporcionadas por Microsoft. Por ejemplo, puede crear una imagen con ASP.NET Core ejecutándose en .NET Framework tradicional y Windows Server Core, que no es un escenario tan común para Docker.
Cuando agrega el nombre de la imagen a su fichero Dockerfile, puede seleccionar el sistema operativo y la versión según la etiqueta que use, como en los ejemplos siguientes:
microsoft/dotnet:2.0.0-runtime-jessie |
.NET Core 2.0 runtime-only en Linux |
microsoft/dotnet:2.0.0-runtime-nanoserver-1709 |
.NET Core 2.0 runtime-only en Windows Nano Server (Windows Server 2016 Fall Creators Update version 1709) |
microsoft/aspnetcore:2.0 |
.NET Core 2.0 multi-arquitectura: Soporta Linux y Windows Nano Server dependiendo del host. La imagen aspnetcore tiene algunas optimizaciones para ASP.NET Core. |
Las imágenes oficiales Docker para .NET son creadas y optimizadas por Microsoft. Están disponibles públicamente en los repositorios de Microsoft en Docker Hub. Cada repositorio puede contener múltiples imágenes, dependiendo de las versiones de .NET y el sistema operativo y sus versiones (Linux Debian, Linux Alpine, Windows Nano Server, Windows Server Core, etc.).
La visión de Microsoft en este aspecto, es tener repositorios .NET granulares y enfocados, donde un repositorio representa un escenario o tipo de trabajo específico. Por ejemplo, las imágenes en microsoft/aspnetcore se deben usar para trabajar con ASP.NET Core en Docker, porque incluyen optimizaciones adicionales para iniciar los contenedores más rápidamente.
Por otro lado, las imágenes .NET Core en microsoft/dotnet están pensadas para aplicaciones de consola basadas en .NET Core. Por ejemplo, los procesos por lotes, Azure WebJobs y otros escenarios de consola deben usar .NET Core. Esas imágenes no incluyen el framework ASP.NET Core, lo que resulta en una imagen de contenedor más pequeña.
La mayoría de los repositorios de imágenes proporcionan un etiquetado amplio para ayudarle a seleccionar no sólo una versión específica de framework, sino también para elegir un sistema operativo (distribución de Linux o versión de Windows).
Para obtener más información acerca de las imágenes oficiales Docker para .NET proporcionadas por Microsoft, consulte el resumen de imágenes Docker para .NET.
Al construir imágenes Docker para desarrolladores, Microsoft se enfocó en los siguientes escenarios principales:
¿Por qué múltiples imágenes? Al desarrollar, construir y ejecutar aplicaciones en contenedores, generalmente se tienen diferentes prioridades. Al proporcionar diferentes imágenes para estos escenarios, Microsoft ayuda a optimizar los procesos de desarrollo, construcción y despliegue de aplicaciones.
Durante el desarrollo, lo importante es qué tan rápido puede iterar al hacer los cambios y la capacidad de depurarlos. El tamaño de la imagen no es tan importante como la capacidad de realizar cambios en el código y verlos rápidamente. Algunas herramientas y "contenedores de build-agents" utilizan de la imagen de desarrollo de ASP.NET Core (microsoft/aspnetcore-build) durante el desarrollo y el proceso de construcción. Al construir dentro de un contenedor Docker, los aspectos importantes son los elementos que se necesitan para compilar la aplicación. Esto incluye el compilador y cualquier otra dependencia de .NET, además de dependencias de desarrollo web como npm, Gulp y Bower.
¿Por qué es importante este tipo de imagen de construcción? Porque esta imagen no se despliega en producción. En cambio, es una imagen que usas para construir el contenido que se coloca en una imagen de producción. Esta imagen se usaría en un entorno de integración continua (CI) o entorno de construcción. Por ejemplo, en lugar de instalar manualmente todas las dependencias de las aplicaciones directamente en un host con el agente de construcción (una máquina virtual, por ejemplo), el agente de construcción instanciaría una imagen de construcción .NET Core con todas las dependencias requeridas para construir la aplicación. El agente de construcción sólo necesita saber cómo ejecutar esta imagen Docker. Esto simplifica su entorno de CI y lo hace mucho más predecible.
En producción, lo importante es qué tan rápido se pueden desplegar e iniciar los contenedores en base a una imagen de producción .NET Core. Por lo tanto, esta imagen está basada en microsoft/aspnetcore y es pequeña, para que pueda viajar rápidamente a través de la red desde el registro Docker hasta los host Docker. Los contenidos están listos para ejecución, para lograr el menor tiempo posible desde el inicio del contenedor hasta obtener los resultados del procesamiento. En producción no hay necesidad de compilar el código C#, como ocurre cuando ejecuta dotnet build o dotnet publish al usar el contenedor de construcción.
En esta imagen optimizada, sólo se colocan los binarios y otro contenido necesario para ejecutar la aplicación. Por ejemplo, el contenido creado por dotnet publish sólo incluye los ficheros binarios .NET compilados, imágenes, ficheros .js y .css. Con el tiempo, incluso verá imágenes que contienen los paquetes pre-jitted (la compilación del IL al código nativo de la plataforma, que se hace en tiempo de ejecución al arrancar la aplicación).
Aunque hay múltiples versiones de las imágenes .NET Core y ASP.NET Core, todas comparten una o más capas, incluida una capa base. Por lo tanto, la cantidad de espacio en disco necesaria para almacenar una imagen es pequeña; sólo consiste en la variación (el delta) entre su imagen personalizada y su imagen base. El resultado es que las imágenes se extraen rápidamente del registro.
Al explorar los repositorios de imágenes .NET en Docker Hub, encontrará múltiples versiones de imágenes clasificadas o marcadas con etiquetas. Estas etiquetas ayudan a decidir cuál usar, dependiendo de la versión que necesite, como las de la siguiente tabla:
microsoft/aspnetcore:2.0 |
ASP.NET Core, sólo con runtime y optimizaciones para ASP.NET Core, en Linux y Windows (multi-arch) |
microsoft/aspnetcore-build:2.0 |
ASP.NET Core, incluyendo los SDKs, en Linux y Windows (multi-arch) |