Aula 13 – Persistência e Resiliência com PV, PVC e Probes no K8S

Aula 13 – Persistência e Resiliência com PV, PVC e Probes no K8S

Voltar para página principal do blog

Todas as aulas desse curso

Aula 12                                           Aula 14

Redes Sociais do Código Fluente:

facebook

 


Scarlett Finch

Scarlett Finch é uma influenciadora virtual criada com IA.

Ela é 🎤 cantora e 🎶compositora pop britânica.

Siga a Scarlett Finch no Instagram:

facebook

 


Conecte-se comigo!

LinkedIn: Fique à vontade para me adicionar no LinkedIn.

Ao conectar-se comigo, você terá acesso a atualizações regulares sobre desenvolvimento web, insights profissionais e oportunidades de networking no setor de tecnologia.

GitHub: Siga-me no GitHub para ficar por dentro dos meus projetos mais recentes, colaborar em código aberto ou simplesmente explorar os repositórios que eu contribuo, o que pode ajudar você a aprender mais sobre programação e desenvolvimento de software.

Recursos e Afiliados

Explorando os recursos abaixo, você ajuda a apoiar nosso site.

Somos parceiros afiliados das seguintes plataformas:

  • https://heygen.com/ – Eleve a produção de seus vídeos com HeyGen! Com esta plataforma inovadora, você pode criar vídeos envolventes utilizando avatares personalizados, ideal para quem busca impactar e conectar com audiências em todo o mundo. HeyGen transforma a maneira como você cria conteúdo, oferecendo ferramentas fáceis de usar para produzir vídeos educativos, demonstrações de produtos e muito mais. Descubra o poder de comunicar através de avatares interativos e traga uma nova dimensão para seus projetos. Experimente HeyGen agora e revolucione sua forma de criar vídeos!
  • letsrecast.ai – Redefina a maneira como você consome artigos com Recast. Esta plataforma transforma artigos longos em diálogos de áudio que são informativos, divertidos e fáceis de entender. Ideal para quem está sempre em movimento ou busca uma forma mais conveniente de se manter informado. Experimente Recast agora.
  • dupdub.com – Explore o universo do marketing digital com DupDub. Esta plataforma oferece ferramentas inovadoras e soluções personalizadas para elevar a sua estratégia de marketing online. Ideal para empresas que buscam aumentar sua visibilidade e eficiência em campanhas digitais. Descubra mais sobre DupDub.
  • DeepBrain AI Studios – Revolucione a criação de conteúdo com a tecnologia de inteligência artificial da DeepBrain AI Studios. Esta plataforma avançada permite que você crie vídeos interativos e apresentações utilizando avatares digitais gerados por IA, que podem simular conversas reais e interações humanas. Perfeito para educadores, criadores de conteúdo e empresas que querem inovar em suas comunicações digitais. Explore DeepBrain AI Studios.
  • Audyo.ai – Transforme a maneira como você interage com conteúdo auditivo com Audyo.ai. Esta plataforma inovadora utiliza inteligência artificial para criar experiências de áudio personalizadas, melhorando a acessibilidade e a compreensão de informações através de podcasts, transcrições automáticas e síntese de voz avançada. Ideal para profissionais de mídia, educadores e qualquer pessoa que deseje acessar informações auditivas de maneira mais eficiente e envolvente. Descubra Audyo.ai e suas possibilidades.
  • Acoust.io – Transforme sua produção de áudio com Acoust.io. Esta plataforma inovadora fornece uma suite completa de ferramentas para criação, edição e distribuição de áudio, ideal para artistas, produtores e empresas de mídia em busca de excelência e inovação sonora. Acoust.io simplifica o processo de levar suas ideias à realidade, oferecendo soluções de alta qualidade que elevam seus projetos de áudio. Experimente Acoust.io agora e descubra um novo patamar de possibilidades para seu conteúdo sonoro.
  • Hostinger – Hospedagem web acessível e confiável. Ideal para quem busca soluções de hospedagem de sites com excelente custo-benefício e suporte ao cliente robusto. Saiba mais sobre a Hostinger.
  • Digital Ocean – Infraestrutura de nuvem para desenvolvedores. Oferece uma plataforma de nuvem confiável e escalável projetada especificamente para desenvolvedores que precisam de servidores virtuais, armazenamento e networking. Explore a Digital Ocean.
  • One.com – Soluções simples e poderosas para o seu site. Uma escolha ideal para quem busca registrar domínios, hospedar sites ou criar presença online com facilidade e eficiência. Visite One.com.

Educação e Networking

Amplie suas habilidades e sua rede participando de cursos gratuitos e comunidades de desenvolvedores:

Canais do Youtube

Explore nossos canais no YouTube para uma variedade de conteúdos educativos e de entretenimento, cada um com um foco único para enriquecer sua experiência de aprendizado e lazer.

Toti

Toti: Meu canal pessoal, onde posto clips artesanais de músicas que curto tocar, dicas de teoria musical, entre outras coisas.

Lofi Music Zone Beats

Lofi Music Zone Beats: O melhor da música Lofi para estudo, trabalho e relaxamento, criando o ambiente perfeito para sua concentração.

Backing Track / Play-Along

Backing Track / Play-Along: Acompanhe faixas instrumentais para prática musical, ideal para músicos que desejam aprimorar suas habilidades.

Código Fluente

Código Fluente: Aulas gratuitas de programação, devops, IA, entre outras coisas.

Putz!

Putz!: Canal da banda Putz!, uma banda virtual, criada durante a pandemia com mais 3 amigos, Fábio, Tatá e Lula.

Scarlett Finch

Scarlett Finch: Cantora e influenciadora criada com IA.

PIX para doações

PIX Nubank

PIX Nubank

Repositório do App (Backend e Frontend)


Aula 13 – Persistência e Resiliência com PV, PVC e Probes no Kubernetes

Implementação de um Sistema de Autenticação de Usuários

Essa é uma aula avançada de DevOps, em especial, Kubernetes (K8S) e Terraform.

Kubernetes:

  • Configuração e uso de Pods para frontend e backend.
  • LoadBalancer para distribuir o tráfego.
  • Configuração de Persistent Volumes (PV) e Persistent Volume Claims (PVC) para armazenamento persistente.
  • Implementação de Liveness e Readiness Probes para monitoramento e saúde dos contêineres.

DevOps:

  • Orquestração e deploy de aplicações distribuídas.
  • Configuração de infraestrutura como código, o que é essencial em práticas de DevOps avançadas.
  • Integração de serviços externos (como o Gmail para notificações de email) com uma aplicação baseada em microserviços.
  • Gerenciamento de tráfego e comunicação entre diferentes componentes e serviços no cluster.

Esses tópicos são avançados, exigindo um bom entendimento de Kubernetes e DevOps para configurar, manter e otimizar a infraestrutura.

Introdução ao Gerenciamento de Volumes

No Kubernetes, os volumes são essenciais para aplicações que precisam armazenar dados de forma duradoura.

Neste caso, vamos configurar um sistema de autenticação de usuários em que a persistência de dados é fundamental para manter informações de sessão e segurança dos dados do usuário.

Descrição do Sistema de Autenticação

O sistema é composto por um backend em Golang usando o framework Fiber e um frontend em React.

Esta combinação proporciona uma interface de usuário robusta e um backend eficiente e seguro, ideal para gerenciar a autenticação e recuperação de senha dos usuários.

Diagrama da Arquitetura

Diagrama da Arquitetura

Configuração do Ambiente com Terraform

Para facilitar a criação e o gerenciamento dos recursos, vamos usar o Terraform para automatizar toda a configuração no Kubernetes, incluindo a criação do banco de dados MySQL na DigitalOcean e a configuração do serviço de email com Gmail para recuperação de senha.

Provisionamento do MySQL com Terraform na Digital Ocean

Usaremos o Terraform para criar uma instância do MySQL diretamente na DigitalOcean.

Isso garante que os dados dos usuários sejam armazenados de forma persistente e segura, gerenciados automaticamente pelo Kubernetes.

Gmail como Serviço de Email

Para o envio de emails de recuperação de senha, vamos substituir o MailHog pelo Gmail.

A integração com o Gmail será automatizada com o Terraform, usando credenciais seguras para que o sistema possa enviar emails transacionais com confiabilidade.

Dockerização do Backend e Frontend

Dockerfile para o Backend

Aqui está o Dockerfile do backend, onde configuramos a aplicação em Golang para rodar em um ambiente Docker. Esse arquivo cria uma imagem leve e eficiente para o backend.

codigo-fluente-fiber-tutorial/devops/Dockerfile-backend


# Nome do arquivo: Dockerfile-backend
FROM golang:1.18 as builder
WORKDIR /app

# Copia os arquivos de gerenciamento de dependências primeiro para aproveitar o cache do Docker
COPY backend/fiber-project/go.mod backend/fiber-project/go.sum ./

# Baixa as dependências
RUN go mod download

# Copia o resto dos arquivos do projeto
COPY backend/fiber-project/ ./

# Compila o aplicativo
RUN go build -o main .

# Etapa final, usando uma imagem limpa para reduzir o tamanho da imagem final
FROM gcr.io/distroless/base-debian10
WORKDIR /app
COPY --from=builder /app/main .

# Comando para executar o aplicativo
CMD ["./main"]

Comando para construir a imagem Docker do Backend

Rode de dentro da pasta raiz (codigo-fluente-fiber-tutorial)

Verifique se o Docker tá funcionando no seu sistema:


docker info

Caso esteja parado, use o comando abaixo para iniciar ele.

No powershell:


docker-machine start default

No linux ou mac:


sudo systemctl start docker

Construção das Imagens Docker

Esse comando abaixo, ativa o BuildKit, que é uma versão mais recente e eficiente do mecanismo de build do Docker.

No powershell:


$env:DOCKER_BUILDKIT=1

No linux ou mac:


DOCKER_BUILDKIT=1

Construção das Imagens Docker

Esse comando irá construir a imagem do backend.


docker build -f devops/Dockerfile-backend -t fiber-auth-api:v1.0 .

Ou se quiser já colocar o prefixo do seu usuário no dockerhub


docker build -f devops/Dockerfile-backend -t your-username-in-dockerhub/fiber-auth-api:v1.0 .

Dockerfile para o Frontend

Este Dockerfile cria a imagem do frontend em duas etapas.

Primeiro, ele instala as dependências e constrói a aplicação usando o Node.js.

Em seguida, configura o Nginx para servir o conteúdo estático gerado, permitindo que o frontend seja acessado por meio do servidor Nginx, que também aplica configurações personalizadas para variáveis de ambiente e segurança.

codigo-fluente-fiber-tutorial/devops/Dockerfile-frontend


# Etapa 1: Construir a aplicação
FROM node:16-alpine as build
WORKDIR /app

# Instalar dependências do sistema necessárias para o npm funcionar corretamente
RUN apk add --no-cache python3 make g++

# Copiar package.json e package-lock.json
COPY frontend/react-auth/package*.json ./

# Instalar dependências com a flag --legacy-peer-deps para evitar problemas de dependência
RUN npm ci --legacy-peer-deps --verbose

# Copiar o restante dos arquivos do projeto
COPY frontend/react-auth/ ./

# Define as variáveis de ambiente para o build
ENV REACT_APP_API_URL=/api

# Construir o aplicativo para produção
RUN npm run build

# Etapa 2: Configurar o servidor de produção usando Nginx para servir o conteúdo estático
FROM nginx:alpine

# Copiar os arquivos estáticos construídos da etapa de build
COPY --from=build /app/build /usr/share/nginx/html

# Copiar a configuração personalizada do Nginx
COPY frontend/react-auth/nginx.conf /etc/nginx/conf.d/default.conf

# Copiar o script de entrada personalizado
COPY frontend/react-auth/docker-entrypoint.sh /docker-entrypoint.sh
RUN chmod +x /docker-entrypoint.sh

# Expor a porta 80 para permitir o acesso ao servidor Nginx
EXPOSE 80

# Usar o script de entrada personalizado para substituir variáveis e iniciar o Nginx
ENTRYPOINT ["/docker-entrypoint.sh"]

# Comando para iniciar o Nginx
CMD ["nginx", "-g", "daemon off;"]

Esse comando irá construir a imagem do frontend.


docker build -f devops/Dockerfile-frontend -t auth-ui:v1.0 . 

Ou se quiser já colocar o prefixo do seu usuário no dockerhub


docker build -f devops/Dockerfile-frontend --no-cache -t your-username-in-dockerhub/auth-ui:v1.0 .

Subindo as Imagens para um Hub

Login no Docker Hub

Faça o login no seu hub, no meu caso hub.docker.com.


docker login

Quando executar esse comando, será solicitado que você insira seu nome de usuário e senha do Docker Hub.

Depois de fornecer suas credenciais, você estará logado e pronto para enviar (push) ou baixar (pull) imagens do Docker Hub.

Se você deseja logar usando um único comando sem interação, pode usar:


docker login -u seu_usuario -p sua_senha

Nota de segurança: O uso do comando acima não é recomendado em scripts ou em terminais compartilhados devido ao risco de exposição da senha.

Uma abordagem mais segura seria utilizar um docker login sem senha no terminal ou configurar o acesso através de tokens secretos ou chaves de API, se disponíveis, para automatizar processos sem expor suas credenciais diretamente.

Subindo as Imagens para um Registro de Containers

Após construir as imagens, o próximo passo é subi-las para um registro de containers, como Docker Hub ou GitHub Container Registry.

Este passo é crucial para permitir que seu ambiente de produção no Kubernetes possa acessar e utilizar estas imagens.

Comando para subir a imagem do backend

Crie a tag com o seu username no dockerhub


docker tag fiber-auth-api:v1.0 your-username-in-dockerhub/fiber-auth-api:v1.0

Agora suba a imagem


docker push your-username-in-dockerhub/fiber-auth-api:v1.0

Comando para subir a imagem do frontend

Crie a tag com o seu username no dockerhub


docker tag auth-ui:v1.0 your-username-in-dockerhub/auth-ui:v1.0

Agora suba a imagem


docker push your-username-in-dockerhub/auth-ui:v1.0

Os Projetos

O projeto de autenticação de usuários é composto por um backend desenvolvido com Fiber em Golang e um frontend feito em React com TypeScript.

Essa estrutura proporciona um sistema completo de registro e autenticação de usuários.

Para quem desejar mais detalhes do app que vamos fazer o deploy, o tutorial completo pode ser acessado aqui: Tutorial Completo.

Agora, vamos abordar um ponto extra sobre o projeto, que está fora do escopo principal da aula, mas é importante para o frontend: o arquivo de configuração Nginx.

Este arquivo define como o Nginx serve a aplicação React no ambiente de produção e como ele lida com questões de segurança e comunicação com o backend.

codigo-fluente-fiber-tutorial/frontend/react-auth/nginx.conf


server {
    listen 80;
    server_name _;
    root /usr/share/nginx/html;
    index index.html;

    # Configuração de CORS mais específica e segura
    add_header 'Access-Control-Allow-Origin' '*' always;
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
    add_header 'Access-Control-Allow-Headers' 'Origin, X-Requested-With, Content-Type, Accept, Authorization' always;
    add_header 'Access-Control-Allow-Credentials' 'true' always;

    # Headers de segurança adicionais
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-XSS-Protection "1" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # Configuração para arquivos estáticos
    location /static/ {
        expires 1y;
        add_header Cache-Control "public, max-age=31536000";
    }

    # Rota específica para reset de senha
    location ^~ /reset/ {
        try_files $uri /index.html;
    }

    location / {
        try_files $uri $uri/ @backend;
    }

    location @backend {
        proxy_pass http://auth-api-service:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        if ($request_method = 'OPTIONS') {
            add_header 'Access-Control-Allow-Origin' '*' always;
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
            add_header 'Access-Control-Allow-Headers' 'Origin, X-Requested-With, Content-Type, Accept, Authorization' always;
            add_header 'Access-Control-Allow-Credentials' 'true' always;
            add_header 'Content-Type' 'text/plain charset=UTF-8';
            add_header 'Content-Length' 0;
            return 204;
        }
    }
}

Configuração Nginx para o Frontend

O arquivo de configuração Nginx do frontend atua como um servidor web e um proxy reverso.

Ele serve o conteúdo estático da aplicação React e configura o roteamento e segurança das requisições que partem do frontend em direção ao backend.

Principais Configurações do Arquivo

  • Servir o conteúdo estático: O Nginx define o diretório raiz da aplicação com o comando root /usr/share/nginx/html, onde os arquivos estáticos (HTML, CSS, JavaScript) são armazenados após o build do React.
  • Configurações de CORS: A configuração CORS permite que o frontend e o backend, em diferentes origens, comuniquem-se sem restrições. O cabeçalho Access-Control-Allow-Origin é definido como * para liberar o acesso de todas as origens. Outros cabeçalhos (como Access-Control-Allow-Methods e Access-Control-Allow-Headers) especificam quais métodos HTTP e tipos de cabeçalhos são permitidos nas requisições, enquanto o Access-Control-Allow-Credentials permite o uso de cookies e credenciais.
  • Cabeçalhos de segurança adicionais: Para proteger a aplicação contra algumas ameaças, como clickjacking e XSS (Cross-Site Scripting), o arquivo inclui cabeçalhos como:
    • X-Frame-Options: SAMEORIGIN — previne que o site seja exibido em iframes fora da origem.
    • X-XSS-Protection: 1 — habilita proteção contra ataques XSS em navegadores compatíveis.
    • X-Content-Type-Options: nosniff — evita que o navegador interprete arquivos de forma incorreta.
    • Referrer-Policy: strict-origin-when-cross-origin — controla quais informações de referrer são enviadas com as requisições.
  • Cache de arquivos estáticos: A localização /static/ define uma política de cache de um ano para arquivos estáticos, melhorando o desempenho ao reduzir requisições repetidas desses recursos.
  • Roteamento interno e proxy reverso: Para rotear requisições ao backend, a configuração location @backend redireciona para o serviço auth-api-service na porta 3000, passando cabeçalhos específicos para manter a integridade da conexão. Quando o método é OPTIONS, uma resposta pré-definida é enviada para permitir que a requisição prossiga, de acordo com as permissões CORS.

Essas configurações tornam o Nginx um ponto central para gerenciar e proteger o tráfego entre o frontend e o backend, além de servir o conteúdo estático com eficiência e segurança.

Configuração do Sistema de Autenticação com Kubernetes e Terraform

Após subir as imagens Docker do frontend e backend para o Docker Hub, usaremos o Terraform para configurar o ambiente necessário para rodar nosso sistema de autenticação no Kubernetes.

O ambiente inclui o deployment das imagens, configuração dos serviços de banco de dados e email, além da definição das variáveis de ambiente.

Persistent Volume (PV) e Persistent Volume Claim (PVC)

Em Kubernetes, PV e PVC são usados para gerenciar o armazenamento persistente dos dados de um aplicativo, como banco de dados, para garantir que os dados não sejam perdidos quando os containers são reiniciados.

  • Persistent Volume (PV): É o recurso de armazenamento que o Kubernetes fornece. Ele representa um espaço físico de armazenamento (como um disco no servidor) onde os dados do aplicativo serão guardados. Um PV pode ser criado e configurado para atender diferentes requisitos de armazenamento, como tamanho e tipo de acesso.
  • Persistent Volume Claim (PVC): É uma “reivindicação” ou pedido de armazenamento feito pelos pods. Os pods solicitam o espaço que precisam criando um PVC, que o Kubernetes associa a um PV adequado, liberando o armazenamento. O PVC especifica os requisitos de armazenamento que o pod precisa, como o espaço e as permissões de leitura/escrita.

Vantagens do PV e PVC

A principal vantagem de usar PV e PVC é garantir que os dados permaneçam mesmo que o container seja removido ou atualizado.

Isso é crucial para aplicativos que usam banco de dados, pois permite que os dados sobrevivam a interrupções no ciclo de vida dos pods.

Sem PV e PVC, os dados armazenados em um container seriam perdidos assim que ele fosse reiniciado.

Probes (ou Verificações)

As probes de readiness e liveness são ferramentas muito úteis no Kubernetes para garantir que os containers de um aplicativo estão funcionando corretamente.

Cada uma tem um papel específico:

Liveness Probe

  • Função: O objetivo da liveness probe é verificar se o container está “vivo” ou “funcionando”. Se a liveness probe detectar que o container está “morto” ou não responde, o Kubernetes vai automaticamente reiniciar esse container.
  • Quando usar: Útil para casos em que um processo no container pode travar, mas o container continua “ligado” (por exemplo, um serviço que fica preso em um loop infinito).
  • Exemplo de aplicação: Imagine que você tem um servidor que, ocasionalmente, pode parar de responder por causa de algum erro. A liveness probe monitora esse comportamento e, se o servidor parar de responder, o Kubernetes reinicia o container para tentar corrigir o problema.

Readiness Probe

  • Função: A readiness probe verifica se o container está “pronto” para receber tráfego. Se a readiness probe detectar que o container não está pronto, ele será temporariamente “removido” do serviço até que esteja preparado para receber novas requisições.
  • Quando usar: Ideal para casos em que um container precisa de algum tempo para inicializar totalmente ou carregar dados antes de começar a responder às requisições. Ele impede que o aplicativo receba tráfego antes de estar pronto.
  • Exemplo de aplicação: Imagine um servidor que precisa carregar uma base de dados ou inicializar alguns serviços. Durante esse processo, ele ainda não está pronto para atender as solicitações de usuários. A readiness probe só direcionará o tráfego para o container quando ele estiver realmente preparado para responder.

Como Elas Funcionam na Prática

Ambas as probes geralmente monitoram o container usando métodos como:

  • HTTP: Envia uma requisição HTTP para uma URL específica. Se a URL retornar um código de sucesso (200 OK, por exemplo), o Kubernetes entende que o container está funcionando.
  • TCP Socket: Tenta se conectar a uma porta no container. Se a conexão for bem-sucedida, o container é considerado saudável.
  • Comando: Executa um comando dentro do container. Se o comando retornar com sucesso, o container é considerado “saudável” ou “pronto”.

Probes no deployment

  • initial_delay_seconds: O tempo (em segundos) que o Kubernetes espera após o container iniciar, antes de começar a fazer as verificações. Isso é útil se o container precisa de um tempo inicial para começar a responder.
  • period_seconds: A frequência das verificações. O Kubernetes verificará o container a cada X segundos.

Arquivo de Definição de Recursos com Terraform (deployment.tf)

O arquivo deployment.tf define o deployment dos componentes frontend e backend, além dos recursos MySQL no Kubernetes.

Inclui readiness e liveness probes, persistência de dados e configuração de recursos.

  • O Persistent Volume (mysql_pv) é configurado para fornecer 5GB de armazenamento com acesso de leitura e escrita para um único pod. Usamos host_path para especificar onde o volume será armazenado fisicamente no servidor.
  • O Persistent Volume Claim (mysql_pvc) é o “pedido” feito pelos pods para usar esse volume. Ele solicita o volume especificado em mysql_pv, garantindo que o banco de dados MySQL tenha armazenamento persistente.

codigo-fluente-fiber-tutorial/devops/deployment.tf


# Data Source para o IP do LoadBalancer
data "kubernetes_service" "auth_ui" {
  metadata {
    name = "auth-ui-service"
  }
  depends_on = [kubernetes_service.auth_ui]
}

# Secret para credenciais do Gmail
resource "kubernetes_secret" "gmail_credentials" {
  metadata {
    name = "gmail-credentials"
  }

  data = {
    username = var.gmail_username
    password = var.gmail_password
  }
}

# Secret para o MySQL
resource "kubernetes_secret" "mysql_secret" {
  metadata {
    name = "mysql-secret"
  }
  data = {
    mysql-root-password = base64encode(var.mysql_root_password)
  }
}

# Persistent Volume para MySQL
resource "kubernetes_persistent_volume" "mysql_pv" {
  metadata {
    name = "mysql-pv"
    labels = {
      type = "local"
      app  = "mysql"
    }
  }

  spec {
    capacity = {
      storage = "5Gi"
    }
    access_modes = ["ReadWriteOnce"]
    storage_class_name = "manual"
    persistent_volume_reclaim_policy = "Retain"

    persistent_volume_source {
      host_path {
        path = "/mnt/data"
        type = "DirectoryOrCreate"
      }
    }
  }
}

# Persistent Volume Claim para MySQL
resource "kubernetes_persistent_volume_claim" "mysql_pvc" {
  metadata {
    name = "mysql-pvc"
    labels = {
      app = "mysql"
    }
  }
  spec {
    access_modes = ["ReadWriteOnce"]
    resources {
      requests = {
        storage = "5Gi"
      }
    }
    storage_class_name = "manual"
    volume_name = kubernetes_persistent_volume.mysql_pv.metadata[0].name
  }

  depends_on = [kubernetes_persistent_volume.mysql_pv]
}

# MySQL Deployment
resource "kubernetes_deployment" "mysql" {
  metadata {
    name = "mysql"
    labels = {
      app = "mysql"
    }
  }
  spec {
    replicas = 1
    selector {
      match_labels = {
        app = "mysql"
      }
    }
    template {
      metadata {
        labels = {
          app = "mysql"
        }
      }
      spec {
        container {
          name  = "mysql"
          image = "mysql:5.7"
          
          env {
            name  = "MYSQL_ROOT_PASSWORD"
            value = var.mysql_root_password
          }

          env {
            name  = "MYSQL_DATABASE"
            value = "mysql"
          }

          resources {
            limits = {
              memory = "512Mi"
              cpu    = "500m"
            }
            requests = {
              memory = "256Mi"
              cpu    = "250m"
            }
          }
          
          port {
            container_port = 3306
            name = "mysql"
          }
          
          volume_mount {
            name       = "mysql-persistent-storage"
            mount_path = "/var/lib/mysql"
          }

          readiness_probe {
            tcp_socket {
              port = 3306
            }
            initial_delay_seconds = 15
            period_seconds       = 10
          }

          liveness_probe {
            tcp_socket {
              port = 3306
            }
            initial_delay_seconds = 20
            period_seconds       = 10
          }
        }

        volume {
          name = "mysql-persistent-storage"
          persistent_volume_claim {
            claim_name = kubernetes_persistent_volume_claim.mysql_pvc.metadata[0].name
          }
        }
      }
    }
  }

  depends_on = [
    kubernetes_persistent_volume_claim.mysql_pvc
  ]
}

# MySQL Service
resource "kubernetes_service" "mysql_service" {
  metadata {
    name = "mysql-service"
    labels = {
      app = "mysql"
    }
  }
  spec {
    selector = {
      app = "mysql"
    }
    type = "ClusterIP"
    port {
      port        = 3306
      target_port = 3306
      name        = "mysql"
    }
  }
}

# Backend Deployment
resource "kubernetes_deployment" "auth_api" {
  metadata {
    name = "auth-api"
    labels = {
      app = "auth-api"
    }
  }
  spec {
    replicas = 1
    selector {
      match_labels = {
        app = "auth-api"
      }
    }
    template {
      metadata {
        labels = {
          app = "auth-api"
        }
      }
      spec {
        init_container {
          name  = "wait-for-mysql"
          image = "busybox:1.28"
          command = ["sh", "-c", "until nc -z mysql-service 3306; do echo waiting for mysql; sleep 2; done;"]
        }

        container {
          name  = "auth-api"
          image = "toticavalcanti/fiber-auth-api:v1.0"

          env {
            name  = "MYSQL_ROOT_PASSWORD"
            value = var.mysql_root_password
          }

          env {
            name  = "DB_DSN"
            value = "root:${var.mysql_root_password}@tcp(mysql-service:3306)/mysql?parseTime=true"
          }

          env {
            name  = "GMAIL_USERNAME"
            value_from {
              secret_key_ref {
                name = kubernetes_secret.gmail_credentials.metadata[0].name
                key  = "username"
              }
            }
          }

          env {
            name  = "GMAIL_PASSWORD"
            value_from {
              secret_key_ref {
                name = kubernetes_secret.gmail_credentials.metadata[0].name
                key  = "password"
              }
            }
          }

          env {
            name  = "APP_URL"
            value = "http://${data.kubernetes_service.auth_ui.status[0].load_balancer[0].ingress[0].ip}"
          }

          port {
            container_port = 3000
            name = "http"
          }

          resources {
            limits = {
              cpu    = "250m"
              memory = "256Mi"
            }
            requests = {
              cpu    = "100m"
              memory = "128Mi"
            }
          }

          readiness_probe {
            http_get {
              path = "/api/health"
              port = 3000
            }
            initial_delay_seconds = 15
            period_seconds       = 10
          }

          liveness_probe {
            http_get {
              path = "/api/health"
              port = 3000
            }
            initial_delay_seconds = 20
            period_seconds       = 10
          }
        }
      }
    }
  }

  depends_on = [
    kubernetes_deployment.mysql,
    kubernetes_service.mysql_service
  ]
}

# Backend Service
resource "kubernetes_service" "auth_api" {
  metadata {
    name = "auth-api-service"
    labels = {
      app = "auth-api"
    }
  }
  spec {
    selector = {
      app = "auth-api"
    }
    type = "ClusterIP"
    port {
      port        = 3000
      target_port = 3000
      name        = "http"
    }
  }
}

# Frontend Deployment
resource "kubernetes_deployment" "auth_ui" {
  metadata {
    name = "auth-ui"
    labels = {
      app = "auth-ui"
    }
  }
  spec {
    replicas = 1
    selector {
      match_labels = {
        app = "auth-ui"
      }
    }
    template {
      metadata {
        labels = {
          app = "auth-ui"
        }
      }
      spec {
        container {
          name  = "auth-ui"
          image = "toticavalcanti/auth-ui:v1.0"

          env {
            name  = "REACT_APP_API_URL"
            value = var.react_app_api_url
          }

          port {
            container_port = 80
            name = "http"
          }

          readiness_probe {
            http_get {
              path = "/index.html"
              port = 80
            }
            initial_delay_seconds = 10
            period_seconds       = 5
            failure_threshold    = 3
            success_threshold    = 1
            timeout_seconds      = 1
          }

          liveness_probe {
            http_get {
              path = "/index.html"
              port = 80
            }
            initial_delay_seconds = 15
            period_seconds       = 10
            failure_threshold    = 3
            success_threshold    = 1
            timeout_seconds      = 1
          }

          resources {
            limits = {
              cpu    = "200m"
              memory = "256Mi"
            }
            requests = {
              cpu    = "100m"
              memory = "128Mi"
            }
          }
        }
      }
    }
  }

  depends_on = [kubernetes_deployment.auth_api]
}

# Frontend Service
resource "kubernetes_service" "auth_ui" {
  metadata {
    name = "auth-ui-service"
    labels = {
      app = "auth-ui"
    }
  }
  spec {
    selector = {
      app = "auth-ui"
    }
    type = "LoadBalancer"
    port {
      port        = 80
      target_port = 80
      name        = "http"
    }
  }
}

Arquivo de Variáveis (variables.tf)

Este arquivo declara variáveis usadas no deployment.tf, facilitando a customização de valores como credenciais de API e dados do banco de dados sem alterar o código principal.

codigo-fluente-fiber-tutorial/devops/variables.tf


variable "do_token" {
  type        = string
  sensitive   = true
  description = "Token de acesso para a API da DigitalOcean"
}

variable "gmail_username" {
  type        = string
  description = "Endereço do Gmail para enviar emails"
}

variable "gmail_password" {
  type        = string
  sensitive   = true
  description = "Senha do Gmail ou Senha de App"
}

variable "mysql_root_password" {
  description = "Senha do root para o MySQL"
  type        = string
}

variable "react_app_api_url" {
  description = "URL para a API do auth-api"
  type        = string
  default     = "/api" 
}

Arquivo de Saídas (outputs.tf)

O outputs.tf exibe informações úteis sobre os serviços criados, como IPs e URLs, facilitando o acesso e uso.

codigo-fluente-fiber-tutorial/devops/outputs.tf


# Frontend UI Service
output "auth_ui_service_name" {
  value       = kubernetes_service.auth_ui.metadata[0].name
  description = "The name of the Kubernetes service for the auth UI"
}

output "auth_ui_service_type" {
  value       = kubernetes_service.auth_ui.spec[0].type
  description = "The type of the Kubernetes service for the auth UI"
}

output "auth_ui_load_balancer_ip" {
  value       = try(kubernetes_service.auth_ui.status[0].load_balancer[0].ingress[0].ip, "Pending")
  description = "The external IP of the load balancer for the auth UI service (if available)"
}

# Backend API Service
output "auth_api_service_name" {
  value       = kubernetes_service.auth_api.metadata[0].name
  description = "The name of the Kubernetes service for the auth API"
}

output "auth_api_service_type" {
  value       = kubernetes_service.auth_api.spec[0].type
  description = "The type of the Kubernetes service for the auth API"
}

output "auth_api_cluster_ip" {
  value       = kubernetes_service.auth_api.spec[0].cluster_ip
  description = "The Cluster IP of the auth API service"
}

# MySQL Service
output "mysql_service_name" {
  value       = kubernetes_service.mysql_service.metadata[0].name
  description = "The name of the Kubernetes service for MySQL"
}

output "mysql_connection_string" {
  value       = "mysql://root:${var.mysql_root_password}@tcp(${kubernetes_service.mysql_service.metadata[0].name}:3306)/mysql"
  sensitive   = true
  description = "MySQL connection string (sensitive)"
}

# Application URL (external access to frontend)
output "application_url" {
  value = try(
    "http://${kubernetes_service.auth_ui.status[0].load_balancer[0].ingress[0].ip}",
    "Pending - External IP not yet assigned"
  )
  description = "The URL to access the application (if available)"
}

# API URL for frontend (internal communication)
output "api_internal_url" {
  value = "http://${kubernetes_service.auth_api.metadata[0].name}:3000/api"
  description = "The internal URL for the backend API service for use within the cluster"
}

Arquivo Principal (main.tf)

O arquivo main.tf define o provider para o DigitalOcean e configura o cluster Kubernetes com Terraform.

codigo-fluente-fiber-tutorial/devops/main.tf


terraform {
  required_providers {
    digitalocean = {
      source  = "digitalocean/digitalocean"
      version = "~> 2.0"
    }
    kubernetes = {
      source = "hashicorp/kubernetes"
      version = "2.33.0"
    }
  }
}

provider "digitalocean" {
  token = var.do_token
}

# Criação do Cluster Kubernetes
resource "digitalocean_kubernetes_cluster" "meu_cluster" {
  name    = "meu-cluster"
  region  = "nyc1"
  version = "1.31.1-do.3"

  node_pool {
    name       = "default-pool"
    size       = "s-1vcpu-2gb"
    node_count = 2
  }

  provisioner "local-exec" {
    command = <<-EOT
      echo "Iniciando kubeconfig..."
      doctl kubernetes cluster kubeconfig save meu-cluster
      echo "Kubeconfig salvo com sucesso!"
      echo "Aguardando cluster ficar pronto..."
      sleep 45
      kubectl wait --for=condition=Ready nodes --all --timeout=300s
      echo "Cluster está pronto!"
    EOT
  }
}

# Configuração do Provider Kubernetes
provider "kubernetes" {
  host  = digitalocean_kubernetes_cluster.meu_cluster.endpoint
  token = digitalocean_kubernetes_cluster.meu_cluster.kube_config[0].token
  cluster_ca_certificate = base64decode(
    digitalocean_kubernetes_cluster.meu_cluster.kube_config[0].cluster_ca_certificate
  )
}

Arquivo de Valores das Variáveis (terraform.tfvars)

O terraform.tfvars armazena os valores das variáveis definidas em variables.tf, como credenciais e configurações de API. Deve ser incluído no .gitignore para evitar a exposição de informações sensíveis.

codigo-fluente-fiber-tutorial/devops/terraform.tfvars


do_token            = "dop_v1_numero-de-seu-token-na-digital-ocean"
gmail_username      = "seu-gmail@gmail.com"
gmail_password      = "v**w u*** **lq ***u"
mysql_root_password = "yourpassword"
react_app_api_url = "/api"

Autenticação e Configuração do Gmail

Configure o backend para enviar emails pelo Gmail.

Ative a autenticação de dois fatores em sua conta e crie uma senha de app no link:

https://myaccount.google.com/apppasswords

Só para ilustrar, o arquivo .env local, seria mais ou menos isso:

.env


DB_DSN=sua-string-de-conexão-do-mysql
GMAIL_USERNAME=seu-endereco-gmail@gmail.com
GMAIL_PASSWORD="v**w u*** **lq ***u"
REACT_APP_API_URL=http://localhost:3000

Veja que no .env local, é padrão usar UPPERCASE, já no terraform.tfvars, é lowercase o padrão.

Autenticação na DigitalOcean

Autentique-se na DigitalOcean com seu token de acesso para executar comandos kubectl:

doctl auth init --access-token seu_token_da_digitalocean

Comandos Terraform

Entre na pasta devops e execute os comandos a seguir para iniciar a configuração do ambiente:


terraform init
terraform validate
terraform plan
terraform apply

Conectando Terminal Local ao Cluster


doctl kubernetes cluster kubeconfig save meu-cluster

Remoção de Recursos

Para destruir todos os recursos criados:

terraform destroy

Conclusão

Com esses passos, configuramos um ambiente de autenticação robusto com Kubernetes e Terraform na DigitalOcean, pronto para escalar conforme necessário.

Por essa aula é só.

Até a próxima!

Aula 12                                           Aula 14

 

Category: DevOps, Kubernates
About The Author
-

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>