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.
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!