RollingUpdate e Recreate como estratégia de implantação declarativa

Por Gaspar Barancelli Junior em 22 de fevereiro de 2023

Fazer um upgrade de um serviço para a próxima versão envolve algumas atividades como iniciar a nova versão, encerrar a versão antiga de forma organizada, esperar e verificar se a nova versão foi iniciada com sucesso e, ocasionalmente, fazer o rollback para a versão anterior caso tenha havido alguma falha.

Existem estratégias para executar essas atividades com e sem downtime. Mas ao optar por estratégias sem downtime temos que levar em conta que utilizaremos mais recursos do Cluster, pois duas versões do serviço estarão sendo executadas durante o processo de atualização.

Felizmente o Kubernetes também automatizou essa atividade. Utilizando o recurso Deployment o qual encapsula o processo de upgrade e rollback de um serviço. No manifesto de um Deployment descrevemos como nossa aplicação deve ser atualizada, qual estrategia de atualização deve ser utilizada, sendo elas RollingUpdate (padrão) ou Recreate.

No centro de um Deployment está a capacidade de iniciar e encerrar um conjunto de Pods de forma previsível. Para que isso funcione de modo esperado, os próprios contêineres em geral detectam e tratam os eventos associados aos ciclo de vida, além de oferecer endpoints para verificação de sanidade, como a definição de Readiness Probe e Liveness Probe.

É importante comentar que internamente, um Deployment cria um ReplicaSet. O objetivo de um ReplicaSet é manter um conjunto estável de Pods de réplica em execução a qualquer momento. Como tal, costuma ser usado para garantir a disponibilidade de um número especificado de Pods idênticos.

Segue o exemplo de um Deployment com uma estratégia de atualização contínua.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: blog
  labels:
    app: blog
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
      selector:
        matchLabels:
          app: blog
      template:
        metadata:
          labels:
            app: blog
        spec:
          containers:
            - name: blog
              image: gasparbarancelli/blog:1.0
              imagePullPolicy: Always
              readinessProbe:
                httpGet:
                  path: /actuator/health
                  port: 8080
                initialDelaySeconds: 60
                periodSeconds: 10
                timeoutSeconds: 10
              livenessProbe:
                httpGet:
                  path: /actuator/health
                  port: 8080
                initialDelaySeconds: 60
                periodSeconds: 60
                timeoutSeconds: 10
                successThreshold: 1
                failureThreshold: 2
              resources:
                requests:
                  memory: "300Mi"
                  cpu: "400m"
                limits:
                  memory: "700Mi"
                  cpu: "600m"
              ports:
                - containerPort: 8080

Há quatro pontos que devemos entrar em detalhes:

  • replicas: Para que uma atualização contínua faça sentido você precisa de mais de uma réplica.

  • maxSurge: Número de Pods que Podem ser executados temporariamente além das replicas especificadas, durante uma atualização.

  • maxUnavailable: Número de Pods que Podem estar indisponíveis durante a atualização.

  • readinessProbe: Sondagem para verificar se o contêiner está pronto.

Ao utilizarmos a estratégia RollingUpdate garantimos que não haverá downtime durante todo processo de atualização. Segue imagem demonstrando o processo de atualização contínua.

1
Figure 1. Ilustração de uma implantação RollingUpdate

Existem quatro opções para disparar uma atualização declarativa:

  • Substituir todo o Deployment utilizando o comando kubectl replace.

  • Utilizar o comando kubectl patch alterando as propriedades do Deployment.

  • Editar o Deployment utilizando o comando kubectl edit.

  • Usar o comando kubectl set image definindo a nova imagem no Deployment.

A estratégia RollingUpdate é conveniente para garantir que não haja downtime durante o processo de atualização. No entanto ao utilizarmos essa estratégia dois contêineres utilizando versões serão executados ao mesmo tempo. Isso Pode causar inúmeros problemas quando a aplicação não for desenvolvida de maneira adequada a suportar duas versões. Para evitar esse tipo de problema em aplicações que não estão preparadas, deve-se utilizar a estratégia de atualização Recreate exemplificada na figura abaixo.

2
Figure 2. Ilustração de uma implantação Recreate

Para aplicar a estratégia Recreate basta definir o valor do atributo maxUnavailable com o mesmo número declarado em replicas. Feito isso todos os contêineres com a versão atual são encerrados e, em seguida, todos os novos contêineres serão iniciados simultaneamente, quando os contêineres antigos forem despejados. Mas vale resaltar que com o resultado dessa ação haverá downtime até que todos os contêineres antigos sejam encerrados e que o novos contêineres passem pela sondagem de verificação. Olhando pelo lado positivo, não haverá duas versões dos contêineres executando ao mesmo tempo.

RollingUpdate e Recreate são as estratégias automatizadas fornecidas pelo Kubernetes, mas utilizando um service mesh como o Istio Podemos implementar novas estratégias, como deploy Canary e Blue/Green utilizando conceito de serviço virtual, o qual vamos explicar num futuro post.

// Livros recomendados relacionados ao assunto do post

// Compartilhe esse Post