domingo, 27 de mayo de 2018

Kubernetes [Best Practices ] mapeo de servicios externos



Nota del editor: En Google Cloud español os presentamos  la sexta entrega de una serie de videos y blogs en siete partes de Google Developer Advocate Sandeep Dinesh sobre cómo aprovechar al máximo su entorno de Kubernetes.

La mayoría de los usuarios de Kubernetes, es probable que use servicios que no pertenecen a su clúster. Por ejemplo, tal vez use la API Twillio para enviar mensajes de texto, o tal vez la API Google Cloud Vision para hacer análisis de imágenes.

Si en sus aplicaciones de diferentes entornos se conectan al mismo punto externo y no tienen pensado llevar el servicio externo a su clúster de Kubernetes, es correcto utilizar el punto extremo de servicio externalizado directamente en su código. Sin embargo, hay muchos escenarios donde este no es el caso.

Un buen ejemplo de esto son las bases de datos. Si bien algunas bases de datos nativas de la nube de Google, como Cloud Firestore o Cloud Spanner, usan un punto final único para todos los accesos, la mayoría de las bases de datos tienen puntos finales separados para diferentes instancias.

En este punto, puede estar pensando que una buena solución para encontrar el punto final es usar ConfigMaps. Simplemente almacene la dirección del punto final en una ConfigMap, y úsela en su código como una variable de entorno.

Si bien esta solución funciona, hay algunas desventajas. Debe modificar su implementación para incluir ConfigMap y escribir código adicional para leer desde las variables de entorno. Pero lo más importante es que, si la dirección del punto final cambia, es posible que deba reiniciar todos los contenedores en ejecución para obtener la dirección del punto final actualizada.

En este episodio de "Kubernetes Best Practices",  aprenderemos a aprovechar los mecanismos integrados de descubrimiento de servicios de Kubernetes para servicios que se ejecutan fuera del clúster, ¡como lo haría con los servicios dentro del clúster! Esto le brinda igualdad en sus entornos de desarrollo y producción, y si finalmente mueve el servicio dentro del clúster, no tiene que cambiar el código en absoluto.


Escenario 1: base de datos fuera del clúster con dirección IP


Un escenario muy común es cuando aloja su propia base de datos, pero lo hace fuera del clúster, por ejemplo, en una instancia de Google Compute Engine. Esto es muy común si ejecuta algunos servicios dentro de Kubernetes y algunos fuera, o necesita más personalización o control de lo que permite Kubernetes.

Con suerte, en algún momento, puede mover todos los servicios dentro del clúster, pero hasta entonces está viviendo en un mundo híbrido. Afortunadamente, puede usar servicios de Kubernetes estáticos para controlar el entorno de conexión.

En este ejemplo, creé un servidor MongoDB usando Cloud Launcher. Debido a que se creó en la misma red (o VPC) que el clúster Kubernetes, se puede acceder utilizando la dirección IP interna, está presenta un alto rendimiento. En Google Cloud, esta es la configuración predeterminada, por lo que no es necesario nada especial para configurar.


Ahora que tenemos la dirección IP, el primer paso es crear un servicio:

kind: Service
apiVersion: v1
metadata:
 name: mongo
Spec:
 type: ClusterIP
 ports:
 - port: 27017
   targetPort: 27017

Es posible que observe que no hay selectores de Pod para este servicio. Esto crea un servicio, pero no sabe a dónde enviar el tráfico. Esto le permite crear manualmente un objeto Endpoints que recibirá tráfico de este servicio.

kind: Endpoints
apiVersion: v1
metadata:
 name: mongo
subsets:
 - addresses:
     - ip: 10.240.0.4
   ports:
     - port: 27017

Puede ver que los puntos finales definen manualmente la dirección IP de la base de datos y usa el mismo nombre que el servicio. Kubernetes usa todas las direcciones IP definidas en los puntos finales como si fueran pods de Kubernetes regulares. Ahora puede acceder a la base de datos con una cadena de conexión simple:



mongodb://mongo

NOTA: ¡No necesita usar direcciones IP en su código! Si la dirección IP cambia en el futuro, puede actualizar el Endpoint con la nueva dirección IP, y sus aplicaciones no necesitarán realizar cambios.

Escenario 2: base de datos remotamente alojada con URI


Si está utilizando un servicio de base de datos alojado de un tercero, es probable que le proporcionen un identificador de recursos unificado (URI) que puede usar para conectarse. Si le dan una dirección IP, puede usar el método que está en el Escenario 1.

En este ejemplo, tengo dos bases de datos MongoDB alojadas en mLab. Uno de ellos es mi base de datos de desarrollo, y el otro es producción.


Las cadenas de conexión para estas bases de datos son las siguientes:

mongodb://<dbuser>:<dbpassword>@ds149763.mlab.com:49763/dev

mongodb://<dbuser>:<dbpassword>@ds145868.mlab.com:45868/prod

mLab le proporciona un URI dinámico y un puerto dinámico, y puede ver que ambos son diferentes. Usemos Kubernetes para crear una capa de abstracción sobre estas diferencias. En este ejemplo, vamos a conectarnos a la base de datos de desarrollo.

Puede crear un servicio Kubernetes "ExternalName", que le proporciona un servicio Kubernetes estático que redirecciona el tráfico al servicio externo. Este servicio realiza una redirección de CNAME simple a nivel de kernel, por lo que su rendimiento tiene un impacto mínimo.

El YAML para el servicio se ve así:

kind: Service
apiVersion: v1
metadata:
 name: mongo
spec:
 type: ExternalName
 externalName: ds149763.mlab.com

Ahora, puede utilizar una cadena de conexión mucho más simplificada:

mongodb://<dbuser>:<dbpassword>@mongo:<port>/dev

Porque "ExternalName" utiliza Redirección CNAME, no puede hacer reasignación de puertos. Esto podría estar bien para los servicios con puertos estáticos, pero desafortunadamente no es suficiente en este ejemplo, donde el puerto es dinámico. El nivel libre de mLab te da un número de puerto dinámico y no puedes cambiarlo. Esto significa que necesita una cadena de conexión diferente para dev y prod.

Sin embargo, si puede obtener la dirección IP, entonces puede hacer la reasignación de puertos, como explicaré en la siguiente sección. Escenario

3: Base de datos alojada remotamente con URI y reasignación de puertos.


El redireccionamiento CNAME funciona muy bien para los servicios con el mismo puerto para cada entorno, no se cumple en los escenarios donde los diferentes puntos finales para cada entorno usan diferentes puertos. Afortunadamente, podemos solucionarlo utilizando algunas herramientas básicas.

El primer paso es obtener la dirección IP del URI.

Si ejecuta el comando dig, nslookup, hostname o ping contra el URI, puede obtener la dirección IP de la base de datos.


Ahora puede crear un servicio que reasigne el puerto mLab y un punto final para esta dirección IP.

kind: Service
apiVersion: v1
metadata:
 name: mongo
spec:
 ports:
 - port: 27017
   targetPort: 49763
---
kind: Endpoints
apiVersion: v1
metadata:
 name: mongo
subsets:
 - addresses:
     - ip: 35.188.8.12
   ports:
     - port: 49763

Nota: ¡Un URI podría usar DNS para balancear la carga a varias direcciones IP, por lo que este método puede ser peligroso si cambian las direcciones IP!.

Si obtiene varias direcciones IP del comando anterior, puede incluirlas en el YAML de puntos finales, y Kubernetes equilibrará el tráfico de carga con todas las direcciones IP.

Con esto, puede conectarse a la base de datos remota sin necesidad de especificar el puerto. . ¡El servicio de Kubernetes hace la reasignación de puertos de forma transparente!

mongodb://<dbuser>:<dbpassword>@mongo/dev

Conclusion


Mapear servicios externos a los internos le brinda la flexibilidad de incorporar estos servicios en el clúster en el futuro mientras minimiza los esfuerzos de refactorización. Incluso si no planea traerlos hoy, nunca se sabe lo que puede traer el mañana. Además, facilita la administración y comprensión de los servicios externos que usa su organización.

Si el servicio externo tiene un nombre de dominio válido y no necesita la reasignación de puertos, el uso del tipo de servicio "ExternalName" es una manera fácil y rápida. mapear el servicio externo a uno interno. Si no tiene un nombre de dominio o necesita hacer una reasignación de puertos, simplemente agregue las direcciones IP a un punto final y utilícelo en su lugar.