<?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: Pedro Oliveira</title>
    <description>The latest articles on DEV Community by Pedro Oliveira (@pramos).</description>
    <link>https://dev.to/pramos</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%2F3778530%2F8adc1f8f-3bad-45e1-897a-e5e9073b2ae8.jpeg</url>
      <title>DEV Community: Pedro Oliveira</title>
      <link>https://dev.to/pramos</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pramos"/>
    <language>en</language>
    <item>
      <title>Kubernetes sem Cloud Provider (Parte 2): Criando Operators em Go para automação e self-service de plataforma</title>
      <dc:creator>Pedro Oliveira</dc:creator>
      <pubDate>Mon, 25 May 2026 23:37:29 +0000</pubDate>
      <link>https://dev.to/pramos/kubernetes-sem-cloud-provider-parte-2-criando-operators-em-go-para-automacao-e-self-service-de-1pp9</link>
      <guid>https://dev.to/pramos/kubernetes-sem-cloud-provider-parte-2-criando-operators-em-go-para-automacao-e-self-service-de-1pp9</guid>
      <description>&lt;p&gt;Uma das maiores cargas cognitivas quando falamos em Kubernetes é a configuração dos vários componentes necessários para entregar valor real ao cluster. Em outras palavras, deparamo-nos com uma quantidade massiva de YAMLs para configurar Custom Resources (CRs), Custom Resource Definitions (CRDs), Roles e tudo o que é necessário para que um serviço tenha um Ingress com TLS, por exemplo.&lt;/p&gt;

&lt;p&gt;Isso ficou ainda mais evidente para mim durante os meus estudos de Kubernetes sem as abstrações fornecidas pelos Cloud Providers (assunto que cobrimos na Parte 1). Para resolver esse problema, decidi aprofundar o conhecimento nas maneiras pelas quais o Kubernetes nos permite automatizar essas operações e criar abstrações &lt;em&gt;self-service&lt;/em&gt; através de &lt;strong&gt;Custom Operators&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Neste artigo, vou documentar a criação de dois operators desenvolvidos em Go que fazem a ponte entre o &lt;code&gt;HashiCorp Vault&lt;/code&gt; e os operadores de mercado &lt;code&gt;External Secrets Operator&lt;/code&gt; e &lt;code&gt;Cert-Manager&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;vaultreaver&lt;/code&gt;:&lt;/strong&gt; Configura a integração e os limites de segurança entre o Kubernetes e a API externa do Vault.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;platform-operator&lt;/code&gt;:&lt;/strong&gt; Atua como o centralizador e orquestrador de configurações dentro do cluster.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Motivação
&lt;/h2&gt;

&lt;p&gt;Durante a configuração manual dos componentes responsáveis pela comunicação com o Vault, percebi um padrão repetitivo de tarefas que gerava muita fricção:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Criar uma &lt;code&gt;ServiceAccount&lt;/code&gt; no Kubernetes.&lt;/li&gt;
&lt;li&gt;Criar uma &lt;code&gt;Vault Role&lt;/code&gt; para permitir a generation de tokens atrelados a essa &lt;code&gt;ServiceAccount&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Criar uma política de acesso no Vault (&lt;code&gt;Vault Policy&lt;/code&gt;) e vinculá-la à Role.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Essa operação é mandatória para que os componentes do cluster se comuniquem de forma segura com o Vault (utilizando o token JWT da &lt;code&gt;ServiceAccount&lt;/code&gt; para se autenticarem). &lt;/p&gt;

&lt;p&gt;Para entender a fundo a mecânica desse ecossistema, resolvi construir um operador que utilizasse esse mesmo mecanismo de autenticação nativa, mas de forma totalmente automatizada. O objetivo era esconder essa complexidade do usuário final da plataforma, automatizando todo o &lt;em&gt;boilerplate&lt;/em&gt; de segurança e gerando os tokens necessários para as operações de cada componente.&lt;/p&gt;




&lt;h2&gt;
  
  
  Como tudo funciona por baixo dos panos
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Nota: Para esse fluxo funcionar, o Vault já deve estar configurado previamente com o método de autenticação de Kubernetes ativo (&lt;code&gt;auth/kubernetes&lt;/code&gt;). O nosso operador utiliza a sua própria identidade no cluster para interagir com a API do Vault e criar as novas permissões.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A configuração começa pelo &lt;strong&gt;&lt;code&gt;vaultreaver&lt;/code&gt;&lt;/strong&gt;. Ele é responsável por gerenciar o ciclo de vida dos recursos fora do cluster (na API do Vault) e recebe dois Custom Resources principais:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;VaultPolicy&lt;/code&gt;:&lt;/strong&gt; Declara a política de segurança com as permissões que a aplicação/componente terá dentro do Vault.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;VaultKubernetesRoleBinding&lt;/code&gt;:&lt;/strong&gt; Faz o vínculo (&lt;em&gt;binding&lt;/em&gt;) da &lt;code&gt;VaultPolicy&lt;/code&gt; com a &lt;code&gt;ServiceAccount&lt;/code&gt; do Kubernetes e a respectiva &lt;code&gt;VaultRole&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;O exemplo abaixo demonstra a criação de uma permissão declarativa de leitura no path &lt;code&gt;kv&lt;/code&gt; do Vault para um namespace de aplicação:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;security.platform.io/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;VaultPolicy&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx-deployment-external-secret&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx-apps&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;path "kv/data/app-teste/secret-secreto" {&lt;/span&gt;
      &lt;span class="s"&gt;capabilities = ["read"]&lt;/span&gt;
    &lt;span class="s"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;vaultPolicyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx-deployment-external-secret-policy&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;security.platform.io/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;VaultKubernetesRoleBinding&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx-deployment-external-secret&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx-apps&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;audience&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vault&lt;/span&gt;
  &lt;span class="na"&gt;authMount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kubernetes&lt;/span&gt;
  &lt;span class="na"&gt;boundNamespaces&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;nginx-apps&lt;/span&gt;
  &lt;span class="na"&gt;boundServiceAccounts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;nginx-deployment-external-secret&lt;/span&gt;
  &lt;span class="na"&gt;roleName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx-deployment-external-secret&lt;/span&gt;
  &lt;span class="na"&gt;tokenPolicies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;nginx-deployment-external-secret-policy&lt;/span&gt;
  &lt;span class="na"&gt;tokenTTL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1h&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depois que o &lt;code&gt;vaultreaver&lt;/code&gt; estabelece com sucesso a ponte de segurança com o Vault, o &lt;code&gt;platform-operator&lt;/code&gt; assume a responsabilidade pelas automações dentro do cluster.&lt;/p&gt;

&lt;p&gt;A API do &lt;code&gt;platform-operator&lt;/code&gt; é desenhada para ser mais ampla, aceitando contratos simplificados focados na experiência do desenvolvedor (Self-Service). O exemplo a seguir demonstra o manifesto &lt;code&gt;VaultCertificate&lt;/code&gt;, que abstrai todo o setup necessário para provisionar TLS via Cert-Manager com o backend de PKI do Vault.&lt;br&gt;
Ao receber este único CR, o operador gera dinamicamente os recursos internos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A ServiceAccount configurada com o RBAC necessário para o fluxo de certificados.&lt;/li&gt;
&lt;li&gt;As regras do Cert-Manager (como o ClusterIssuer ou Issuer apontando para o Vault).&lt;/li&gt;
&lt;li&gt;O recurso final de Certificate que dispara a emissão real do certificado TLS.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;security.platform.io/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;VaultCertificate&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx-deployment-tls&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx-apps&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;vaultUrl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;//172.18.0.12&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;8200&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;&lt;span class="s"&gt;(http://172.18.0.12:8200)&lt;/span&gt;
  &lt;span class="na"&gt;authPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/v1/auth/kubernetes&lt;/span&gt;
  &lt;span class="na"&gt;vaultRole&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx-deployment-tls-role&lt;/span&gt;
  &lt;span class="na"&gt;pkiPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pki/sign/internal-dot-infra&lt;/span&gt;
  &lt;span class="na"&gt;commonName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-app.internal.infra&lt;/span&gt;
  &lt;span class="na"&gt;dnsNames&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;my-app.internal.infra&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;my-app-teste.internal.infra&lt;/span&gt;
  &lt;span class="na"&gt;SecretName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx-deployment-tls&lt;/span&gt;
  &lt;span class="na"&gt;certManagerServiceAccount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cert-manager"&lt;/span&gt;
  &lt;span class="na"&gt;certManagerNamespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cert-manager"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Da mesma forma, o operador possui controllers dedicados a simplificar o uso do &lt;code&gt;ExternalSecrets&lt;/code&gt;, encapsulando e criando de forma automatizada o &lt;code&gt;SecretStore&lt;/code&gt; e o &lt;code&gt;ExternalSecret&lt;/code&gt; correspondente a partir de uma interface limpa.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testes e Validação
&lt;/h2&gt;

&lt;p&gt;Para validar o comportamento do ecossistema e garantir a idempotência dos controllers em Go, montei um laboratório completo que pode ser conferido neste repositório de demonstração:&lt;br&gt;
&lt;a href="https://github.com/pedrohro1992/homelab-app-demo" rel="noopener noreferrer"&gt;https://github.com/pedrohro1992/homelab-app-demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;O objetivo do laboratório foi:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Garantir a automação e o provisionamento de um certificado TLS válido via Cert-Manager usando o nosso platform-operator.&lt;/li&gt;
&lt;li&gt;Injetar credenciais sensíveis via ExternalSecrets vindas diretamente do Vault de forma totalmente declarativa.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;O cenário de teste consiste no deployment de uma aplicação Nginx. Através do Ingress configurado com o TLS gerado e com o segredo resolvido pelo operador, conseguimos expor com sucesso a aplicação. Ao acessar a página de diagnóstico, é possível validar visualmente que todas as variáveis de ambiente baseadas no segredo do Vault (como o nosso VALOR_SECRET) foram injetadas perfeitamente no container em tempo de execução.&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%2F4ru9pv8o8nw4buyzkqry.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%2F4ru9pv8o8nw4buyzkqry.png" alt=" " width="800" height="615"&gt;&lt;/a&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%2Fb8zc37mhuhgi6ek1h0ia.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%2Fb8zc37mhuhgi6ek1h0ia.png" alt=" " width="800" height="241"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Considerações finais
&lt;/h1&gt;

&lt;p&gt;Apesar de existirem diversas soluções prontas para integração entre Kubernetes e Vault, implementar esses Operators foi um excelente exercício para compreender:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reconciliação de recursos no Kubernetes&lt;/li&gt;
&lt;li&gt;Desenvolvimento de Operators com Kubebuilder&lt;/li&gt;
&lt;li&gt;Fluxos de autenticação Kubernetes ↔ Vault&lt;/li&gt;
&lt;li&gt;Automação de plataforma&lt;/li&gt;
&lt;li&gt;Criação de abstrações de self-service&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Além do aprendizado técnico, o projeto também ajudou a reduzir significativamente a complexidade operacional envolvida na configuração manual desses componentes.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>go</category>
      <category>devops</category>
      <category>terraform</category>
    </item>
    <item>
      <title>Kubernetes sem Cloud Provider (Parte 1): CNI, Storage Dinâmico, Ingress e Arquitetura Terraform</title>
      <dc:creator>Pedro Oliveira</dc:creator>
      <pubDate>Wed, 18 Feb 2026 02:40:16 +0000</pubDate>
      <link>https://dev.to/pramos/kubernetes-sem-cloud-provider-parte-1-cni-storage-dinamico-ingress-e-arquitetura-terraform-eof</link>
      <guid>https://dev.to/pramos/kubernetes-sem-cloud-provider-parte-1-cni-storage-dinamico-ingress-e-arquitetura-terraform-eof</guid>
      <description>&lt;p&gt;Durante muito tempo — e ainda hoje em muitos cenários on-premises — o Kubernetes foi (e é) executado sem integrações nativas de cloud provider. Antes de EKS, GKE e AKS se tornarem padrão, era responsabilidade do operador configurar explicitamente rede, armazenamento, ingress, identidade e registry.&lt;/p&gt;

&lt;p&gt;Este artigo é a primeira parte de uma série onde documento a construção de um cluster executando sobre &lt;code&gt;Kubernetes in Docker (KinD)&lt;/code&gt;, focando em:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configuração de CNI (rede de pods)&lt;/li&gt;
&lt;li&gt;Provisionamento dinâmico de storage com CSI&lt;/li&gt;
&lt;li&gt;Configuração de Ingress&lt;/li&gt;
&lt;li&gt;Estruturação e automação completa via Terraform&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;A parte de TLS com Vault e cert-manager ficará para o próximo artigo, pois ainda estou finalizando essa integração.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  🎯 Motivação
&lt;/h1&gt;

&lt;p&gt;Em ambientes gerenciados, temos automaticamente:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CNI integrada à VPC&lt;/li&gt;
&lt;li&gt;Provisionamento dinâmico de discos&lt;/li&gt;
&lt;li&gt;Load Balancers&lt;/li&gt;
&lt;li&gt;Integrações de identidade&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mas nada disso é inerente ao Kubernetes. São componentes adicionais configurados pelo provedor.&lt;/p&gt;

&lt;p&gt;A proposta deste projeto foi remover essas abstrações e implementar manualmente as capacidades essenciais de um cluster funcional.&lt;/p&gt;

&lt;p&gt;Você não precisa saber mecânica para dirigir.&lt;br&gt;
Mas quando o carro para no meio da estrada, esse conhecimento faz diferença.&lt;/p&gt;




&lt;h1&gt;
  
  
  🧱 Arquitetura do Projeto
&lt;/h1&gt;

&lt;p&gt;O cluster foi criado com KinD e inicialmente não possuía:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CNI funcional&lt;/li&gt;
&lt;li&gt;Provisionador de storage&lt;/li&gt;
&lt;li&gt;Ingress Controller&lt;/li&gt;
&lt;li&gt;Integrações externas&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A partir disso, foram adicionados os seguintes componentes.&lt;/p&gt;

&lt;h2&gt;
  
  
  🌐 Rede — CNI com Calico
&lt;/h2&gt;

&lt;p&gt;Para permitir comunicação entre pods e nós, foi instalado o:&lt;br&gt;
&lt;code&gt;Calico&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Funções habilitadas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Atribuição de IPs aos pods&lt;/li&gt;
&lt;li&gt;Encapsulamento VXLAN&lt;/li&gt;
&lt;li&gt;Network Policies&lt;/li&gt;
&lt;li&gt;Comunicação inter-node&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sem CNI, pods não se comunicam — esse é o primeiro requisito real para um cluster utilizável.&lt;/p&gt;

&lt;h2&gt;
  
  
  💾 Storage Dinâmico — OpenEBS
&lt;/h2&gt;

&lt;p&gt;Para substituir o papel que discos gerenciados fariam em cloud, implementei:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;OpenEBS&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Com isso foi possível:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Criar StorageClasses&lt;/li&gt;
&lt;li&gt;Provisionar PVCs dinamicamente&lt;/li&gt;
&lt;li&gt;Utilizar backend baseado em armazenamento local&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Isso reproduz o comportamento esperado por aplicações stateful em qualquer ambiente Kubernetes.&lt;/p&gt;




&lt;h1&gt;
  
  
  🧠 Estrutura Terraform — Organização Modular
&lt;/h1&gt;

&lt;p&gt;O repositório do módulo:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/pedrohro1992/tf-module-kind-cluster.git" rel="noopener noreferrer"&gt;https://github.com/pedrohro1992/tf-module-kind-cluster.git&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;O módulo  &lt;code&gt;tf-module-kind-blueprint&lt;/code&gt; atua como um módulo agregador.&lt;/p&gt;

&lt;p&gt;Essa técnica é conhecida como:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root Module Composition Pattern (ou Aggregator Module Pattern)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Nesse modelo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;O módulo raiz não implementa recursos diretamente&lt;/li&gt;
&lt;li&gt;Ele compõe módulos especializados&lt;/li&gt;
&lt;li&gt;Centraliza variáveis e dependências&lt;/li&gt;
&lt;li&gt;Expõe uma interface limpa para consumo
Cada componente (CNI, OpenEBS, NGINX Ingress) está encapsulado em seu próprio módulo Terraform.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vantagens arquiteturais:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Separação clara de responsabilidades&lt;/li&gt;
&lt;li&gt;Modularidade&lt;/li&gt;
&lt;li&gt;Reutilização&lt;/li&gt;
&lt;li&gt;Organização escalável&lt;/li&gt;
&lt;li&gt;Manutenção facilitada&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  🧩 Desafios Técnicos
&lt;/h1&gt;

&lt;p&gt;Alguns pontos relevantes durante a implementação:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Coordenação entre providers (Kubernetes e Helm)&lt;/li&gt;
&lt;li&gt;Garantia de ordem correta de dependências&lt;/li&gt;
&lt;li&gt;Idempotência dos módulos&lt;/li&gt;
&lt;li&gt;Encadeamento de outputs&lt;/li&gt;
&lt;li&gt;Inicialização do provider Kubernetes apenas após cluster estar disponível&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Automatizar componentes de infraestrutura dentro de um cluster recém-criado exige atenção especial ao ciclo de vida dos recursos.&lt;/p&gt;




&lt;h1&gt;
  
  
  ⚠️ Disclaimer
&lt;/h1&gt;

&lt;p&gt;Tenho um senso de humor peculiar.&lt;/p&gt;

&lt;p&gt;O nome fictício da “empresa” usada como estudo de caso no código é:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cacetinho-SA&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sim, há referências a isso no código.&lt;br&gt;
É intencional. 😄&lt;/p&gt;

&lt;p&gt;💽 &lt;strong&gt;PVC&lt;/strong&gt; provisionado dinamicamente pelo OpenEBS&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%2Fj9oasy9zgmagfdk8mz5h.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%2Fj9oasy9zgmagfdk8mz5h.png" alt=" " width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🔮 Próximos Passos
&lt;/h3&gt;

&lt;p&gt;Nos próximos artigos da série:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configuração de Identity Provider&lt;/li&gt;
&lt;li&gt;OIDC e Workload Identity&lt;/li&gt;
&lt;li&gt;DNS automático do Ingress com ExternalDNS&lt;/li&gt;
&lt;li&gt;TLS automático no Ingress com:&lt;/li&gt;
&lt;li&gt;cert-manager&lt;/li&gt;
&lt;li&gt;HashiCorp Vault como CA&lt;/li&gt;
&lt;li&gt;Evolução para Gateway API&lt;/li&gt;
&lt;li&gt;Service Mesh multi-cluster com Istio&lt;/li&gt;
&lt;li&gt;Empacotamento como Composition Function no Crossplane&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(E provavelmente mais algumas ideias que surgirem no caminho.)&lt;/p&gt;




&lt;h1&gt;
  
  
  📌 Conclusão
&lt;/h1&gt;

&lt;p&gt;Rodar Kubernetes sem cloud provider não é algo exótico — foi assim que muitos clusters rodaram por anos, e ainda rodam em ambientes bare-metal, edge e data centers privados.&lt;/p&gt;

&lt;p&gt;Cloud providers apenas empacotam e integram:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CNI&lt;/li&gt;
&lt;li&gt;CSI&lt;/li&gt;
&lt;li&gt;Ingress&lt;/li&gt;
&lt;li&gt;Identidade&lt;/li&gt;
&lt;li&gt;PKI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Implementar manualmente essas camadas muda completamente o nível de entendimento sobre a plataforma.&lt;/p&gt;

&lt;p&gt;Este projeto não foi apenas sobre “fazer funcionar”, mas sobre compreender o que está acontecendo por baixo das abstrações.&lt;/p&gt;

&lt;p&gt;No próximo artigo, entro na camada de identidade e TLS — que está se mostrando um desafio interessante.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>devops</category>
      <category>platformengineering</category>
      <category>terraform</category>
    </item>
  </channel>
</rss>
