RollingUpdate e Recreate como estratégia de implantação declarativa
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.
Existem quatro opções para disparar uma atualização declarativa:
-
Substituir todo o Deployment utilizando o comando
.kubectl replace
-
Utilizar o comando
alterando as propriedades do Deployment.kubectl patch
-
Editar o Deployment utilizando o comando
.kubectl edit
-
Usar o comando
definindo a nova imagem no Deployment.kubectl set image
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.
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.