Primeros pasos con Kubernetes (VI): Estrategias de prototipaje


En algún momento nos habremos encontrado que tenemos una aplicación y hemos hecho algún cambio interno que no afecta a la funcionalidad pero si que mejora o solución ciertos problemas internos. Algunas veces este tipo de fixs experimentales aplicados sobre piezas clave de la arquitectura, pueden ser extremadamente delicados por el alto acoplamiento de sistemas externos sobre nuestra aplicación y la posible afectación que tener hacer un cambio en producción.

Este tipo de escenarios, en ámbitos más conservadores, se acostumbran a afrontar subiendo una instancia de la aplicación modificada y que esta conviva con el resto de instancias originales. El balanceador repartirá la carga entre todas las instancias, entre las cuales se encuentra la instancia experimental. Pasado un tiempo determinado, se comprueba si el cambio observado ha reaccionado como se esperaba y en tal caso, se actualizar el entorno de producción con una nueva release que incluya ese cambio que estábamos experimentando.

Este tipo de actuaciones se llaman Canary Strategy y veremos a continuación como abordar esta situación con Kubernetes.

Tras el script de prearranque, tenemos nuestra aplicación que saluda con un “Hello World” y con 3 instancias balanceadas con un Service. El Service se encuentra configurado con un Label Selector que apunta a cualquier clave/valor que sea “app=hello”.


{
    "kind": "Service",
    "apiVersion": "v1",
    "metadata": {
        "name": "hello"
    },
    "spec": {
        "type": "LoadBalancer",
        "selector": {
            "app": "hello"
        },
        "ports": [
            {
                "protocol": "TCP",
                "port": 80,
                "targetPort": 8080
            }
        ]
    }
}


Y el descriptor de nuestro Deployment es (esta vez hemos definido dos labels)


apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: hello
spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: hello
        version: v1
    spec:
      containers:
      - name: hello
        image: gcr.io/k8slabs-142707/hello:v1
        ports:
        - containerPort: 8080


Queremos introducir un cambio en nuestra aplicación que hemos visto anteriormente y que consiste en un saludo diferente del “Hello World”.

Para ello, deberemos configurar el deployment de este hello world experimental de la siguiente manera.


kind: Deployment
metadata:
  name: helloexperimental
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: hello
        version: v1.experimental
    spec:
      containers:
      - name: helloexperimental
        image: gcr.io/k8slabs-142707/helloexperimental:v1
        ports:
        - containerPort: 8080



Crearemos un Deployment con otro nombre diferente de la aplicación original, pero el label “app” le ponemos el mismo nombre que la aplicación a la que queremos hacerle el Canary Strategy.

Con este camio, cuando despleguemos esta aplicación experimental, al tener el mismo label “app” que la aplicación original, el Service también balanceará sobre este nuevo Pod.


$ sudo docker build -f ./apps/helloexperimental/v1/Dockerfile -t gcr.io/k8slabs-142707/helloexperimental:v1 ./apps/helloexperimental/v1

Sending build context to Docker daemon 3.584 kB
Step 1 : FROM node:4.4
 ---> 93b396996a16
Step 2 : EXPOSE 8080
 ---> Using cache
 ---> a2c7be748e22
Step 3 : COPY server.js .
 ---> 8500060cddff
Removing intermediate container 9080650bd1a6
Step 4 : CMD node server.js
 ---> Running in 385804f9f2fe
 ---> 4140d75d82f7
Removing intermediate container 385804f9f2fe
Successfully built 4140d75d82f7

$ sudo gcloud docker push gcr.io/k8slabs-142707/helloexperimental:v1

The push refers to a repository [gcr.io/k8slabs-142707/helloexperimental]
14f7e10c17ff: Layer already exists
20a6f9d228c0: Layer already exists
80c332ac5101: Layer already exists
04dc8c446a38: Layer already exists
1050aff7cfff: Layer already exists
66d8e5ee400c: Layer already exists
2f71b45e4e25: Layer already exists
v1: digest: sha256:5b8f20ec9b0c104f69030ac74533c57b9cca3d0fe816284fa7d9fa555e26d677 size: 9406

$ kubectl create -f ./apps/helloexperimental/deployment.yml

deployment "helloexperimental" created

$ kubectl get pods

NAME                               READY     STATUS    RESTARTS   AGE
hello-2129575991-dpqx3             1/1       Running   0          2m
hello-2129575991-srklx             1/1       Running   0          2m
hello-2129575991-sv7oc             1/1       Running   0          2m
helloexperimental-84791183-ickwj   1/1       Running   0          8s

$ kubectl get svc

NAME         CLUSTER-IP      EXTERNAL-IP     PORT(S)   AGE
hello        10.67.242.150   104.199.15.87   80/TCP    2m
kubernetes   10.67.240.1               443/TCP   11h

$ curl http://104.199.15.87

Hello World!

$ curl http://104.199.15.87

Hello World!
 
$ curl http://104.199.15.87

Luke, I am your father... My ip address is 10.64.2.4

$ curl http://104.199.15.87

Hello World!


Como hemos podido ver, hemos introducido una versión experimental de nuestra aplicación y se encuentra conviviendo con la original. Ahora ya quedaría en nuestro tejado decidir si el experimento ha salido como lo esperado o desechamos la prueba.

Dado que tenemos 4 instancias balanceadas, 3 de ellas la original, en el peor de los casos la afectación en producción habría sido de un 25%. En ese escenario de afectación, únicamente deberíamos eliminar la aplicación experimental del sistema y el Service volvería a balancear sobre las 3 instancias originales, habiendo dejado el entorno de producción inestable (en un 25%) durante el tiempo de la prueba y evitándose redespliegues de la versión original.

No hay comentarios