Compartir a través de


Aprovechamiento de contenedores y orquestadores

Sugerencia

Este contenido es un extracto del libro electrónico, Arquitectura de aplicaciones .NET nativas de nube para Azure, disponible en .NET Docs o como un PDF descargable gratuito que se puede leer sin conexión.

Miniatura de la portada del libro electrónico

Los contenedores y orquestadores están diseñados para resolver problemas comunes a los enfoques de implementación monolíticos.

Desafíos con implementaciones monolíticas

Tradicionalmente, la mayoría de las aplicaciones se han implementado como una sola unidad. Estas aplicaciones se conocen como un monolito. Este enfoque general de la implementación de aplicaciones como unidades únicas incluso si se componen de varios módulos o ensamblados se conoce como arquitectura monolítica, como se muestra en la figura 3-1.

Arquitectura monolítica.

Figura 3-1. Arquitectura monolítica.

Aunque tienen el beneficio de la simplicidad, las arquitecturas monolíticas se enfrentan a muchos desafíos:

Despliegue

Además, requieren un reinicio de la aplicación, que puede afectar temporalmente a la disponibilidad si no se aplican técnicas de tiempo de inactividad cero durante la implementación.

Ampliación

Una aplicación monolítica se hospeda completamente en una sola instancia de máquina, a menudo requiere hardware de alta funcionalidad. Si alguna parte del monolito requiere escalado, se debe implementar otra copia de toda la aplicación en otra máquina. Con un monolito, no se pueden escalar los componentes de la aplicación individualmente, es todo o nada. El escalado de componentes que no requieren el escalado da como resultado un uso de recursos ineficaz y costoso.

Medio ambiente

Normalmente, las aplicaciones monolíticas se implementan en un entorno de hospedaje con un sistema operativo, un entorno de ejecución y dependencias de biblioteca preinstalados. Este entorno no puede coincidir con el que se desarrolló o probó la aplicación. Las incoherencias entre entornos de aplicación son un origen común de problemas para las implementaciones monolíticas.

Acoplamiento

Es probable que una aplicación monolítica experimente un acoplamiento elevado en sus componentes funcionales. Sin límites duros, los cambios del sistema suelen dar lugar a efectos secundarios no deseados y costosos. Las nuevas características y correcciones se vuelven complicadas, lentas y costosas de implementar. Las actualizaciones requieren pruebas exhaustivas. El acoplamiento también dificulta la refactorización de componentes o el intercambio en implementaciones alternativas. Incluso cuando se construye con una separación estricta de cada aspecto, la erosión arquitectónica se hace cada vez más patente a medida que la base de código monolítica se va deteriorando con un sinfín de "casos especiales".

Bloqueo de plataformas

Una aplicación monolítica se construye con una sola pila de tecnología. Aunque ofrece uniformidad, este compromiso puede convertirse en una barrera para la innovación. Las características y componentes nuevos se crearán con la pila actual de la aplicación, incluso cuando las tecnologías más modernas puedan constituir una mejor opción. Un riesgo a largo plazo es que el stack tecnológico se vuelva anticuado y obsoleto. Rediseñar una aplicación completa en una plataforma nueva y más moderna es a la mejor cara y arriesgada.

¿Cuáles son las ventajas de los contenedores y orquestadores?

Hemos introducido contenedores en el capítulo 1. Hemos resaltado cómo Cloud Native Computing Foundation (CNCF) clasifica la contenedorización como primer paso en su mapa de seguimiento deCloud-Native : guía para las empresas que comienzan su recorrido nativo de la nube. En esta sección se describen las ventajas de los contenedores.

Docker es la plataforma de administración de contenedores más popular. Funciona con contenedores en Linux o Windows. Los contenedores proporcionan entornos de aplicación independientes pero reproducibles que se ejecutan de la misma manera en cualquier sistema. Este aspecto hace que sean perfectos para desarrollar y hospedar servicios nativos en la nube. Los contenedores están aislados entre sí. Dos contenedores en el mismo hardware host pueden tener versiones diferentes de software, sin causar conflictos.

Los contenedores se definen mediante archivos simples basados en texto que se convierten en artefactos de proyecto y se comprueban en el control de código fuente. Aunque los servidores completos y las máquinas virtuales requieren un esfuerzo manual para actualizarlos, los contenedores se gestionan fácilmente con control de versiones. Las aplicaciones creadas para ejecutarse en contenedores se pueden desarrollar, probar e implementar mediante herramientas automatizadas como parte de una canalización de compilación.

Los contenedores son inmutables. Una vez definido un contenedor, puede volver a crearlo y ejecutarlo exactamente de la misma manera. Esta inmutabilidad se presta al diseño basado en componentes. Si algunas partes de una aplicación evolucionan de forma diferente a otras, ¿por qué volver a implementar toda la aplicación cuando solo puede implementar las partes que cambian con más frecuencia? Se pueden dividir diferentes características y preocupaciones transversales de una aplicación en unidades independientes. En la figura 3-2 se muestra cómo una aplicación monolítica puede aprovechar los contenedores y microservicios mediante la delegación de determinadas características o funcionalidades. La funcionalidad restante de la propia aplicación también se ha contenedorizado.

Descomposición de una aplicación monolítica para usar microservicios en el back-end

Figura 3-2. Descomponir una aplicación monolítica para adoptar microservicios.

Cada servicio nativo en la nube se compila e implementa en un contenedor independiente. Cada uno puede actualizar según sea necesario. Los servicios individuales se pueden hospedar en nodos con recursos adecuados para cada servicio. El entorno en el que se ejecuta cada servicio es inmutable, compartido entre entornos de desarrollo, pruebas y producción, y con versiones sencillas. El acoplamiento entre diferentes áreas de la aplicación se produce explícitamente como llamadas o mensajes entre servicios, no como dependencias en tiempo de compilación dentro del monolito. También puedes elegir la tecnología que mejor se adapte a una funcionalidad determinada sin necesidad de realizar cambios en el resto de la aplicación.

Los servicios en contenedores requieren administración automatizada. No sería factible administrar manualmente un gran conjunto de contenedores implementados de forma independiente. Por ejemplo, tenga en cuenta las siguientes tareas:

  • ¿Cómo se aprovisionarán las instancias de contenedor en un clúster con muchas máquinas?
  • Una vez implementado, ¿cómo detectarán los contenedores y se comunicarán entre sí?
  • ¿Cómo se puede aumentar o reducir el número de contenedores a demanda?
  • ¿Cómo se supervisa el estado de cada contenedor?
  • ¿Cómo protege un contenedor frente a errores de hardware y software?
  • ¿Cómo se actualizan los contenedores de una aplicación activa sin tiempo de inactividad?

Los orquestadores de contenedores abordan y automatizan estos y otros problemas.

En el sistema ecológico nativo de la nube, Kubernetes se ha convertido en el orquestador de contenedores de facto. Es una plataforma de código abierto administrada por Cloud Native Computing Foundation (CNCF). Kubernetes automatiza la implementación, el escalado y las preocupaciones operativas de las cargas de trabajo en contenedores en un clúster de máquinas. Sin embargo, la instalación y administración de Kubernetes es notoriamente compleja.

Un enfoque mucho mejor es aprovechar Kubernetes como servicio administrado de un proveedor de nube. La nube de Azure incluye una plataforma de Kubernetes totalmente administrada titulada Azure Kubernetes Service (AKS). AKS abstrae la complejidad y la sobrecarga operativa de la administración de Kubernetes. Consume Kubernetes como servicio en la nube; Microsoft asume la responsabilidad de administrarla y apoyarla. AKS también se integra estrechamente con otros servicios y herramientas de desarrollo de Azure.

AKS es una tecnología basada en clústeres. Un grupo de máquinas virtuales federadas o nodos se implementa en la nube de Azure. Juntos forman un entorno de alta disponibilidad o un clúster. El clúster aparece como una entidad única e integrada sin interrupciones en su aplicación nativa de la nube. En segundo plano, AKS implementa los servicios en contenedor en estos nodos siguiendo una estrategia predefinida que distribuye uniformemente la carga.

¿Cuáles son las ventajas de escalado?

Los servicios basados en contenedores pueden aprovechar las ventajas de escalado proporcionadas por herramientas de orquestación como Kubernetes. Por diseño, los contenedores solo saben de sí mismos. Una vez que tenga varios contenedores que necesiten trabajar juntos, debe organizarlos en un nivel superior. Organizar un gran número de contenedores y sus dependencias compartidas, como la configuración de una red... ¡ahí es donde las herramientas de orquestación entran en juego para salvarnos los muebles! Kubernetes crea una capa de abstracción sobre grupos de contenedores y las organiza en pods. Los pods se ejecutan en máquinas de trabajo denominadas nodos. Esta estructura organizada se conoce como un clúster. En la figura 3-3 se muestran los distintos componentes de un clúster de Kubernetes.

Componentes del clúster de Kubernetes. Figura 3-3. Componentes del clúster de Kubernetes.

El escalado de cargas de trabajo en contenedores es una característica clave de los orquestadores de contenedores. AKS admite el escalado automático entre dos dimensiones: instancias de contenedor y nodos de proceso. Juntos proporcionan a AKS la capacidad de responder de forma rápida y eficaz a los picos de demanda y agregar recursos adicionales. Analizaremos el escalado en AKS más adelante en este capítulo.

Declarativo frente a imperativo

Kubernetes admite la configuración declarativa e imperativa. El enfoque imperativo implica ejecutar varios comandos que indican a Kubernetes qué hacer cada paso del camino. Ejecute esta imagen. Elimina este pod. Expón este puerto. Con el enfoque declarativo, se crea un archivo de configuración, denominado manifiesto, para describir lo que desea en lugar de lo que debe hacer. Kubernetes lee el manifiesto y transforma el estado final deseado en estado final real.

Los comandos imperativos son excelentes para el aprendizaje y la experimentación interactiva. pero lo más conveniente es crear archivos de manifiesto de Kubernetes de forma declarativa para adoptar el uso de una infraestructura como enfoque de código, lo que dará lugar a implementaciones confiables y repetibles. El archivo de manifiesto se convierte en un elemento del proyecto y se usa en el flujo de trabajo de CI/CD para automatizar las implementaciones de Kubernetes.

Si ya ha configurado el clúster mediante comandos imperativos, puede exportar un manifiesto declarativo mediante kubectl get svc SERVICENAME -o yaml > service.yaml. Este comando genera un manifiesto similar al que se muestra a continuación:

apiVersion: v1
kind: Service
metadata:
  creationTimestamp: "2019-09-13T13:58:47Z"
  labels:
    component: apiserver
    provider: kubernetes
  name: kubernetes
  namespace: default
  resourceVersion: "153"
  selfLink: /api/v1/namespaces/default/services/kubernetes
  uid: 9b1fac62-d62e-11e9-8968-00155d38010d
spec:
  clusterIP: 10.96.0.1
  ports:
  - name: https
    port: 443
    protocol: TCP
    targetPort: 6443
  sessionAffinity: None
  type: ClusterIP
status:
  loadBalancer: {}

Al usar la configuración declarativa, puede obtener una vista previa de los cambios que se realizarán antes de confirmarlos mediante kubectl diff -f FOLDERNAME en la carpeta donde se encuentran los archivos de configuración. Una vez que esté seguro de que desea aplicar los cambios, ejecute kubectl apply -f FOLDERNAME. Agregue -R para procesar recursivamente una jerarquía de carpetas.

También puede usar la configuración declarativa con otras características de Kubernetes, una de las cuales son implementaciones. Las implementaciones declarativas ayudan a administrar versiones, actualizaciones y escalado. Indican al controlador de implementación de Kubernetes cómo implementar nuevos cambios, escalar horizontalmente o revertir a una revisión anterior. Si un clúster es inestable, una implementación declarativa devolverá automáticamente el clúster a un estado deseado. Por ejemplo, si un nodo se bloquea, el mecanismo de implementación desplegará nuevamente un reemplazo para alcanzar el estado deseado.

El uso de la configuración declarativa permite representar la infraestructura como código que se puede comprobar y versionar junto con el código de la aplicación. Proporciona un mejor control de los cambios y una mejor compatibilidad con la implementación continua mediante una canalización de tipo "crear e implementar".

¿Qué escenarios son ideales para usar contenedores y orquestadores?

Los escenarios siguientes son ideales para usar contenedores y orquestadores.

Aplicaciones que requieren un alto tiempo de actividad y escalabilidad

Las aplicaciones individuales que tienen requisitos de alto tiempo de actividad y escalabilidad son candidatas ideales para las arquitecturas nativas de la nube mediante microservicios, contenedores y orquestadores. Se pueden desarrollar en contenedores, probarse en entornos con versiones e implementarse en producción sin tiempo de inactividad. El uso de clústeres de Kubernetes garantiza que estas aplicaciones también se pueden escalar a petición y recuperarse automáticamente de errores de nodo.

Gran número de aplicaciones

Las organizaciones que implementan y mantienen un gran número de aplicaciones se benefician de contenedores y orquestadores. El esfuerzo inicial de configurar entornos en contenedor y clústeres de Kubernetes es principalmente un costo fijo. La implementación, el mantenimiento y la actualización de aplicaciones individuales tiene un costo que varía con el número de aplicaciones. Además de algunas aplicaciones, la complejidad de mantener las aplicaciones personalizadas manualmente supera el costo de implementar una solución mediante contenedores y orquestadores.

¿Cuándo debería usted evitar el uso de contenedores y orquestadores?

Si no puede construir su aplicación siguiendo los principios de Twelve-Factor App, debe considerar evitar el uso de contenedores y orquestadores. En estos casos, considere una plataforma de hospedaje basada en máquinas virtuales o, posiblemente, algún sistema híbrido. Siempre puedes desglosar ciertos elementos de funcionalidad en contenedores independientes o incluso funciones sin servidor.

Recursos de desarrollo

En esta sección se muestra una breve lista de recursos de desarrollo que pueden ayudarle a empezar a usar contenedores y orquestadores para la siguiente aplicación. Si busca instrucciones sobre cómo diseñar la aplicación de arquitectura de microservicios nativos en la nube, lea el complemento de este libro, Microservicios de .NET: Arquitectura para aplicaciones .NET en contenedor.

Desarrollo de Kubernetes local

Las implementaciones de Kubernetes proporcionan un gran valor en entornos de producción, pero también se pueden ejecutar localmente en la máquina de desarrollo. Aunque puede trabajar en microservicios individuales de forma independiente, puede haber ocasiones en las que tendrá que ejecutar todo el sistema localmente, igual que se ejecutará cuando se implemente en producción. Hay varias herramientas que pueden ayudar: Minikube y Docker Desktop. Visual Studio también proporciona herramientas para el desarrollo de Docker.

Minikube

¿Qué es Minikube? El proyecto Minikube dice que "Minikube implementa un clúster de Kubernetes local en macOS, Linux y Windows". Sus objetivos principales son "ser la mejor herramienta para el desarrollo de aplicaciones locales de Kubernetes y admitir todas las características de Kubernetes que se ajusten". La instalación de Minikube es independiente de Docker, pero Minikube admite hipervisores diferentes que los compatibles con Docker Desktop. Las siguientes características de Kubernetes son compatibles actualmente con Minikube:

  • DNS (Sistema de Nombres de Dominio)
  • NodePorts
  • ConfigMaps y secretos
  • Tableros de control
  • Entornos de ejecución de contenedor: Docker, rkt, CRI-O y containerd
  • Habilitar la interfaz de red para contenedores (CNI)
  • Entrada

Después de instalar Minikube, puede empezar a usarlo rápidamente mediante la ejecución del minikube start comando , que descarga una imagen e inicia el clúster de Kubernetes local. Una vez iniciado el clúster, interactúe con él mediante los comandos estándar de Kubernetes kubectl .

Docker Desktop

También puede trabajar con Kubernetes directamente desde Docker Desktop en Windows. Es la única opción si usa contenedores de Windows y también es una opción excelente para los contenedores que no son de Windows. En la figura 3-4 se muestra cómo habilitar la compatibilidad con Kubernetes local al ejecutar Docker Desktop.

Configuración de Kubernetes en Docker Desktop

Figura 3-4. Configuración de Kubernetes en Docker Desktop.

Docker Desktop es la herramienta más popular para configurar y ejecutar aplicaciones en contenedores localmente. Cuando trabajas con Docker Desktop, tienes la posibilidad de desarrollar localmente con el mismo conjunto preciso de imágenes de contenedor de Docker que implementarás en producción. Docker Desktop está diseñado para "compilar, probar y enviar" aplicaciones en contenedores localmente. Admite contenedores de Linux y Windows. Una vez insertadas las imágenes en un registro de imágenes, como Azure Container Registry o Docker Hub, AKS puede extraerlas e implementarlas en producción.

Herramientas de Docker de Visual Studio

Visual Studio admite el desarrollo de Docker para aplicaciones basadas en web. Al crear una nueva aplicación ASP.NET Core, tiene la opción de configurarla con compatibilidad con Docker, como se muestra en la figura 3-5.

Habilitación de La compatibilidad con Docker en Visual Studio

Figura 3-5. Habilitar la compatibilidad con Docker en Visual Studio

Cuando se selecciona esta opción, el proyecto se crea con un Dockerfile en su raíz, que se puede usar para crear y alojar la aplicación en un contenedor de Docker. En la figura 3-6 se muestra un dockerfile de ejemplo.

FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /src
COPY ["eShopWeb/eShopWeb.csproj", "eShopWeb/"]
RUN dotnet restore "eShopWeb/eShopWeb.csproj"
COPY . .
WORKDIR "/src/eShopWeb"
RUN dotnet build "eShopWeb.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "eShopWeb.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "eShopWeb.dll"]

Figura 3-6. Dockerfile generado por Visual Studio

Una vez agregada la compatibilidad, puede ejecutar la aplicación en un contenedor de Docker en Visual Studio. En la figura 3-7 se muestran las distintas opciones de ejecución disponibles en un nuevo proyecto de ASP.NET Core creado con la compatibilidad con Docker agregada.

Opciones de ejecución de Docker de Visual Studio

Figura 3-7. Opciones de ejecución de Docker de Visual Studio

Además, en cualquier momento puede agregar compatibilidad con Docker a una aplicación ASP.NET Core existente. En el Explorador de soluciones de Visual Studio, haga clic con el botón derecho en el proyecto y seleccione Agregar>compatibilidad con Docker, como se muestra en la figura 3-8.

Adición de compatibilidad con Docker en Visual Studio

Figura 3-8. Adición de compatibilidad con Docker a Visual Studio

Herramientas Docker de Visual Studio Code

Hay muchas extensiones disponibles para Visual Studio Code que admiten el desarrollo de Docker.

Microsoft proporciona la extensión Docker para Visual Studio Code. Esta extensión simplifica el proceso de agregar compatibilidad con contenedores a las aplicaciones. Estructura los archivos necesarios, construye imágenes de Docker y le permite depurar su aplicación dentro de un contenedor. La extensión incluye un explorador visual que facilita la ejecución de acciones en contenedores e imágenes, como iniciar, detener, inspeccionar, quitar y mucho más. La extensión también admite Docker Compose, lo que le permite administrar varios contenedores en ejecución como una sola unidad.