Capítulo 4. Nube
Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y comentarios: translation-feedback@oreilly.com
Pocas tendencias tecnológicas han tenido el impacto que la computación en nube ha tenido en la industria de TI, salvo quizás la virtualización, que es una tecnología fundamental para la computación en nube. Como ingeniero de redes, quizá te preguntes: "¿Qué tiene que ver conmigo la computación en nube?". Algunos ingenieros de redes pueden descartar la computación en nube, pensando que sus conocimientos no son necesarios para los entornos basados en la nube. Otros, incluidos los autores, opinan de forma muy distinta.
Las redes son tan importantes "en la nube" como en un entorno local. De hecho, algunos dirían que las redes son incluso más importantes en la nube. Además, para aprovechar realmente las ventajas de la computación en la nube -fácil escalado de entrada y salida, recursos a la carta y entornos efímeros, entre otros- es necesario adoptar la automatización. Por tanto, es natural que un libro sobre automatización y programabilidad de redes trate el papel de la computación en nube en el contexto de la automatización de redes.
Se han escrito y probablemente se seguirán escribiendo tomos enteros sobre los distintos proveedores de la nube, los servicios que ofrecen, cómo diseñar soluciones para los proveedores de la nube y mucho más. Teniendo esto en cuenta, este capítulo pretende ser un poco más compacto y proporcionar sólo lo esencial que necesitas como ingeniero de redes para añadir la computación en nube a tu arsenal de herramientas para resolver los problemas que te encuentras a diario.
El mejor punto de partida es establecer una definición básica de la computación en nube.
Breve definición de computación en nube
Aunque existen muchas definiciones de la computación en nube (basta con realizar una búsqueda en Internet de "qué es la computación en nube"), una de las definiciones por excelencia procede del Departamento de Comercio de Estados Unidos, concretamente del Instituto Nacional de Normas y Tecnología (NIST). La definición de computación en nube del NIST, recogida en la Publicación Especial 800-145, es una definición polifacética que aborda las características clave de la computación en nube, así como los modelos de servicio e implementación. La definición del NIST describe todas estas cosas de forma agnóstica respecto a los proveedores.
El NIST define cinco características esenciales de la computación en nube:
- Autoservicio a la carta
-
Los usuarios (el NIST se refiere a los "consumidores") deben poder aprovisionar sus propios recursos del proveedor de la nube según sus necesidades, sin necesidad de interacción humana (en otras palabras, ¡sin tener que presentar un ticket de servicio!).
- Amplio acceso a la red
-
Los entornos en nube son intrínsecamente entornos en red, accesibles a través de la red. Aquí es donde se origina la afirmación de que la red es igual de importante en la nube: sin acceso ubicuo a la red, no es computación en nube.
- Puesta en común de recursos
-
Los recursos del proveedor de la nube se agrupan y se sirven a varios inquilinos automáticamente. Esto incluye recursos físicos y virtuales y se aplica no sólo a la capacidad informática, sino también al almacenamiento y a la red.
- Elasticidad rápida
-
Los recursos de la nube deben poder escalarse "infinitamente" (desde la perspectiva del usuario, parecen ilimitados). Aumentarlos o reducirlos, en función de la demanda, debe ser posible, incluso automático en algunos casos.
- Servicio medido
-
Los recursos de la nube se miden, y a los usuarios se les cobra por lo que utilizan. El uso se puede monitorizar, controlar y notificar.
La definición del NIST también define tres modelos de servicio:
- Software como servicio (SaaS)
-
El "servicio" suministrado aquí es el acceso a las aplicaciones de un proveedor, que probablemente se ejecutan en una infraestructura en la nube. Sin embargo, la infraestructura de la nube no está expuesta a los usuarios; sólo la aplicación y la(s) interfaz(es) de la aplicación están expuestas a los usuarios. Algunos ejemplos de ofertas SaaS son Salesforce, Okta y Microsoft 365.
- Plataforma como servicio (PaaS)
-
En el caso del PaaS, los usuarios tienen acceso a una plataforma -que incluye "lenguajes de programación, bibliotecas, servicios y herramientas respaldados por el proveedor" (según la terminología del NIST)- en la que pueden implementar aplicaciones. Estas aplicaciones pueden ser adquiridas por el usuario (compradas o bajo licencia) o creadas por él mismo. Los usuarios no tienen acceso a la infraestructura de la nube bajo esta plataforma; sólo la(s) interfaz(es) de la plataforma está(n) expuesta(s) a los usuarios. Ejemplos de ofertas PaaS son Heroku y AWS Elastic Beanstalk.
- Infraestructura como servicio (IaaS)
-
El último modelo de servicio es el IaaS, en el que los usuarios tienen acceso a la provisión de recursos informáticos, de almacenamiento, de red, de seguridad y otros recursos fundamentales para desplegar software arbitrario, hasta instancias de SO y aplicaciones. Los usuarios no tienen acceso a la infraestructura de la nube directamente, pero tienen acceso a controlar la forma en que se aprovisionan los recursos fundamentales. Por ejemplo, los usuarios no tienen acceso a los componentes subyacentes que hacen funcionar una Nube Privada Virtual (VPC) de Amazon, pero sí tienen capacidad para crear y configurar VPC. Ejemplos reales de ofertas de IaaS son muchos aspectos de AWS, muchos servicios de Microsoft Azure y múltiples servicios de Google Cloud.
Nota
La redacción que utilizamos en nuestros ejemplos de IaaS - "muchos aspectos de" o "muchos servicios prestados por"- es intencionada. Todos, o casi todos, los proveedores de nube ofrecen una variedad de servicios que se encuadran en diferentes modelos de servicio. Algunas ofertas son directamente IaaS, mientras que otras pueden clasificarse como PaaS o incluso SaaS.
Por último, la definición del NIST habla de los modelos de implementación:
- Nube privada
-
En una nube privada, la infraestructura de la nube -definida por el NIST como el conjunto de hardware y software que permite las cinco características esenciales de la computación en nube- se aprovisiona para su uso por una sola entidad, como una empresa. Esta infraestructura puede estar dentro o fuera de las instalaciones; la ubicación no importa. Del mismo modo, puede ser operada por la organización o por otra persona. La distinción clave aquí es el público al que va dirigido, que es sólo la organización o entidad.
- Nube comunitaria
-
Una nube comunitaria se proporciona para su uso por una comunidad compartida de usuarios: un grupo de empresas con requisitos normativos compartidos, quizás, u organizaciones que comparten una causa común. Puede ser propiedad de una o varias de las organizaciones de la comunidad o de un tercero. Del mismo modo, puede ser explotada por uno o varios miembros de la comunidad o por un tercero y no distingue entre dentro y fuera de las instalaciones.
- Nube pública
-
La infraestructura de una nube pública se proporciona para su uso por el público en general. La propiedad, gestión y explotación de la nube pública puede corresponder a una empresa, una organización gubernamental, una entidad académica o una combinación de éstas. Una nube pública existe en las instalaciones del proveedor (la organización que posee, gestiona y opera la infraestructura de la nube).
- Nube híbrida
-
Una nube híbrida es simplemente una composición de dos o más infraestructuras de nube distintas que están unidas de alguna manera, ya sea por tecnología estandarizada o propietaria que permite cierto nivel de portabilidad de datos y aplicaciones entre las infraestructuras de nube.
Como puedes ver en estos tres aspectos -las características esenciales de la computación en nube, los modelos de servicio mediante los que se ofrece a los usuarios y los modelos de implementación mediante los que se suministra a un público-, la computación en nube es polifacética.
En este capítulo, nos centramos principalmente en los modelos de implementación de la nube pública, que suelen corresponder al modelo de servicio IaaS.
Nota
Decimos "suelen pertenecer al modelo de servicios IaaS" porque algunas ofertas de servicios no pueden clasificarse claramente como IaaS o PaaS. El sector ha acuñado incluso nuevos términos, como funciones como servicio(FaaS) o base de datos como servicio(DBaaS), para describir ofertas que no entran claramente en una de las categorías definidas por el NIST.
Ahora, una vez definidas las distintas formas de computación en la nube, conviene que dirijas tu atención a los fundamentos de la creación de redes en la nube.
Fundamentos de las redes en la nube
Aunque el funcionamiento de las redes es un poco diferente en los entornos en nube, al final, las redes en nube son sólo redes. Muchas (¿la mayoría?) de las habilidades que has desarrollado como ingeniero de redes se siguen aplicando al trabajar en redes en entornos de nube, y los conocimientos y la experiencia que tanto te ha costado adquirir no se echarán a perder.
En concreto, ya no tendrás que preocuparte mucho por los detalles de bajo nivel (es decir, no necesitas saber cómo se implementan las construcciones de red, como las redes virtuales, sólo tienes que utilizar las abstracciones), pero cosas como los protocolos de enrutamiento, las topologías de enrutamiento, el diseño de topologías de red y la planificación de direcciones IP siguen siendo muy necesarias. Empecemos echando un vistazo a algunos de los componentes clave de las redes en la nube.
Bloques de construcción de redes en la nube
Aunque los detalles específicos varían entre los proveedores de la nube, todos los principales ofrecen algunas características y funcionalidades básicas (genéricas):
- Aislamiento lógico de la red
-
Del mismo modo que los protocolos de encapsulación como VXLAN, Generic Routing Encapsulation (GRE) y otros crean redes superpuestas que aíslan el tráfico de red lógico de la red física, todos los principales proveedores de nube ofrecen mecanismos de aislamiento de red. En AWS, se trata de una Nube Privada Virtual (VPC), que explicamos mejor en "Una pequeña topología de red en la nube"; en Azure, es una Red Virtual (también conocida como VNet); y en Google Cloud, se llama simplemente Red. Independientemente del nombre, el propósito básico es el mismo: proporcionar un medio por el que las redes lógicas puedan aislarse y segregarse unas de otras. Estas redes lógicas proporcionan una segregación completa de todas las demás redes lógicas, pero -como verás dentro de un momento- aún pueden conectarse de diversas formas para lograr los resultados deseados.
- Direccionamiento público y privado
-
A las cargas de trabajo se les pueden asignar direcciones públicas (enrutables) o privadas (no enrutables). Ver RFC 1918 y RFC 6598. El proveedor de la nube suministra mecanismos para proporcionar conectividad a Internet tanto a las cargas de trabajo públicas como a las privadas. Todo esto suele hacerse en la capa de red lógica, lo que significa que cada red lógica puede tener sus propias asignaciones de direcciones IP. Se permite el solapamiento de espacios de direcciones IP entre varias redes lógicas.
- Direccionamiento persistente
-
Las direcciones de red pueden asignarse a la cuenta de un cliente del proveedor de la nube, y luego asignarse a una carga de trabajo. Más tarde, esa misma dirección puede desasignarse de la carga de trabajo y asignarse a una carga de trabajo diferente, pero la dirección permanece constante y persistente hasta que se libera de la cuenta del cliente y se devuelve al proveedor de la nube. Las direcciones IP elásticas (EIP) en AWS o las direcciones IP públicas en Azure son ejemplos.
- Topologías complejas
-
Los bloques de construcción de aislamiento de redes lógicas (VPC en AWS, VNets en Azure, Redes en Google Cloud) pueden combinarse de formas complejas, y los ingenieros de redes tienen control sobre cómo se enruta el tráfico entre estos bloques de construcción. Las grandes empresas que utilizan una cantidad significativa de recursos en la nube pueden acabar teniendo varios cientos de redes lógicas, junto con conexiones VPN de sitio a sitio y conexiones de vuelta a las redes locales (autogestionadas). El nivel de complejidad aquí puede rivalizar o superar fácilmente incluso a las redes locales más completas, y la necesidad de ingenieros de red cualificados que sepan diseñar estas redes y automatizar su implementación es grande.
- Equilibrio de carga
-
Los proveedores de la nube ofrecen soluciones de equilibrio de carga que se aprovisionan bajo demanda y proporcionan equilibrio de carga de Capa 4 (TCP/UDP), equilibrio de carga de Capa 7 (HTTP) (con o sin terminación TLS), equilibrio de carga interno (interno al espacio de direcciones privado/no enrutable de un cliente dentro de una VPC o VNet), o equilibrio de carga externo (para tráfico procedente de fuentes basadas en Internet).
Nota
Para los ingenieros de redes que se están acostumbrando a las redes en la nube, el libro de Timothy McConnaughy The Hybrid Cloud Handbook for AWS (Carpe DMVPN) es una buena introducción a los fundamentos de las redes de AWS.
Los principales proveedores de nube ofrecen muchos otros servicios de red que no se tratan aquí, como pasarelas API, controles de acceso a nivel de red, controles de acceso a nivel de instancia e incluso compatibilidad con dispositivos de red de terceros para realizar funciones adicionales. Además, todos estos servicios de red están disponibles bajo demanda. Los usuarios sólo tienen que iniciar sesión en su consola de gestión de la nube, utilizar la herramienta CLI del proveedor de la nube, interactuar con las API del proveedor de la nube, o incluso utilizar una de las numerosas herramientas de automatización de la nube para instanciar uno o varios de estos servicios de red (hablaremos de estas herramientas con más detalle en el Capítulo 12). Nuestro objetivo en este capítulo no es tratar de explicarlos todos, sino ayudarte a ver cómo encajan estas piezas en el panorama general de la programabilidad y automatización de la red.
Con estos conocimientos de alto nivel sobre los componentes básicos de las redes en la nube, vamos a centrarnos en algunos ejemplos de cómo ensamblarlos.
Topologías de red en la nube
Para ayudar a solidificar los conceptos que subyacen a las construcciones relativamente genéricas descritas en la sección anterior, esta sección reduce el debate a las ofertas reales de un proveedor específico de nubes públicas: AWS. AWS es el mayor de los proveedores de nubes públicas, tanto por la amplitud de los servicios que ofrece como por su adopción en el mercado (según un informe de Gartner de junio de 2022, AWS tenía el 38,9% del mercado total de nubes). Por tanto, es un buen punto de partida. Sin embargo, conceptos similares se aplican a otros proveedores de nubes, como Microsoft Azure, Google Cloud Platform, Alibaba Cloud, Oracle Cloud, DigitalOcean y otros.
Consejo
Hay muchos recursos en línea que pueden ayudarte a conocer las buenas prácticas de diseño de los proveedores de la nube. En el caso de AWS, puedes empezar con el marco AWS Well-Architected.
Esta sección analiza cuatro escenarios:
-
Un entorno pequeño con una sola red lógica
-
Un entorno de tamaño medio con unas pocas o varias redes lógicas
-
Un entorno más amplio con varias decenas de redes lógicas
-
Un entorno híbrido con redes locales y en la nube
Una topología de red de nube pequeña
Nuestra pequeña topología de red en la nube aprovecha una sola VPC. Al principio, esto puede parecer limitante: ¿podrá una sola VPC escalar lo suficiente? ¿Es este tipo de topología válida sólo para los clientes más pequeños, los que sólo tienen unas pocas cargas de trabajo en la nube pública?
Nota
El concepto de red virtual, como AWS VPC, es el elemento clave para la creación de redes en la nube. Aunque cada proveedor de nube implementa esta construcción de forma un poco diferente, la idea básica es la misma: una red virtual es una red lógica (con algunas características de Capa 2 y 3) que puede utilizarse para aislar las cargas de trabajo entre sí. Todos los servicios en la nube se conectan a una red virtual para que puedan comunicarse entre sí (suponiendo que existan los controles de acceso a la red adecuados).
Al pensar en la escalabilidad, considera estas dimensiones:
- Direccionamiento IP
-
Cuando creas una VPC, debes especificar un bloque de Enrutamiento entre dominios sin clase (CIDR). Se recomienda utilizar bloques de los rangos especificados en RFC 1918 y RFC 6598. El tamaño del bloque puede variar desde un /16 (que proporciona hasta 65.536 direcciones IP) hasta un /28 (que sólo proporciona 16 direcciones IP). Además, puedes añadir bloques CIDR -hasta cuatro bloques adicionales no superpuestos para un total de cinco bloques CIDR en una VPC- para proporcionar aún más direcciones IP disponibles. Cinco bloques CIDR /16 proporcionarían más de 300.000 direcciones IP.
- Ancho de banda
-
AWS proporciona dos mecanismos para conectar cargas de trabajo a Internet. Para las cargas de trabajo en una subred pública, se utiliza una puerta de enlace a Internet. AWS describe este componente como "un componente de VPC de escala horizontal, redundante y de alta disponibilidad" que "no causa riesgos de disponibilidad ni limitaciones de ancho de banda"; consulta la documentación de Amazon VPC. Para las cargas de trabajo en una subred privada, se suele utilizar una pasarela NAT , que según AWS escala hasta 100 Gbps. NAT significa traducción de direcciones de red, que es el nombre que recibe el proceso de asignación de direcciones IP privadas (no enrutables) a direcciones IP públicas (enrutables).
Nota
A efectos de este libro, una subred pública se define como una subred que está conectada o tiene una ruta por defecto a una pasarela de Internet. Normalmente, una subred pública también se configura para asignar una dirección IP enrutable a los recursos. Una subred privada se define como una subred que está conectada o tiene una ruta por defecto a una pasarela NAT (o a una instancia NAT autogestionada). Una subred privada también suele estar configurada para utilizar sólo direcciones IP no enrutables (RFC 1918/6598).
- Disponibilidad
-
Una única VPC puede abarcar varias zonas de disponibilidad, pero no varias regiones. Una región en el lenguaje de AWS es una agrupación de centros de datos. Una zona de disponibilidad(AZ) dentro de una región es una agrupación lógica físicamente separada y aislada de centros de datos. Cada zona de disponibilidad tiene energía independiente, refrigeración y conectividad de red redundante. Las AZ dentro de una región tienen conexiones de alta velocidad y baja latencia entre ellas. La idea que subyace a la expansión de varias AZ es que un fallo en una AZ de una región no afectará, por lo general, a otras AZ de una región, lo que te da la capacidad de soportar fallos de una forma más resistente. Una subred está limitada a una sola AZ y no puede abarcar varias AZ.
- Seguridad
-
AWS ofrece mecanismos de filtrado del tráfico a nivel de red (denominados listas de control de acceso a la red) y mecanismos de filtrado del tráfico a nivel de host (denominados grupos de seguridad). Las listas de control de acceso a la red (ACL de red o NACL) son sin estado y funcionan a nivel de red (concretamente, a nivel de subred). Las reglas NACL se procesan en orden, según el número asignado a la regla (piensa en el número como una prioridad: la regla con el número más bajo decide primero o tiene la prioridad más alta). Los grupos de seguridad, en cambio, son de estado, operan a nivel de instancia y evalúan todas las reglas antes de decidir si permiten o no el tráfico. Las NACL y los grupos de seguridad pueden -y deben- utilizarse juntos para ayudar a proporcionar el mayor nivel de control sobre los tipos de tráfico que se permiten o no entre cargas de trabajo dentro de una VPC.
Ninguna de estas áreas presenta limitaciones o restricciones significativas, lo que demuestra que incluso un diseño de red en la nube que aproveche una única VPC puede escalar hasta miles de cargas de trabajo, proporcionando suficiente ancho de banda, disponibilidad y seguridad a las cargas de trabajo que alberga.
En la Figura 4-1, reunimos todos estos conceptos para crear un diseño típico de VPC única en AWS.
Todas estas cosas podrían crearse manualmente y enlazarse mediante la consola de administración de AWS, pero la oportunidad para el ingeniero de redes en este caso es automatizar este proceso utilizando herramientas y procesos de IaC. En "Automatización de redes en la nube", hablamos de las opciones programables disponibles para gestionar los recursos de la nube.
Una topología de red de nube media
Aunque una única VPC es muy escalable, hay razones para utilizar varias VPC. La razón más obvia es que una VPC no puede abarcar una región de AWS; cada VPC está limitada a la región en la que se creó. Si necesitas diseñar una topología de red que pueda acomodar cargas de trabajo en varias regiones, lo que buscas es una topología multi-VPC. También hay otras razones para utilizar varias VPC; que puedas poner miles de cargas de trabajo en una sola VPC no significa necesariamente que debas hacerlo.
Los proveedores de nubes públicas diseñan su infraestructura para que sea altamente resistente y lo expresan en forma de SLA de disponibilidad y durabilidad. Para ir más allá de estas ofertas de servicios, tienes que crear tus propias arquitecturas de alta disponibilidad, del mismo modo que hacemos en los centros de datos locales.
En cuanto tu topología pasa a múltiples VPC, surgen nuevos retos para el ingeniero de redes:
- Gestión del espacio de direcciones IP
-
Aunque cada VPC puede tener bloques CIDR idénticos y solapados, en cuanto quieras conectar VPC entre sí, necesitarás tener bloques CIDR no solapados. Es necesario diseñar un plan sobre cómo se repartirán y asignarán las direcciones IP a las VPC de varias regiones, al tiempo que se planifica el crecimiento futuro (en términos de subredes futuras en las VPC existentes, VPC adicionales en las regiones actuales y nuevas VPC en regiones completamente nuevas).
- Conectividad
-
AWS proporciona una forma de conectar VPCs entre sí a través del peering de VPC. El peering de VPC proporciona conectividad no transitiva entre dos VPC y requiere que las tablas de rutas de VPC se actualicen explícitamente con rutas a VPC pares. Cuando el número de VPC es pequeño, el uso del peering de VPC es manejable, pero a medida que el número de VPC crece, el número de conexiones -y el número de tablas de rutas que hay que actualizar y mantener- también crece.
- Precios basados en el uso
-
Aunque como ingeniero de redes puedas diseñar una topología que proporcione la conectividad necesaria entre todas las cargas de trabajo en todas las VPC de todas las regiones, ¿proporcionará tu topología esa conectividad de la forma más rentable cuando aumente el uso? Esto significa tener en cuenta las tarifas de tráfico entre Zonas, entre Regiones y las tarifas de salida a destinos basados en Internet (por nombrar sólo algunos).
Todas estas consideraciones se suman a lo que ya se necesita para las topologías de red en la nube más pequeñas (VPC única).
Una gran topología de red en la nube
En una gran topología de red en la nube, te enfrentas a varias docenas de VPC en varias regiones de todo el mundo. El peering de VPC ya no es una opción útil; simplemente hay demasiadas conexiones de peering que gestionar, y la conectividad no transitiva está introduciendo aún más complejidad.
Surge un nuevo conjunto de consideraciones de diseño:
- Conectividad centralizada
-
Utilizando herramientas como las VPC de tránsito y las puertas de enlace de tránsito, puedes pasar de la conectividad de peering de VPC no transitiva a una solución totalmente enrutada. Estas herramientas también te permiten integrar opciones adicionales como AWS Direct Connect (para una conectividad dedicada de vuelta a las redes locales) o capacidades VPN. También es posible implementar una salida centralizada, en la que las VPC "radios" utilicen las puertas de enlace NAT de la VPC "central" para la conectividad a Internet. En todos estos casos, tienes que definir una estrategia de enrutamiento, como el enrutamiento estático o un protocolo de enrutamiento dinámico como BGP.
- Arquitecturas multicuenta
-
En topologías tan grandes, es mucho más probable que te encuentres con situaciones en las que VPC enteras pertenezcan a diferentes cuentas de AWS. Tu topología de red debe abordar ahora cómo proporcionar conectividad de forma segura entre recursos que pertenecen a entidades diferentes.
Utilizando las mencionadas herramientas de conectividad centralizada, podemos ampliar las redes en la nube para que lleguen a otros proveedores en la nube y a las redes locales.
Una topología de red de nube híbrida
Con el aumento de la adopción de servicios en la nube, cada vez son más comunes los nuevos escenarios de interconexión. Estos escenarios incluyen la conexión de distintos proveedores de nube (pública o privada) y la conexión de estos proveedores de nube a redes locales; el Informe sobre el Estado de la Nube 2023 de Flexera dice que el 87% de las organizaciones encuestadas utilizan más de un proveedor de nube. Además, los límites entre estos entornos son cada vez más difusos, porque los servicios en la nube pueden funcionar en los centros de datos locales de sus clientes (por ejemplo, el servicio AWS Outposts). El sector utiliza el término nube híbrida para describir estos escenarios.
Aparte de los retos de gestionar y orquestar varios servicios de infraestructura (y las aplicaciones que se encuentran encima), el primer reto obvio es cómo conectar todos estos entornos entre sí.
La conectividad de la nube híbrida no tiene una solución única. Todos los proveedores de servicios de nube pública ofrecen mecanismos para conectar sus servicios a las redes locales. Estos mecanismos suelen basarse en conexiones VPN o servicios de conectividad dedicados (por ejemplo, AWS Direct Connect o Azure ExpressRoute). La Figura 4-2 muestra un ejemplo de topología de red de nube híbrida: una red local se conecta a las nubes AWS y Azure mediante servicios de transporte dedicados, y la información de enrutamiento se intercambia mediante BGP. Observa que también podrías conectar ambos proveedores de nube a través de la red local.
Nota
Cuando conectas tu red a un proveedor de la nube, puedes llegar a tus propias cargas de trabajo en la nube (por ejemplo, tus bases de datos y máquinas virtuales), pero también a los servicios generales de la nube. En el caso de AWS, puedes llegar al sistema de almacenamiento de objetos Amazon Simple Storage Service (S3) y a muchos más.
Sin embargo, estos mecanismos de conectividad no son capaces de interconectar directamente a los proveedores en nube. Para interconectar a los proveedores de la nube, tienes que utilizar otras soluciones, como enrutarlos a través de una red local, como se describe en la Figura 4-2, o crear tus propias conexiones VPN entre los proveedores de la nube para crear una red superpuesta. Si la gestión de esta interconexión resulta engorrosa, puedes recurrir a proveedores externos que orquesten estas conexiones por ti. Dos ejemplos de estos proveedores son Aviatrix y Alkira.
En cualquier caso, tienes que diseñar una topología de red que permita conectar todos estos entornos, con un direccionamiento de red y un plan de encaminamiento coherentes entre los segmentos de la red. Esto requiere las habilidades de red que has desarrollado en tu carrera de ingeniería de redes.
Además, a medida que crece el nivel de complejidad, aumenta exponencialmente la necesidad de automatización. Hay más "cosas" que gestionar y configurar, como conexiones de pasarela de tránsito, conexiones VPN o subredes separadas para determinados tipos de conexiones. Gestionarlas eficazmente requerirá alguna forma de automatización.
Automatización de redes en la nube
Las redes en la nube, a diferencia de muchas redes locales, están intrínsecamente diseñadas para la automatización. Es un subproducto de las cualidades de la computación en nube según la definición del NIST proporcionada en "Breve definición de computación en nube". Podemos enfocar la automatización de la red en la nube de tres maneras:
-
Utilizar directamente las API del proveedor de la nube
-
Utilizar la herramienta CLI del proveedor de la nube
-
Utilizar una herramienta especialmente diseñada para automatizar la nube
Utilizar las API del proveedor de la nube
Los proveedores de la nube ofrecen plataformas API-first. Disponen de un rico conjunto de API que pueden utilizarse para automatizar el aprovisionamiento de recursos en la nube. Cualquier otro método (es decir, herramientas CLI o herramientas creadas específicamente) no es más que una envoltura de estas API.
Sin embargo, normalmente no utilizarás las API directamente. Incluso cuando quieras acceder directamente a las API, es probable que utilices un lenguaje de programación que implemente una envoltura alrededor de la API (es decir, un kit de desarrollo de software, o SDK). Por ejemplo, AWS mantiene muchos SDK para varios lenguajes de programación, como Boto3 para Python y AWS SDK para Go. Utilizar un SDK facilita la interacción con la API porque no tienes que preocuparte de las solicitudes HTTP y las respuestas.
Consejo
Las API admiten diferentes versiones para mantener la compatibilidad con versiones anteriores en tu código. Lo mismo se aplica a los SDK.
En los capítulos siguientes, aprenderás sobre lenguajes de programación (Capítulos 6 y 7) y sobre las API HTTP(Capítulo 10). Por lo tanto, no cubriremos aquí ejemplos de uso de las API. Encontrarás ejemplos de uso de las API REST, como las expuestas por AWS, en el Capítulo 10. Allí aprenderás a interactuar con las API con diversos métodos, como cURL, Postman, Python y Go. Aunque esos ejemplos no son específicos del aprovisionamiento de servicios en la nube, todas las API comparten los mismos principios, por lo que puedes aplicar los conocimientos al caso de uso del aprovisionamiento de servicios en la nube.
Utilizar la herramienta CLI del proveedor de la nube
La interfaz de línea de comandos es otra interfaz de usuario para interactuar con las API del proveedor de la nube. La herramienta CLI que ofrecen muchos de estos proveedores de la nube suele ser una envoltura de las API estándar de ese proveedor, lo que facilita la interacción con ellas. Por ejemplo, la herramienta CLI de AWS aprovecha un SDK de Python para exponer esta interfaz de usuario.
La herramienta CLI puede ser utilizada manualmente por un operador para gestionar los servicios en la nube, o puede utilizarse de forma automatizada. Por ejemplo, puedes utilizar la herramienta CLI en un script de shell. En el Capítulo 12 encontrarás ejemplos (con la configuración necesaria) de uso de la herramienta CLI de AWS para interactuar con la nube de AWS. Mientras tanto, te ofrecemos un ejemplo del repositorio de la CLI de AWS sobre cómo utilizar la herramienta CLI de AWS para crear una VPC:
$ aws ec2 create-vpc \
--cidr-block 10.0.0.0/16 \
--tag-specification ResourceType=vpc,Tags=[{Key=Name,Value=MyVpc}]`
{
"Vpc": { "CidrBlock": "10.0.0.0/16", "DhcpOptionsId": "dopt-5EXAMPLE", "State": "pending", "VpcId": "vpc-0a60eb65b4EXAMPLE", "OwnerId": "123456789012", "InstanceTenancy": "default", "Ipv6CidrBlockAssociationSet": [], "CidrBlockAssociationSet": [ { "AssociationId": "vpc-cidr-assoc-07501b79ecEXAMPLE", "CidrBlock": "10.0.0.0/16", "CidrBlockState": { "State": "associated" } } ], "IsDefault": false, "Tags": [ { "Key": "Name", "Value": MyVpc" } ] } }
aws ec2 create-vpc
es el comando para crear una VPC;ec2
es el tipo de servicio de AWS, ycreate-vpc
es la acción específica.Cada recurso de AWS tiene argumentos obligatorios y opcionales. Para la VPC, sólo el bloque CIDR es obligatorio.
Tags
o metadatos, es un parámetro común a todos los recursos de la nube para añadir más dimensiones que mejoren la clasificación de los recursos.La salida del comando es un objeto JSON con los detalles de la VPC creada, tomados directamente de la respuesta de la API de AWS.
Puedes encontrar la documentación completa de la herramienta CLI de AWS en línea.
Utilizar una herramienta especialmente diseñada para automatizar la nube
Además de las API del proveedor de la nube, se han creado herramientas específicas para automatizar la nube. Estas herramientas suelen denominarse herramientas de infraestructura como código, porque te permiten definir tu infraestructura como código (con un enfoque declarativo) y luego gestionarla en consecuencia.
También podrías automatizar la nube mediante un enfoque imperativo (por ejemplo, utilizando Ansible), pero consideramos que es un antipatrón para el aprovisionamiento de servicios, por lo que lo recomendamos para la gestión de la configuración. Ambos enfoques pueden coexistir, ya que puede que necesites configurar algunos servicios después de aprovisionarlos. Profundizaremos en este tema en el Capítulo 12.
Para clasificar las herramientas que aplican un enfoque declarativo, tenemos que considerar dos dimensiones: si son específicas de la nube o multicloud, y si utilizan un lenguaje de programación o un lenguaje específico del dominio (DSL) para definir la infraestructura.
Nota
Un DSL está diseñado para resolver un problema concreto; en cambio, un lenguaje de programación es un lenguaje de propósito general que puede utilizarse para resolver cualquier problema. Los DSL están pensados para ser utilizados por no programadores, por lo que suelen ser más fáciles de aprender y utilizar que los lenguajes de programación, pero son menos flexibles. Ejemplos de DSL son el Lenguaje de Consulta Estructurado (SQL), HTML, la sintaxis basada en YAML que se utiliza en los playbooks de Ansible y el Lenguaje de Configuración de HashiCorp (HCL) que se utiliza en Terraform (más información sobre YAML en el Capítulo 8 y sobre Ansible y HCL/Terraform en el Capítulo 12).
En la Tabla 4-1, utilizamos estas dos dimensiones para clasificar algunas de las herramientas más populares.
Lenguaje de programación | Lenguaje específico del dominio | |
---|---|---|
Nube única |
Kit de desarrollo en la nube de AWS (CDK) |
AWS CloudFormation, Azure Resource Manager |
Multicloud |
Pulumi, CDK para Terraform |
Terraforma |
Debes elegir la herramienta más adecuada para tu contexto. En el Capítulo 12, explicamos cómo utilizar Terraform y su DSL, porque es la herramienta más popular para entornos multicloud.
Tras esta breve introducción a las redes en la nube, pasaremos a hablar de la relación entre los contenedores y los servicios en la nube.
Contenedores
A menos que no hayas estado prestando atención a lo que ocurre en el mundo de la tecnología, casi seguro que has oído hablar de los contenedores. Al fin y al cabo, los contenedores no son más que procesos que se ejecutan en una instancia del SO. De hecho, una faceta clave para entender los contenedores es que no existe tal cosa como un contenedor. A lo que nos referimos como contenedor es a un proceso que se ejecuta en una instancia del SO que puede o no aprovechar ciertas características del SO. Entonces, ¿por qué se presta tanta atención a los contenedores? Hay algunas razones muy buenas:
- Aislamiento
-
Un proceso que se ejecuta en un contenedor -o podrías decir un proceso en contenedor- está aislado de otros procesos por una serie de características a nivel del SO llamadas espacios de nombres. Estos espacios de nombres pueden aislar el proceso de varias maneras; por ejemplo, se puede hacer creer al proceso que es el único que se ejecuta en el SO (mediante el "identificador de proceso" o espacio de nombres PID), que tiene sus propias interfaces de red (mediante el espacio de nombres de red) o que tiene su propio conjunto de usuarios y grupos (mediante el espacio de nombres de usuario). El conjunto exacto de espacios de nombres empleado variará en función deltiempo de ejecución del contenedor (el motor responsable de configurar los procesos que se ejecutan en un contenedor), de la presencia o ausencia de unorquestador de contenedores (una herramienta responsable de gestionar el ciclo de vida de los contenedores) y de otros factores. Verás esto en acción en la sección sobre Kubernetes, una popular plataforma de orquestación de contenedores.
- Distribución
-
Otro aspecto de los contenedores es que se construyen a partir de una imagen de contenedor. Una imagen de contenedor agrupa todas las dependencias que necesita el proceso que se ejecutará en un contenedor; esto incluye cualquier ejecutable, biblioteca del sistema, herramienta o incluso variables de entorno y otros ajustes de configuración necesarios. Como una imagen de contenedor no incluye el núcleo y el SO completos -sólo contiene lo necesario para ejecutar un proceso concreto-, las imágenes suelen ser mucho más pequeñas que una imagen de máquina virtual. Cuando se combinan con la capacidad de los creadores de imágenes de publicar fácilmente(push) imágenes de contenedor en un registro, y de otros usuarios de recuperar fácilmente(pull) imágenes de un registro, resulta fácil distribuir ampliamente imágenes de contenedor.
Considera un proyecto de código abierto como el servidor web Caddy. Los mantenedores de un proyecto como Caddy pueden empaquetar su proyecto y todas sus dependencias y ofrecerlo en una imagen de contenedor a los usuarios. Los usuarios pueden extraer la imagen del contenedor Caddy e implementarla en unos minutos con un par de comandos.
- Reutiliza
-
Las imágenes contenedoras no sólo son fáciles de construir, distribuir y consumir, sino que también son fáciles de reutilizar por otros. Considera de nuevo el servidor web Caddy. Si un usuario quiere implementar Caddy junto con contenido web personalizado, es trivial construir una nueva imagen de contenedor basada en la imagen de contenedor de Caddy. Otra persona podría, a su vez, reutilizar su imagen personalizada para construir su propia imagen personalizada. Podría decirse que esta facilidad de reutilización podría ser uno de los aspectos más importantes e influyentes de los contenedores y las imágenes de contenedor.
- Velocidad
-
Un contenedor no se ejecuta más rápido que un proceso "normal" en un host, pero los contenedores en sí son mucho más rápidos de iniciar que otros mecanismos de aislamiento, como las VM. El tamaño de una imagen de contenedor -a veces de tan sólo decenas de megabytes, frente a los cientos o miles de megabytes de una imagen de disco de una VM- hace que los contenedores sean más rápidos de distribuir. Por último, a menudo se pueden crear nuevas imágenes de contenedor en cuestión de minutos, lo que hace que la inversión de tiempo para crear una nueva imagen de contenedor sea significativamente menor que la inversión de tiempo necesaria para crear una imagen de VM, incluso cuando se utilizan herramientas de automatización de creación de imágenes de VM como Packer.
Los contenedores se popularizaron en 2013 con la introducción de Docker, pero llevan existiendo mucho más tiempo. Antes de la llegada de Docker, los contenedores existían en forma de LXC, Linux-VServer, OpenVZ, FreeBSD jails, Oracle Solaris Containers (y Solaris Zones), e incluso desde la introducción original de chroot
. Todos ellos eran mecanismos a nivel de sistema operativo para aislar los procesos que se ejecutaban en un host. LXC se considera la primera implementación completa de contenedores que aprovechó tanto los grupos de control (cgroups) para limitar la utilización de recursos como los espacios de nombres para aislar procesos sin necesidad de un núcleo Linux personalizado.
Nota
Si te interesa conocer más detalles sobre Docker, te recomendamos Using Docker de Adrian Mouat (O'Reilly).
Tras el auge de Docker, empezaron a surgir normas en torno a los contenedores. Un esfuerzo clave de estandarización es la Iniciativa de Contenedores Abiertos (OCI), formada en 2015 por Docker, CoreOS (ahora parte de Red Hat, que a su vez forma parte de IBM) y otros. La OCI ayudó a establecer normas en torno a la especificación de la imagen (cómo se construyen las imágenes de contenedores), la especificación del tiempo de ejecución (cómo se instancian los contenedores a partir de una imagen de contenedor) y la especificación de la distribución (cómo se distribuyen y comparten los contenedores a través de un registro). Impulsadas en parte por esta estandarización (y en cierta medida ayudando a impulsar esta estandarización), también empezaron a surgir implementaciones estándar: runc como una implementación estándar para ejecutar un contenedor según la especificación OCI, y containerd como una implementación estándar de un tiempo de ejecución de contenedor de ciclo de vida completo que se basa en runc.
Nota
Aunque en los últimos años se han dado grandes pasos para añadir la funcionalidad de la contenedorización a Windows, los contenedores siguen siendo -incluso hoy- en gran medida algo exclusivo de Linux. A lo largo de la discusión sobre contenedores en este capítulo, la atención se centra únicamente en los contenedores Linux.
Ahora que entiendes mejor qué son los contenedores y qué ventajas ofrecen, vamos a responder a la pregunta de por qué hablamos de contenedores en un capítulo centrado en la nube, sobre todo cuando ya has visto que los contenedores son predominantemente una entidad centrada en Linux. ¿Por qué no ponerlos en el capítulo de Linux, en vez de en el de la nube?
¿Qué tienen que ver los contenedores con la nube?
En sí mismos, los contenedores no tienen nada que ver con la computación en nube. Son una construcción a nivel de sistema operativo y son igualmente adecuados para tu portátil local, para una máquina virtual que se ejecuta en un hipervisor en un centro de datos o para una instancia en la nube. Sin embargo, desde su introducción, los contenedores se han convertido en una parte clave de la computación nativa en la nube. Según la definición de la Cloud Native Computing Foundation (CNCF), la computación nativa en la nube permite "a las organizaciones crear y ejecutar aplicaciones escalables en entornos modernos y dinámicos, como nubes públicas, privadas e híbridas, aprovechando al máximo las ventajas de la computación en la nube".
Dada la importancia de los contenedores para la computación nativa en la nube, y dada la prevalencia de los servicios basados en contenedores que ofrecen hoy en día los proveedores de nubes públicas -como AWS ECS, Google Cloud Run y Azure Container Instances (ACI)-, creemos que la ubicación de los contenedores en este capítulo sobre la nube es apropiada.
¿Qué tienen que ver los contenedores con las redes?
Es de esperar que la inclusión de contenedores en un capítulo centrado en la nube tenga sentido, pero todavía hay que responder a una pregunta más importante: ¿qué tienen que ver los contenedores con las redes? Creemos que es importante incluir contenido sobre contenedores en un libro sobre programabilidad y automatización de redes por varias razones:
-
La forma en que se gestionan las redes de contenedores -especialmente cuando los contenedores se utilizan junto con la plataforma de orquestación de contenedores Kubernetes- es lo suficientemente diferente de lo que la mayoría de los ingenieros de redes ya conocen y comprenden como para que merezca la pena destacar estas diferencias y tender un puente entre los conceptos de Kubernetes y las construcciones de redes más "típicas".
-
Los ingenieros de redes pueden querer utilizar contenedores junto con otras herramientas. Un ingeniero de redes puede, por ejemplo, querer crear una imagen de contenedor de "herramientas de red" que contenga todas las herramientas más importantes de automatización de redes y redes (incluidas todas las dependencias necesarias) agrupadas en una sola imagen que se distribuya y ejecute fácilmente en cualquier plataforma compatible. Los contenedores también simplifican la creación de entornos de desarrollo de redes, como aprenderás en el Capítulo 5, cuando hablemos de Containerlab.
-
Los proveedores de redes pueden aprovechar los contenedores en sus plataformas, especialmente en las basadas en Linux. Los mecanismos de aislamiento de los contenedores, combinados con la facilidad de distribución, hacen de los contenedores un método ideal para ofrecer nuevas funcionalidades a las plataformas de red.
Así pues, repasaremos los fundamentos de la interconexión de contenedores, incluyendo cómo se aíslan entre sí y cómo pueden comunicarse entre sí y con el mundo exterior.
Ampliar la red Linux para contenedores
Una de las razones por las que el capítulo sobre Linux(Capítulo 3) dedica tanto tiempo a los componentes básicos de las redes es que los contenedores (y los sistemas de orquestación de contenedores como Kubernetes, como verás en "Kubernetes") amplían estos componentes básicos para dar cabida a las redes de contenedores. Una buena comprensión de los componentes básicos proporciona la estructura necesaria que hace más fácil añadir conceptos específicos de los contenedores a tu conjunto de habilidades.
Para los contenedores de red, se aprovechan cinco tecnologías Linux:
-
Espacios de nombres de red
-
Interfaces Ethernet virtuales (veth)
-
Puentes
-
Enrutamiento IP
-
Enmascaramiento de IP
De ellas, los puentes y el enrutamiento IP -término utilizado para describir la capacidad de un sistema basado en Linux para actuar como un enrutador de Capa 3 (basado en IP)- se trataron en el Capítulo 3. Las tres tecnologías restantes se tratan en los siguientes apartados. Empezaremos con los espacios de nombres de red de Linux.
Espacios de nombres de red Linux
Espacios de nombres son un mecanismo de aislamiento; limitan lo que los procesos pueden "ver" en el espacio de nombres. El núcleo de Linux proporciona múltiples espacios de nombres; para las redes, los espacios de nombres de red son los principales espacios de nombres implicados. Los espacios de nombres de red pueden utilizarse para soportar varias tablas de enrutamiento independientes o varias configuraciones de iptables independientes, o para delimitar la visibilidad de las interfaces de red. En algunos aspectos, probablemente estén más relacionados con las instancias VRF en el mundo de las redes.
Nota
Aunque los espacios de nombres de red pueden utilizarse para crear instancias VRF, en la comunidad del núcleo de Linux se está trabajando ahora mismo por separado para crear una funcionalidad VRF "adecuada" en Linux. Esta funcionalidad VRF propuesta proporcionaría una separación lógica adicional de Capa 3 dentro de un espacio de nombres. Aún es demasiado pronto para saber adónde nos llevará, pero queremos que lo conozcas.
Algunos casos de uso de los espacios de nombres de red Linux son los siguientes:
- Enrutamiento por proceso
-
Ejecutar un proceso en su propio espacio de nombres de red te permite configurar el enrutamiento por proceso.
- Activar configuraciones VRF
-
Al principio de esta sección hemos mencionado que los espacios de nombres de red son probablemente los más estrechamente relacionados con las instancias VRF en el mundo de las redes, por lo que es natural que habilitar configuraciones similares a VRF sea un caso de uso primordial para los espacios de nombres de red.
- Soporte para espacios de direcciones IP solapados
-
También podrías utilizar espacios de nombres de red para dar soporte a espacios de direcciones IP solapados, en los que la misma dirección (o rango de direcciones) podría utilizarse para distintos fines y tener significados diferentes. A grandes rasgos, probablemente necesitarías combinar esto con una red superpuesta y/o NAT para dar pleno soporte a este caso de uso.
- Red de contenedores
-
Los espacios de nombres de red también son una parte clave de la forma en que los contenedores se conectan a una red. Los tiempos de ejecución de los contenedores utilizan los espacios de nombres de red para limitar las interfaces de red y las tablas de enrutamiento que el proceso en contenedor puede ver o utilizar.
En este debate sobre los espacios de nombres de red, nos centraremos en ese último caso de uso -la creación de redes de contenedores- y en cómo se utilizan los espacios de nombres de red. Además, nos centraremos únicamente en cómo el tiempo de ejecución del contenedor Docker utiliza los espacios de nombres de red. Por último, como ya hemos dicho, sólo hablaremos de contenedores Linux, no de contenedores de otros sistemas operativos.
En general, cuando ejecutas un contenedor, Docker creará un espacio de nombres de red para ese contenedor. Puedes observar este comportamiento utilizando el comando lsns
. Por ejemplo, si ejecutas lsns -t net
para mostrar los espacios de nombres de red en un sistema Ubuntu Linux que no tenga ningún contenedor en ejecución, sólo verás un único espacio de nombres de red en la lista.
Sin embargo, si lanzas un contenedor Docker en ese mismo sistema con docker run --name nginxtest -p 8080:80 -d nginx
, entonces lsns -t net
mostrará algo diferente. Aquí tienes la salida de lsns -t net -J
, que enumera los espacios de nombres de red serializados como JSON:
{
"namespaces"
:
[
{
"ns"
:
4026531840
,
"type"
:
"net"
,
"nprocs"
:
132
,
"pid"
:
1
,
"user"
:
"root"
,
"netnsid"
:
"unassigned"
,
"nsfs"
:
null
,
"command"
:
"/sbin/init"
},
{
"ns"
:
4026532233
,
"type"
:
"net"
,
"nprocs"
:
5
,
"pid"
:
15422
,
"user"
:
"root"
,
"netnsid"
:
"0"
,
"nsfs"
:
"/run/docker/netns/b87b15b217e8"
,
"command"
:
"nginx: master process nginx -g daemon off;"
}
]
}
El espacio de nombres host (o raíz, o por defecto) que está presente en todos los sistemas Linux. Cuando interactuamos con interfaces de red, tablas de enrutamiento u otras configuraciones de red, generalmente lo hacemos con el espacio de nombres por defecto.
El espacio de nombres creado por Docker cuando ejecutamos (creamos) el contenedor. Hay varias formas de determinar que éste es el espacio de nombres creado por Docker, pero en este caso, el campo
command
lo indica.
En algunos casos -como en el ejemplo anterior- es fácil saber qué espacio de nombres de red es cada uno. Para una determinación más concreta, puedes utilizar los siguientes pasos:
-
Utiliza el comando
docker container ls
para obtener el ID del contenedor en cuestión. -
Utiliza el comando
docker container inspect container-id
para que el tiempo de ejecución de Docker proporcione información detallada sobre el contenedor especificado. Esto producirá mucha salida en JSON; puede que te resulte más fácil manejar esta información si la canalizas a través de una utilidad comojq
. -
Busca la propiedad
SandboxKey
. Especificará una ruta, como/var/run/docker/netns/b87b15b217e8
. Compáralo con la salida delsns
y su propiedadnsfs
. A excepción del prefijo/var
, los valores coincidirán cuando estés mirando el mismo contenedor.
Nota
Si no quieres ejecutar un contenedor en un espacio de nombres de red, Docker admite una bandera --network=host
que lanzará el contenedor en el espacio de nombres del anfitrión (también conocido como raíz, o por defecto).
El comando ip
(del paquete iproute2
), que el Capítulo 3 trata con gran detalle, también tiene un subcomando ip netns
que permite al usuario crear, manipular y eliminar espacios de nombres de red. Sin embargo, los espacios de nombres de red creados por Docker y otros tiempos de ejecución de contenedores no suelen ser visibles para el usuario, aunque se utilicen con sudo
para obtener permisos elevados. Sin embargo, el comando lsns
utilizado en esta sección mostrará los espacios de nombres de red creados por Docker u otro tiempo de ejecución de contenedores. (Aunque aquí sólo hablamos de espacios de nombres de red, lsns
puede mostrar cualquier tipo de espacio de nombres, no sólo los de red).
Los espacios de nombres de red tienen más casos de uso además de los contenedores; para ver con más detalle cómo trabajar con espacios de nombres de red en un caso de uso distinto de los contenedores, consulta el contenido adicional en línea de este libro. Allí encontrarás ejemplos de cómo utilizar el comando ip netns
para manipular la configuración de red de un host Linux, incluyendo la adición de interfaces a un espacio de nombres de red y la eliminación de interfaces del mismo.
Los espacios de nombres de red de Linux son un componente crítico para la creación de redes de contenedores, pero sin un mecanismo que simule el comportamiento de una interfaz de red real, la capacidad de nuestros sistemas para conectar contenedores a la red se vería muy limitada. De hecho, sólo podríamos ejecutar tantos contenedores como interfaces de red físicas cupieran en el host. Las interfaces Ethernet virtuales son la solución a esa limitación.
Interfaces Ethernet virtuales
Al igual que los espacios de nombres de red permiten al núcleo Linux proporcionar aislamiento de red, las interfaces Ethernet virtuales (también llamadas veth) te permiten romper intencionadamente ese aislamiento. Las interfaces veth son interfaces lógicas; no se corresponden con una NIC física u otra pieza de hardware. Como tales, puedes tener muchas más interfaces veth que interfaces físicas.
Además, las interfaces veth siempre vienen en pares: el tráfico que entra por una interfaz del par sale por la otra interfaz del par. Por esta razón, puedes verlas referenciadas como pares veth. Al igual que otros tipos de interfaces de red, una interfaz veth puede asignarse a un espacio de nombres de red. Cuando una interfaz veth -un miembro de un par veth- se asigna a una interfaz de red, acabas de conectar dos espacios de nombres de red entre sí (porque el tráfico que entra en una interfaz veth de un espacio de nombres saldrá por la otra interfaz veth del otro espacio de nombres).
Este comportamiento subyacente -colocar un miembro de un par veth en un espacio de nombres de red y dejar la interfaz homóloga en el espacio de nombres del host (o predeterminado)- es la clave de las redes de contenedores. Así es como funcionan todas las redes básicas de contenedores.
Utilizando el mismo contenedor Docker creado en la sección anterior (que era un simple contenedor nginx lanzado con el comando docker run --name nginxtest -p 8080:80 -d nginx
), veamos cómo se gestiona la red de este contenedor.
En primer lugar, ya conoces el espacio de nombres de red que utiliza el contenedor nginx; lo has determinado utilizando lsns -t net
junto con docker container inspect
y cotejando las propiedades nsfs
y SandboxKey
de cada comando, respectivamente.
Con esta información, ahora puedes utilizar el comando nsenter
para ejecutar otros comandos dentro del espacio de nombres del contenedor Docker nginx. (Para más detalles sobre el comando nsenter
, utiliza man nsenter
.) La bandera nsenter
para ejecutar un comando en el espacio de nombres de red de otro proceso es -n
, o --net
, y es con esa bandera con la que proporcionarás el espacio de nombres de red para el contenedor nginx, como se muestra en el Ejemplo 4-1.
Ejemplo 4-1. Ejecutar ip link
dentro de un espacio de nombres con nsenter
ubuntu2004:~$ sudo nsenter --net=/run/docker/netns/b87b15b217e8 ip link list 1: lo: LOOPBACK,UP,LOWER_UP mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 4: eth0@if5: BROADCAST,MULTICAST,UP,LOWER_UP mtu 1500 qdisc noqueue state UP mode DEFAULT group default link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
Compáralo con la salida de ip link list
en el espacio de nombres de red host (por defecto) del Ejemplo 4-2.
Ejemplo 4-2. Ejecutar ip link
en el espacio de nombres de la red anfitriona
ubuntu2004:~
$
ip
link
list
1
:
lo:
LOOPBACK,UP,LOWER_UP
mtu
65536
qdisc
noqueue
state
UNKNOWN
mode
DEFAULT
group
default
qlen
1000
link/loopback
00
:00:00:00:00:00
brd
00
:00:00:00:00:00
2
:
ens5:
BROADCAST,MULTICAST,UP,LOWER_UP
mtu
9001
qdisc
mq
state
UP
mode
DEFAULT
group
default
qlen
1000
link/ether
02
:95:46:0b:0f:ff
brd
ff:ff:ff:ff:ff:ff
altname
enp0s5
3
:
docker0:
BROADCAST,MULTICAST,UP,LOWER_UP
mtu
1500
qdisc
noqueue
state
UP
mode
DEFAULT
group
default
link/ether
02
:42:70:88:b6:77
brd
ff:ff:ff:ff:ff:ff
5
:
veth68bba38@if4:
BROADCAST,MULTICAST,UP,LOWER_UP
mtu
1500
qdisc
noqueue
master
docker0
state
UP
mode
DEFAULT
group
default
link/ether
a6:e9:87:46:e7:45
brd
ff:ff:ff:ff:ff:ff
link-netnsid
0
La conexión entre ambas salidas es el sufijo @ifX
. En la interfaz con el índice de 5
(que es la interfaz veth68bba38
en el espacio de nombres del host), el sufijo apunta a la interfaz cuyo índice es 4
. La interfaz con el índice de 4
es eth0
en el espacio de nombres de red del contenedor nginx (ver Ejemplo 4-1), y su sufijo apunta al índice de interfaz 5
(ver Ejemplo 4-2).
Así es como puedes determinar los pares veth, y en este caso, ves que Docker ha creado un par veth para este contenedor. Un miembro del par veth se coloca en el espacio de nombres de red del contenedor nginx, y el otro miembro del par veth permanece en el espacio de nombres del host. En esta configuración, el tráfico que entra en eth0@if5
en el espacio de nombres de red del contenedor nginx saldrá de veth68bba38@if4
en el espacio de nombres del anfitrión, atravesando así de un espacio de nombres a otro espacio de nombres.
Consejo
También puedes utilizar ip -d link list
para mostrar más detalles en la salida. La salida adicional incluirá una línea con la palabra veth
para denotar un miembro de un par veth. Este comando también indicará claramente cuándo una interfaz es una interfaz puente (la salida adicional mostrará bridge
), así como cuándo una interfaz forma parte de un puente (denotado por bridge_slave
). Además, en caso de que el sufijo @ifX
no esté presente, puedes utilizar ethtool -S veth-interface
que incluirá la línea peer_ifindex:
en la salida. El número que aparece allí se refiere al índice de interfaz del otro miembro del par veth.
Las interfaces veth permiten que el tráfico del contenedor pase de su propio espacio de nombres de red al espacio de nombres de red del host (suponiendo que el contenedor no se haya iniciado con la bandera --network=host
), pero ¿cómo llega el tráfico desde la interfaz veth en el espacio de nombres del host a la red física? Si has adivinado un puente, ¡has acertado! (Para ser justos, los puentes figuraban al principio de esta sección como un componente de la red de contenedores).
La clave en el Ejemplo 4-2, tal y como se explica en "Puentes (Conmutación)", es la aparición de master docker0
relacionado con veth68bba38@if4
. La presencia de este texto indica que la interfaz especificada es miembro del puente Linux docker0
. Sin embargo, si examinas el resto de las interfaces, observarás que ninguna de ellas es miembro del puente docker0
. Entonces, ¿cómo llega realmente el tráfico a la red física? Aquí es donde entra en juego el enmascaramiento de IP, que es la pieza final del rompecabezas de cómo los contenedores Docker de un host Linux se conectan a una red.
Nota
Las interfaces veth tienen más usos, aunque la mayoría implican conectar espacios de nombres de red entre sí. Consulta el contenido extra en línea de este libro para ver ejemplos adicionales de uso de interfaces veth, así como un vistazo a los comandos para crear, modificar y eliminar interfaces veth.
Enmascaramiento de IP
Al explorar las redes de contenedores, hasta ahora has visto cómo los tiempos de ejecución de contenedores como Docker utilizan espacios de nombres de red para proporcionar aislamiento, impidiendo que un proceso de un contenedor tenga acceso a la información de red de otro contenedor o del host. También has visto cómo se utilizan las interfaces veth (también denominadas pares veth) para conectar el espacio de nombres de red de un contenedor con el espacio de nombres de red del host. Basándote en la amplia cobertura de interfaces, puentes y enrutamiento del Capítulo 3, también has visto cómo se utiliza un puente Linux junto con las interfaces veth en el espacio de nombres del host. Sólo queda una pieza: conseguir que el tráfico del contenedor salga del host y llegue a la red. Aquí es donde interviene el enmascaramiento IP.
El enmascaramiento de IP permite configurar Linux para que realice NAT. NAT, como probablemente sepas, permite que uno o varios ordenadores se conecten a una red utilizando una dirección diferente. En el caso del enmascaramiento de IP, uno o varios ordenadores se conectan a una red utilizando la propia dirección IP del servidor Linux. El RFC 2663 se refiere a esta forma de NAT como traducción de puerto de dirección de red(NAPT); también se conoce como NAT de uno a muchos, NAT de muchos a uno, traducción de dirección de puerto(PAT) y sobrecarga de NAT.
Antes de ir mucho más lejos, conviene señalar aquí que Docker admite varios tipos de redes:
- Puente
-
Este es el tipo de red por defecto. Utiliza interfaces veth, un puente Linux y enmascaramiento IP para conectar los contenedores a las redes físicas.
- Anfitrión
-
Cuando se utiliza la red en modo anfitrión, el contenedor Docker no está aislado en su propio espacio de nombres de red, sino que utiliza el espacio de nombres de red del anfitrión (por defecto). Los puertos expuestos del contenedor están disponibles en la dirección IP del anfitrión.
- Superposición
-
Las redes superpuestas crean una red distribuida que abarca varios hosts Docker. Las redes superpuestas se construyen utilizando VXLAN, definida en el RFC 7348. Este modo de red está directamente vinculado al mecanismo de orquestación de contenedores de Docker, llamado Swarm.
- IPvlan y macvlan
-
La red IPvlan permite que varias direcciones IP compartan la misma dirección MAC de una interfaz, mientras que la red macvlan asigna varias direcciones MAC a una interfaz. Esta última, en particular, no suele funcionar en las redes de proveedores de nube. Tanto las redes IPvlan como las macvlan son menos comunes que otros tipos de redes.
Consejo
La creación de redes macvlan mediante interfaces macvlan puede utilizarse para otros fines además de la creación de redes de contenedores. Encontrarás más información sobre interfaces macvlan y redes macvlan en el contenido extra en línea de este libro.
A lo largo de esta sección sobre la red de contenedores, el debate se ha centrado en la red en modo puente con Docker.
Cuando se utiliza la red en modo puente con Docker, el tiempo de ejecución del contenedor utiliza un espacio de nombres de red para aislar el contenedor y un par veth para conectar el espacio de nombres de red del contenedor con el espacio de nombres del host. En el espacio de nombres del anfitrión, una de las interfaces veth está conectada a un puente Linux. Si lanzas más contenedores en la misma red Docker, se conectarán al mismo puente Linux, y tendrás conectividad inmediata de contenedor a contenedor en el mismo host Docker. Hasta aquí, todo correcto.
Cuando un contenedor intenta conectarse a algo que no está en la misma red Docker, Docker ya ha configurado una regla de enmascaramiento de IP que indicará al núcleo Linux que realice NAT de muchos a uno para todos los contenedores de esa red Docker. Esta regla se crea cuando se crea la red Docker. Docker crea una red por defecto cuando se instala, y puedes crear redes adicionales utilizando el comando docker network create
.
En primer lugar, echemos un vistazo a la red Docker por defecto. Ejecutando docker network ls
se muestra una lista de las redes Docker creadas. En un sistema recién instalado, sólo aparecerán tres redes en la lista (los ID de red listados variarán):
ubuntu2004:~$docker
network
ls
NETWORK
ID
NAME
DRIVER
SCOPE
b4e8ea1af51b
bridge
bridge
local
add089049cda
host
host
local
fbe6029ce9f7
none
null
local
El comando docker network inspect
muestra todos los detalles sobre la red especificada. Aquí tienes la salida de docker network inspect b4e8ea1af51b
mostrando más información sobre la red puente por defecto (algunos campos se han omitido o eliminado por brevedad):
[
{
"Name"
:
"bridge"
,
"Id"
:
"b4e8ea1af51ba023a8be9a09f633b316f901f1865d37f69855be847124cb3f7c"
,
"Created"
:
"2023-04-16T18:27:14.865595005Z"
,
"Scope"
:
"local"
,
"Driver"
:
"bridge"
,
"EnableIPv6"
:
false
,
"IPAM"
:
{
"Driver"
:
"default"
,
"Options"
:
null
,
"Config"
:
[
{
"Subnet"
:
"172.17.0.0/16"
}
]
},
"Options"
:
{
"com.docker.network.bridge.default_bridge"
:
"true"
,
"com.docker.network.bridge.enable_icc"
:
"true"
,
"com.docker.network.bridge.enable_ip_masquerade"
:
"true"
,
"com.docker.network.bridge.host_binding_ipv4"
:
"0.0.0.0"
,
"com.docker.network.bridge.name"
:
"docker0"
,
"com.docker.network.driver.mtu"
:
"1500"
},
"Labels"
:
{}
#
o
u
t
p
u
t
o
m
i
tte
d
f
o
r
b
r
e
v
i
t
y
}
]
La subred utilizada por los contenedores es
172.17.0.0/16
.Docker creará automáticamente las reglas de enmascaramiento de IP necesarias para esta red.
El nombre del puente para esta red es
docker0
(se muestra en la secciónOptions
).
Puedes verificar esta información utilizando los comandos de red de Linux que se tratan en el Capítulo 3 y en esta sección. Por ejemplo, para ver la dirección IP asignada al contenedor Docker nginx lanzado anteriormente, utiliza una combinación de nsenter
y ip addr list
, como ésta:
ubuntu2004:~$ sudo nsenter --net=/run/docker/netns/b87b15b217e8 ip addr list eth0 4: eth0@if5: BROADCAST,MULTICAST,UP,LOWER_UP mtu 1500 qdisc noqueue state UP group default link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0 valid_lft forever preferred_lft forever
Del mismo modo, la ejecución de ip -br link list
(la bandera -br
significa breve y muestra una salida abreviada) muestra que el puente creado por Docker se llama efectivamente docker0
:
ubuntu2004:~$ ip -br link list lo UNKNOWN 00:00:00:00:00:00 LOOPBACK,UP,LOWER_UP ens5 UP 02:95:46:0b:0f:ff BROADCAST,MULTICAST,UP,LOWER_UP docker0 UP 02:42:70:88:b6:77 BROADCAST,MULTICAST,UP,LOWER_UP veth68bba38@if4 UP a6:e9:87:46:e7:45 BROADCAST,MULTICAST,UP,LOWER_UP
La última pieza es la regla de enmascaramiento de IP, que puedes verificar utilizando el comando iptables
. El uso de iptables es un tema que podría tener su propio libro, y de hecho lo tiene: véase Linux iptables Pocket Reference de Gregor N. Purdy (O'Reilly). No podemos abarcarlo en gran detalle, pero podemos proporcionar una visión general de alto nivel y luego explicar dónde encaja la regla de enmascaramiento de IP de Docker.
Nota
Técnicamente, iptables ha sido sustituido por nftables. Sin embargo, la comunidad Linux sigue haciendo referencia en gran medida a iptables, incluso cuando se refiere a nftables. Además, el comando iptables
(y su sintaxis) sigue funcionando aunque el mecanismo subyacente sea ahora nftables.
Iptables proporciona varias tablas, como la tabla de filtros, la tabla NAT y la tabla mangle. Además, hay varias cadenas, como PREROUTING
, INPUT
, OUTPUT
, FORWARD
y POSTROUTING
. Cada cadena es una lista de reglas, y no todas las tablas tendrán todas las cadenas. Las reglas de una cadena hacen coincidir el tráfico en función de propiedades como la dirección de origen, la dirección de destino, el protocolo, el puerto de origen o el puerto de destino. Cada regla especifica un destino, como ACCEPT
, DROP
, o REJECT
.
Con esta información adicional en la mano, veamos el comando iptables
. En el Ejemplo 4-3, utilizamos este comando para mostrarte todas las cadenas y reglas de la tabla NAT.
Ejemplo 4-3. Listado de todas las cadenas y reglas de la tabla NAT con iptables
ubuntu2004:~$ iptables -t nat -L -n ...output omitted... Chain POSTROUTING (policy ACCEPT) target prot opt source destination MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0 ...output omitted...
El parámetro
-t nat
indica aiptables
que muestre la tabla NAT.-L
enumera las reglas, y-n
simplemente indica aiptables
que muestre los números en lugar de intentar resolverlos en nombres.La regla especifica el origen como
172.17.0.0/16
. Sabemos que es la subred asignada a la red puente Docker por defecto.La regla especifica un destino de
0.0.0.0/0
(cualquier lugar).El destino del tráfico que coincide con la regla es
MASQUERADE
. Este objetivo sólo está disponible en la tabla NAT y en la cadenaPOSTROUTING
.
En el momento en que el tráfico llega a la cadena POSTROUTING
de la tabla NAT, el enrutamiento ya se ha determinado (para más detalles sobre cómo funciona el enrutamiento en Linux, consulta "Enrutamiento como host final" y "Enrutamiento como router"), y la interfaz de salida ya se ha seleccionado en función de la configuración de enrutamiento del host. El destino MASQUERADE
indica a iptables que utilice la dirección IP de la interfaz de salida como dirección para el tráfico que coincida con la regla: en este caso, el tráfico procedente de la subred de la red Docker con destino a cualquier lugar. Así es como, al utilizar una red puente Docker, el tráfico llega realmente a la red: el host Linux determina una ruta y una interfaz de salida basándose en su tabla de enrutamiento y envía el tráfico del contenedor Docker a esa interfaz. La regla de la cadena POSTROUTING
de la tabla NAT coge ese tráfico y realiza la NAT de muchos a uno utilizando la dirección IP de la interfaz seleccionada.
Muchas distribuciones de Linux también proporcionan un comando iptables-save
, que muestra las reglas iptables en un formato ligeramente distinto. Así es como iptables-save
muestra la regla creada por Docker para enmascarar una red puente:
ubuntu2004:~$ sudo iptables-save -t nat ...output omitted... -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE ...output omitted...
En este formato, puedes ver claramente la fuente especificada (-s 172.17.0.0/16
), y el ! -o docker0
es una abreviatura que indica que la interfaz de salida no es el puente docker0
. En otras palabras, el tráfico procedente de 172.17.0.0/16
no está destinado a ningún destino en el puente docker0
. El destino se especifica mediante -j MASQUERADE
.
Antes de pasar a la siguiente sección, presentamos un resumen visual de los ejemplos anteriores en la Figura 4-3. Puedes observar que el contenedor se ejecuta en un espacio de nombres de red (pueden existir otros espacios de nombres en el mismo host) que está conectado al espacio de nombres por defecto (host) a través de una interfaz veth. La interfaz veth está conectada al puente docker0
, que a su vez está conectado a la interfaz física del host, y finalmente, mediante enmascaramiento IP, al resto de la red.
Ha llegado el momento de pasar a Kubernetes, una popular herramienta de orquestación de contenedores. Aunque muchos de los conceptos introducidos en esta sección siguen siendo válidos -después de todo, Kubernetes trabaja con contenedores-, existen algunas diferencias notables. La siguiente sección aborda esas diferencias y se basa en lo que ya sabes sobre las redes de contenedores.
Kubernetes
Aunque no está estrictamente ligado a la computación en nube -se podría argumentar que Kubernetes es una forma de PaaS-, el uso de "nativo de la nube" para describir a Kubernetes ha vinculado inevitable y profundamente este exitoso proyecto de código abierto con la computación en nube. Ya has visto cómo los propios contenedores se asocian a la computación en nube; es natural que la posición de Kubernetes en el ámbito de la computación en nube se consolide aún más por el hecho de que es un orquestador de contenedores y es responsable de orquestar el ciclo de vida de los contenedores a través de una flota de instancias de computación.
Por último, los predecesores de Kubernetes fueron sistemas internos de Google -Borg y más tarde Omega- que se encargaban de gestionar la capacidad de cálculo en miles de nodos. Kubernetes, realmente, "nació de la nube".
Nota
Kubernetes no es el único orquestador de contenedores, pero es el más popular. Otros orquestadores de contenedores son Nomad y Docker Swarm. Muchas otras plataformas de orquestación de contenedores están construidas sobre Kubernetes, como OpenShift y Rancher de SUSE.
Antes de hablar de las redes en Kubernetes, es importante explicar algunos de los conceptos clave que se encuentran en Kubernetes, junto con la terminología importante.
Conceptos clave de Kubernetes
Kubernetes es, en esencia, un sistema distribuido responsable de gestionar cargas de trabajo informáticas en unidades de capacidad informática. Las "cargas de trabajo informáticas" son contenedores, razón por la que a menudo se hace referencia a Kubernetes como una plataforma de orquestación de contenedores. Las "unidades de capacidad de cálculo" se denominan nodos, y pueden ser instancias bare-metal, instancias en la nube o máquinas virtuales que se ejecutan en un hipervisor local como VMware vSphere.
Los nodos Kubernetes se organizan en clústeres. Cada clúster tiene un plano de control, que se ejecuta en uno o más nodos, y que proporciona las funciones de gestión para el clúster. Los nodos que ejecutan los componentes del plano de control para el clúster son nodos del plano de control. Los nodos que no forman parte del plano de control son nodos trabajadores.
El plano de control consta de tres piezas: el servidor API, el gestor de controladores y el programador. Cada uno de ellos desempeña una función diferente pero crítica en el funcionamiento general del clúster Kubernetes. El programador es responsable de colocar las cargas de trabajo en los nodos trabajadores, mientras que el servidor API expone el plano de control a los usuarios (y a otros sistemas). En breve hablaremos del gestor de controladores.
El respaldo del plano de control es un almacén distribuido de valores clave llamado etcd; normalmente se ejecuta en los nodos del plano de control, pero puede ejecutarse fuera del clúster si se desea. Etcd -que también forma clusters- necesita tener un número impar de instancias para establecer y mantener el quórum. Normalmente, verás tres instancias de etcd, junto con tres instancias de plano de control, en un clúster Kubernetes de alta disponibilidad. Sin embargo, es posible ejecutar un clúster Kubernetes con una única instancia de etcd y un único nodo de plano de control.
Nota
Aunque etcd tiene requisitos sobre el número de instancias para establecer y mantener el quórum, los propios componentes del plano de control de Kubernetes no tienen tales requisitos. Puedes ejecutar dos nodos del plano de control para la disponibilidad, si lo deseas. Sin embargo, etcd suele ejecutarse en los propios nodos del plano de control, y en ese caso, encontrarás tres instancias de los componentes del plano de control de Kubernetes.
El servidor API de Kubernetes implementa una API declarativa RESTful: los usuarios (y otros sistemas) no utilizan la API de Kubernetes para decirle al clúster lo que debe hacer (un enfoque imperativo), sino para decirle cuál es el resultado deseado (un enfoque declarativo). Por ejemplo, utilizando la API, no ordenarías a un clúster de Kubernetes que creara tres cargas de trabajo. En su lugar, le dirías al clúster Kubernetes que quieres tres cargas de trabajo en ejecución, y dejarías el "cómo" al propio clúster.
Este comportamiento se conoce como reconciliación del estado deseado (el resultado deseado que le has dado al clúster) contra el estado real (lo que realmente se está ejecutando o no en el clúster). Esto ocurre constantemente en Kubernetes y a menudo se denomina bucle de reconciliación. Conciliar el estado deseado con el estado real es la esencia de todo lo que hace Kubernetes y de su funcionamiento.
Todas las acciones que se realizan en un clúster de Kubernetes pasan por el bucle de reconciliación. Para cada tipo de objeto API que conoce Kubernetes, algo tiene que implementar el bucle de reconciliación. Ese "algo" se conoce como controlador, y el gestor de controladores -la tercera parte del plano de control de Kubernetes, junto con el programador y el servidor API- es responsable de gestionar los controladores de los objetos integrados que Kubernetes entiende. Pueden crearse nuevos tipos de objetos, mediante una definición personalizada de recursos (CRD), pero los nuevos tipos de objetos también requerirán un controlador que comprenda el ciclo de vida de esos objetos: cómo se crean, cómo se actualizan y cómo se eliminan.
Consejo
O'Reilly ha publicado varios libros centrados exclusivamente en Kubernetes que pueden resultar útiles. Algunos títulos a tener en cuenta son Kubernetes: Up and Running, 3ª edición, de Brendan Burns y otros; Kubernetes Cookbook, de Sébastien Goasguen y Michael Hausenblas; Production Kubernetes, de Josh Rosso y otros; y Kubernetes Patterns, de Bilgin Ibryam y Roland Huß.
Se podría decir mucho más sobre Kubernetes, pero esta introducción básica te da información suficiente para entender el resto de esta sección. A continuación hablaremos de algunos de los componentes básicos de las redes Kubernetes.
Bloques de construcción de redes en Kubernetes
Kubernetes introduce algunas construcciones nuevas, pero es importante recordar que Kubernetes está construido sobre tecnologías existentes. En las siguientes secciones, hablaremos de los componentes básicos de las redes de Kubernetes y de cómo se relacionan con las tecnologías de redes que ya conoces, como las redes de contenedores, el enrutamiento IP y el equilibrio de carga, entre otras.
Vainas
Un Pod es una colección de contenedores que comparten acceso a la red y volúmenes de almacenamiento. Un Pod puede tener uno o varios contenedores. Sin embargo, independientemente del número de contenedores dentro de un Pod, ciertas cosas permanecen constantes:
-
Todos los contenedores de un Pod se programan juntos, se crean juntos y se destruyen juntos. Los Pods son la unidad atómica de programación.
-
Todos los contenedores de un Pod comparten la misma identidad de red (dirección IP y nombre de host). Kubernetes lo consigue haciendo que varios contenedores compartan el mismo espacio de nombres de red, lo que significa que comparten la misma configuración de red y la misma identidad de red. Esto introduce algunas limitaciones; por ejemplo, dos contenedores de un Pod no pueden exponer el mismo puerto. Los fundamentos de cómo estos contenedores se conectan al mundo exterior siguen siendo los descritos en "Contenedores", con algunos cambios menores.
-
La identidad de red de un Pod (dirección IP y nombre de host) es efímera; se asigna dinámicamente cuando se crea el Pod y se libera cuando éste se destruye o muere. Como resultado, nunca deberías construir sistemas o arquitecturas que se basen en el direccionamiento directo de los Pods; después de todo, ¿qué ocurrirá cuando necesites ejecutar varios Pods? ¿O qué ocurrirá cuando un Pod muera y se vuelva a crear con una identidad de red diferente?
-
Todos los contenedores de un Pod comparten los mismos volúmenes de almacenamiento y montajes. Esto se consigue haciendo que los contenedores compartan el espacio de nombres responsable de gestionar los volúmenes y los montajes.
-
Kubernetes proporciona mecanismos de nivel superior para gestionar el ciclo de vida de los Pods. Por ejemplo, las Implementaciones son utilizadas por Kubernetes para gestionar ReplicaSets, que a su vez gestionan grupos de Pods para garantizar que el número especificado de Pods se ejecuta siempre en el clúster.
Para crear un Pod, un usuario u operador de clúster suele utilizar un archivo YAML que describe el Pod al servidor API de Kubernetes. El término que se da a estos archivos YAML es manifiestos, y se envían al servidor API (normalmente a través de la herramienta de línea de comandos de Kubernetes, kubectl
, o su equivalente). El Ejemplo 4-4 muestra un manifiesto para un Pod sencillo.
Ejemplo 4-4. Manifiesto del Pod de Kubernetes
apiVersion
:
v1
kind
:
Pod
metadata
:
name
:
assets
labels
:
app.kubernetes.io/name
:
zephyr
spec
:
containers
:
-
name
:
assets
image
:
nginx
ports
:
-
name
:
http-alt
containerPort
:
8080
protocol
:
TCP
La API de Kubernetes admite el control de versiones. Cuando la versión de la API es sólo
v1
, como aquí, significa que este objeto forma parte de la API central de Kubernetes.kind
se refiere a los tipos de construcciones de Kubernetes, como Pod, Implementaciones, Servicios, Ingress, NetworkPolicy, etc.Cada objeto de la API también tiene metadatos asociados. A veces los metadatos son sólo un nombre, pero muchas veces -como en este ejemplo- también incluyen etiquetas. Las etiquetas se utilizan mucho en Kubernetes.
Un Pod puede tener uno o más contenedores. En este ejemplo, sólo hay un contenedor en el Pod, pero podría haber más elementos en la lista.
El
image
especificado aquí es una imagen de contenedor. Esta imagen de contenedor se utilizará para crear un nuevo contenedor en el Pod. En este ejemplo, sólo hay un contenedor en el Pod.Los pods pueden exponer uno o más puertos al resto del clúster.
Al crear Pods, el propio Kubernetes no crea los contenedores; esa tarea se deja al tiempo de ejecución del contenedor. Kubernetes interactúa con el tiempo de ejecución del contenedor a través de una interfaz estándar conocida como Interfaz de Tiempo de Ejecución del Contenedor (IRC). Existen interfaces estándar similares para el almacenamiento (Container Storage Interface, o CSI) y la red (Container Network Interface, o CNI). CNI es una parte importante de la red de Kubernetes, pero antes de hablar de CNI, echemos un vistazo a los Servicios de Kubernetes.
Servicios
Si no puedes conectarte directamente a un Pod, ¿cómo es posible que las aplicaciones que se ejecutan en Pods se comuniquen a través de la red? Esta es una de las desviaciones más significativas de Kubernetes respecto a los modelos de red anteriores. Cuando trabajas con máquinas virtuales, la identidad de red de la máquina virtual (dirección IP y/o nombre de host) suele durar mucho tiempo, y puedes esperar poder conectarte a la máquina virtual para acceder a cualquier aplicación que se esté ejecutando en ella. Cuando te pasas a los contenedores y Kubernetes, eso ya no es cierto. La identidad de red de un Pod es efímera. Además, ¿qué ocurre si hay múltiples réplicas de un Pod? ¿A qué Pod debe conectarse otra aplicación o sistema?
En lugar de conectarse a un Pod (o a un grupo de Pods), Kubernetes utiliza un Servicio(el Ejemplo 4-5 muestra el manifiesto de uno sencillo). Un Servicio cumple dos funciones importantes:
-
Asignar una identidad de red estable a un subconjunto definido de Pods. Cuando se crea un Servicio, se le asigna una identidad de red (dirección IP y nombre de host). Esta identidad de red permanecerá estable durante toda la vida del Servicio.
-
Sirve como equilibrador de carga para un subconjunto definido de Pods. El tráfico enviado al Servicio se distribuye al subconjunto de Pods incluidos en el Servicio.
Ejemplo 4-5. Manifiesto del Servicio Kubernetes
apiVersion
:
v1
kind
:
Service
metadata
:
name
:
zephyr-assets
spec
:
selector
:
app.kubernetes.io/name
:
zephyr
ports
:
-
protocol
:
TCP
port
:
80
targetPort
:
8080
El selector indica una etiqueta, y el resultado efectivo de incluir esta etiqueta en el selector es que los Pods con esa etiqueta serán "parte" del Servicio; es decir, los Pods con esta etiqueta recibirán el tráfico enviado al Servicio.
El puerto por el que el Servicio es accesible al resto del cluster.
El puerto, expuesto por los Pods en el Servicio, al que se enviará el tráfico.
Este ejemplo ilustra cómo se gestiona la relación entre un Pod y un Servicio. Utilizando selectores de etiquetas, el Servicio seleccionará dinámicamente los Pods coincidentes. Si un Pod coincide con las etiquetas, será "parte" del Servicio y recibirá el tráfico enviado al Servicio. Los Pods que no coincidan con la definición del Servicio no se incluirán en él.
Las etiquetas, como la mostrada en el ejemplo anterior, son simplemente pares clave-valor, como app.kubernetes.io/name: zephyr
. Las etiquetas pueden ser arbitrarias, pero deben utilizarse los estándares emergentes en torno a las etiquetas, y Kubernetes tiene normas relativas a la longitud de las claves y valores de una etiqueta.
Kubernetes admite varios tipos de Servicios:
- ServicioClusterIP
-
Este tipo de Servicio por defecto tiene una dirección IP virtual -que sólo es válida dentro del propio clúster- asignada al Servicio. También se crea un nombre DNS correspondiente, y tanto la dirección IP como el nombre DNS permanecerán constantes hasta que se elimine el Servicio. Los Servicios ClusterIP no son accesibles desde fuera del clúster.
- ServicioNodePort
-
Expone el Servicio en un puerto definido. El tráfico enviado a la dirección IP de un nodo en ese puerto se reenvía al ClusterIP del Servicio, que a su vez distribuye el tráfico a los Pods que forman parte del Servicio. Los Servicios NodePort te permiten exponer el Servicio a través de un proceso externo o manual, como apuntar una instancia HAProxy externa al NodePort definido del Servicio.
- ServicioLoadBalancer
-
Requiere algún tipo de controlador que sepa cómo aprovisionar un equilibrador de carga externo. Este controlador puede formar parte del proveedor de la nube de Kubernetes, que admite la integración con las plataformas de nube subyacentes, o puede ser un software independiente que instales en el clúster. En cualquier caso, Kubernetes configura un Servicio NodePort y luego configura el equilibrador de carga externo para que reenvíe el tráfico al puerto de nodo asignado. Los Servicios del Balanceador de Carga son la principal forma de exponer Servicios fuera de un clúster Kubernetes.
Los Servicios operan en la Capa 4 del modelo OSI (en la capa TCP/UDP). Como tal, un Servicio no puede distinguir entre los nombres de host utilizados en una solicitud HTTP, por ejemplo. Dado que muchas empresas utilizaban Kubernetes para ejecutar aplicaciones basadas en web a través de HTTP/HTTPS, esto se convirtió en una limitación de los Servicios. En respuesta, la comunidad de Kubernetes creó Ingresses.
Entra en
Un Ingress se utiliza para realizar el enrutamiento de Capa 7 (HTTP), basándose en varias propiedades de una solicitud HTTP. Un Ingress es gestionado por un controlador de entrada, que es una pieza específica de software desplegada en tu clúster Kubernetes para realizar las funciones necesarias. Existen numerosos controladores de entrada, entre los que se incluyen el controlador de entrada ginx, el controlador de entrada HAProxy, el controlador de entrada Traefik y varios controladores de entrada basados en Envoy Proxy (como Ambassador, Contour y Gloo). El conjunto exacto de características de estos diversos controladores de entrada varía, pero todos realizan la misma función básica: recibir definiciones de entrada de la API de Kubernetes y realizar el enrutamiento HTTP basado en esas definiciones.
Nota
La comunidad de Kubernetes está definiendo un sustituto de Ingress. Este proyecto se conoce como el proyecto de la pasarela de API, y hay más información disponible en el sitio web de la pasarela de API de Kubernetes. Este esfuerzo también implica a muchos productos y proyectos que se clasifican como pasarelas de API, ya que su funcionalidad básica es similar a la que proporciona la API de pasarela.
Una definición de entrada (o un objeto de entrada) proporciona un conjunto de reglas que definen cómo debe realizar el enrutamiento HTTP el controlador de entrada. Digamos, por ejemplo, que el Servicio A atiende las solicitudes de la API /users
, mientras que el Servicio B atiende las solicitudes de la API /devices
. Podrías tener un objeto Ingress con múltiples reglas que dirija el tráfico enviado a /users
al Servicio A (refiriéndose aquí a un Servicio Kubernetes) y dirija el tráfico enviado a /devices
al Servicio B. Ambas rutas podrían estar expuestas detrás de un único FQDN, como api.company.com
o similar.
Alternativamente, podrías tener un objeto de Entrada que defina reglas basadas en el FQDN de la solicitud y dirija el tráfico a varios Servicios en función de ese FQDN. El Ejemplo 4-6 muestra un manifiesto de Entrada.
Ejemplo 4-6. Manifiesto de entrada de Kubernetes
apiVersion
:
networking.k8s.io/v1
kind
:
Ingress
metadata
:
name
:
minimal-ingress
annotations
:
nginx.ingress.kubernetes.io/rewrite-target
:
/
spec
:
ingressClassName
:
nginx-example
rules
:
-
http
:
paths
:
-
path
:
/testpath
pathType
:
Prefix
backend
:
service
:
name
:
test
port
:
number
:
80
La versión de la API para un objeto Ingress también especifica un grupo de API (
networking.k8s.io
). A medida que Kubernetes ha ido creciendo y madurando, se han utilizado grupos de API para ayudar a organizar la creciente superficie de API.Estas reglas definen cómo debe dirigir el tráfico el controlador de entrada en función de diversas condiciones.
En este caso, sólo hay una única condición (el
path
tiene que empezar por/testpath
). Sin embargo, añadir nuevos elementos a la listapaths
nos permitiría definir reglas adicionales para dirigir el tráfico a diferentes Servicios.En todos los casos, el destino del tráfico dirigido por un controlador de entrada es un Servicio Kubernetes.
Consejo
Un controlador de entrada dirige el tráfico a los Servicios Kubernetes, que suelen definirse como Servicios ClusterIP. Sin embargo, el controlador de entrada también debe estar expuesto mediante un Servicio, normalmente un Servicio LoadBalancer. Esto permite que el tráfico entre en el clúster Kubernetes a través del Servicio del controlador de entrada, y luego sea dirigido por el controlador de entrada al Servicio apropiado. De este modo, sólo el controlador de entrada es accesible desde fuera del clúster, y todos los demás Servicios están "cerrados" tras el controlador de entrada.
Otras construcciones de red
Pods, Servicios e Ingresses forman la mayor parte de lo que se utiliza habitualmente para definir cómo Kubernetes debe exponer y conectar las cargas de trabajo. Sin embargo, merece la pena mencionar brevemente un par de construcciones más:
- Políticas de red
-
Las Políticas de Red permiten a los usuarios controlar el tráfico que entra o sale de los Pods. Son, esencialmente, cortafuegos de Pods. Sin embargo, ten cuidado con la comparación con los cortafuegos; aunque es conveniente, las Políticas de Red se comportan de forma diferente a la mayoría de los cortafuegos: no hay acción de denegación, por ejemplo, ni sentido de prioridad u orden de las reglas.
- Espacios de nombres
-
Los espacios de nombres de Kubernetes, que no deben confundirse con los espacios de nombres del núcleo de Linux, proporcionan una separación lógica de los objetos de la API, como los Pods y los Servicios. Los espacios de nombres también afectan al descubrimiento automático de servicios basado en DNS que Kubernetes realiza para todos los Servicios.
Hasta ahora, has visto algunos de los objetos de alto nivel de la API de Kubernetes que forman los bloques de construcción de la red Kubernetes. Sin embargo, un componente clave se sitúa un nivel por debajo y es responsable de gestionar toda la fontanería que hace que funcionen las construcciones de nivel superior. Se trata de la CNI, presentada anteriormente en este capítulo, que proporciona un mecanismo estándar para conectar diversos modelos de red a Kubernetes.
Interfaz de red de contenedores
La CNI es una especificación genérica que define cómo debe implementarse un complemento de red de contenedores. Define un conjunto de API y el formato de la configuración esperada. Kubernetes, como solución orquestadora de contenedores, implementa la especificación CNI, por lo que cualquier complemento compatible con CNI puede utilizarse con cualquier clúster de Kubernetes.
Un plug-in CNI puede implementar una o muchas funciones de red, como la asignación de direcciones IP a los Pods, la encapsulación pod-a-pod, la conectividad externa, las políticas de red, etc. La comunidad CNI mantiene algunos plug-ins básicos, pero muchos plug-ins de terceros implementan funciones avanzadas. Algunos de ellos son los plug-ins CNI de Calico, Cilium y Amazon ECS.
La especificación CNI no dicta cómo debe funcionar el plug-in; sólo dicta cómo interactuar con él utilizando una interfaz específica. El plug-in CNI puede escribirse en cualquier lenguaje, pero se espera que se proporcione y ejecute como un binario.
Consejo
Puedes encontrar más información sobre CNI, la especificación CNI y los complementos CNI disponibles en el repositorio CNI GitHub.
Utilizando la especificación CNI, Kubernetes puede implementar una amplia variedad de modelos de red. En la Figura 4-4, representamos una de las muchas soluciones de red de Kubernetes utilizando el enfoque kube-router para ilustrar cómo ambos mundos pueden trabajar juntos. En este caso, cada host Kubernetes utiliza un demonio BGP (que se ejecuta en el espacio de nombres predeterminado de Kubernetes) para anunciar los CIDR del host mediante el protocolo BGP. El reflector de rutas BGP externo simplifica la arquitectura BGP, evitando la necesidad de una malla completa de peers BGP. Todos los hosts de Kubernetes conocen los prefijos de los demás, así como los prefijos externos (por ejemplo, Internet).
Por último, para completar esta exploración de alto nivel de Kubernetes, echemos un vistazo a un nuevo patrón de red que está ganando mucha popularidad: la malla de servicios.
Malla de servicio
Las aplicaciones de software modernas han llevado a la reciente adopción de la arquitectura de microservicios. Esta arquitectura se caracteriza por utilizar muchos servicios pequeños e independientes que trabajan juntos para proporcionar la funcionalidad de la aplicación, lo que permite una arquitectura más flexible, reutilizable y escalable. Sin embargo, esta arquitectura también introduce retos, y algunos están relacionados con la conexión en red: ¿cómo garantizamos que todos los servicios puedan comunicarse entre sí? ¿Y cómo garantizamos que la comunicación sea segura, fiable y observable?
Una respuesta a estas preguntas es el patrón de malla de servicios. Una malla de servicios es una capa de infraestructura dedicada que gestiona la comunicación entre servicios. Suele implementarse como un conjunto de proxies ligeros que se despliegan junto a los servicios de las aplicaciones. Estos proxies interceptan todo el tráfico hacia y desde las aplicaciones para proporcionar lo siguiente:
- Gestión del tráfico
-
Descubrimiento de servicios, equilibrio de carga y tolerancia a fallos
- Seguridad
-
Cifrado, autenticación y autorización, y control de acceso
- Observabilidad
-
Métricas de la aplicación, rastreo distribuido y registros
El objetivo es claro: descargar las tareas comunes de red de la aplicación a la malla de servicios. Esto permite a las aplicaciones centrarse en su lógica empresarial principal sin preocuparse de los detalles relacionados con la conectividad.
La malla de servicios no se limita a Kubernetes. Netflix, en 2017, ya disponía de una pila tecnológica para implementar funciones de malla de servicios. Sin embargo, hoy en día Kubernetes es la plataforma más popular para la implementación de la malla de servicios, y ambas se utilizan a menudo juntas (la mayoría de los ejemplos que mencionamos a continuación se ejecutan sólo en Kubernetes).
Para conseguirlo, la arquitectura de malla de servicios requiere dos componentes principales:
- Plano de datos
-
Esta capa de comunicación se encarga de la comunicación real entre servicios. Se implementa como un conjunto de proxies ligeros (sidecar proxies) que se despliegan junto a los servicios de aplicación. Estos proxies interceptan todo el tráfico hacia y desde los servicios e implementan las funciones de malla de servicios. Ejemplos de proxies del plano de datos son Envoy, Cilium, NGINX y HAProxy.
- Plano de control
-
Esta capa de gestión proporciona la configuración para el plano de datos. Toma la configuración de tu malla de servicios y la traduce en la configuración para los proxies del plano de datos. También distribuye actualizaciones de descubrimiento de servicios y recoge datos de observabilidad del plano de datos. Algunos de los planos de control más populares son Istio, Consul y Linkerd (que también proporciona un proxy del plano de datos), o soluciones específicas de la nube como AWS App Mesh.
Nota
Mastering Service Mesh de Anjali Khatri y Vikram Khatri (Packt) es un buen recurso para aprender más sobre ello.
En Kubernetes, el plano de datos se implementa como un conjunto de contenedores sidecar desplegados junto a los contenedores de aplicación en el mismo Pod. El tráfico de los contenedores de aplicación se redirige a través de los contenedores sidecar, aunque el mecanismo específico utilizado para ello varía entre las implementaciones de malla de servicios. El plano de control programa estos contenedores sidecar con la configuración necesaria para implementar las características de la malla de servicios, de forma centralizada.
Podrías entender la malla de servicios como otra abstracción de superposición, pero la más cercana a la aplicación. Pero, como ocurre con cualquier otra superposición, la malla de servicios depende de la infraestructura de red subyacente para proporcionar conectividad. Tu papel como ingeniero de redes será implementar la malla de servicios o proporcionar la infraestructura de red subyacente para soportarla e interconectarla con el resto de la red. En ambos casos, comprender cómo funciona la malla de servicios es una habilidad que hay que dominar.
Resumen
La computación en nube no niega la necesidad de tener conocimientos de redes. De hecho, como has visto en este capítulo, las redes y la automatización de redes son tan importantes en los entornos basados en la nube como en tus entornos locales.
Los servicios de red en la nube, las redes de contenedores y Kubernetes, y la malla de servicios son tecnologías de red que resuelven problemas específicos. No son mutuamente excluyentes. Puedes combinarlas para proporcionar soluciones de red completas para tus aplicaciones basadas en la nube, extendiéndolas al mismo tiempo a tus redes locales.
En el siguiente capítulo, cambiamos de marcha para iniciar tu viaje por el mundo de la automatización de redes, y te mostramos cómo los contenedores pueden ayudarte a arrancar entornos de laboratorio. Empecemos con tu entorno de desarrollo de red local.
Get Programabilidad y Automatización de Redes, 2ª Edición now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.