sábado, 30 de mayo de 2020

Rollback de Deployments y Namespace en Kubernetes

Hola Ardillas, para estos posts largos prefiero ir directamente al asunto y evitar chistes como el del hacker, así que arranquemos.

Para los que no quieren leer todo (aunque lo recomiendo) podríamos dividir el post en 3 grandes títulos que voy a marcarlos en mayúscula, azul y subrayado así TITULO
Los títulos son:
CREAR NAMESPACE
CREAR DEPLOYMENT
ROLLBACK DE UN DEPLOYMENT

Antes de avanzar con el post de rollback de un deployment en si vamos a ver que tengo corriendo actualmente en mi cluster de Kubernetes:

Para ver un listado de lo que tenemos corriendo en el cluster:

kubectl get all


Fijense que diferencia los PODs, de los servicios, de los deployments y las replicas.

Ver detalle de todos los PODs en el namespaces actual:

kubectl get pods -o wide


Ver detalle de todos los PODs en todos los namespaces:

kubectl get pods --all-namespaces  


En este caso vemos los mismos ya que tengo solo un namespace (los otros que aparecen son del sistema).

Ver cantidad de replicas:

kubectl get rs


Ver historial de cambios de un deployment:

kubectl rollout history deployment/nombredeldeploy




CREAR NAMESPACE

Bueno, como yo no quiero eliminar nada de esto para hacer el ejercicio lo que voy a hacer es crear un nuevo namespace donde voy a correr todo el despliegue que viene a continuación.

Primero vemos que namespaces tenemos:

kubectl get namespaces


Kubernetes arranca con tres espacios de nombres inicialmente:

default El espacio de nombres por defecto para aquellos objetos que no especifican ningún espacio de nombres
kube-system El espacio de nombres para aquellos objetos creados por el sistema de Kubernetes
kube-public Este espacio de nombres se crea de forma automática y es legible por todos los usuarios (incluyendo aquellos no autenticados). Este espacio de nombres se reserva principalmente para uso interno del clúster, en caso de que algunos recursos necesiten ser visibles y legibles de forma pública para todo el clúster. La naturaleza pública de este espacio de nombres es simplemente por convención, no es un requisito.

Para crear un nuevo espacio de nombres ejecutamos:

kubectl create namespace nombredelnamespace

En mi caso lo llamé mirollback


Verificamos que se haya creado:

kubectl get namespaces

Vemos creado mirollback
Ahora configuramos la preferencia del namespace para que nos tome este por defecto y que todas las llamadas futuras a comandos kubectl se ejecuten acá:

kubectl config set-context --current --namespace=mirollback

Verificamos:

kubectl config view | grep mirollback


IMPORTANTE: No todos los objetos están en un espacio de nombres.
La mayoría de los recursos de Kubernetes (pods, services, replication controllers y otros) están en algunos espacios de nombres.

Para comprobar qué recursos de Kubernetes están y no están en un espacio de nombres ejecutamos:

Recursos que SI estan en un espacio de nombres:

kubectl api-resources --namespaced=true


Recursos que NO estan en un espacio de nombres:

kubectl api-resources --namespaced=false


Vemos por ejemplo que los PODs si están, pero los NODOS no.
Para mas info podes entrar aca.

Verificamos si tenemos nodos y pods en nuestro nuevo namespace default (llamado rollback):


Vemos que no hay ningún POD. Pero si miramos el namespace default (que ya no es mas el default, es solo el nombre, el default ahora es mirollback).

kubectl -n default get pods (con -n default le decimos que chequee ese namespace)


Vemos que en el default siguen corriendo los PODs que tenia al principio.

CREAR DEPLOYMENT

Ya creado nuestro nuevo namespace y puesto por default procedemos con la creación de un deployment.

Creamos un deployment con 3 PODs con nginx 1.14.2:

nginx-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment #Nombre del despliegue
  labels:
    app: nginx
spec:
  replicas: 3 #Cantidad de replicas
  selector:
    matchLabels:
      app: nginx #Etiqueta con la que se encuentra al POD
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2 #Version de la imagen de Docker Hub de nginx que ejecuta
        ports:
        - containerPort: 80

Crear despliegue:

kubectl apply -f nginx-deployment.yaml

Ver despliegues creados:

kubectl get deployments


El nuestro es el que dice nginx-deployment, que es el nombre del despliegue que definimos en el archivo yaml.
La columna READY nos muestra cuantas replicas están disponibles (listas/deseadas).
UP-TO-DATE nos muestra el número de réplicas que se han actualizado para lograr el estado deseado.
AVAILABLE nos muestra cuántas réplicas de la aplicación están disponibles.
AGE nos muestra la cantidad de tiempo que hace que se ejecuta la aplicación.

Para ver el estado de implementación del despliegue, ejecutar:

kubectl rollout status deployment.v1.apps/nginx-deployment

En este caso vemos que dice que "successfully rolled out".

Para ver la cantidad de replicas de cada POD (ReplicaSet):

kubectl get rs


NAME enumera los nombres de los ReplicaSets en el espacio de nombres (namespace mirollback).
DESIRED nos muestra el número deseado de réplicas de la aplicación, que definimos cuando creamos la implementación.
CURRENT nos muestra cuántas réplicas se están ejecutando actualmente.
READY nos muestra cuántas réplicas están disponibles para los usuarios.
AGE nos muestra la cantidad de tiempo que la aplicación lleva ejecutándose.

El nombre del ReplicaSet siempre tiene el formato [DEPLOYMENT-NAME]-[RANDOM-STRING]. La cadena se genera aleatoriamente. En nuestro caso:

nginx-deployment-6b474476c4 

Para ver las etiquetas generadas para cada Pod, ejecutamos:

kubectl get pods --show-labels


La etiqueta es:

app=nginx,pod-template-hash=6b474476c4

Debe especificarse un selector apropiado y etiquetas de template Pod en una implementación (en este caso, app:nginx). No superpongan etiquetas o selectores con otros controladores, Kubernetes no lo evita, y si varios controladores tienen selectores superpuestos, pueden entrar en conflicto y comportarse inesperadamente.

Etiqueta pod-template-hash:

Ésta etiqueta no se debe cambiar. El pod-template-hash agrega la etiqueta a cada ReplicaSet de una implementacion. Esta etiqueta garantiza que los ReplicaSets secundarios de una implementación no se superpongan. Se genera mediante el hash PodTemplate del ReplicaSet.

Ahora vamos a actualizar nuestra implementación:

Vamos a hacer que los PODs usen la imagen nginx 1.16.1 en lugar de la 1.14.2:

kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1 --record

o use el siguiente comando:

kubectl --record deployment.apps/nginx-deployment set image deployment.v1.apps/nginx-deployment nginx=nginx:1.16.1


Vemos que aparece actualizado.

Para ver el estado de implementación ejecutamos:

kubectl rollout status deployment.v1.apps/nginx-deployment


Vemos que se aplico correctamente:
deployment "nginx-deployment" successfully rolled out

Ejecutamos kubectl get rs para ver que la implementación actualizó los Pods creando un nuevo ReplicaSet y escalandolos a 3 réplicas, y reduciendo el antiguo ReplicaSet 0 réplicas.

kubectl get rs



Vemos que las 3 replicas anteriores estan en cero y las nuevas en 3.

Ahora verificamos los nuevos PODs con la nueva version:

kubectl get pods


Durante el deployment solo una cierta cantidad de Pods están inactivos mientras se actualizan. De forma predeterminada, al menos el 75% del número deseado de Pods esta activo (25% máximo no disponible).

También se puede crear un cierto número de Pods por encima del número deseado. De forma predeterminada, garantiza que como máximo el 125% del número deseado de Pods esté activo (aumento máximo del 25%).

En nuestro ejemplo, Kubernetes no mata los PODs antiguos hasta que haya surgido un número suficiente de PODs nuevos, y no crea nuevos hasta que se haya matado una cantidad suficiente de PODs viejos. Se asegura que al menos 2 PODs estén disponibles, y que como máximo haya 4.

Para ver los detalles del despliegue:

kubectl describe deployments


Aquí puede ver que cuando creó el despliegue, creó un ReplicaSet (nginx-deployment-6b474476c4) y lo amplió a 3 réplicas directamente. Cuando actualizó la implementación, creó un nuevo ReplicaSet (nginx-deployment-7b45d69949) y lo amplió a 1 y luego redujo el antiguo ReplicaSet a 2, de modo que al menos 2 Pods estaban disponibles y como máximo 4. Luego continuó ampliando y reduciendo el ReplicaSet nuevo y el antiguo, con la misma estrategia de actualización continua. Finalmente, quedaron 3 réplicas disponibles en el nuevo ReplicaSet, y el antiguo ReplicaSet quedo en cero.

Rollover (actualizaciones al vuelo), ¿que es?

Si actualizamos un despliegue mientras otro está en progreso, el despliegue nuevo crea un nuevo ReplicaSet según la actualización y comienza a escalar eso, y reinicia el ReplicaSet que estaba escalando anteriormente; lo agrega a su lista de antiguos ReplicaSets y comienza a reducirlo.

Por ejemplo, supongamos que crea una nueva implementación para crear 5 réplicas de nginx:1.14.2, pero luego mientras se implementan la actualiza para crear 5 réplicas nginx:1.16.1. Solo se habian creado 3 replicas de nginx:1.14.2. En ese caso, el despliegue comienza inmediatamente a matar los 3 PODs nginx:1.14.2, y comienza a crear los de la version nginx:1.16.1.
Es decir, no espera a que se creen las 5 replicas con nginx:1.14.2 sino que sobre la marcha mata los viejos que creó y va creando los nuevos con la nueva versión.

ROLLBACK DE UN DEPLOYMENT:

El rollout se activa si y solo si se modifica el template del Pod de implementación (.spec.template), por ejemplo, si actualiza las etiquetas o las imágenes del contenedor de la plantilla. Otras actualizaciones, como el escalado de la implementación, no crean una revisión de implementación. Esto significa que cuando retrocede a una revisión anterior, solo se revierte la parte del template del Pod de implementación.

Supongamos cometimos un error tipográfico al actualizar la implementación, pusimos el nombre de la imagen como en nginx:1.161 en lugar de nginx:1.16.1:

kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.161 --record=true

Nos va a aparecer que se actualizó el despliegue, aunque luego se va a colgar.

Para verificar el estado de la implementación ejecutamos:

kubectl rollout status deployment.v1.apps/nginx-deployment


Podemos ver que el estado queda detenido en:

Waiting for deployment "nginx-deployment" rollout to finish: 1 out of 3 new replicas have been updated...

Presionamos Ctrl-C para detener.
Ahora vemos que el número de réplicas antiguas (nginx-deployment-7b45d69949 y nginx-deployment-6b474476c4) es 2 y el de réplicas nuevas (nginx-deployment-777654c9c) es 1.

kubectl get rs


Ahora vamos a ver los Pods que tenemos funcionando, veremos que uno de esos Pods (el creado por el nuevo ReplicaSet) está colgado (ImagePullBackOff).

kubectl get pods


El controlador de implementación detiene el despliegue incorrecto automáticamente y deja de escalar el nuevo ReplicaSet. Esto depende de los parámetros que haya especificado, Kubernetes por defecto establece el valor en 25%.

Vamos a ver el detalle de la implementación:

kubectl describe deployment


Fijense las ultimas 2 lineas:

Normal  ScalingReplicaSet  57m   deployment-controller  Scaled down replica set nginx-deployment-6b474476c4 to 0

Normal  ScalingReplicaSet  22m   deployment-controller  Scaled up replica set nginx-deployment-777654c9c to 1

Ahi se ve con claridad cuando baja una de las replicas viejas para levantar la nueva con la versión que no existe y que finalmente quedó colgada.

Para solucionar esto, hay que volver a una revisión estable anterior.

Primero verificamos el historial de la implementación:

kubectl rollout history deployment.v1.apps/nginx-deployment


Aquí vemos los 2 cambios que hicimos:
En la revisión 2 cambiamos la version de nginx a nginx=nginx:1.16.1
Y en la revisión 3 la cambiamos a nginx=nginx:1.161 

Si hubiese muchas revisiones que ver podriamos ver directamente la que deseamos con este comando:

kubectl rollout history deployment.v1.apps/nginx-deployment --revision=2

Aca se puede ver el detalle de la revisión 2 y la revisión 3
Retroceder a una revisión anterior

En nuestro caso vamos a hacer el rollback a la version anterior que era la que se encontraba funcionando.
Para revertir la versión actual y hacer el rollback a la versión anterior ejecutamos:

kubectl rollout undo deployment.v1.apps/nginx-deployment


Si quisiéramos ir a una versión especifica podríamos ejecutar:

kubectl rollout undo deployment.v1.apps/nginx-deployment --to-revision=2

Chequeamos el detalle de la implementación:

kubectl describe deployment nginx-deployment

Vemos corriendo la imagen de nginx 1.16.1
Podriamos escalar nuestra implementación mediante el siguiente comando:

kubectl scale deployment.v1.apps/nginx-deployment --replicas=10

Antes y despues del escalado
Primero con 3 replicas listas y luego con las 10
Suponiendo que el escalado automático de POD horizontal esté habilitado en su clúster, puede configurar un auto escalador para su implementación y elegir el número mínimo y máximo de pods que desea ejecutar en función de la utilización de la CPU de sus PODs existentes:

kubectl autoscale deployment.v1.apps/nginx-deployment --min=10 --max=15 --cpu-percent=80


Para pausar implementacion:

kubectl rollout pause deployment.v1.apps/nginx-deployment

Para actualizar la imagen de la implementación:

kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.17.0

Ver historial del despliegue:

kubectl rollout history deployment.v1.apps/nginx-deployment

La nueva implementacion no tendrá ningún efecto mientras esté en pausa.

Para reanudar la implementación:

kubectl rollout resume deployment.v1.apps/nginx-deployment


Despliegue completo, ¿que es?

Kubernetes marca una implementación como completa cuando tiene las siguientes características:

Todas las réplicas asociadas con la implementación se han actualizado a la última versión que ha especificado, lo que significa que se han completado las actualizaciones que ha solicitado.
Todas las réplicas asociadas con la implementación están disponibles.
No se están ejecutando réplicas antiguas para la implementación.

Por último, para verificar si se ha completado una implementación ejecutamos:

kubectl rollout status deployment.v1.apps/nginx-deployment


Nos indica que se implementó con éxito.

Para mas info y detalle podes ingresar acá.

Como accedo al servicio del POD

Una cosita mas, si quisieramos acceder al nginx para ver que carajo esta mostrando ¬¬ seguimos estos pasos:

Primero, vemos la IP de nuestro Minikube para luego usar en la URL del servicio:

minikube ip

Luego vemos que servicios hay corriendo:

kubectl get services

En este namespace yo no tengo ninguno.
Ahora exponemos el puerto de nuestro nginx:

kubectl expose deployment nginx-deployment --type=NodePort --port=80

Verificamos los servicios de nuevo:

kubectl get services

Y ahora si, vamos a ver el de nginx-deployment corriendo con un puerto externo asociado al 80, en mi caso el 32737.
Entonces para acceder pongo la IP del Minikube y ese puerto y puedo acceder con curl así:

curl http://192.168.99.100:32737

Aca dejo una imagen con todos los pasos:


Por interfaz grafica lo vemos asi:


Antes de terminar una mas, quiero convertirlos en una fuente inagotable de conocimiento (?).
Se acuerdan que al principio del post creamos un nuevo namespace para separar lo que íbamos a hacer de lo que ya estaba hecho, ¿no? (Si no se acuerdan vuelvan al principio ¬¬)

Ver PODs de los diferentes namespace

Bueno, acá lo podemos ver con mas claridad, para ver los PODs del namespace default:

kubectl get pods -n default


Vemos los PODs de nombre hello, hello-http, mi-app y nginx-deployment.

Luego no le especificamos el namespace y nos muestra el predeterminado (que ahora es mirollback porque lo habíamos configurado al principio):

kubectl get pods


Vemos que tenemos 10 PODs con el nombre nginx-deployment pero que a diferencia de los otros que llevan 2 días, estos llevan apenas 129 minutos.

Para verlo con total claridad vamos a mostrar todos los PODs de todos los namespace:

kubectl get pods -A


Y acá vemos todos los PODs de nuestro cluster, y en la columna de la izquierda podemos diferenciar a que namespace pertenece cada uno.

Ahora si, no me pidas mas ¬¬
Ahora te pido yo:

COMPARTI, ¿que te cuesta? Son 2 clicks, no lo puedo entender.

Arrobame, que te quiero agradecer, como le agradecí a Ramón.

Y si no te agradezco no te aflijas, porque a cambio, te enseño a convertirte en el mejor técnico de la historia, en 100 días, y sin perder la memoria (?).

Mas no puedo hacer. Te estoy dando el negocio de tu vida en un posteo.
Chau, cuidate (?).

No hay comentarios:

Publicar un comentario