O que é o Argo Rollouts?
Argo Rollouts é um controlador para Kubernetes e um conjunto de CRDs que oferecem capacidades avançadas de deploy, como blue-green, canary, análise de canary, experimentação e recursos de entrega progressiva para o Kubernetes.
O Argo Rollouts pode se integrar (opcionalmente) com controladores de ingress e service meshes, aproveitando suas habilidades de manipulação de tráfego para direcionar gradualmente o tráfego para a nova versão durante uma atualização. Além disso, o Rollouts pode consultar e interpretar métricas de diversos provedores para verificar KPIs importantes e automatizar a promoção ou o rollback durante uma atualização.
Repositórios
Diversas seções dos manifestos presentes no repositório acima foram portadas ou inspiradas a partir do repositório msc-research-stuff, do Matheus Fidelis. Se você leva Engenharia a sério, recomendo fortemente que veja https://fidelissauro.dev/.
Este outro repositório pode te ajudar a provisionar, com Terraform, um cluster EKS funcional com a maior parte dos componentes utilizados no lab.
Componentes do cluster
Para esse lab, foram utilizados essencialmente os componentes abaixo em um cluster EKS:
- ArgoCD
- Argo Rollouts
- Kube-prometheus-stack
- Istio (base, istiod e gateway)
Projects e Applications no ArgoCD
Foram criados dois Projects e quatro Applications, uma para cada cenário explorado:
| Project | Application |
|---|---|
| lab-apps-blue-green | lab-apps-blue-green-simple |
| lab-apps-blue-green | lab-apps-blue-green-automated |
| lab-apps-canary | lab-apps-canary-simple |
| lab-apps-canary | lab-apps-canary-metrics |
Exemplo de um manifesto de AppProject e Application no ArgoCD:
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: lab-apps-canary
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
description: Lab Apps with Argo Rollouts
sourceRepos:
- 'https://github.com/paulofponciano/lab-argo-rollouts'
- 'oci://registry-1.docker.io/paulofponciano'
- 'registry-1.docker.io/paulofponciano'
destinations:
- namespace: '*'
server: 'https://kubernetes.default.svc'
name: 'in-cluster'
clusterResourceWhitelist:
- group: '*'
kind: '*'
namespaceResourceWhitelist:
- group: '*'
kind: '*'
orphanedResources:
warn: false
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: lab-apps-canary-simple
namespace: argocd
labels:
type: application
spec:
project: lab-apps-canary
source:
repoURL: https://github.com/paulofponciano/lab-argo-rollouts
path: argo-apps/canary/simple
targetRevision: HEAD
destination:
server: https://kubernetes.default.svc
namespace: argocd
syncPolicy:
automated:
prune: true
selfHeal: true
Cenário com Blue/Green simples
Usamos uma app demo bem simples que já possui um helm chart criado. Com um ApplicationSet do Argo, passamos alguns valores para configuração:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: demo-ms-2
namespace: argocd
spec:
generators:
- list:
elements:
- cluster: https://kubernetes.default.svc
template:
metadata:
name: demo-ms-2
spec:
project: lab-apps-blue-green
source:
repoURL: 'registry-1.docker.io/paulofponciano'
chart: demo-ms-intercomunicacao
targetRevision: 0.1.18
helm:
valuesObject:
# ...omitted for brevity...
strategy:
type: blueGreen
blueGreen:
activeService: demo-ms-2
previewService: demo-ms-2-green
autoPromotionEnabled: false
# ...omitted for brevity...
destination:
server: '{{ cluster }}'
namespace: argocd
syncPolicy:
automated: {}
No manifesto acima, veja que informamos dois services para a estratégia Blue/Green: activeService e previewService. Enquanto não existir uma réplica green, ambos os services direcionam o tráfego para a réplica estável (blue). No entanto, o tráfego de "produção" chegará através do Virtual Service do Istio, que por sua vez roteia apenas para o activeService:
Recursos criados pelo ApplicationSet:
No pod que, atualmente, é a réplica estável, podemos ver que existe uma label rollouts-pod-template-hash: 667677c457 (além da label app):
Essa label é utilizada como selector tanto no activeService quanto no previewService. Isso é controlado pelo Argo Rollouts:
Como mencionado anteriormente, o Virtual Service do Istio possui uma rota com destination apenas para demo-ms-2.demo.svc.cluster.local, que é o activeService:
Enviando requisições para o endpoint /version tanto pelo activeService (que passa pelo VS do Istio) quanto pelo previewService, vemos que a resposta é a mesma, pois estamos acessando a mesma réplica (blue):
while true; do curl http://demo-ms-2.demo.svc.cluster.local:5000/version; done
while true; do curl http://demo-ms-2-green.demo.svc.cluster.local:5000/version; done
No ApplicationSet, alteramos a APP_VERSION de "0.1.1" para "0.1.2", essa alteração vai gerar um Blue/Green para essa app:
Agora com duas réplicas, uma continua sendo a estável (blue) recebendo tráfego de produção e a outra green com a nova versão da app. O Argo Rollouts altera o selector no previewService para que o endpoint seja a réplica green:
Com isso, temos a chance de validar se a nova versão está se comportando como o esperado antes de enviar tráfego de produção. Em alguns casos, é interessante também para aplicações que possuem particularidades em relação a warm-up:
Uma vez que decidimos que está tudo certo com a nova versão (green), podemos promovê-la. No Rollout, selecionamos 'Promote-Full':
Desta vez, o selector do activeService é alterado para que o endpoint seja a réplica da nova versão rollouts-pod-template-hash: 65dcC64c86 (que agora passa a ser blue):
Voltamos ao cenário inicial, mas desta vez com a nova versão "0.1.2" já recebendo tráfego de produção. A réplica que antes era blue na versão "0.1.1" é terminada:
Cenário com Blue/Green automatizado
Para aumentar a confiabilidade, no processo de rollout podemos incluir testes automatizados na réplica green, tornando a promoção da nova versão condicionada ao sucesso desses testes. Ainda assim, neste cenário, o parâmetro autoPromotionEnabled permanece configurado como false, o que significa que a promoção completa (Promote-Full) continuará sendo realizada manualmente, assim como no cenário anterior. Essa abordagem é uma escolha para manter maior controle sobre o processo.
No ApplicationSet adicionamos os templates de prePromotionAnalysis bem como values para o Helm renderizar esses templates:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: demo-ms-4
namespace: argocd
spec:
generators:
- list:
elements:
- cluster: https://kubernetes.default.svc
template:
metadata:
name: demo-ms-4
spec:
project: lab-apps-blue-green
source:
repoURL: 'registry-1.docker.io/paulofponciano'
chart: demo-ms-intercomunicacao
targetRevision: 0.1.18
helm:
valuesObject:
# ...omitted for brevity...
strategy:
type: blueGreen
blueGreen:
activeService: demo-ms-4
previewService: demo-ms-4-green
autoPromotionEnabled: false
prePromotionAnalysis:
templates:
- templateName: demo-ms-4-http-bench-analysis
- templateName: demo-ms-4-check-success
analysisTemplates:
- name: demo-ms-4-http-bench-analysis
spec:
metrics:
- name: http-bench-analysis
failureLimit: 1
provider:
job:
spec:
backoffLimit: 1
template:
metadata:
labels:
istio-injection: disabled
sidecar.istio.io/inject: "false"
spec:
containers:
- name: http-bench-analysis
image: rogerw/cassowary:v0.14.0
command: ["cassowary"]
args: ["run", "-u", "http://demo-ms-4-green.demo.svc.cluster.local:5000/healthz", "-c", "3", "-n", "1000"]
restartPolicy: Never
count: 1
- name: demo-ms-4-check-success
spec:
metrics:
- name: success-rate
interval: 1m
successCondition: result[0] >= 0.95
failureLimit: 1
provider:
prometheus:
address: http://kube-prometheus-stack-prometheus.o11y.svc.cluster.local:9090
query: |
sum(irate(
istio_requests_total{destination_service=~"demo-ms-4-green.demo.svc.cluster.local",response_code!~"5.*"}[1m]
)) /
sum(irate(
istio_requests_total{destination_service=~"demo-ms-4-green.demo.svc.cluster.local"}[1m]
))
count: 1
# ...omitted for brevity...
destination:
server: '{{ cluster }}'
namespace: argocd
syncPolicy:
automated: {}
Para gerar o Blue/Green alteramos no ApplicationSet a APP_VERSION de "0.1.3" para "0.1.4":
Testes http-bench-analysis (Job) e success-rate com métricas do Prometheus:
Como os testes passaram com sucesso, a réplica green fica disponível para ser promovida:
while true; do curl http://demo-ms-4.demo.svc.cluster.local:5000/version; done
while true; do curl http://demo-ms-4-green.demo.svc.cluster.local:5000/version; done
A forma como o Argo Rollouts manipula o selector tanto no activeService quanto no previewService é a mesma do cenário anterior, e o Virtual Service do Istio continua apenas com uma rota, tendo como destination o activeService.
Após o 'Promote-Full', a réplica com a versão "0.1.3" é removida e a nova versão "0.1.4" agora é a blue:
Cenário com Canary simples
Com a estratégia de canary releases, podemos realizar um rollout gradual em ambiente de produção, avaliando a "aceitação estruturada do usuário". Caso sejam identificados problemas ou a aceitação não seja satisfatória, é fundamental que seja possível realizar um rollback (Abort) imediato para a versão anterior.
Nesse cenário, a progressão do rollout será manual entre os steps. Os values para o helm são informados no ApplicationSet:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: demo-ms-7
namespace: argocd
spec:
generators:
- list:
elements:
- cluster: https://kubernetes.default.svc
template:
metadata:
name: demo-ms-7
spec:
project: lab-apps-canary
source:
repoURL: 'registry-1.docker.io/paulofponciano'
chart: demo-ms-intercomunicacao
targetRevision: 0.1.18
helm:
valuesObject:
# ...omitted for brevity...
strategy:
type: canary
canary:
steps:
- setWeight: 0
- pause: {}
- setWeight: 10
- pause: {}
- setWeight: 20
- pause: {}
- setWeight: 40
- pause: {}
- setWeight: 80
- pause: {}
- setWeight: 100
# ...omitted for brevity...
destination:
server: '{{ cluster }}'
namespace: argocd
syncPolicy:
automated: {}
Para iniciar o processo, alteramos no ApplicationSet a APP_VERSION de "0.1.5" para "0.1.6". Como o primeiro step é setWeight: 0, nada acontecerá até avançarmos manualmente o canary:
while true; do curl http://demo-ms-7.demo.svc.cluster.local:5000/version; done
while true; do curl http://demo-ms-7-canary.demo.svc.cluster.local:5000/version; done
Semelhante ao que vimos nos cenários de Blue/Green, aqui os dois services estão com o selector na mesma réplica (réplica estável):
No entanto, o Virtual Service do Istio possui uma rota com dois destination e com pesos (weight) distintos. Como ainda não avançamos para o próximo step, o peso para a versão canary é 0, ou seja, sem tráfego (até mesmo porque não existe ainda uma réplica canary):
Seguindo, podemos selecionar 'Resume' no rollout e avançar para o segundo step:
Agora a réplica canary é criada:
O Argo Rollouts modifica o selector do canaryService para selecionar a réplica criada:
No Virtual Service do Istio, o peso no destination do canary é alterado para atender o step setWeight: 10:
Exemplo visual do canaryService e stableService:
Podemos ver que as requisições enviadas para o Virtual Service do Istio, que responde pelo host do stableService (demo-ms-7.demo.svc.cluster.local) já estão sendo direcionadas também para a versão canary, seguindo o setWeight: 10 até que avancemos mais um step manualmente:
Com apoio visual do Kiali, vemos a distribuição do tráfego:
Já com o canary mais avançado, no step setWeight: 80:
Manualmente, iremos progredir o canary até que o tráfego seja totalmente direcionado para a nova versão, no step setWeight: 100. Depois disso, o Argo Rollouts ajustará o selector do stableService para que o endpoint seja a réplica com a versão "0.1.6" e no Virtual Service do Istio os pesos serão ajustados para os valores que vimos inicialmente, ou seja, 100 para a réplica estável e 0 para a réplica canary (do próximo rollout no caso).
Cenário com Canary automatizado
Neste cenário, vamos deixar a progressão do canary condicionada ao sucesso dos testes automatizados. Assim como no cenário anterior, cada step irá direcionar uma parcela maior do tráfego de produção para a réplica canary, até que seja totalmente concluído o rollout.
No ApplicationSet, adicionamos o template de análise na seção analysis de cada step:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: demo-ms-10
namespace: argocd
spec:
generators:
- list:
elements:
- cluster: https://kubernetes.default.svc
template:
metadata:
name: demo-ms-10
spec:
project: lab-apps-canary
source:
repoURL: 'registry-1.docker.io/paulofponciano'
chart: demo-ms-intercomunicacao
targetRevision: 0.1.18
helm:
valuesObject:
# ...omitted for brevity...
strategy:
type: canary
canary:
steps:
- setWeight: 10
- pause: { duration: 30s }
- analysis:
templates:
- templateName: demo-ms-10-check-success
- setWeight: 20
- pause: { duration: 1m }
- analysis:
templates:
- templateName: demo-ms-10-check-success
- setWeight: 40
- pause: { duration: 1m }
- analysis:
templates:
- templateName: demo-ms-10-check-success
- setWeight: 80
- pause: { duration: 1m }
- analysis:
templates:
- templateName: demo-ms-10-check-success
- setWeight: 100
analysisTemplates:
- name: demo-ms-10-check-success
spec:
metrics:
- name: success-rate
interval: 1m
successCondition: result[0] >= 0.95
failureLimit: 1
provider:
prometheus:
address: http://kube-prometheus-stack-prometheus.o11y.svc.cluster.local:9090
query: |
sum(irate(
istio_requests_total{destination_service=~"demo-ms-10.demo.svc.cluster.local",response_code!~"5.*"}[1m]
)) /
sum(irate(
istio_requests_total{destination_service=~"demo-ms-10.demo.svc.cluster.local"}[1m]
))
count: 1
# ...omitted for brevity...
destination:
server: '{{ cluster }}'
namespace: argocd
syncPolicy:
automated: {}
Para iniciar o processo, alteramos no ApplicationSet a APP_VERSION de "0.1.7" para "0.1.8". Depois disso, não esperamos mais nenhuma ação "manual", ficamos aguardando os testes acontecerem em cada step e o canary progredir:
Argo Rollouts ajustando os pesos (weight) no Virtual Service do Istio:
while true; do curl http://demo-ms-10.demo.svc.cluster.local:5000/version; done
while true; do curl http://demo-ms-10-canary.demo.svc.cluster.local:5000/version; done
Conclusão do rollout e remoção da réplica com a versão "0.1.7":
Os pesos são reajustados para os valores iniciais, 100 para a réplica estável:
Importante!
Utilizar Canary releases e Blue/Green deployments é sensacional. Mas não devemos esquecer do que pode (e muitas vezes vai) inviabilizar esse tipo de estratégia (e outros tipos também): a CAMADA DE DADOS. É necessário investir um esforço de Engenharia considerável nesse ponto.
Referências e recomendações:
- DIA 4 - CLOUD #SEMANADEVOPS LINUXtips - Track com Matheus Fidelis
- Argo Rollouts - Kubernetes Progressive Delivery Controller
- Argo Rollouts - Istio traffic-management
- Istio - Virtual Service
- Matheus Fidelis - Technical Blog
Keep shipping! 🐳












































Top comments (0)