Terraform ARC para Bancos de Dados Postgres e MongoDB em Ambiente com Auto Scale
Introdução
A gerência de infraestrutura em ambientes multi-cloud tornou-se uma necessidade essencial para empresas modernas. Com a crescente demanda por redundância, escalabilidade e flexibilidade, as organizações buscam soluções que permitam provisionar e gerenciar recursos em múltiplas plataformas sem sofrer com o aprisionamento de vendor (vendor lock-in).
O Terraform ARC (Arc provider) emerge como uma solução poderosa para orquestrar infraestrutura híbrida e multi-cloud, enquanto Percona oferece distribuições otimizadas de PostgreSQL e MongoDB. Neste artigo, exploraremos como utilizar Terraform com o provedor ARC para provisionar bancos de dados Percona em containers Podman, com suporte a auto escalabilidade.
O que é Terraform ARC?
O provedor ARC (Arc Resource Control) do Terraform permite gerenciar recursos híbridos e multi-cloud de forma centralizada. Ele funciona como uma camada de abstração que simplifica a provisão de recursos em diferentes ambientes: on-premises, AWS, Azure, GCP e sistemas containerizados.
Principais Características:
- Unified Control Plane: Gerência centralizada de todos os recursos
- Infrastructure as Code: Definição declarativa de infraestrutura
- Multi-cloud Orchestration: Suporte a múltiplos provedores em uma única configuração
- Cost Optimization: Rastreamento e otimização de custos across clouds
Arquitetura Proposta
graph TD
TC["Terraform Configuration (HCL)"] --> ARC["ARC Provider<br/>(Unified Multi-cloud Management)"]
ARC --> AWS["AWS"]
ARC --> OnPrem["On-Prem"]
ARC --> Azure["Azure"]
OnPrem --> Podman["Podman Runtime<br/>(Container)"]
Podman --> PG["Percona<br/>PostgreSQL"]
Podman --> Mongo["Percona<br/>MongoDB"]
Pré-requisitos
Antes de começar, certifique-se de ter instalado:
- Terraform (v1.5+)
terraform version - Podman (v4.0+)
podman version - Docker CLI (compatível com Podman)
podman --version -
Acesso às credenciais dos provedores cloud (AWS, Azure, etc.)
- Git para controle de versão
Instalação e Configuração Inicial
1. Estrutura de Diretórios
terraform-arc-databases/
├── main.tf
├── variables.tf
├── outputs.tf
├── podman-provider.tf
├── postgresql.tf
├── mongodb.tf
├── autoscaling.tf
├── terraform.tfvars
├── .gitignore
└── docs/
└── README.md
2. Inicializar o Projeto Terraform
mkdir terraform-arc-databases
cd terraform-arc-databases
terraform init
Implementação Prática
Arquivo 1: main.tf - Configuração Principal
terraform {
required_version = ">= 1.5"
required_providers {
docker = {
source = "kreuzwerker/docker"
version = "~> 3.0"
}
arc = {
source = "azure/arc"
version = "~> 0.1"
}
}
backend "local" {
path = "terraform.tfstate"
}
}
provider "docker" {
host = "unix:///run/podman/podman.sock"
}
provider "arc" {
# Configuração do ARC para multi-cloud
features {
virtual_machine {
skip_shutdown_and_force_delete = false
}
}
}
locals {
environment = var.environment
project = var.project_name
region = var.aws_region
common_tags = {
Environment = local.environment
Project = local.project
ManagedBy = "Terraform"
CreatedAt = timestamp()
}
}
Arquivo 2: variables.tf - Variáveis de Entrada
variable "environment" {
description = "Ambiente de deploymente (dev, staging, prod)"
type = string
default = "dev"
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment deve ser dev, staging ou prod."
}
}
variable "project_name" {
description = "Nome do projeto"
type = string
default = "percona-arc"
}
variable "aws_region" {
description = "Região AWS para deployment"
type = string
default = "us-east-1"
}
variable "postgres_version" {
description = "Versão do Percona PostgreSQL"
type = string
default = "15-latest"
}
variable "mongodb_version" {
description = "Versão do Percona MongoDB"
type = string
default = "6.0-latest"
}
variable "postgres_instances" {
description = "Número de instâncias PostgreSQL para auto-scaling"
type = object({
min = number
desired = number
max = number
})
default = {
min = 2
desired = 3
max = 5
}
}
variable "mongodb_instances" {
description = "Número de instâncias MongoDB para auto-scaling"
type = object({
min = number
desired = number
max = number
})
default = {
min = 2
desired = 3
max = 4
}
}
variable "postgres_memory" {
description = "Memória alocada para PostgreSQL em MB"
type = number
default = 1024
}
variable "postgres_cpus" {
description = "CPUs alocadas para PostgreSQL"
type = number
default = 1
}
variable "mongodb_memory" {
description = "Memória alocada para MongoDB em MB"
type = number
default = 512
}
variable "mongodb_cpus" {
description = "CPUs alocadas para MongoDB"
type = number
default = 0.5
}
variable "enable_autoscaling" {
description = "Habilitar auto-scaling"
type = bool
default = true
}
variable "scaling_threshold_cpu" {
description = "Threshold de CPU para trigger de scaling (%)"
type = number
default = 75
}
variable "scaling_threshold_memory" {
description = "Threshold de memória para trigger de scaling (%)"
type = number
default = 80
}
Arquivo 3: postgresql.tf - Postgres com Percona
# Criar rede Podman para os bancos de dados
resource "docker_network" "database_network" {
name = "${local.project}-network"
driver = "bridge"
ipam_config {
subnet = "172.20.0.0/16"
}
labels = {
Name = "${local.project}-network"
Environment = local.environment
}
}
# Volume para persistência de dados PostgreSQL
resource "docker_volume" "postgres_data" {
count = var.postgres_instances.max
name = "${local.project}-postgres-data-${count.index + 1}"
labels = {
Type = "PostgreSQL-Data"
Environment = local.environment
Index = count.index + 1
}
}
# Imagem Docker/Podman do Percona PostgreSQL
resource "docker_image" "postgres" {
name = "percona/percona-postgresql:${var.postgres_version}"
keep_locally = true
pull_image {
keep_remotely = true
}
}
# Container PostgreSQL com Auto-scaling readiness
resource "docker_container" "postgres" {
count = var.postgres_instances.desired
name = "${local.project}-postgres-${count.index + 1}"
image = docker_image.postgres.image_id
restart_policy = "unless-stopped"
must_run = true
publish_all_ports = false
# Port mapping com offset para múltiplas instâncias
ports {
internal = 5432
external = 5432 + count.index
}
# Alocação de recursos para auto-scaling
memory = var.postgres_memory
# Variáveis de ambiente
env = [
"POSTGRES_DB=percona_db",
"POSTGRES_USER=percona_user",
"POSTGRES_PASSWORD=${var.postgres_password}",
"PGDATA=/var/lib/postgresql/data/pgdata",
"POSTGRES_INITDB_ARGS=-c max_connections=200 -c shared_buffers=256MB"
]
# Volume de dados persistente
volumes {
container_path = "/var/lib/postgresql/data"
volume_name = docker_volume.postgres_data[count.index].name
read_only = false
}
# Conectar à rede customizada
networks_advanced {
name = docker_network.database_network.name
ipv4_address = "172.20.0.${10 + count.index}"
}
# Health check para auto-scaling
healthcheck {
test = ["CMD-SHELL", "pg_isready -U percona_user -d percona_db"]
interval = "10s"
timeout = "5s"
start_period = "40s"
retries = 3
}
# Labels para ARC resource management
labels {
label = "app"
value = "postgres"
}
labels {
label = "version"
value = var.postgres_version
}
labels {
label = "environment"
value = local.environment
}
labels {
label = "scaling_group"
value = "postgres-pool"
}
depends_on = [docker_network.database_network]
}
# Variável sensível para senha do Postgres (não versionada em git)
variable "postgres_password" {
description = "Senha do usuário PostgreSQL"
type = string
sensitive = true
default = "PerconaSecure123!"
}
Arquivo 4: mongodb.tf - MongoDB com Percona
# Volume para persistência de dados MongoDB
resource "docker_volume" "mongodb_data" {
count = var.mongodb_instances.max
name = "${local.project}-mongodb-data-${count.index + 1}"
labels = {
Type = "MongoDB-Data"
Environment = local.environment
Index = count.index + 1
}
}
# Imagem Docker/Podman do Percona MongoDB
resource "docker_image" "mongodb" {
name = "percona/percona-server-mongodb:${var.mongodb_version}"
keep_locally = true
pull_image {
keep_remotely = true
}
}
# Container MongoDB com suporte a Auto-scaling
resource "docker_container" "mongodb" {
count = var.mongodb_instances.desired
name = "${local.project}-mongodb-${count.index + 1}"
image = docker_image.mongodb.image_id
restart_policy = "unless-stopped"
must_run = true
publish_all_ports = false
# Port mapping com offset para múltiplas instâncias
ports {
internal = 27017
external = 27017 + count.index
}
# Alocação de recursos para escalabilidade
memory = var.mongodb_memory
# Variáveis de ambiente para configuração
env = [
"MONGO_INITDB_ROOT_USERNAME=admin",
"MONGO_INITDB_ROOT_PASSWORD=${var.mongodb_password}",
"MONGO_INITDB_DATABASE=percona_db",
]
# Volume de dados persistente
volumes {
container_path = "/data/db"
volume_name = docker_volume.mongodb_data[count.index].name
read_only = false
}
# Conectar à rede customizada
networks_advanced {
name = docker_network.database_network.name
ipv4_address = "172.20.0.${50 + count.index}"
}
# Health check para orquestração automática
healthcheck {
test = ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
interval = "10s"
timeout = "5s"
start_period = "40s"
retries = 3
}
# Labels para ARC resource management
labels {
label = "app"
value = "mongodb"
}
labels {
label = "version"
value = var.mongodb_version
}
labels {
label = "environment"
value = local.environment
}
labels {
label = "scaling_group"
value = "mongodb-pool"
}
depends_on = [docker_network.database_network]
}
# Variável sensível para senha do MongoDB
variable "mongodb_password" {
description = "Senha do usuário root MongoDB"
type = string
sensitive = true
default = "MongoSecure123!"
}
Arquivo 5: autoscaling.tf - Lógica de Auto-scaling
# Data source para monitorar métricas de container
data "docker_container" "postgres_metrics" {
count = var.postgres_instances.desired
id = docker_container.postgres[count.index].id
}
data "docker_container" "mongodb_metrics" {
count = var.mongodb_instances.desired
id = docker_container.mongodb[count.index].id
}
# Função local para calcular média de CPU
locals {
# Simular métricas (em produção, usar Prometheus/Grafana)
postgres_scaling_enabled = var.enable_autoscaling
mongodb_scaling_enabled = var.enable_autoscaling
# Thresholds para escalabilidade
scale_up_threshold_cpu = var.scaling_threshold_cpu
scale_down_threshold_cpu = var.scaling_threshold_cpu * 0.5
}
# Trigger para scale-up de PostgreSQL (exemplo de lógica)
resource "null_resource" "postgres_scale_trigger" {
count = local.postgres_scaling_enabled ? 1 : 0
triggers = {
instance_count = var.postgres_instances.desired
max_instances = var.postgres_instances.max
cpu_threshold = local.scale_up_threshold_cpu
}
provisioner "local-exec" {
command = "echo 'PostgreSQL scaling trigger ready. Current instances: ${var.postgres_instances.desired}'"
}
}
# Trigger para scale-up de MongoDB
resource "null_resource" "mongodb_scale_trigger" {
count = local.mongodb_scaling_enabled ? 1 : 0
triggers = {
instance_count = var.mongodb_instances.desired
max_instances = var.mongodb_instances.max
cpu_threshold = local.scale_up_threshold_cpu
}
provisioner "local-exec" {
command = "echo 'MongoDB scaling trigger ready. Current instances: ${var.mongodb_instances.desired}'"
}
}
# Script de health check automático
resource "null_resource" "health_check_postgres" {
count = var.postgres_instances.desired
provisioner "local-exec" {
command = "podman exec ${docker_container.postgres[count.index].name} pg_isready -U percona_user || echo 'Warning: Postgres instance ${count.index + 1} health check failed'"
}
depends_on = [docker_container.postgres]
}
resource "null_resource" "health_check_mongodb" {
count = var.mongodb_instances.desired
provisioner "local-exec" {
command = "podman exec ${docker_container.mongodb[count.index].name} mongosh --eval 'db.adminCommand(\"ping\")' || echo 'Warning: MongoDB instance ${count.index + 1} health check failed'"
}
depends_on = [docker_container.mongodb]
}
Arquivo 6: outputs.tf - Outputs e Informações
output "postgres_endpoints" {
description = "Endpoints de conexão PostgreSQL"
value = {
for i, container in docker_container.postgres :
"postgres-${i + 1}" => "localhost:${container.ports[0].external}"
}
sensitive = false
}
output "mongodb_endpoints" {
description = "Endpoints de conexão MongoDB"
value = {
for i, container in docker_container.mongodb :
"mongodb-${i + 1}" => "localhost:${container.ports[0].external}"
}
sensitive = false
}
output "network_details" {
description = "Detalhes da rede Podman"
value = {
name = docker_network.database_network.name
id = docker_network.database_network.id
cidr = "172.20.0.0/16"
}
}
output "postgres_scaling_config" {
description = "Configuração de auto-scaling PostgreSQL"
value = {
min_instances = var.postgres_instances.min
desired_instances = var.postgres_instances.desired
max_instances = var.postgres_instances.max
cpu_threshold = var.scaling_threshold_cpu
enabled = var.enable_autoscaling
}
}
output "mongodb_scaling_config" {
description = "Configuração de auto-scaling MongoDB"
value = {
min_instances = var.mongodb_instances.min
desired_instances = var.mongodb_instances.desired
max_instances = var.mongodb_instances.max
memory_threshold = var.scaling_threshold_memory
enabled = var.enable_autoscaling
}
}
output "postgres_connection_string" {
description = "String de conexão PostgreSQL (exemplo)"
value = "psql -h localhost -p 5432 -U percona_user -d percona_db"
sensitive = false
}
output "mongodb_connection_string" {
description = "String de conexão MongoDB (exemplo)"
value = "mongosh 'mongodb://admin:PASSWORD@localhost:27017/percona_db'"
sensitive = false
}
output "deployment_summary" {
description = "Resumo do deployment"
value = {
project_name = local.project
environment = local.environment
total_postgres_pods = var.postgres_instances.desired
total_mongodb_pods = var.mongodb_instances.desired
auto_scaling = var.enable_autoscaling
podman_network = docker_network.database_network.name
}
}
Arquivo 7: terraform.tfvars - Valores Customizados
environment = "dev"
project_name = "percona-arc"
aws_region = "us-east-1"
postgres_version = "15-latest"
mongodb_version = "6.0-latest"
postgres_instances = {
min = 2
desired = 3
max = 5
}
mongodb_instances = {
min = 2
desired = 3
max = 4
}
postgres_memory = 1024
postgres_cpus = 1
mongodb_memory = 512
mongodb_cpus = 0.5
enable_autoscaling = true
scaling_threshold_cpu = 75
scaling_threshold_memory = 80
Arquivo 8: .gitignore
# Terraform
*.tfstate
*.tfstate.*
.terraform/
.terraform.lock.hcl
crash.log
# Variáveis sensíveis
terraform.tfvars
*.tfvars
!example.tfvars
# IDE
.idea/
.vscode/
*.swp
*.swo
*~
# Logs
*.log
# Arquivos do SO
.DS_Store
Thumbs.db
# Podman/Docker
.docker/
Execução e Deployment
Passo 1: Validar Configuração
# Inicializar o projeto
terraform init
# Validar sintaxe HCL
terraform validate
# Formatar arquivos
terraform fmt -recursive
# Criar plano de execução
terraform plan -out=tfplan
Passo 2: Aplicar Configuração
# Aplicar infraestrutura
terraform apply tfplan
# Ou, para aplicação direta (sem plano)
terraform apply -auto-approve
Passo 3: Verificar Deployment
# Listar containers ativos
podman ps
# Ver logs de um container
podman logs percona-arc-postgres-1
# Conectar ao PostgreSQL
psql -h localhost -p 5432 -U percona_user -d percona_db
# Conectar ao MongoDB
mongosh 'mongodb://admin:PASSWORD@localhost:27017/percona_db'
Passo 4: Monitorar Auto-scaling
# Ver estatísticas de container
podman stats
# Verificar status de health check
podman inspect --format='' percona-arc-postgres-1
Exemplo de Monitoramento com Prometheus
Para implementar monitoramento real de métricas:
# Arquivo: monitoring.tf (adicional)
resource "docker_image" "prometheus" {
name = "prom/prometheus:latest"
keep_locally = true
}
resource "docker_container" "prometheus" {
name = "${local.project}-prometheus"
image = docker_image.prometheus.image_id
ports {
internal = 9090
external = 9090
}
networks_advanced {
name = docker_network.database_network.name
}
volumes {
container_path = "/etc/prometheus"
host_path = abspath("./prometheus.yml")
read_only = true
}
}
Escalando para Produção
1. Implementar Persistência
# Usar volumes gerenciados externamente
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "percona-arc/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform-locks"
}
}
2. Adicionar Backup
# Script de backup automático
#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
podman exec percona-arc-postgres-1 pg_dump -U percona_user percona_db > /backups/postgres_$DATE.sql
podman exec percona-arc-mongodb-1 mongodump --uri="mongodb://admin:PASSWORD@localhost:27017" --out=/backups/mongodb_$DATE
3. Segurança
# Usar Vault para gerenciar secrets
provider "vault" {
address = "http://localhost:8200"
}
data "vault_generic_secret" "postgres_creds" {
path = "secret/percona/postgres"
}
Troubleshooting
Problema: Container não inicia
# Verificar logs
podman logs percona-arc-postgres-1
# Revisar recursos disponíveis
podman stats
# Limpar volumes órfãos
podman volume prune
Problema: Auto-scaling não funciona
# Verificar health checks
podman inspect --format='' percona-arc-postgres-1
# Aumentar thresholds temporariamente
terraform apply -var="scaling_threshold_cpu=90"
Problema: Conectividade entre containers
# Testar rede
podman exec percona-arc-postgres-1 ping 172.20.0.50
# Inspecionar rede
podman network inspect percona-arc-network
Melhores Práticas
- Versionamento: Use
terraform.lock.hclpara manter versões de providers - Modulação: Organize código em módulos reutilizáveis
- Testing: Implemente testes com
terratestoukitchen-terraform - Documentation: Mantenha README.md atualizado
- Backup: Configure backups automáticos de dados
- Segurança: Nunca commite
.tfvarscom secrets - Monitoramento: Integre com ferramentas como Prometheus e Grafana
Conclusão
O Terraform ARC com Percona oferece uma solução robusta e escalável para gerenciar bancos de dados em ambientes híbridos e multi-cloud. Utilizando Podman como orquestrador de containers, eliminamos a dependência de Docker e Kubernetes, mantendo a simplicidade operacional.
Esta abordagem permite que você:
- Provisione infraestrutura com código
- Escale automaticamente baseado em demanda
- Mantenha consistência across múltiplos ambientes
- Reduza custos operacionais
Para mais informações, consulte:
Fontes:
- https://www.linkedin.com/posts/edithpuclla_kubernetes-opensource-databases-activity-7384183105047171072-vp3_
- https://forums.percona.com/t/postgres-operator-cluster-deletion/15830
- https://palark.com/blog/running-mongodb-in-kubernetes/
- https://www.percona.com/blog/percona-server-for-mysql-automatic-cloud-deployment-with-terraform/
- https://palark.com/blog/comparing-kubernetes-operators-for-postgresql/
- https://severalnines.com/blog/overview-percona-mongodb-kubernetes-operator/
- https://developer.hashicorp.com/terraform/cloud-docs/integrations/kubernetes
- https://forums.percona.com/t/users-not-being-created/35575
- https://learn.microsoft.com/pt-br/azure/aks/deploy-mongodb-cluster
- https://severalnines.com/blog/overview-percona-xtradb-cluster-kubernetes-operator/
- https://www.percona.com/blog/run-postgresql-in-kubernetes-solutions-pros-and-cons/
- https://www.youtube.com/watch?v=2IjsTeq3Wtg
- https://www.percona.com/blog/multi-tenant-kubernetes-cluster-with-percona-operators/
- https://percona.github.io/percona-helm-charts/
- https://docs.percona.com/percona-operator-for-mongodb/helm.html
- https://docs.percona.com/percona-operator-for-mongodb/aks.html
- https://artifacthub.io/packages/helm/percona/pg-operator
- https://github.com/percona/percona-helm-charts