<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Marcos Xavier</title>
    <description>The latest articles on DEV Community by Marcos Xavier (@marcos_xavier).</description>
    <link>https://dev.to/marcos_xavier</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3103639%2Ff1efa7d1-e64c-406a-ba44-d29e55f21751.jpeg</url>
      <title>DEV Community: Marcos Xavier</title>
      <link>https://dev.to/marcos_xavier</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/marcos_xavier"/>
    <language>en</language>
    <item>
      <title>Canary Deployments no Kubernetes utilizando um CRD (desenvolvido por mim)</title>
      <dc:creator>Marcos Xavier</dc:creator>
      <pubDate>Sat, 03 May 2025 04:07:03 +0000</pubDate>
      <link>https://dev.to/marcos_xavier/automatizando-canary-deployments-no-kubernetes-com-um-crd-3kck</link>
      <guid>https://dev.to/marcos_xavier/automatizando-canary-deployments-no-kubernetes-com-um-crd-3kck</guid>
      <description>&lt;p&gt;Realizar o deploy de novas versões de aplicações é uma parte crítica do ciclo de vida de desenvolvimento de software e, dependendo a criticidade de uma aplicação, isso se torna mais complicado.&lt;/p&gt;

&lt;p&gt;Por padrão, o Kubernetes, utiliza a estratégia de atualização chamada rolling update, que garante a disponibilidade de uma aplicação. Isso é ótimo, mas em alguns cenários, você pode querer  ter mais controle sobre como ocorrem as atualizações de uma aplicação. Por exemplo, você quiser expor gradualmente uma nova versão para um pequeno grupo de usuários, monitorar seu desempenho por algum tempo e só então liberá-la completamente para todos usuários? Aqui que o Canary Deployment começa a fazer sentido.&lt;/p&gt;

&lt;p&gt;Embora ferramentas como o Istio possuem meios  que permitem o desvio de tráfego e também gerenciar todo o ciclo de vida de um canary deployment, ajustar pesos do tráfego ou ainda promover uma versão canary para stable, realizar isso de forma manual não é tão simples e pode exigir até criação de scripts complexos, que podem não garantir o perfeito funcionamento das coisas.&lt;/p&gt;

&lt;p&gt;Unido ao desejo de me desafiar e querer muito desenvolver um CRD, coloquei a mão e aqui estamos. Criei o CRD em questão junto a um controlador gerenciá-lo. Ele foi construído utilizando Go Lang e feito  especificamente para gerenciar uma ou mais aplicações, permitindo testar, em um cluster Kubernetes, uma versão &lt;strong&gt;canary&lt;/strong&gt; sem afetar totalmente a versão &lt;strong&gt;stable&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Apresentando o Custom Resource Definition (CRD)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;O projeto em questão está hospedado nesse &lt;a href="https://github.com/oliveiraxavier/canary-crd" rel="noopener noreferrer"&gt;repositório do GitHub&lt;/a&gt;. Trata-se de um CRD que nomeei CanaryDeployment. Esse CRD junto ao controlador, permite que, ao invés de gerenciarmos manualmente versões de uma aplicação, tudo ocorra de forma automática.&lt;/p&gt;

&lt;p&gt;Esse projeto foi baseado na proposta do &lt;a href="https://argoproj.github.io/argo-rollouts/" rel="noopener noreferrer"&gt;Argo Rollouts&lt;/a&gt; e após algumas discussões técnicas com amigos, um pouco de estudo e também experiências obtidas no dia a dia. &lt;/p&gt;

&lt;p&gt;Importante frisar que ele funciona baseado no Virtual Services do Istio, logo, ter o Istio instalado no seu cluster Kubernetes, é um requisito. Para instalar o Istio, você pode se basear em um desses links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://istio.io/latest/docs/setup/getting-started/" rel="noopener noreferrer"&gt;https://istio.io/latest/docs/setup/getting-started/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://istio.io/latest/docs/setup/install/istioctl/" rel="noopener noreferrer"&gt;https://istio.io/latest/docs/setup/install/istioctl/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://istio.io/latest/docs/setup/install/helm/" rel="noopener noreferrer"&gt;https://istio.io/latest/docs/setup/install/helm/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Certifique-se de ter instalado também o kubectl. Você pode instalar seguindo &lt;a href="https://kubernetes.io/pt-br/docs/tasks/tools/install-kubectl-linux/" rel="noopener noreferrer"&gt;esse link&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1 - Instalando o CRD  e o controlador&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl apply -f https://raw.githubusercontent.com/oliveiraxavier/canary-crd/refs/heads/master/dist/install.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;2 - Testando a instalação&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;1 - Digite no seu terminal&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl api-resources | grep canarydeployments
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Você deverá ver algo como:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fovbcsipiwzhs4tl706rc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fovbcsipiwzhs4tl706rc.png" alt="kubectl api-resources | grep canarydeployments" width="800" height="32"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;2 - Digite no seu terminal&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl get deployments  -n canary-deployment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Você deverá ver algo como:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdl00fy2zy1568toe33kv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdl00fy2zy1568toe33kv.png" alt="kubectl get deployments  -n canary-deployment" width="783" height="52"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3 - Instalando o Nginx para utilizar como exemplo&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Para maior praticidade, utilizarei como exemplo o namespace &lt;strong&gt;default&lt;/strong&gt;. &lt;br&gt;
&lt;em&gt;1 - Digite no seu terminal&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl apply -f https://raw.githubusercontent.com/oliveiraxavier/canary-crd/refs/heads/master/config/samples/sample-deploy/deploy-v1.yaml -n default
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esse manifesto instalará o nginx 1.26 no seu cluster junto a um service, um destination rule, um virtual service e um configmap.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;2 - Digite no seu terminal&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl get deploy nginx -n default
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Você deverá ver algo como:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqdhz6fpu2hk43getvyvx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqdhz6fpu2hk43getvyvx.png" alt="kubectl get deploy nginx -n default" width="800" height="49"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4 - Simulando as requisições&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Vamos enviar algumas requisições para nossa aplicação e mais abaixo, veremos a "mágica" acontecendo. Para isso vamos subir um pod e através desse pod, iremos enviar várias requisições para testar o comportamento.&lt;/p&gt;

&lt;p&gt;Crie um arquivo chamado nginx-pod.yaml com o seguinte conteúdo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: v1
kind: Pod
metadata:
  name: nginx-curl
spec:
  containers:
  - name: nginx-curl
    image: nginx:1.26-alpine
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 80

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;1 - Digite no seu terminal&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl apply -f nginx-pod.yaml -n default

kubectl exec --stdin --tty nginx-curl -- /bin/sh

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;2 -Agora, dentro do container, digite:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;while true;do curl -s "nginx-svc:80/not-found" \
| grep  "nginx/" \
| sed  's/^.*nginx\/\(.*\)&amp;lt;.*$/\1/' &amp;amp;&amp;amp; sleep 3; done
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deixe esse terminal aberto enquanto seguimos com o post.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5 - Utilizado o CRD&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;1 - Digite no seu terminal&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl apply -f 
https://raw.githubusercontent.com/oliveiraxavier/canary-crd/refs/heads/master/config/samples/apps_v1alpha1_canarydeployment.yaml -n default
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;2 - Digite no seu terminal&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl get canarydeployment canarydeployment-1 -n default
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Você deverá ver algo como:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpokph63oqwye2xv7lwg2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpokph63oqwye2xv7lwg2.png" alt="kubectl get canarydeployment canarydeployment-1 -n default" width="800" height="58"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Se tudo correu bem até aqui, você terá 2 deployments no seu cluster, um &lt;strong&gt;nginx na versão 1.26-alpine&lt;/strong&gt; e um &lt;strong&gt;nginx-canary na versão 1.27-alpine&lt;/strong&gt;. Confira digitando no seu terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl get deployments -n default

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ypgkdjpdbqck515pwvs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ypgkdjpdbqck515pwvs.png" alt="kubectl get deployments -n default" width="800" height="49"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6 - Testando as requisições&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Retorne ao terminal que você utilizou no tópico 4 e observe a saída. Você verá algumas saídas 1.27.N e várias 1.26.N e de acordo com o tempo as saídas 1.27.N irão aparecer mais vezes. Isso demonstra que o controlador funcionou e alterou o virtual service para ir modificando o tráfego entre a versão &lt;strong&gt;stable&lt;/strong&gt; e a versão &lt;strong&gt;canary&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7 - Analisando o conteúdo do manifesto de canarydeployment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg6kpnke4yx3nmjeo7m8e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg6kpnke4yx3nmjeo7m8e.png" alt="Imagem canarydeployment" width="800" height="605"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Nas linhas 1 e 2, declaramos que iremos utilizar a &lt;strong&gt;apiVersion: mox.app.br/v1alpha1&lt;/strong&gt; e o &lt;strong&gt;kind: CanaryDeployment&lt;/strong&gt; do CRD, tema desse post.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Na linha 6 declaramos que o nome da aplicação (deployment) que utilizaremos para aplicar a estratégia de canary deployment.&lt;br&gt;
As linhas 8 e 9, respectivamente, tratam da versão atual (stable) e a nova versão (canary).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Na linha 11 declaramos o nome do &lt;strong&gt;VirtualService&lt;/strong&gt;. Isso será utilizado pelo controlador gerenciar os ”pesos” entre as versões stable e canary.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Na linha 12 declaramos &lt;strong&gt;configMapNames&lt;/strong&gt;. O controlador irá utilizar para configurar o configmap a ser utilizado pela deployment na versão canary. Você pode declarar mais de um configmap.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Na linha 15 declaramos &lt;strong&gt;secretNames&lt;/strong&gt;. O controlador irá utilizar para configurar a secret a ser utilizada pela versão canary. Você pode declarar mais de uma secret.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Na linha 17 a 27 declaramos, respectivamente, o peso e o tempo que desejamos que cada peso seja mantido (em segundos, no caso).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Abaixo um pequeno resumo de cada passo declarado no manifesto:&lt;/p&gt;

&lt;p&gt;1 - 10% das requisições irão para a versão canary&amp;nbsp;e 90%&amp;nbsp; das requisições irão para a versão stable, durante 60 segundos.&lt;/p&gt;

&lt;p&gt;2 - 25% das requisições irão para a versão canary&amp;nbsp;e 75%&amp;nbsp; das requisições irão para a versão stable, durante 2 minutos.&lt;/p&gt;

&lt;p&gt;3 - 50% das requisições irão para a versão canary&amp;nbsp;e 50%&amp;nbsp; das requisições irão para a versão stable, durante 5 minutos.&lt;/p&gt;

&lt;p&gt;4 - 100% das requisições irão para a versão canary ou seja, a versão canary se torna a versão stable.&lt;/p&gt;

&lt;p&gt;Observação. &lt;br&gt;
É possível declarar as pausas em segundos, minutos e horas (seconds, minutes e hours)&lt;/p&gt;

&lt;p&gt;Enquanto a versão canary existir, você pode usar os comandos abaixo para visualizar as alterações:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl get vs nginx -n default -o yaml
kubectl get deploy --l="run-type: stable" -n default
kubectl get deploy --l="run-type: canary" -n default
kubectl get canarydeployment canarydeployment-1 -n default -o yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Abaixo, podemos visualizar a estrutura do manifesto de Canary Deployment enquanto o controlador estiver gerenciando as versões stable e canary:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw685qdbs9nq1pghhx4gh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw685qdbs9nq1pghhx4gh.png" alt="manifesto de Canary Deployment" width="799" height="914"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8 - Limpando a bagunça&lt;/strong&gt;&lt;br&gt;
Caso deseje remover o CRD, execute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl delete -f https://raw.githubusercontent.com/oliveiraxavier/canary-crd/refs/heads/master/dist/install.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;9 - That's all folks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Após a execução de todos os passos(steps) que definimos no nosso manifesto de exemplo, a versão canary se tornará a versão stable e o que era a versão stable, será removida do cluster.&lt;/p&gt;

&lt;p&gt;Claro que, ao menos nesse momento, o projeto é algo bem simples. O que eu almejava era aplicar os conhecimentos que adquiri recentemente em Golang e deu certo e postar aqui, penso ser uma forma interessante de compartilhar conhecimento.&lt;/p&gt;

&lt;p&gt;Agradecimentos aos gigantes Lucas Lobo, Edson Rocha, Adriano Ohana, Pedro Adalberto, Guilherme Lacerda e Thiago Pereira. Todos que me ajudaram de algum modo para chegar até a escrita desse post.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
