Documentos de Académico
Documentos de Profesional
Documentos de Cultura
aplicaciones y el
desacoplamiento de la
infraestructura,
servicios y equipos
Por Eric Brewer y Jennifer Lin, Google
Asimismo, las nubes públicas ofrecen plataformas bastante diferentes entre sí, lo que
dificulta la portabilidad de cargas de trabajo, tanto en lo que respecta a cuestiones técnicas
como a las habilidades de los desarrolladores. Aun así, la mayoría de las empresas acepta la
opción de usar múltiples nubes, cambiar de proveedores a lo largo del tiempo y, en general,
conservar cierto nivel de independencia con sus proveedores.
Cargas
de trabajo
modernizadas
Cargas
de trabajo
tradicionales
On-premise Nube
La segunda función de los contenedores, conocida como "contenedores Linux", se trata del
aislamiento entre diferentes aplicaciones en una misma máquina. Google fue pionero en esta
metodología hace ya más de diez años, de modo tal que podemos empaquetar varias
aplicaciones en el mismo hardware sin tener que preocuparnos (mucho) sobre cómo
interfieren entre sí. Esto nos permite visualizar la programación en un clúster principalmente
como un problema bin-packing independiente de las aplicaciones específicas.1
Debido al éxito de la portabilidad y el aislamiento del desempeño bajo el lema "escribir una
vez, ejecutar en cualquier ubicación" sobre infraestructuras compartidas, los contenedores
han demostrado ser un primer paso crítico hacia el desacoplamiento de las aplicaciones y la
infraestructura, así como un elemento fundamental de la modernización de las aplicaciones.
¹ Google ha resuelto el problema de empaquetamiento de forma interna, no a través de los contenedores como
Docker, sino mediante una combinación de enlaces estáticos y el uso forzoso de algunas bibliotecas de nivel inferior.
Esto funciona cuando tenemos control pleno de nuestras aplicaciones, pero es muy difícil de administrar y no es tan
flexible como el modelo de Docker. Ver: https://research.google.com/pubs/pub43438.html?hl=es
El hecho de que cada pod tenga su propia dirección IP también permite el desacoplamiento:
cada pod tiene todos los puertos disponibles. Esto es muy importante ya que, por cuestiones
de convención, la mayoría de las aplicaciones supone la disponibilidad de un puerto fijo, como
el puerto 80 para HTTP. El sistema de nombres de dominio (DNS) promueve esta convención
al no incluir números de puertos en su resolución (en condiciones normales de uso), de modo
tal que se supone la existencia del puerto.2 Tradicionalmente, los puertos son un concepto
tanto de aplicación como de máquina, por lo que, para tener diferentes aplicaciones por
máquina, es necesario contar con diferentes direcciones IP por máquina. Los pods dan
solución a este problema: una máquina con 100 pods tendrá, al menos 100 direcciones IP.
² La solución interna de Google, la cual estamos dejando de lado paulatinamente, consistía en cambiar el DNS para
incluir números de puerto y, luego, asignar de forma dinámica puertos a las aplicaciones según sea necesario. Esto no
es conveniente para la mayoría de las aplicaciones empaquetadas y, en general, lo que queremos es evitar la
necesidad de modificar las aplicaciones.
Servicios básicos
Las arquitecturas modernas están "orientadas a los servicios". Pero antes de responder al
interrogante "¿qué es un servicio?", abordaremos la abstracción de servicio incorporada en
Kubernetes. Más adelante, hablaremos sobre funcionalidades más avanzadas que se han
desarrollado sobre la base de estos servicios básicos.
En Kubernetes, un servicio básico suele ser un conjunto dinámico de pods apoyados en
un mecanismo de agrupamiento que aplica diferentes políticas y mantiene la IP del
servicio. Esta clase de servicios suele denominarse microservicio, aunque los servicios no
necesariamente son pequeños, sino que hace referencia más al building block y al equipo
responsable del servicio. Google ejecuta más de 10,000 servicios en todo momento.
El hecho de contar con un nombre persistente y altamente disponible es, por mucho, el
aspecto más importante de un servicio. El nombre primario es simplemente una dirección IP:
Los servicios de Kubernetes usan una "IP de servicio" que es independiente de las IP de los
pods subyacentes que implementan ese servicio.3 El "mecanismo de agrupamiento" que
mencionamos anteriormente suele ser un proxy, aunque también puede ser una forma de
enrutamiento o traducción de direcciones de red (NAT) que mapee la IP de servicio a las IP del
pod de manera dinámica. Las formas no basadas en proxy son menos flexibles, pero tienen un
mejor desempeño.
• Actualizaciones en línea, que son posibles gracias al desacoplamiento del ciclo de vida
de los servicios y los pods que los componen
• Escalamiento más sencillo, ya que los pods pueden adaptarse a la carga transparente
(manual o automática) para los clientes
• Tareas de definición y aplicación de políticas unificadas a través de diferentes proxy
para simplificar la administración de propiedades más complejas, como la seguridad y
las operaciones
Los servicios llevan a que muchos grupos implementen actualizaciones compatibles con
versiones anteriores, de manera independiente. Los números de versión ayudan a los equipos
desacoplados a comprender si los clientes cuentan con la seguridad necesaria para utilizar
una nueva versión. En la práctica, los equipos suelen usar convenciones en torno a los
números de versiones para reflejar cambios semánticos5.
Los servicios básicos en Kubernetes también son servicios sin estado. Por regla, todos
los pods de un servicio provienen de la misma especificación, y es posible reiniciarlos a partir
de esa especificación en cualquier momento. Este nivel de abstracción es suficiente para la
mayoría de los servicios y también para aplicaciones con la metodología "twelve-factor".
Estos casos suponen que el almacenamiento a lo largo del tiempo es administrado dentro de
otro servicio.
4
https://grpc.io/
5
Ver, por ejemplo, https://semver.org/
6 Para acceder a un valioso debate sobre la definición de límites y tamaños de servicios, consultar la sección sobre
"módulos" en el reciente libro de Ousterhout, A Philosophy of Software Design.
El caso de uso inicial para el proxy es simplemente el balanceo de cargas: distribución de las
solicitudes entrantes en el conjunto activo de pods. Además de permitir la alta disponibilidad
en la IP de servicio, esto también permite dividir el tráfico entre versiones para fines de
pruebas de detección de fallos y pruebas A/B. También es el mecanismo que se utiliza para
la implementación progresiva de una nueva versión.
Como hemos desarrollado hasta ahora, el proxy opera en la capa 4 (L4), y funciona a nivel de
TCP e IP. Esto es suficiente para los servicios básicos y también para las aplicaciones
heredadas que esperan DNS, una dirección IP y un número de puerto. Los servicios ubicados
en la capa 7 (L7) suelen usar solicitudes HTTPS, que ofrecen más información y, por lo tanto,
habilitan el uso de políticas más sofisticadas. A largo plazo, esperamos que se utilice L4 para
el control de acceso por partes (a un clúster o espacio de nombres dentro de un clúster) y L7
para implementar las políticas más complejas que requieren las empresas modernas y las
aplicaciones dinámicas. Estas políticas L7 pueden administrarse a través de mecanismos
modernos de control de origen que contemplan una revisión explícita y la aceptación de
cambios en el código fuente (por ejemplo: GitOps).
El proxy también es útil para la telemetría: puede medir indicadores de nivel de servicio (SLI)
para un servicio, como el throughput y la latencia de solicitudes, sin necesidad de conocer el
servicio (un punto clave del desacoplamiento). También puede verificar el estado de los pods
y eliminar cargas en estos. Su telemetría ofrece la base para el ajuste automático de escala
en el servicio.
En las próximas dos secciones, abordaremos dos usos de los proxy. El primer tipo de estos
administra el tráfico entre servicios dentro de una aplicación, lo que suele denominarse
tráfico “Este-Oeste” en virtud del diagrama típico de arquitectura donde los usuarios se
ubican en la parte superior (Norte) y los servicios se distribuyen de izquierda a derecha
7
Ver https://people.eecs.berkeley.edu/~brewer/papers/TACC-sosp.pdf.
(Oeste a Este). El segundo tipo corresponde a un proxy más tradicional orientado al usuario,
que administra el tráfico hacia la aplicación (Norte-Sur) y ofrece autenticación de usuarios,
control de acceso, diferentes clases de gestión de API y mediación.
Administración de tráfico
Istio gestiona un grupo de proxy que combina los componentes de una malla de servicios.
Esta malla representa el conjunto de servicios dentro de una aplicación de mayor tamaño
orientada al usuario. Istio administra el tráfico de servicio a servicio (Este-Oeste) y depende
de los proxy que se encuentran en cada servicio. Esto permite el balanceo de cargas en el
lado del cliente, entre las instancias de servicio, además de capacidades de tráfico entrante,
como las pruebas A/B y las versiones para detección de fallos en servicios orientados al
usuario. Los proxy de malla también habilitan capacidades de tráfico saliente, como tiempos
de espera, reintentos e interruptores de circuito, y además mejoran la tolerancia a fallas al
enrutar a servicios web externos. Como cuestión crítica, Istio ofrece una administración
centralizada y sistemática de estos proxy y, por lo tanto, de las políticas que implementan.
Esto es probablemente el desacoplamiento más sobresaliente: las políticas desacopladas de
los servicios. En especial, permite que los desarrolladores eviten políticas de codificación
dentro de los servicios, lo que implica que pueden modificar las políticas sin tener que volver
a desplegar el servicio. De hecho, solo deben actualizar la configuración de los proxy
relevantes para cambiar las políticas. Al ser centralizada la administración, es fácil lograr una
coherencia entre todos los servicios, y es posible aplicar actualizaciones en una
implementación controlada, con el fin de mejorar la seguridad.
En un nivel superior, este modelo también permite desacoplar las operaciones del
desarrollo. Un problema frecuente en TI es que los equipos de operaciones quieren aplicar
8
https://cloud.google.com/blog/products/gcp/istio-modern-approach-to-developing-and
políticas de forma uniforme, pero deben trabajar junto con los desarrolladores para asegurar
que todos los servicios funcionen de la manera correcta. Esto resulta en una costosa
coordinación, como el bloqueo de lanzamientos por revisión de políticas. En el modelo Istio,
es necesaria una menor coordinación, ya que las políticas residen fuera de los servicios. A su
vez, los equipos de desarrollo tienen una mayor velocidad, pueden lanzar diario (o con mayor
frecuencia) y suelen tener más autonomía y más moralidad. En términos generales,
queremos que los desarrolladores de servicios sean responsables de aquellos aspectos
operativos que se relacionan con la disponibilidad y las implementaciones, dejando la
definición y aplicación de políticas amplias a grupos más centralizados.
El tráfico es
interceptado de forma
transparente y asignado
al proxy. La app
desconoce la presencia
del proxy.
Servicio A Servicio B
Istio toma estos proxy, como se muestran en la Figura 2, y los administra, lo que ofrece
distintos beneficios que permiten convertir servicios básicos en servicios avanzados con
propiedades y políticas coherentes.
Seguridad
Además de la administración de tráfico, Istio autentica los servicios entre sí, encripta el
tráfico entre servicios (TLS mutuo), ofrece control de acceso para servicios solicitantes, y
genera un registro de auditoría. Estas características se incorporan de forma transparente
sin necesidad de realizar cambios en la aplicación o una administración tediosa de
Observabilidad
La visibilidad de los servicios es crucial para el objetivo de una ejecución segura en
producción, y comprender la telemetría en tiempo real es la pieza clave para construir una
plataforma segura. Istio automatiza la instrumentación de los servicios al generar y recopilar,
de forma automática, datos y registros sobre estos. Al instrumentar de esta manera, Istio
permite una recopilación coherente de datos entre diferentes servicios y backend9. Esto
facilita la generación de paneles y posibilita indicadores de nivel de servicio (SLI) comunes,
como distribuciones de latencia, throughput y tasas de error. A su vez, la consistencia de las
métricas simplifica las tareas de automatización, como el ajuste de escala automático y las
verificaciones de estado (ver ejemplos de telemetría de Istio).
9 Los adaptadores de Istio Mixer habilitan backend conectables para datos como métricas y registros (https://istio.io/-
docs/reference/config/policy-and-telemetry/adapters/)
Google fue pionero en el modelo confianza cero de seguridad de acceso, utilizando proxy
Norte-Sur para aplicar controles más detallados sin sacrificar la coherencia en la experiencia
de los usuarios (ver BeyondCorp: A New Approach to Enterprise Security10 y BeyondCorp:
The Access Proxy11). Al ser un componente clave de la arquitectura general de seguridad de
Google (más detalles en https://cloud.google.com/security/overview/whitepaper), este
modelo de acceso adaptable al contexto aplica la autenticación de servicios y usuarios,
además de controles dinámicos de acceso en todas las aplicaciones.
Empleado
Aplicaciones SaaS de
¿Es correcto que el Google y de terceros
Contratista usuario acceda a las
aplicaciones SaaS?
10
https://ai.google/research/pubs/pub43231
11
https://research.google.com/pubs/pub45728.html?hl=tr
utiliza un contexto "en tiempo real" para aplicar, de manera adecuada, políticas de acceso
para puntos finales en la nube, además de minimizar las costosas violaciones de datos.
Todos los accesos de usuarios o servicios son verificados y otorgados a través de políticas
de acceso con privilegios mínimos:
• Se utilizan múltiples factores (más allá del control de acceso basado en funciones
(RBAC), tokens) para autorizar usuarios, recursos y servicios.
• De manera predeterminada, los datos se encriptan en reposo y los datos en tránsito se
movilizan por conexiones encriptadas.
• La autorización binaria asegura que se firmen y se verifiquen las cargas de trabajo
portátiles (VM, contenedores) de forma adecuada.
• Telemetría coherente para los accesos a servicios detallados, no solo sobre registros y
monitoreo sobre IP e infraestructuras.
• Descubrimiento constante para localizar e indexar datos, y controles que permiten
analizar, clasificar y proteger datos.
• Todos los secretos y certificados son firmados a través de un certificado de raíz válido,
sujeto a la administración continua de certificados.
• Expertos confiables de la industria tienen a su cargo el mantenimiento de actualizaciones
de seguridad y parches automatizados.
Hoy más que nunca, las empresas están adoptando rápidamente marcos de software de
código abierto (Kubernetes, Istio, Knative) en sus entornos actuales empresariales y de TI.
Esta evolución en términos de arquitectura posibilita la portabilidad de cargas de trabajo, así
como las abstracciones de servicios en la nube independientes del proveedor y la
administración centralizada de políticas en entornos heterogéneos, lo que mejora el
control de costos y de seguridad y simplifica considerablemente las migraciones
futuras hacia la nube.
Hasta ahora hemos explicado el uso de contenedores y pods, además del uso de proxy
administrados para crear servicios avanzados con un buen nivel de desacoplamiento. La
pieza fundamental que resta agregar en nuestra plataforma de servicios en la nube es el
modelo de configuración.
La configuración suele ser uno de los aspectos más complejos de las aplicaciones modernas
y, a lo largo de los años, Google ha creado y descartado una gran variedad de sistemas de
configuración, desde los más simples hasta los más complejos.
El primer interrogante hace referencia a qué usar como lenguaje de configuración. Es muy
tentador, incluso para Google en ciertas ocasiones, emplear un lenguaje de para fines
generales, como Python o secuencias de comando de bash. Los lenguajes de este tipo son
muy potentes, ya que pueden resolver casi cualquier problema, pero dan lugar a sistemas
complejos, pueden no ser claros, y su interpretación durante el período de ejecución puede
llevar a comportamientos inesperados en producción.
Por el contrario, utilizamos un lenguaje restringido, YAML, que expresa el estado deseado de
un recurso, pero no lo procesa directamente. El objetivo es ser declarativo, esto es, el
lenguaje YAML debería manifestar (“declarar”) el estado deseado del sistema. YAML puede
usarse de forma directa o puede generarse fácilmente mediante herramientas; nosotros
podemos brindar herramientas inteligentes para combinar los cambios cuando existen
múltiples actores involucrados en la configuración (lo que resulta común en sistemas de
mayor tamaño).
del controlador consiste en realizar ajustes sobre el sistema de ejecución (como agregar un
nuevo pod), de modo tal que el estado actual se alinee ("se concilie") con el estado deseado.
Este es un método sólido de coherencia eventual: es posible que lleve algo de tiempo
completar la conciliación, pero cuando el estado real se desvía del estado deseado (sin
modificar) a causa de fallas, el mismo proceso vuelve a alinearlo.
Biblioteca
apiserver controladores
CLI
IU
etcd
Una de las ventajas de este modelo declarativo es que funciona muy bien con sistemas
modernos de control de versiones, como git: los archivos de configuración son tratados
como código fuente y pueden (y deberían) ser administrados bajo las mismas nociones de
control de versiones, pruebas e implementación. Este sistema es muy general y muy
coherente; por ejemplo, existe un metadato común para los archivos de configuración,
además de una semántica bien definida para actualizaciones, validaciones y eliminación de
recursos. La lógica de negocio de los recursos reside dentro de los controladores, mientras
que el resto es bastante genérica y, por ende, reutilizable y ampliable.
Conclusiones
La clave para el desarrollo moderno consiste en desacoplar los equipos para que puedan ser
más productivos. El desacoplamiento se presenta de diversas formas, que, en conjunto,
llevan a una evolución más rápida y a mejores sistemas en menos tiempo, con un menor
esfuerzo. Hemos explicado las diferentes maneras que encontramos en el desacoplamiento:
Al desacoplar la infraestructura, los servicios y los equipos, las empresas pueden mejorar la
agilidad de sus desarrolladores y acelerar la innovación sin poner en riesgo la eficiencia
operativa, la seguridad y la gobernanza.