Mostrando entradas con la etiqueta kubernetes best practices. Mostrar todas las entradas
Mostrando entradas con la etiqueta kubernetes best practices. Mostrar todas las entradas

lunes, 4 de junio de 2018

Kubernetes [Best Practices ] actualizar tus clústeres sin parada



Nota del traductor: En Google cloud Español os indicamos que es la última 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 máxima de -Si funciona no toques-  no es aplicable a los servicios que están expuestos en internter y es una buena práctica mantener la aplicación actualizada para optimizar la seguridad y el rendimiento. Kubernetes y Docker pueden hacer que estas actualizaciones sean mucho más fáciles, ya que puede construir un nuevo contenedor con las actualizaciones y desplegar lo con relativa facilidad.

Al igual que sus aplicaciones, Kubernetes constantemente obtiene nuevas funciones y actualizaciones de seguridad, por lo que los nodos subyacentes y la infraestructura de Kubernetes deben mantenerse actualizados.

En este episodio de Best-Practices de Kubernetes, echemos un vistazo a cómo Google Kubernetes Engine puede hacer que la actualización de su clúster de Kubernetes sea transparente.




Las dos partes de un grupo


Cuando se trata de actualizar su clúster, hay dos partes y ambos necesitan actualizarse: los maestros y los nodos. Los maestros necesitan actualizarse primero, y luego los nodos pueden seguir. Veamos cómo actualizar ambos usando Kubernetes Engine.

Actualización del maestro sin tiempo de inactividad

Kubernetes Engine actualiza automáticamente el maestro a medida que se lanzan nuevas versiones , sin embargo, por lo general, no se actualizará automáticamente a una nueva versión (por ejemplo, de 1.7 a 1.8). Cuando esté listo para actualizar a una nueva versión, puede simplemente hacer clic en el botón maestro de actualización en la consola de Kubernetes Engine, por ejemplo:


Sin embargo, es posible que haya notado que el cuadro de diálogo dice lo siguiente:
"Cambiar la versión maestra puede provocar varios minutos de inactividad de la gestionde  control. Durante ese período, no podrá editar este clúster ".
Cuando el maestro realiza una actualización, las implementaciones, los servicios, etc. continúan funcionando como se esperaba. Sin embargo, todo lo que requiere la API de Kubernetes deja de funcionar. Esto significa que kubectl deja de funcionar, las aplicaciones que usan la API de Kubernetes para obtener información sobre el funcionamiento del clúster y, básicamente, no puede realizar ningún cambio en el clúster mientras se está actualizando.

Entonces, ¿cómo actualizar el maestro sin incurrir en tiempo de inactividad?

Masters de alta disponibilidad con clusters regionales de Kubernetes Engine

Mientras que los clústeres Kubernetes Engine estándar "Zonal" solo tienen un nodo maestro que los respalda, puede crear clústeres "Regionales" que proporcionan maestros de zonas múltiples y altamente disponibles.

Al crear su clúster, asegúrese de seleccionar la opción "regional":


¡Y eso es! Kubernetes Engine crea automáticamente sus nodos y maestros en tres zonas, con los maestros detrás de una dirección IP con equilibrio de carga, por lo que la API de Kubernetes continuará funcionando durante una actualización.

Actualización de nodos con tiempo de inactividad cero


Al actualizar los nodos, hay algunas estrategias diferentes que puede usar. Hay dos en los que me quiero enfocar:

  1. Actualización continua
  2. Migración con grupos de nodos
1. Actualización continua
La forma más sencilla de actualizar los nodos de Kubernetes es utilizar una actualización continua. Es el mecanismo de actualización predeterminado que Kubernetes Engine usa para actualizar sus nodos.

Una actualización continua funciona de la siguiente manera. Uno a uno, un nodo se vacía y se condiciona para que no haya más módulos que se ejecutan en ese nodo. Luego se elimina el nodo y se crea un nuevo nodo con la versión actualizada de Kubernetes. Una vez que ese nodo está en funcionamiento, se actualiza el siguiente nodo. Esto continúa hasta que se actualicen todos los nodos.

Puede dejar que Kubernetes Engine administre este proceso por completo habilitando actualizaciones automáticas de nodos en el grupo de nodos.



Si no selecciona esto, el panel de control de Kubernetes Engine le avisará cuando haya disponible una actualización:


Simplemente haga clic en el enlace y siga el mensaje para comenzar la actualización continua.

Advertencia: asegúrese de que sus pods sean administrados por ReplicaSet, Deployment, StatefulSet o algo similar. ¡Los pods autónomos no serán gestionados!

Si  es simple realizar una actualización continua en Kubernetes Engine, tiene algunos inconvenientes.

Una desventaja es que obtiene un nodo menos de capacidad en su clúster. Este problema se resuelve fácilmente escalando su grupo de nodos para agregar capacidad adicional, y luego des-escalando nuevamente una vez que la actualización ha finalizado.

La naturaleza completamente automatizada de la actualización progresiva lo hace fácil pero usted tiene menos control sobre el proceso. También lleva tiempo volver a la versión anterior si hay un problema, ya que debe detener la actualización continua y luego deshacerla.


2. Migración con grupos de nodos
En lugar de actualizar el grupo de nodos "activo" como lo haría con una actualización continua, puede crear un grupo de nodos nuevo, esperar a que se ejecuten todos los nodos y luego migrar las cargas de trabajo de un nodo a la vez.

Supongamos que nuestro clúster de Kubernetes tiene tres VM en este momento. Puede ver los nodos con el siguiente comando:

kubectl get nodes
NAME                                        STATUS  AGE
gke-cluster-1-default-pool-7d6b79ce-0s6z    Ready   3h
gke-cluster-1-default-pool-7d6b79ce-9kkm    Ready   3h
gke-cluster-1-default-pool-7d6b79ce-j6ch    Ready   3h

Creando el nuevo grupo de nodos

Para crear el nuevo grupo de nodos con el nombre "pool-two", ejecute el siguiente comando:

gcloud container node-pools create pool-two

Nota: Recuerde personalizar este comando para que el nuevo grupo de nodos sea el mismo que el anterior. También puede usar la GUI para crear un nuevo grupo de nodos si lo desea.

Ahora bien, si comprueba los nodos, notará que hay tres nodos más con el nuevo nombre del grupo:

$ kubectl get nodes
NAME                                        STATUS  AGE
gke-cluster-1-pool-two-9ca78aa9–5gmk        Ready   1m
gke-cluster-1-pool-two-9ca78aa9–5w6w        Ready   1m
gke-cluster-1-pool-two-9ca78aa9-v88c        Ready   1m
gke-cluster-1-default-pool-7d6b79ce-0s6z    Ready   3h
gke-cluster-1-default-pool-7d6b79ce-9kkm    Ready   3h
gke-cluster-1-default-pool-7d6b79ce-j6ch    Ready   3h


¡Sin embargo, los pods todavía están en los nodos antiguos! Cambiemos los pods de lugar.

Elimine el antiguo grupo

Ahora necesitamos mover el trabajo al nuevo grupo de nodos. Avancemos sobre un nodo a la vez de manera continua.

Primero, ajuste cada uno de los nodos antiguos. Esto evitará que se programen nuevos pods en ellos. Es como meterlos en mantenimiento.

kubectl cordon nodo

Una vez que todos los nodos antiguos están acordonados, los pods solo se pueden programar en los nuevos nodos. Esto significa que puede comenzar a eliminar pods de los nodos antiguos, y Kubernetes los programa automáticamente los crea dentro de  los nuevos nodos. 

Advertencia: asegúrese de que sus pods sean administrados por ReplicaSet, Deployment, StatefulSet o algo similar. ¡Los pods independientes no se reprogramarán!

Ejecute el siguiente comando para vaciar cada nodo. Esto elimina todos los pods en ese nodo.


kubectl drain  --force

Después de vaciar un nodo, asegúrese de que los nuevos pods estén activos y en ejecución antes de continuar con el siguiente.

Si tiene algún problema durante la migración, conecte el antiguo grupo y luego desactive  y vacié  el nuevo grupo. Los pods vuelven a programarse en el grupo anterior.


Borre el grupo anterior. 

Una vez que todos los pods se reprograman de manera segura, es hora de eliminar el grupo anterior. Reemplazar "grupo predeterminado" con el grupo que desea eliminar. 


gcloud container node-pools delete default-pool

¡Aacabas de actualizar con éxito todos sus nodos! 


Conclusión

Para utilizar Kubernetes Engine, puede mantener su clúster de Kubernetes actualizado con solo unos pocos clics.

Si no está utilizando un servicio gestionado como Kubernetes, puede seguir utilizando la actualización continua o método de grupos de nodos con su propio clúster para actualizar los nodos. La diferencia es que debe agregar manualmente los nuevos nodos a su clúster y realizar la actualización maestra a a mano, lo cual puede ser complicado.

Recomiendo utilizar los clústeres regionales de Kubernetes Engine para los maestros de alta disponibilidad y las actualizaciones automáticas de nodos para tener una solución rápida, tenga en cuenta que la actualización es gratuita.

Si necesita el controlar de forma adicional  las actualizaciones de su nodo, el uso de conjuntos de nodos le otorga ese control sin renunciar a las ventajas de una plataforma administrada de Kubernetes que Kubernetes Engine le ofrece.

Y así concluye esta serie sobre las mejores prácticas de Kubernetes. Si tiene ideas para otros temas que le gustaría abordar en el futuro, puede encontrarme en Twitter.

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.


sábado, 19 de mayo de 2018

Kubernetes [Best Practices ] apagando según el manual


Nota del editor: En Google Cloud Español seguimos con las entregas de  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.

Cuando se trata de sistemas distribuidos, manejar los fallo sen muy importante. Kubernetes ayuda con esto al utilizar controladores que pueden ver el estado de su sistema y reiniciar los servicios que han dejado de funcionar. Por otro lado, Kubernetes a menudo puede terminar su aplicación a la fuerza como parte del funcionamiento normal del sistema.

En este episodio de "Kubernes best Practices", veamos cómo puede ayudar a Kubernetes a hacer su trabajo de manera más eficiente y reducir el tiempo de inactividad que puede aparecer en  sus aplicaciones.




Antes de la aparción de los  contenedores, la mayoría de las aplicaciones se ejecutaban en máquinas virtuales o máquinas físicas. Si una aplicación se bloquea, llevó bastante tiempo arrancar o reinicar de nuevo. Si solo tenía una o dos máquinas para ejecutar la aplicación, este tipo de tiempo de recuperación es inaceptable.

En cambio, se volvió común usar la supervisión a nivel de proceso para reiniciar aplicaciones cuando se bloqueaban. Si la aplicación se bloqueó, el proceso de monitoreo podría capturar el código de salida y reiniciar la aplicación al instante.

Con la llegada de sistemas como Kubernetes, los sistemas de monitoreo de procesos ya no son necesarios, ya que Kubernetes maneja el reinicio de las aplicaciones bloqueadas. Kubernetes usa un ciclo de eventos para asegurarse de que los recursos como los contenedores y los nodos están en un estado correcto o funcionado. Esto significa que ya no necesita ejecutar estos procesos de monitoreo manualmente. Si un recurso no pasa un control de checking, Kubernetes automáticamente activa un proceso de reinicio.

Tienes ya publicado en el blog que indican como activar el checking de tus servicios para ver cómo puede configurar comprobaciones de salud personalizadas.


El ciclo de vida de terminación de Kubernetes


Kubernetes hace mucho más que controlar su aplicación en caso de fallo. Puede crear más copias de su aplicación para ejecutar en múltiples máquinas, actualizar su aplicación e incluso ejecutar múltiples versiones de su aplicación al mismo tiempo.

Esto significa que hay muchas razones por las que Kubernetes podría terminar un contenedor  que este perfecto. Si actualiza su implementación con una actualización continua, Kubernetes termina lentamente las antiguas unidades mientras activa otras nuevas. Si termina un nodo, Kubernetes termina todas los pods en ese nodo. Si un nodo se queda sin recursos, Kubernetes termina los pods para liberar esos recursos (consulte esta entrada anterior para obtener más información sobre los recursos).

¡Es importante que su aplicación controle la terminación con cuidado para que tenga un impacto mínimo en el usuario final y el tiempo de recuperación sea lo más rápido posible!

En la práctica, esto significa que su aplicación necesita controlar el mensaje SIGTERM y comenzar a apagar cuando lo recibe. Esto significa guardar todos los datos que se deben guardar, cerrar las conexiones de red, finalizar el trabajo restante y otras tareas similares.

Una vez que Kubernetes ha decidido finalizar su pod, se produce una serie de eventos. Veamos cada paso del ciclo de vida de terminación de Kubernetes.

1 - Pod se establece en el estado "Terminating" y se elimina de la lista de puntos finales de todos los Servicios

En este punto, el pod deja de recibir nuevo tráfico. Los contenedores que se ejecutan en los pods no se verán afectados.

2 - Se ejecuta preStop Hook

PreStop Hook es un comando especial o una solicitud http que se envía a los contenedores en el pod.

Si su aplicación no se cierra de forma correcto (el clasico "cuelge de la aplicación"), al recibir un SIGTERM, puede usar este hook para activar un apagado correcto. La mayoría de los programas se cierran correctamente al recibir un SIGTERM, pero si utiliza un código de terceros o está administrando un sistema que no tiene control, el Hook preStop es una gran manera de activar un apagado ordenado sin modificar la aplicación.

3 - La señal SIGTERM se envía al pod

En este punto, Kubernetes enviará una señal SIGTERM a los contenedores en el pod. Esta señal les permite a los contenedores saber que van a cerrar pronto.

Tu código debería escuchar este evento y comenzar a cerrarse limpiamente en este punto. Esto puede incluir detener cualquier conexión de larga duración (como una conexión a una base de datos o un flujo de WebSocket), guardar el estado actual, o algo por el estilo.

Incluso si está utilizando el Hook preStop, es importante que pruebe lo que le sucede a su aplicación si le envía una señal SIGTERM, ¡para que no se sorprenda de la producción!

4 - Kubernetes espera un período de gracia

En este punto, Kubernetes espera durante un tiempo específico llamado  "período de gracia" -> termination grace,  de terminación. Por defecto, esto es 30 segundos. Es importante tener en cuenta que esto sucede en paralelo al Hook preStop y la señal SIGTERM. Kubernetes no espera a que finalice el Hook preStop.

Si su aplicación termina cerrándose y se cierra antes de finalizar terminationGracePeriod, Kubernetes pasa al siguiente paso inmediatamente.

Si su módulo se demora generalmente más de 30 segundos para apagarse, asegúrese de aumentar el período de gracia. Puede hacerlo configurando la opción terminationGracePeriodSeconds en el Pod YAML. Por ejemplo, para cambiarlo a 60 segundos:


apiVersion: v1 
Kind: Pod
metadata:
  name: my-pod
spec:
  containers:
  - name: my-container
    image: busybox
  terminationGracePeriodSeconds: 60


5 - La señal de SIGKILL se envía al pod y se retira el pod

Si los contenedores siguen funcionando después del período de gracia, se les envía la señal SIGKILL y se los elimina por la fuerza. En este punto, todos los objetos de Kubernetes se limpian también.

Conclusión

Kubernetes puede finalizar los pods por una variedad de razones, y asegurarse de que su aplicación maneje estas terminaciones correctamente es fundamental para crear un sistema estable y proporcionar una excelente experiencia de usuario.


martes, 15 de mayo de 2018

Kubernetes [Best Practices ] Solicitudes de recursos y límites

En Google Cloud Español estamos  pendientes de  Sandeep Dinesh y sus publicaciones sobre kubernetes, esta semana nos habla de la gestion de los Recursos y sus limites con kubernetes.

Cuando en Kubernetes se programa un Pod, es importante que los contenedores tengan recursos suficientes para que puedan trabajar. Si se programa una aplicación que necesita grandes requerimientos  en un nodo con recursos limitados, es posible que el nodo se quede sin memoria o recursos de la CPU y que las cosas dejen de funcionar.

También es posible que las aplicaciones tomen más recursos de los que deberían. Esto podría deberse a que un equipo genere más réplicas de las necesarias para reducir artificialmente la latencia (¡es más fácil generar más copias que hacer que su código sea más eficiente!), A un cambio de configuración incorrecto que hace que un programa salga de controlar y usar el 100% de la CPU disponible. Independientemente de si el problema está causado por un mal desarrollador, código incorrecto o mala suerte, lo importante es que no se pierda el control. 



Solicitudes y límites


Las solicitudes y los límites son los mecanismos que Kubernetes usa para controlar recursos como la CPU y la memoria. Las solicitudes son lo que el contenedor está garantizado. Si un contenedor solicita un recurso, Kubernetes solo lo programará en un nodo que pueda darle ese recurso. Los límites, por otro lado, aseguran que un contenedor nunca supere un cierto valor. El contenedor solo puede subir hasta el límite y luego está restringido.

Es importante recordar que el límite nunca puede ser más bajo que la solicitud. Si prueba esto, Kubernetes arrojará un error y no le permitirá ejecutar el contenedor.

Las solicitudes y los límites son por contenedor. Los pod's pueden conetner un solo contenedor, pero es común pod's con varios contenedores. Cada contenedor del Pod's obtiene su propio límite y solicitud individual, pero como los Pods siempre se programan como un grupo, debe agregar los límites y las solicitudes de cada contenedor para obtener un valor agregado para el Pod. Para controlar qué solicitudes y límites puede tener un contenedor, puede establecer cuotas en el nivel del contenedor y en el namespaces.

Configuraciones del contenedor


Hay dos tipos de recursos: CPU y memoria. El planificador de Kubernetes los usa para averiguar dónde ejecutar tus pods. Los documentos para estos recursos. Si está ejecutando en Google Kubernetes Engine, el namespace "default"  ya tiene algunas solicitudes y límites configurados.

 

Estas configuraciones predeterminadas son correctas para "Hello World", pero es importante cambiarlas para adaptarlas a su aplicación. Una especificación típica de Pod para recursos podría verse más o menos así. Este pod tiene dos contenedores:

containers: 
- name: container1
  image: busybox
  resources:
    request:
      memory: "32Mi"
      cpu: "200m"
    limits:
      memory: "64Mi"
      cpu: "250m"
- name: container2
  image: busybox
  resources:
    request:
      memory: "96Mi"
      cpu: "300m"
    limits:
      memory: "192Mi"
      cpu: "750m"

Cada contenedor en el Pod puede establecer sus propias solicitudes y límites, y todos son aditivos. Por lo tanto, en el ejemplo anterior, el Pod tiene una solicitud total de 500 mCPU y 128 MiB de memoria, y un límite total de 1 CPU y 256Mib de memoria.

CPU


Los recursos de CPU  se definen en milicores. Si su contenedor necesita dos núcleos completos para ejecutar, pondría el valor "2000m". Si su contenedor solo necesita ¼ de un núcleo, pondría un valor de "250 m". Una cosa a tener en cuenta acerca de las solicitudes de CPU es que si ingresas un valor mayor que el recuento de núcleos de tu nodo más grande, tu pod nunca se programará.

Digamos que tiene un pod que necesita cuatro núcleos, pero su clúster de Kubernetes está compuesto por máquinas virtuales de doble núcleo: ¡su módulo nunca se programará! A menos que su aplicación esté diseñada específicamente para aprovechar múltiples núcleos, generalmente es una buena práctica mantener la solicitud de CPU en '1' o menos y ejecutar más réplicas para escalar.

Esto le da al sistema más flexibilidad y confiabilidad. Cuando se trata de límites de CPU, las cosas se ponen interesantes. La CPU se considera un recurso "comprimible". Si su aplicación comienza a alcanzar los límites de su CPU, Kubernetes comienza a estrangular su contenedor. Esto significa que la CPU será artificialmente restringida, ¡lo que le dará a tu aplicación un peor rendimiento! Sin embargo, no se dará por terminado ni se desalojará.

Puede usar un health check Liveness para asegurarse de que el rendimiento no se haya visto afectado. Los recursos de memoria de memoria se definen en bytes. Normalmente, le da un valor mebibyte a la memoria (esto es básicamente lo mismo que un megabyte), pero puede dar cualquier cosa, desde bytes hasta petabytes.

Memoria


Al igual que la CPU, si pones una solicitud de memoria que es más grande que la cantidad de memoria en tus nodos, el pod nunca se programará. A diferencia de los recursos de la CPU, la memoria no se puede comprimir. Debido a que no hay forma de reducir el uso de memoria, si un contenedor sobrepasa su límite de memoria, terminará. Si su pod está gestionado por un "Deployment", "StatefulSet", "DaemonSe"t u otro tipo de controlador, el controlador se ejecutara solo si es posible.

Nodos


Es importante recordar que no puede establecer solicitudes que son más grandes que los recursos proporcionados por sus nodos. Por ejemplo, si tiene un clúster de máquinas de doble núcleo, ¡nunca se programará un Pod con una solicitud de 2.5 núcleos! Aquí puede encontrar los recursos totales para las VM de Kubernetes Engine.

Configuraciones de Namespace


En un mundo ideal, la configuración de contenedores de Kubernetes sería lo suficientemente buena para encargarse de todo, pero el mundo es un lugar oscuro y terrible. Las personas pueden olvidarse fácilmente de establecer los recursos, o un equipo  puede establecer las solicitudes y límites muy altos y ocupar más de su parte en del clúster.

Para evitar estos escenarios, puede configurar ResourceQuotas y LimitRanges a nivel de Namespaces.

ResourceQuotas


Después de crear espacios de nombres, puede bloquearlos utilizando ResourceQuotas. Las ResourceQuotas son muy potentes, pero veamos cómo se pueden usar para restringir el uso de recursos de CPU y memoria. Una cuota para los recursos puede ser similar a esto:
 

apiVersion: v1 
Kind: ResourceQuota
metadata:
  name: demo
spec:
  hard:
    requests.cpu: "500m"
    requests.memory: "100MIB"
    limits.cpu: "700m"
    limits.memory: "500Mib"

Examinando  este ejemplo, puede ver que hay cuatro secciones. Configurar cada una de estas secciones que es opcional.

requests.cpu es el máximo de solicitudes combinadas de CPU en millicores para todos los contenedores en el Namespace. En el ejemplo anterior, puede tener 50 contenedores con solicitudes de 10m, cinco contenedores con solicitudes de 100m o incluso un contenedor con una solicitud de 500m. ¡Siempre y cuando la CPU total solicitada en Namespace sea inferior a 500m!

requests.memory es el máximo de solicitudes combinadas de memoria para todos los contenedores en el Namespace. En el ejemplo anterior, puede tener 50 contenedores con solicitudes de 2MiB, cinco contenedores con solicitudes de CPU de 20MiB o incluso un solo contenedor con una solicitud de 100Mib. ¡Siempre y cuando la Memoria total solicitada en Namespace sea inferior a 100MiB!

limits.cpu es el límite de CPU máximo combinado para todos los contenedores en Namespace. Es como requests.cpu pero para el límite.  

limits.memory es el límite máximo de Memoria combinada para todos los contenedores en Namespace. Es solo como request.memory pero para el límite. Si está utilizando un Namespaces de producción y desarrollo (a diferencia de un Namespace por equipo o servicio), un patrón común es no poner cuotas en el Namespace de producción y cuotas estrictas en Namespace de desarrollo. Esto permite que la producción tome todos los recursos que necesita en caso de un pico en el tráfico.

LimitRange


También puedes crear un LimitRange en el Namespace. A diferencia de una Cuota, que considera el Namespace como un todo, un LimitRange se aplica a un contenedor individual.

Esto puede ayudar a evitar que las personas creen contenedores súper pequeños o súper grandes dentro Namespace. Un LimitRange podría tener un aspecto similar al siguiente:

apiVersion: v1 
Kind: LimitRange
metadata:
 name: demo
spec:
 limits:
-default:
   cpu: 600m
   memory: 100Mib
 defaultRequest:
   cpu: 100m
   memory: 50Mib
 max:
   cpu: 1000m
   memory: 200Mib
 min:
   cpu: 10m
   memory: 10Mib
 type: Container

Examinando este ejemplo, puede ver que hay cuatro secciones. De nuevo, configurar cada una de estas secciones es opcional.

La sección default establece los límites predeterminados para un contenedor en un pod. Si establece estos valores en limitRange, a los contenedores que no los establezcan explícitamente se les asignarán los valores predeterminados.

La sección defaultRequest establece las solicitudes predeterminadas para un contenedor en un pod. Si establece estos valores en limitRange, a los contenedores que no los establezcan explícitamente se les asignarán los valores predeterminados.

La sección max establecerá los límites máximos que un contenedor en un Pod puede establecer. La sección predeterminada no puede ser más alta que este valor. Del mismo modo, los límites establecidos en un contenedor no pueden ser superiores a este valor. Es importante tener en cuenta que si se establece este valor y la sección predeterminada no, todos los contenedores que no establezcan explícitamente estos valores recibirán los valores máximos como límite.

La sección min establece las Solicitudes mínimas que un contenedor en un Pod puede establecer. La sección de solicitud predeterminada no puede ser menor que este valor. Del mismo modo, las solicitudes establecidas en un contenedor tampoco pueden ser inferiores a este valor. Es importante tener en cuenta que si se establece este valor y la sección defaultRequest no lo está, el valor mínimo también se convierte en el valor predeterminado de solicitud.

El lifecycle de un Pod en Kubernetes


Al final del día, el planificador Kubernetes utiliza estas solicitudes de recursos para ejecutar tus cargas de trabajo Es importante entender cómo funciona esto para que pueda ajustar sus contenedores correctamente.

Digamos que quieres ejecutar un Pod en tu Cluster. Asumiendo que las especificaciones del Pod son válidas, el planificador de Kubernetes usará el balanceo de carga round-robin para elegir un Nodo para ejecutar su carga de trabajo.

Nota: La excepción a esto es si usa un nodeSelector o mecanismo similar para forzar a Kubernetes a programar su Pod en un lugar específico. Las verificaciones de recursos aún ocurren cuando usa un nodeSelector, pero Kubernetes solo verificará los nodos que tengan la etiqueta requerida.

Kubernetes luego verifica si el Nodo tiene suficientes recursos para cumplir con las solicitudes de recursos en los contenedores del Pod. Si no lo hace, pasa al siguiente nodo. Si ninguno de los Nodos en el sistema tiene recursos para completar las solicitudes, los Pods entran en un estado "pendiente".

Al utilizar las características de Kubernetes Engine, como el Node Autoscaler, Kubernetes Engine puede detectar automáticamente este estado y crear más Nodos automáticamente. Si hay un exceso de capacidad, el escalador automático también puede escalar y eliminar nodos para ahorrar dinero.

Pero, ¿qué pasa con los límites? Como sabe, los límites pueden ser más altos que las solicitudes. ¿Qué sucede si tiene un Nodo donde la suma de todos los Límites del contenedor es en realidad más alta que los recursos disponibles en la máquina? En este punto, Kubernetes entra en algo llamado "estado comprometido".

Aquí es donde las cosas se ponen interesantes. Debido a que la CPU se puede comprimir, Kubernetes se asegurará de que sus contenedores obtengan la CPU que solicitaron y estrangulará el resto. La memoria no se puede comprimir, por lo que Kubernetes necesita comenzar a tomar decisiones sobre qué contenedores terminar si el Nodo se queda sin memoria.

Imaginemos un escenario en el que tenemos una máquina que se está quedando sin memoria. ¿Qué hará Kubernetes?

Nota: Lo siguiente es cierto para Kubernetes 1.9 y superior. En versiones anteriores, utiliza un proceso ligeramente diferente. Vea este documento para una explicación detallada.

Kubernetes busca Pods que estén usando más recursos de los que solicitaron. Si los contenedores de su Pod no tienen solicitudes, entonces, de forma predeterminada, están utilizando más de lo solicitado, por lo que estos son los principales candidatos para la terminación.

Otros candidatos principales son los contenedores que han revisado su solicitud, pero aún están por debajo de su límite. Si Kubernetes encuentra varios pods que han revisado sus solicitudes, los clasificará según la prioridad del Pod y terminará primero con los pods de prioridad más baja.

Si todos los Pods tienen la misma prioridad, Kubernetes termina el Pod que es más sobre su solicitud.

En escenarios muy raros, Kubernetes podría verse obligado a terminar Pods que todavía están dentro de sus solicitudes. Esto puede suceder cuando los componentes críticos del sistema, como el kubelet o el docker, comienzan a tomar más recursos de los que estaban reservados para ellos.

Conclusión


Si bien su clúster de Kubernetes podría funcionar bien sin establecer límites y solicitudes de recursos, comenzará a tener problemas de estabilidad a medida que crezcan sus equipos y proyectos. ¡Agregar solicitudes y límites a sus Pods y Namespaces solo requiere un pequeño esfuerzo adicional, y puede evitar que sufra muchos dolores de cabeza más adelante!

sábado, 5 de mayo de 2018

Kubernetes [Best practices] health checks

Este viernes Sandeep Dinesh, Google Developer Advocate, no ha faltado a si cita de kubernetes Best practices y como no aquí lo tenéis en google cloud español.

Los sistemas distribuidos pueden ser difíciles de administrar. Hay mucho codigo que cambia y esta en mantenimiento. Si se rompe una parte pequeña, el sistema tiene que detectarla, enrutarla y arreglarla. ¡Y todo esto debe hacerse automáticamente!

Los controles de estad, a partir de ahora health checks son una forma sencilla de informar al sistema si una instancia de su aplicación está funcionando o no funciona. Si una instancia de su aplicación no funciona, entonces otros servicios no deberían acceder a ella o enviarle una solicitud. En su lugar, las solicitudes se deben enviar a otra instancia de la aplicación que esté lista o que se vuelva a intentar en otro momento. El sistema también debería devolver tu aplicación a un health OK .

De manera predeterminada, Kubernetes comienza a enviar tráfico a un pod cuando se inician todos los contenedores dentro del pod y reinicia los contenedores cuando se cuelgan. Si bien esto puede ser "lo suficientemente bueno" cuando está comenzando, puede hacer que sus implementaciones sean más robustas mediante la creación de controles de estado personalizados. Afortunadamente, Kubernetes lo hace relativamente sencillo, ¡así que no hay excusa para no hacerlo!

En este episodio de "Mejores prácticas de Kubernetes", aprendamos sobre la manera  de las sondas de de crear sondas de estado, y cuándo usar qué sonda y cómo configurarlas en su clúster de Kubernetes.



Tipos de health checks

Kubernetes le ofrece dos tipos de controles de salud, y es importante comprender las diferencias entre los dos y sus usos.

Readiness
Las sondas de readiness están diseñadas para informar a Kubernetes cuando su aplicación está lista para servir el tráfico. Kubernetes se asegura de que la sonda de readiness pase antes de permitir que un servicio envíe tráfico al pod. Si una sonda de preparación comienza a fallar, Kubernetes deja de enviar tráfico a la cápsula hasta que pasa.

Liveness
Las sondas de Liveness le informan a Kubernetes si su aplicación está viva o muerta. Si tu aplicación está viva, Kubernetes la deja sola. Si su aplicación está muerta, Kubernetes elimina el Pod y comienza una nueva para reemplazarlo.

Cómo ayuda un health checks

Veamos dos escenarios en los que las sondas de readiness y lveness pueden ayudarlo a crear una aplicación más sólida.

Readiness 

Imaginemos que su aplicación tarda un minuto en arrancar y comenzar. Su servicio no funcionará hasta que esté en funcionamiento, aunque el proceso haya comenzado. También tendrá problemas si desea ampliar esta implementación para tener múltiples copias. Una copia nueva no debe recibir tráfico hasta que esté completamente lista, pero Kubernetes comienza a enviar el tráfico de manera predeterminada en cuanto comienza el proceso dentro del contenedor. Al usar una sonda de readiness, Kubernetes espera hasta que la aplicación se haya iniciado completamente antes de permitir que el servicio envíe tráfico a la nueva copia.


Liveness

Imaginemos otro escenario en el que su aplicación tenga un problema, un caso de punto muerto, lo que hará que se cuelgue indefinidamente y deje de atender solicitudes. Debido a que el proceso continúa ejecutándose, Kubernetes piensa de manera predeterminada que todo está bien y continúa enviando solicitudes al pod roto. Mediante el uso de una sonda de liveness, Kubernetes detecta que la aplicación ya no está atendiendo solicitudes y reinicia el pod corrupto.


Tipo de sondas

El siguiente paso es definir las sondas que prueban la readiness y la liveness. Hay tres tipos de sondeos: HTTP, Comando y TCP. Puede usar cualquiera de ellos para comprobar su liveness y readiness.

HTTP

Las sondas HTTP son probablemente el tipo más común de health checks personalizada. Incluso si su aplicación no es un servidor HTTP, puede crear un servidor HTTP liviano dentro de su aplicación para responder al sondeo de liveness. Kubernetes marca un camino, y si obtiene una respuesta HTTP en el rango 200 o 300, marca la aplicación como saludable. De lo contrario, está marcado como caido.
Puede leer más sobre las sondas HTTP aquí.

Comando

Para las sondas de comando, Kubernetes ejecuta un comando dentro de su contenedor. Si el comando regresa con el código de salida 0, entonces el contenedor se marca como correcto. De lo contrario, está marcado como caido. Este tipo de sonda es útil cuando no puede o no quiere ejecutar un servidor HTTP, pero puede ejecutar un comando que puede verificar si su aplicación está sana o no.
Puede leer más sobre las sondas de comando aquí.

TCP

El último tipo de sonda es la sonda TCP, donde Kubernetes intenta establecer una conexión TCP en el puerto especificado. Si puede establecer una conexión, el contenedor se considera saludable; si no puede, se considera insalubre.
Las sondas TCP son útiles si tiene un escenario donde las sondas HTTP o la sonda de comando no funcionan bien. Por ejemplo, un servicio gRPC o FTP es un candidato ideal para este tipo de check.
Puede leer más sobre las sondas TCP aquí.

Configurar el retraso de sondeo inicial

Las sondas se pueden configurar de muchas maneras. Puede especificar con qué frecuencia deben ejecutarse, cuáles son los umbrales de éxito y falla y cuánto esperar para obtener respuestas. La documentación sobre la configuración de las sondas es bastante clara sobre las diferentes opciones y lo que hacen.

Sin embargo, hay una configuración muy importante que debe configurar cuando usa sondas de health. Esta es la configuración initialDelaySeconds.

Como mencioné anteriormente, una error en la health check hace que el pod se reinicie. Debe asegurarse de que la sonda no se inicie hasta que la aplicación esté lista. De lo contrario, la aplicación se reiniciará constantemente y ¡nunca estará listo!

Recomiendo usar el tiempo de inicio p99 como initialDelaySeconds, o simplemente tomar el tiempo promedio de inicio y agregar un búfer. A medida que el tiempo de inicio de la aplicación se vuelve más rápido o más lento, asegúrese de actualizar este número.

Conclusión

La mayoría de las personas le dirá que los health checks son un requisito para cualquier sistema distribuido, y Kubernetes no es una excepción. El uso de controles de estado le brinda a sus servicios de Kubernetes una base sólida, una mayor confiabilidad y un mayor tiempo de actividad. Afortunadamente, ¡Kubernetes lo hace fácil de configurar!

lunes, 30 de abril de 2018

Kubernetes [Best Practices] Organización con Namespaces

Hoy en google cloud español hablamos sobre la segunda entrega de una serie de videos y blogs en siete partes de Google Developer Advocate Sandeep Dinesh y sobre cómo aprovechar al máximo el entorno de Kubernetes.

A medida que comienzas a construir más y más servicios encima de Kubernetes, las tareas simples se vuelven más complicadas. Por ejemplo, los equipos no pueden crear Servicios de Kubernetes o Implementaciones con el mismo nombre.
Si tienes muchísimos pods, serviciosdeployments , solo listarlos tiene un coste alto de tiempo y de gestión, ¡y mucho menos administrarlos! Y estos son solo la punta del iceberg.

Veamos cómo Kubernetes Namespaces puede facilitar la gestión de los recursos de Kubernetes.



¿Qué es Namespace?

El mejor acercamiento como concepto al Namespace es  como un clúster virtual dentro de su clúster de Kubernetes. Puede tener múltiples namespaces dentro de un único clúster de Kubernetes, y todos están lógicamente aislados el uno del otro. ¡Te ayudara  en la gestión de  equipos con organización, seguridad e incluso rendimiento!

El "default" Namespace 

En la mayoría de las distribuciones de Kubernetes, el clúster de inicio por defecto esta dentro del Namespace "default". De hecho, hay tres namespaces con los que Kubernetes se crea: default, kube-system (utilizado para componentes de Kubernetes) y kube-public ( usado para recursos públicos). kube-public no se usa mucho actualmente, y generalmente es una buena idea dejar solo el kube-system, especialmente en un sistema administrado como Google Kubernetes Engine. Esto deja el namespace default como el lugar donde se crean sus servicios y aplicaciones por defecto.

No hay absolutamente nada de especial en este Espacio de nombres, excepto que las herramientas de Kubernetes están configuradas de serie para usar este espacio de nombres y no puede eliminarlo. Si bien es bueno para comenzar y para sistemas de producción más pequeños, recomendaría no usarlo en sistemas de producción grandes. Esto se debe a que es muy fácil para un equipo sobrescribir accidentalmente o interrumpir otro servicio sin siquiera darse cuenta. En su lugar, cree múltiples namespaces y úselos para segmentar sus servicios en zonas mas manejables.

Creando Namespaces

No tengas miedo de crear namespaces. No penalizan el  rendimiento, y en muchos casos pueden mejorar el rendimiento, ya que la API de Kubernetes tendrá un conjunto de objetos más pequeño para trabajar.

Crear un espacio de nombres se puede hacer con un solo comando. Si quisiera crear un espacio de nombres llamado 'test',  tendrías que ejecutar:


kubectl create namespace test

O puede crear un archivo YAML y aplicarlo como cualquier otro recurso de Kubernetes.
test.yaml:
kind: Namespace
apiVersion: v1
metadata:
  name: test
  labels:
    name: test
kubectl apply -f test.yaml

Visualización de Namespaces

Puede ver todos los namespaces con el siguiente comando:

kubectl get namespace


$kubectl get namespace
NAME         STATUS      AGE
default      Active      5d
kube-public  Active      5d
kube-sistem  Active      5d
test         Active      3m

Puede ver los tres namespaces basicos, así como el nuevo espacio de nombres llamado 'test'.

Crear recursos en el namespace

Echemos un vistazo a un simple YAML para crear un Pod:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
  labels:
    name: mypod
spec:
  containers:
  - name: mypod
    image: nginx

Normalmente en los ejemplos no se indica el namespace en ningún lado. Si ejecuta una aplicación `kubectl` en este archivo, creará el Pod en el espacio de nombres activo actual. Este será el espacio de nombres "default" a menos que lo cambie.

Hay dos formas de decirle explícitamente a Kubernetes en qué Namespace desea crear sus recursos.
Una forma es establecer el indicador de "namespace" al crear el recurso:

kubectl apply -f pod.yaml --namespace=test

También puede especificar un espacio de nombres en la declaración YAML.

apiVersion: v1
kind: Pod
metadata:
  name: mypod
  namespace: test
  labels:
    name: mypod
spec:
  containers:
  - name: mypod
    image: nginx

Si especifica un espacio de nombre en la declaración YAML, el recurso siempre se creará en ese espacio de nombres. Si intenta utilizar el indicador "namespace" para establecer otro espacio de nombres, el comando fallará.

Visualización de recursos en el Namespace

Si intentas encontrar tus Pod, ¡es posible que notes que no los ves!

$ kubectl get pods
No resources found.

No se encontraron recursos. Esto se debe a que todos los comandos se ejecutan contra el namespace actualmente activo. Para encontrar su Pod, debe usar el indicador "namespace".

$ kubectl get pods --namespace=test
NAME      READY     STATUS    RESTARTS   AGE
mypod     1/1       Running   0          10s

Esto puede ser incomdo, especialmente si es un desarrollador que trabaja en un equipo que usa su propio Espacio de nombres para todo y no desea usar el indicador de "espacio de nombre" para cada comando. Veamos un ejemplo de cómo podemos facilitar este caso.

Administrar tu namespace activo

Por defecto al iniciar el namespace es "default". A menos que especifique un Namespace en el YAML, todos los comandos de Kubernetes usarán el Namespace activo.

Desafortunadamente, tratar de administrar tu espacio de nombre activo con kubectl puede ser una incomodo. Afortunadamente, hay una herramienta realmente buena llamada kubens (creada por  Ahmet Alp Balkan) que hace que sea muy fácil.

Cuando ejecuta el comando 'kubens', debería ver todos los namespaces, con el espacio de nombres activo resaltado:

$ kubens
default
kube-public
kube-system
test


Para cambiar su espacio de nombres activo al espacio de nombres 'test', ejecuta:

kubens test

Ahora puede ver que el espacio de nombres de 'prueba' está activo:

$ kubens
default
kube-public
kube-system
test

Ahora, si ejecuta los comandos kubectl, namespace será 'test' en lugar de 'default'. Esto significa que no necesita el indicador de espacio de nombres para ver el pod en el namespace de test.

$ kubectl get pods
NAME      READY     STATUS    RESTARTS   AGE
mypod     1/1       Running   0          10m

Comunicación entre distintos Namespaces

Los namespaces están "ocultos" entre sí, pero no están completamente aislados por defecto. Un servicio en un namespaces puede hablar con un servicio en otro namespaces. Esto a menudo puede ser muy útil, por ejemplo para que el servicio de su equipo en su namespaces se comunique con el servicio de otro equipo en otro namespaces.

Cuando su aplicación quiere acceder a los servicios de Kubernetes, puede usar el descubrimiento del servicio DNS integrado y simplemente apuntar su aplicación a nombre del Servicio. Sin embargo, puede crear un servicio con el mismo nombre en múltiples namespaces. Afortunadamente, es fácil evitar esto al usar la forma expandida de la dirección DNS.

Los servicios en Kubernetes exponen su punto final utilizando un patrón DNS común. Se parece a esto:

<Service Aame>.<Namespace Name>.svc.cluster.local

Normalmente, solo necesita el nombre del Servicio y el DNS se resolverá automáticamente en la dirección completa. Sin embargo, si necesita acceder a un Servicio en otro Espacio de nombre simplemente use el Nombre del servicio más el nombre del namespaces.

Por ejemplo, si desea conectarse al servicio de "base de datos" en el espacio de nombres de "test", puede usar la siguiente dirección:

database.test

Si desea conectarse al servicio de "base de datos" en el espacio de nombres de "production", puede usar la siguiente dirección:

database.production

Advertencia: si crea un espacio de nombres que se asigna a un TLD como "com" u "org", y luego crea un servicio que tiene el mismo nombre que un sitio web, como "google" o "reddit", Kubernetes interceptará las solicitudes a " google.com "o" reddit.com "y envíelos a su Servicio. Esto a menudo puede ser muy útil para probar, ¡pero también puede liar las cosas fácilmente en su clúster!

Nota: Si desea aislar los namespaces, debe usar las políticas de red para lograr esto. Estén atentos para más sobre esto en un episodio futuro!

Granularidad del Namespaces

Una pregunta común que recibo es cuántos namespaces crear y con qué propósito. ¿Cuantos son necesarios para ser manejable? Si se crean demasiados namespaces  se interpongan en su camino, pero haga muy pocos y se pierden  los beneficios.
Creo que la respuesta radica en en qué etapa se encuentra su proyecto o compañía: desde un equipo pequeño hasta una gran empresa, cada uno tiene su propia estructura organizativa. Dependiendo de su situación, puede adoptar la estrategia concrete en su namespaces.

En una empresa pequeña

En este escenario, usted es parte de un pequeño equipo que está trabajando en 5-10 microservicios y puede reunir fácilmente a todos al rededor de unas pizzas. En esta situación, tiene sentido lanzar todos los servicios de producción en el espacio de nombres "default". Es posible que desee tener un espacio de nombres de "producción" y "desarrollo" si quiere gestionar mas fino, pero probablemente esté probando su entorno de desarrollo en su máquina local usando algo como Minikube.

En una empresa que se expande

En este escenario, tiene un equipo en rápido crecimiento que está trabajando en más de 10 microservicios. Está empezando a dividir el equipo en múltiples sub-equipos que cada uno posee sus propios microservicios. Si bien todos pueden saber cómo funciona el sistema completo, cada vez es más difícil coordinar cada cambio con los demás. Tratar de hacer girar la pila completa en su máquina local se vuelve más complicado cada día.

En este punto, es necesario usar múltiples clusters o namespaces para producción y desarrollo. Cada equipo puede elegir tener su propio espacio de nombre para una administración más fácil.

Empresa que crece

En una gran empresa, no todos conocen a los demás. Los equipos están trabajando en funciones que otros equipos podrían no conocer. Los equipos están utilizando contratos de servicios para comunicarse con otros microservicios (por ejemplo, gRPC) y mallas de servicio para coordinar la comunicación (por ejemplo, istio). Tratar de ejecutar toda la pila localmente es imposible. Se recomienda encarecidamente utilizar un sistema de CD compatible con Kubernetes (p. Ej., Spinnaker).

En este punto, cada equipo definitivamente necesita su propio namespaces. Cada equipo incluso puede optar por múltiples namespaces para ejecutar sus entornos de desarrollo y producción. Configurar RBAC y ResourceQuotas es una buena idea también. Múltiples clusters comienzan a tener mucho sentido, pero pueden no ser necesarios.

Gran empresa

En esta escala, hay grupos que ni siquiera conocen la existencia de otros grupos. Los grupos también pueden ser compañías externas, y los servicios se consumen a través de API bien documentadas. Cada grupo tiene múltiples equipos que tienen múltiples microservicios. Usar todas las herramientas que mencioné anteriormente son necesarias; las personas no deberían desplegar servicios a mano y deberían estar bloqueados fuera de los namespaces que no les pertenecen.
En este punto, probablemente tenga sentido tener múltiples clusters para reducir el radio de infestación de aplicaciones mal configuradas y facilitar la administración de recursos y facturación.

Conclusión

Los namespaces pueden ayudar significativamente a organizar sus recursos de Kubernetes y pueden aumentar la velocidad de sus equipos. Estén atentos para futuros episodios de Kubernetes Best Practices donde les mostraré cómo pueden bloquear recursos en un Namespace e introducir más seguridad y aislamiento en su clúster.