Skip to main content

Rolling Updates y Rollbacks

Cuando estamos creando un deployment podemos definir estrategias de actualización.

  • Recreate: destruye los viejos y crea nuevos dejando la aplicación fuera del aire mientras el proceso termina.
  • RollingUpdate (predeterminado): va destruyendo uno y levantando otro manteniendo la aplicación en el aire.

Alt text

Podemos hacer update de varias cosas:

  • Nueva imagen
  • Labels
  • Número de réplicas
  • Argumentos
  • Variables de entorno
  • etc

La mejor manera de hacer esto es reconfigurando el archivo de manifiesto y usando el comando kubectl apply -f manifesto.yaml, para mantener un archivo de configuración fiel a lo que está desplegado en el clúster.

A través del edit o del comando set image como se muestra abajo:

k create deployment nginx --image nginx
deployment.apps/nginx created

# nginx es el nombre de la imagen
k set image deployment/nginx nginx=1.17
deployment.apps/nginx image updated

# Vamos a desplegar un pod con dos contenedores por ejemplo.
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: pod-com-dois-containers
spec:
containers:
- name: alpha
image: nginx
ports:
- containerPort: 80
- name: beta
image: alpine
command: ["/bin/sh", "-c"]
args:
- sleep 4800
EOF

# Vamos a actualizar solamente la imagen del nginx en el pod
kubectl set image pods/pod-com-dois-containers alpha=nginx:1.17
pod/pod-com-dois-containers image updated

Vea la diferencia de los eventos que acontecen usando esas dos estrategias.

Alt text

El Recreate escaló para 0 y después para 5 nuevamente y el RollingUpdate fue reduciendo y aumentando gradualmente.

El deployment crea el replicaset que despliega un conjunto de pods. Cuando actualizamos el deployment él creará otro replicaset que desplegará nuevos pods y mantendrá el antiguo en cero, pero aún está presente.

Alt text

Es por eso que conseguimos hacer un rollback de la aplicación para el replicaset antiguo en caso de que el nuevo esté con algún problema.

El deployment crea revisiones apuntando para los replicaset correctos y a través de eso podemos volver con el comando rollout.

Alt text

kubectl create deployment nginx --image nginx:1.24 --replicas 2 -o yaml --dry-run=client > nginx.yaml

kubectl apply -f nginx1.24.yaml
deployment.apps/nginx created

kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
nginx 2/2 2 2 11s

kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-69d846b5c6 2

# Creé otro archivo basado en el 1.24 pero con la imagen de 1.25
kubectl apply -f nginx1.25.yaml
deployment.apps/nginx configured # Observe que no fue creado, solo configurado

kubectl describe deployments.apps nginx
Name: nginx
Namespace: default
CreationTimestamp: Sat, 30 Dec 2023 11:26:06 -0300
Labels: app=nginx
# usa annotations para definir la revisión
Annotations: deployment.kubernetes.io/revision: 2
Selector: app=nginx
Replicas: 2 desired | 2 updated | 2 total | 2 available | 0 unavailable
# Observe que RollingUpdate es default y él mata 25% y sube 25%
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: app=nginx
Containers:
nginx:
Image: nginx:1.25
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
# Vea que él muestra el replicaset anterior y el nuevo
OldReplicaSets: nginx-69d846b5c6 (0/0 replicas created)
NewReplicaSet: nginx-7b8df77865 (2/2 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 2m9s deployment-controller Scaled up replica set nginx-69d846b5c6 to 2
Normal ScalingReplicaSet 30s deployment-controller Scaled up replica set nginx-7b8df77865 to 1
Normal ScalingReplicaSet 23s deployment-controller Scaled down replica set nginx-69d846b5c6 to 1 from 2
Normal ScalingReplicaSet 23s deployment-controller Scaled up replica set nginx-7b8df77865 to 2 from 1
Normal ScalingReplicaSet 16s deployment-controller Scaled down replica set nginx-69d846b5c6 to 0 from 1

kubectl rollout history deployment nginx
deployment.apps/nginx
REVISION CHANGE-CAUSE
1 <none>
2 <none>

# El undo vuelve para revisión anterior, o sea usando el old-replica
# El rollout funciona para daemonset deployment statefulset
kubectl rollout undo deployment nginx
deployment.apps/nginx rolled back

kubectl describe deployments.apps nginx
Name: nginx
Namespace: default
CreationTimestamp: Sat, 30 Dec 2023 11:26:06 -0300
Labels: app=nginx
Annotations: deployment.kubernetes.io/revision: 3
Selector: app=nginx
Replicas: 2 desired | 2 updated | 2 total | 2 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: app=nginx
Containers:
nginx:
Image: nginx:1.24
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: nginx-7b8df77865 (0/0 replicas created)
NewReplicaSet: nginx-69d846b5c6 (2/2 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 10m deployment-controller Scaled up replica set nginx-69d846b5c6 to 2
Normal ScalingReplicaSet 8m22s deployment-controller Scaled up replica set nginx-7b8df77865 to 1
Normal ScalingReplicaSet 8m15s deployment-controller Scaled down replica set nginx-69d846b5c6 to 1 from 2
Normal ScalingReplicaSet 8m15s deployment-controller Scaled up replica set nginx-7b8df77865 to 2 from 1
Normal ScalingReplicaSet 8m8s deployment-controller Scaled down replica set nginx-69d846b5c6 to 0 from 1
Normal ScalingReplicaSet 6m54s deployment-controller Scaled up replica set nginx-69d846b5c6 to 1 from 0
Normal ScalingReplicaSet 6m52s deployment-controller Scaled down replica set nginx-7b8df77865 to 1 from 2
Normal ScalingReplicaSet 6m52s deployment-controller Scaled up replica set nginx-69d846b5c6 to 2 from 1
Normal ScalingReplicaSet 6m51s deployment-controller Scaled down replica set nginx-7b8df77865 to 0 from 1

kubectl get replicasets.apps
NAME DESIRED CURRENT READY AGE
nginx-69d846b5c6 2 2 2 10m
nginx-7b8df77865 0 0 0 9m6s

# Aunque él vuelva para usar el primer replicaset, él crea una nueva revisión
❯ kubectl rollout history deployment nginx
deployment.apps/nginx
REVISION CHANGE-CAUSE
2 <none>
3 <none>

# Podríamos volver para una revisión específica
kubectl rollout undo deployment nginx --to-revision 2
deployment.apps/nginx rolled bac

# Volvió para la revisión 2 pero creó la 4, pues él siempre crea una revisión, pues lo que importa para él es el replicaset que será utilizado.
kubectl rollout history deployment nginx
deployment.apps/nginx
REVISION CHANGE-CAUSE
3 <none>
4 <none>

# Él descarta antiguos
kubectl rollout undo deployment nginx --to-revision 1
error: unable to find specified revision 1 in history

Para mantener más revisiones podríamos definir la etiqueta revisionHistoryLimit.

Para que las causas de los cambios aparezcan correctamente basta usar annotations

apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: nginx
name: nginx
annotations:
kubernetes.io/change-cause: "Actualización de imagen para versión 1.25"
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
creationTimestamp: null
labels:
app: nginx
spec:
containers:
- image: nginx:1.25
name: nginx
resources: {}
# Podríamos cambiar también algunos parámetros y definir las estrategias
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
type: RollingUpdate
revisionHistoryLimit: 10 # Así