From ad8c9af8c2c0fa20983e71b00893e5b6e9a251fa Mon Sep 17 00:00:00 2001 From: MarcosYudi <199306647+marcos-yudi@users.noreply.github.com> Date: Tue, 10 Nov 2026 12:32:27 -0300 Subject: [PATCH] infra GCP --- infra/README.md | 158 +++++++++++++++++++++++++++++---- infra/cloud-init.yaml | 127 ++++++++++++++++++++++++++ infra/main.tf | 157 +++++++++++++------------------- infra/outputs.tf | 45 +++++++++- infra/terraform.tfvars.example | 14 +++ infra/variables.tf | 86 ++++++++++++++---- infra/versions.tf | 19 ++-- 7 files changed, 464 insertions(+), 142 deletions(-) create mode 100644 infra/cloud-init.yaml create mode 100644 infra/terraform.tfvars.example diff --git a/infra/README.md b/infra/README.md index ae62e45..58534af 100644 --- a/infra/README.md +++ b/infra/README.md @@ -1,27 +1,149 @@ -# infra/ — o ambiente como código (Terraform) +# infra/ — o Auditor na Google Cloud com Terraform -Descreve com Terraform o mesmo ambiente do `docker-compose.yml`: rede, volumes, banco (PostgreSQL + pgvector), API e painel. Usa o provider **Docker** e roda **no próprio computador**: não precisa de conta em nuvem nem gera custo. É a base para levar o projeto a uma nuvem depois, trocando o provider. +O Terraform cria na Google Cloud (GCP) uma máquina virtual Ubuntu que, ao ligar, instala o Docker, clona este repositório e sobe o mesmo `docker-compose.yml` do dia a dia: banco (PostgreSQL + pgvector), API e painel. Nada do código da aplicação muda. -O dia a dia continua pelo `.\iniciar.ps1` ou pelo `docker compose`. O Terraform é uma alternativa de implantação, não um passo obrigatório. +Não é preciso Docker no computador de quem roda o Terraform: o Docker roda dentro da VM. O dia a dia continua pelo `.\iniciar.ps1`; a nuvem é para a demo e para mostrar a infraestrutura como código. -## Como usar +## O que é criado (5 recursos) -Pré-requisitos: [Terraform](https://developer.hashicorp.com/terraform/install) ≥ 1.6 e Docker Desktop ligado. +| Recurso | Para quê | +|---|---| +| Rede `auditor-rede` | Rede própria. Rede nova não aceita conexão de fora até uma regra liberar | +| Sub-rede `auditor-subrede` | O pedaço da rede, em São Paulo, onde a VM se conecta | +| IP fixo `auditor-ip` | Endereço do painel e da API | +| Firewall `auditor-firewall` | Abre só 22 (SSH), 8000 (API) e 8501 (painel), e só para `ips_liberados`. O banco (5432) não fica exposto | +| VM `auditor-vm` `e2-medium` (2 vCPUs, 4 GB) com Ubuntu 24.04 | Roda o Docker Compose | -1. Pare o ambiente do compose (usa as mesmas portas): `docker compose down` -2. Construa a imagem da aplicação (uma vez): `docker compose build api` -3. Na pasta `infra/`: +Na Azure eram 8 recursos: a GCP não tem grupo de recursos (o projeto faz esse papel) nem placa de rede separada (o firewall se liga à VM por etiqueta). + +## O que vai para a nuvem e por qual caminho + +| O quê | Como chega na VM | +|---|---| +| Código | A VM clona o repositório (público) no primeiro boot | +| Chave do Gemini e chave do grupo | Variáveis `TF_VAR_...` → `.env` da VM | +| Esquema do banco (`db/01` a `05`) | Roda sozinho na 1ª criação do volume do Postgres | +| Normas no pgvector | A VM indexa sozinha no primeiro boot | +| Modelo `pacote_prever_v1.joblib` | Fora do Git: vai por `scp` (passo 14) | +| CSVs de Euribor, Selic e taxa do BC | Fora do Git: vão por `scp` junto com o modelo e o `completar.sh` carrega no banco | +| Base da Bondora | Não vai: o painel e o formulário funcionam sem ela | + +## Arquivos + +- `versions.tf`: usa o plugin da Google Cloud (login pelo `gcloud`). +- `variables.tf`: as configurações (projeto, região, tamanho, IPs, segredos). +- `main.tf`: os recursos. +- `cloud-init.yaml`: o que a VM faz no primeiro boot (`montar.sh`) e o passo final (`completar.sh`). +- `outputs.tf`: endereços e comandos prontos que aparecem no fim. +- `terraform.tfvars.example`: modelo das respostas; o `terraform.tfvars` real fica fora do Git. + +## Passo a passo (PowerShell, Windows) + +Os passos 1 a 11 não gastam crédito. O gasto começa no 12, e o 16 desliga tudo. + +### Conta (uma vez, quem tem o cartão) + +1. Em `cloud.google.com/free`, clique em **Começar gratuitamente** e cadastre o cartão. São US$ 300 por 90 dias. A cobrança só acontece se alguém clicar em **Ativar conta completa / Upgrade**: **ninguém clica nisso**. +2. No console: **Faturamento → Orçamentos e alertas → Criar orçamento** de US$ 20, com alerta por e-mail em 50%, 90% e 100%. +3. Para outra pessoa do grupo rodar o Terraform, dê a ela o papel **Editor** no projeto (IAM e administrador → Conceder acesso), com o Gmail dela. + +### Preparar (uma vez, quem vai rodar o Terraform) + +4. Instalar as ferramentas e reabrir o terminal: + ```powershell + winget install Hashicorp.Terraform + winget install Google.CloudSDK + terraform -version + gcloud version + ``` +5. Entrar e escolher o projeto. O ID do projeto aparece no topo do console (ex.: `auditor-preco-justo-4821`): + ```powershell + gcloud auth login + gcloud auth application-default login + gcloud config set project + gcloud services enable compute.googleapis.com ``` - terraform init - $env:TF_VAR_google_api_key = "" # opcional; sem ela o parecer sai pelo template - terraform apply + O `application-default login` é o login que o Terraform usa; o `services enable` liga a API de VMs (leva um ou dois minutos). +6. Criar a chave SSH da VM (Enter nas perguntas): + ```powershell + ssh-keygen -t rsa -b 4096 -f $HOME\.ssh\auditor_gcp ``` - No Linux/macOS, acrescente `-var 'docker_host=unix:///var/run/docker.sock'` (e use `export TF_VAR_google_api_key=...`). -4. Abra os endereços que o `terraform apply` mostra no fim (painel em http://localhost:8501). -5. Para remover tudo: `terraform destroy` (apaga também o volume do banco deste ambiente). +7. Descobrir o próprio IP e acrescentar `/32` (ex.: `200.100.50.25/32`): + ```powershell + Invoke-RestMethod https://api.ipify.org + ``` + +### Colocar os arquivos no projeto + +8. Criar o ramo: + ```powershell + git checkout main + git pull + git checkout -b feature/infra-gcp + ``` +9. Trocar o conteúdo da pasta `infra/` por estes arquivos. Apague o `infra/.terraform.lock.hcl` antigo (era do provider Docker); o `terraform init` gera um novo. O `.gitignore` da raiz já tem as linhas do Terraform. +10. Preencher as respostas e passar os segredos pelo terminal (não por arquivo): + ```powershell + cd infra + copy terraform.tfvars.example terraform.tfvars + notepad terraform.tfvars # projeto_id e ips_liberados + $env:TF_VAR_google_api_key = "" + $env:TF_VAR_cliente_id_sal = "" + ``` + As variáveis `$env:` valem só para aquela janela do PowerShell. Sem a chave do Gemini, o parecer sai pelo template de reserva. + +### Ensaio sem custo + +11. Conferir a receita: + ```powershell + terraform init + terraform fmt + terraform validate + terraform plan + ``` + Esperado: `Success! The configuration is valid.` e `Plan: 5 to add, 0 to change, 0 to destroy.` A saída do `plan` serve de print para a banca. + +### Subir na nuvem + +12. Criar os recursos (digitar `yes`). Em 1 a 2 minutos aparecem o IP, os endereços e os comandos prontos: + ```powershell + terraform apply + ``` +13. Esperar a VM se montar (15 a 20 minutos: Docker, build da imagem com PyTorch, banco e normas). Na raiz do projeto: + ```powershell + cd .. + terraform -chdir=infra output -raw comando_esperar_vm # copie e rode o comando que aparece + ``` + O comando fica parado até o fim e mostra `status: done` e os três containers: `auditor_postgres`, `auditor_api` e `auditor_streamlit`. Se mostrar `status: error`, veja o log com `sudo tail -100 /var/log/cloud-init-output.log` dentro da VM. +14. Enviar o modelo e os CSVs de custo do dinheiro (fora do Git) e completar: + ```powershell + terraform -chdir=infra output -raw comando_enviar_arquivos # copie e rode + terraform -chdir=infra output -raw comando_completar # copie e rode + ``` + O `scp` espera os arquivos em `modelos_salvos/` e `data/raw/` com os nomes do projeto. Se algum nome for diferente no seu computador, ajuste o comando. O `completar.sh` termina mostrando a resposta da API, com `"modelo_carregavel": true`. +15. Testar no navegador: `terraform -chdir=infra output painel_url` deve abrir o painel e rodar uma auditoria pelo formulário, e `api_url` deve abrir a documentação da API. + +### Desligar e entregar + +16. Destruir ao fim de cada teste (digitar `yes`) e conferir no console (Compute Engine → Instâncias de VM) que a `auditor-vm` sumiu: + ```powershell + cd infra + terraform destroy + ``` +17. Commit e PR. O `git status` não pode mostrar `terraform.tfvars` nem `.tfstate`; o novo `.terraform.lock.hcl` vai junto: + ```powershell + cd .. + git add -A infra/ + git status + git commit -m "Infra: Terraform na Google Cloud (VM com Docker Compose)" + git push -u origin feature/infra-gcp + ``` -## Observações +## Cuidados -- Os containers e volumes têm o prefixo `auditor_tf_`, para não se misturar com os do compose. -- O banco começa vazio, como no compose: o esquema de `db/` roda sozinho na 1ª criação; as cargas e a indexação das normas seguem o README da raiz, apontando para a porta 5432. -- A chave do Gemini nunca vai para arquivo: só pela variável de ambiente `TF_VAR_google_api_key`. Os arquivos de estado (`*.tfstate`) ficam fora do Git, pois podem conter a chave. +- **Segredos:** `terraform.tfvars` e `terraform.tfstate` ficam fora do Git. O `.tfstate` guarda a chave do Gemini, porque ela vai para o metadado da VM: quem tem acesso ao projeto também consegue vê-la. +- **Estado em uma máquina só:** o `terraform.tfstate` fica no computador onde o `apply` rodou. O `destroy` precisa sair dele (ou de uma cópia da pasta `infra/` inteira, com o `.tfstate`). +- **IP que muda:** se o IP de quem acessa mudar (outra rede, celular), atualize `ips_liberados` e rode `terraform apply` de novo. Só o firewall muda, a VM fica. +- **Custo:** a VM, o disco e o IP gastam crédito por hora enquanto existirem, mesmo sem uso. Crie perto do uso e destrua logo depois. +- **Fim do trial:** depois de 90 dias ou dos US$ 300, a Google para os recursos e apaga os dados. Nada é cobrado sem o upgrade manual. +- **Zona sem máquina:** se o `apply` der `ZONE_RESOURCE_POOL_EXHAUSTED`, troque a `zona` no `terraform.tfvars` (ex.: `southamerica-east1-a` ou `-c`) e rode de novo. diff --git a/infra/cloud-init.yaml b/infra/cloud-init.yaml new file mode 100644 index 0000000..a9842f4 --- /dev/null +++ b/infra/cloud-init.yaml @@ -0,0 +1,127 @@ +#cloud-config +# O que a VM faz sozinha no primeiro boot (o Ubuntu da GCP lê este arquivo do metadado user-data). +# Os trechos com cifrão e chaves são preenchidos pelo Terraform (templatefile no main.tf); +# por isso os scripts abaixo usam $VARIAVEL sem chaves. +# Acompanhe dentro da VM com: cloud-init status --wait +# Log completo: sudo tail -f /var/log/cloud-init-output.log + +package_update: true +packages: + - git + - ca-certificates + - curl + +# Cria o usuário já no boot; a chave SSH vem do metadado ssh-keys (main.tf). +users: + - default + - name: ${usuario} + shell: /bin/bash + sudo: ALL=(ALL) NOPASSWD:ALL + +write_files: + # Segredos do .env (o mesmo formato do .env local). Nunca vai para o Git. + # DATABASE_URL não entra: o docker-compose.yml já aponta a API para o banco. + - path: /opt/auditor/.env + permissions: "0600" + content: | + GOOGLE_API_KEY=${google_api_key} + CLIENTE_ID_SAL=${cliente_id_sal} + + # Montagem do primeiro boot (roda como root, uma vez só). Faz o que o iniciar.ps1 faz no Windows. + - path: /opt/auditor/montar.sh + permissions: "0755" + content: | + #!/usr/bin/env bash + set -euxo pipefail + USUARIO="${usuario}" + PROJETO="/home/$USUARIO/Auditor-Project" + + # 1. Memória de reserva (swap de 2 GB): o build do PyTorch e a API juntos apertam os 4 GB. + if [ ! -f /swapfile ]; then + fallocate -l 2G /swapfile + chmod 600 /swapfile + mkswap /swapfile + swapon /swapfile + echo '/swapfile none swap sw 0 0' >> /etc/fstab + fi + + # 2. Docker (com o docker compose). + curl -fsSL https://get.docker.com | sh + usermod -aG docker "$USUARIO" + + # 3. Projeto: clona e coloca o .env e as pastas que ficam fora do Git. + git clone --branch "${ramo}" "${repositorio}" "$PROJETO" + install -m 600 /opt/auditor/.env "$PROJETO/.env" + mkdir -p "$PROJETO/modelos_salvos" "$PROJETO/data/raw" "/home/$USUARIO/envio" + chown -R "$USUARIO:$USUARIO" "$PROJETO" "/home/$USUARIO/envio" + cd "$PROJETO" + + # 4. Imagem da aplicação (a parte demorada: PyTorch e bibliotecas). + docker compose build api + + # 5. Banco: o esquema de db/ roda sozinho na 1ª criação do volume. + docker compose up -d postgres + for i in $(seq 1 60); do + [ "$(docker inspect -f '{{.State.Health.Status}}' auditor_postgres)" = "healthy" ] && break + sleep 3 + done + + # 6. Normas do parecer no pgvector (o mesmo comando do iniciar.ps1). + docker compose run --rm --no-deps api python genai/src/indexar.py \ + || echo "AVISO: normas não indexadas; o parecer sai sem fontes" + + # 7. API e painel em segundo plano. + docker compose up -d api streamlit + touch /opt/auditor/pronto + echo "Montagem concluída." + + # Passo final, rodado pelo usuário depois de enviar os arquivos para ~/envio (README de infra/). + - path: /home/${usuario}/completar.sh + owner: ${usuario}:${usuario} + permissions: "0755" + defer: true + content: | + #!/usr/bin/env bash + set -euo pipefail + shopt -s nullglob + PROJETO="$HOME/Auditor-Project" + ENVIO="$HOME/envio" + + if [ ! -f /opt/auditor/pronto ]; then + echo "A VM ainda está se montando. Espere com: cloud-init status --wait" + exit 1 + fi + cd "$PROJETO" + + # 1. Modelo (.joblib não está no Git) + if [ -f "$ENVIO/pacote_prever_v1.joblib" ]; then + mv "$ENVIO/pacote_prever_v1.joblib" modelos_salvos/ + echo "[ok] modelo em modelos_salvos/" + fi + + # 2. Séries de custo do dinheiro (Euribor, Selic, BC) para o banco + for arq in "$ENVIO"/*.csv; do mv "$arq" data/raw/; done + E12="" + for nome in ECB_Euribor_Serie-Mensal-12-Meses_csv.csv ECB_Euribor_Serie-Mensal-12-Meses.csv; do + if [ -f "data/raw/$nome" ]; then E12="$nome"; break; fi + done + if [ -n "$E12" ]; then + sudo docker compose run --rm --no-deps -v "$PROJETO/data/raw:/app/data/raw:ro" api \ + python src/dados/carga_macro.py --ddl db/02_schema_macro.sql --euribor-12m "data/raw/$E12" + echo "[ok] Euribor, Selic e taxa do BC carregadas" + else + echo "[aviso] CSVs da Euribor não encontrados em ~/envio: o parecer sai sem a referência do BC" + fi + + # 3. Reinicia a API para ela ver o modelo e espera ficar saudável + sudo docker compose restart api + for i in $(seq 1 48); do + [ "$(sudo docker inspect -f '{{.State.Health.Status}}' auditor_api)" = "healthy" ] && break + sleep 5 + done + echo + curl -s http://localhost:8000/ + echo + +runcmd: + - [bash, /opt/auditor/montar.sh] diff --git a/infra/main.tf b/infra/main.tf index 31434c3..cb079ea 100644 --- a/infra/main.tf +++ b/infra/main.tf @@ -1,114 +1,81 @@ -# Infraestrutura do Auditor de Preço Justo como código. -# Descreve o mesmo ambiente do docker-compose.yml (banco, API e painel) com o provider -# Docker do Terraform, rodando no próprio computador. Para a nuvem, troca-se o provider -# e os recursos de container mantendo a mesma estrutura. +# main.tf +# A "lista de compras": tudo o que o Terraform cria na Google Cloud. +# São 5 recursos. O "terraform plan" deve mostrar "Plan: 5 to add". +# Não existe "grupo de recursos" como na Azure: o projeto da GCP faz esse papel, +# e o "terraform destroy" apaga os 5 recursos (o projeto continua existindo). -locals { - raiz = abspath("${path.module}/..") - url_do_banco = "postgresql://postgres:postgres@auditor_tf_postgres:5432/auditor_db" +# 1. Rede própria (VPC). Uma rede nova não aceita nenhuma conexão de fora até +# que uma regra de firewall libere: por isso a porta do banco (5432) fica fechada. +resource "google_compute_network" "rede" { + name = "${var.prefixo}-rede" + auto_create_subnetworks = false } -resource "docker_network" "auditor" { - name = "auditor_tf_rede" +# 2. Sub-rede: o "pedaço" da rede, na região escolhida, onde a VM se conecta. +resource "google_compute_subnetwork" "subrede" { + name = "${var.prefixo}-subrede" + ip_cidr_range = "10.10.1.0/24" + region = var.regiao + network = google_compute_network.rede.id } -resource "docker_volume" "dados_banco" { - name = "auditor_tf_postgres_data" +# 3. IP público fixo: o endereço que você digita no navegador. +resource "google_compute_address" "ip" { + name = "${var.prefixo}-ip" + region = var.regiao } -resource "docker_volume" "cache_modelos" { - name = "auditor_tf_cache_modelos_hf" -} - -resource "docker_image" "postgres" { - name = "pgvector/pgvector:pg16" - keep_locally = true -} - -# Construída antes pelo compose (o Dockerfile usa recursos do BuildKit). -resource "docker_image" "app" { - name = var.imagem_app - keep_locally = true -} +# 4. Firewall: só abre SSH (22), API (8000) e painel (8501), e só para os IPs liberados. +# Vale para as VMs com a etiqueta "auditor" (na Azure era a ligação NSG + placa de rede). +resource "google_compute_firewall" "entrada" { + name = "${var.prefixo}-firewall" + network = google_compute_network.rede.name -resource "docker_container" "postgres" { - name = "auditor_tf_postgres" - image = docker_image.postgres.image_id - restart = "unless-stopped" - env = ["POSTGRES_USER=postgres", "POSTGRES_PASSWORD=postgres", "POSTGRES_DB=auditor_db"] + direction = "INGRESS" + source_ranges = var.ips_liberados + target_tags = [var.prefixo] - networks_advanced { - name = docker_network.auditor.name - } - ports { - internal = 5432 - external = 5433 - } - volumes { - volume_name = docker_volume.dados_banco.name - container_path = "/var/lib/postgresql/data" - } - volumes { - host_path = "${local.raiz}/db" - container_path = "/docker-entrypoint-initdb.d" - read_only = true + allow { + protocol = "tcp" + ports = ["22", "8000", "8501"] } - healthcheck { - test = ["CMD-SHELL", "pg_isready -U postgres -d auditor_db"] - interval = "5s" - timeout = "5s" - retries = 20 - } - wait = true } -resource "docker_container" "api" { - name = "auditor_tf_api" - image = docker_image.app.image_id - restart = "unless-stopped" - env = compact([ - "DATABASE_URL=${local.url_do_banco}", - var.google_api_key == "" ? "" : "GOOGLE_API_KEY=${var.google_api_key}", - ]) +# 5. A máquina virtual: Ubuntu 24.04 que, ao ligar, roda o cloud-init.yaml +# (instala Docker, clona o repositório, sobe banco, indexa as normas, sobe API e painel). +resource "google_compute_instance" "vm" { + name = "${var.prefixo}-vm" + machine_type = var.tipo_maquina + zone = var.zona + tags = [var.prefixo] - networks_advanced { - name = docker_network.auditor.name - aliases = ["api"] - } - ports { - internal = 8000 - external = var.porta_api + boot_disk { + initialize_params { + image = "ubuntu-os-cloud/ubuntu-2404-lts-amd64" + size = var.tamanho_disco_gb + type = "pd-balanced" + } } - volumes { - host_path = "${local.raiz}/modelos_salvos" - container_path = "/app/modelos_salvos" - read_only = true - } - volumes { - volume_name = docker_volume.cache_modelos.name - container_path = "/root/.cache/huggingface" + + network_interface { + subnetwork = google_compute_subnetwork.subrede.id + access_config { + nat_ip = google_compute_address.ip.address + } } - depends_on = [docker_container.postgres] -} -resource "docker_container" "painel" { - name = "auditor_tf_streamlit" - image = docker_image.app.image_id - restart = "unless-stopped" - command = ["streamlit", "run", "src/app/main.py", "--server.port=8501", "--server.address=0.0.0.0"] - env = ["API_URL=http://api:8000"] + metadata = { + # Entrada só por chave SSH (sem senha), para o usuário definido em var.usuario. + ssh-keys = "${var.usuario}:${trimspace(file(pathexpand(var.caminho_chave_publica)))}" + enable-oslogin = "FALSE" - networks_advanced { - name = docker_network.auditor.name - } - ports { - internal = 8501 - external = var.porta_painel - } - volumes { - host_path = "${local.raiz}/docs/resultados" - container_path = "/app/docs/resultados" - read_only = true + # Script executado uma única vez, no primeiro boot (o Ubuntu da GCP lê o cloud-init daqui). + user-data = templatefile("${path.module}/cloud-init.yaml", { + usuario = var.usuario + repositorio = var.repositorio_git + ramo = var.ramo_git + google_api_key = var.google_api_key + cliente_id_sal = var.cliente_id_sal + }) } - depends_on = [docker_container.api] } diff --git a/infra/outputs.tf b/infra/outputs.tf index 520a70b..5735e1e 100644 --- a/infra/outputs.tf +++ b/infra/outputs.tf @@ -1,7 +1,44 @@ -output "painel" { - value = "http://localhost:${var.porta_painel}" +# outputs.tf +# O que aparece no fim do "terraform apply" (e a qualquer momento com "terraform output"). +# Os comandos são para rodar no PowerShell, na RAIZ do projeto. +# Para copiar um comando sem aspas extras: terraform output -raw comando_ssh + +locals { + ip = google_compute_address.ip.address + chave = "$HOME/.ssh/auditor_gcp" +} + +output "ip_publico" { + description = "Endereço da VM" + value = local.ip +} + +output "painel_url" { + description = "Painel do analista (Streamlit)" + value = "http://${local.ip}:8501" +} + +output "api_url" { + description = "Documentação da API (FastAPI)" + value = "http://${local.ip}:8000/docs" +} + +output "comando_ssh" { + description = "Para entrar na VM" + value = "ssh -i ${local.chave} ${var.usuario}@${local.ip}" +} + +output "comando_esperar_vm" { + description = "Espera o primeiro boot terminar (Docker, build da imagem, banco e normas)" + value = "ssh -i ${local.chave} ${var.usuario}@${local.ip} 'cloud-init status --wait; sudo docker ps'" +} + +output "comando_enviar_arquivos" { + description = "Envia o modelo e os CSVs de custo do dinheiro, que não estão no Git" + value = "scp -i ${local.chave} modelos_salvos/pacote_prever_v1.joblib data/raw/ECB_Euribor_Serie-Mensal-12-Meses_csv.csv data/raw/ECB_Euribor_Serie-Mensal-3-Meses.csv data/raw/BC-Selic_Acumulada_Mes_Anuzalizada_base252.csv data/raw/bcdata_sgs_20742.csv ${var.usuario}@${local.ip}:~/envio/" } -output "api" { - value = "http://localhost:${var.porta_api}/docs" +output "comando_completar" { + description = "Põe o modelo no lugar, carrega Euribor/Selic/BC no banco e reinicia a API" + value = "ssh -i ${local.chave} ${var.usuario}@${local.ip} 'bash ~/completar.sh'" } diff --git a/infra/terraform.tfvars.example b/infra/terraform.tfvars.example new file mode 100644 index 0000000..f35b2e9 --- /dev/null +++ b/infra/terraform.tfvars.example @@ -0,0 +1,14 @@ +# Copie para terraform.tfvars e preencha. O terraform.tfvars NUNCA vai para o Git. + +projeto_id = "auditor-preco-justo-xxxx" # gcloud config get-value project +ips_liberados = ["200.100.50.25/32"] # seu IP + "/32" (um por pessoa que vai acessar) + +# Opcionais (já têm valor padrão): +# regiao = "southamerica-east1" +# zona = "southamerica-east1-b" +# tipo_maquina = "e2-medium" +# ramo_git = "main" + +# Segredos: NÃO coloque aqui. Passe pelo terminal antes do plan/apply: +# $env:TF_VAR_google_api_key = "" +# $env:TF_VAR_cliente_id_sal = "" diff --git a/infra/variables.tf b/infra/variables.tf index 34253de..ec830c7 100644 --- a/infra/variables.tf +++ b/infra/variables.tf @@ -1,33 +1,81 @@ -variable "docker_host" { - description = "Onde está o Docker. Windows (Docker Desktop): npipe; Linux/macOS: unix:///var/run/docker.sock" +# variables.tf +# As "perguntas" que o Terraform faz. As respostas ficam em terraform.tfvars +# (fora do Git). As que têm "default" já vêm respondidas e só mudam se precisar. + +variable "projeto_id" { + description = "ID do projeto na GCP (gcloud config get-value project)" type = string - default = "npipe:////./pipe/docker_engine" } -variable "imagem_app" { - description = "Imagem da aplicação, construída antes com: docker compose build api" +variable "ips_liberados" { + description = "Quem pode acessar painel, API e SSH, no formato CIDR. Ex.: [\"200.100.50.25/32\"]" + type = list(string) +} + +variable "regiao" { + description = "Região da GCP. southamerica-east1 = São Paulo (menor latência na demo)." type = string - default = "auditor-preco-justo:latest" + default = "southamerica-east1" } -variable "google_api_key" { - description = "Chave do Gemini para o parecer. Passe por TF_VAR_google_api_key; vazia = template de reserva" +variable "zona" { + description = "Zona dentro da região (a, b ou c). Se der ZONE_RESOURCE_POOL_EXHAUSTED, troque a letra." type = string - default = "" - sensitive = true + default = "southamerica-east1-b" +} + +variable "prefixo" { + description = "Começo do nome de todos os recursos, para achá-los fácil no console" + type = string + default = "auditor" +} + +variable "tipo_maquina" { + description = "Tamanho da VM. e2-medium = 2 vCPUs e 4 GB de RAM (a e2-micro gratuita não aguenta o PyTorch)." + type = string + default = "e2-medium" +} + +variable "tamanho_disco_gb" { + description = "Disco da VM. As imagens Docker com bibliotecas de ML ocupam espaço." + type = number + default = 64 +} + +variable "usuario" { + description = "Usuário Linux criado na VM (é o que vai no ssh e no scp)" + type = string + default = "auditor" +} + +variable "caminho_chave_publica" { + description = "Arquivo .pub da chave SSH criada com ssh-keygen" + type = string + default = "~/.ssh/auditor_gcp.pub" +} + +variable "repositorio_git" { + description = "Repositório clonado pela VM (é público, então não precisa de senha)" + type = string + default = "https://github.com/yaMimis/Auditor-Project.git" } -variable "porta_api" { - type = number - default = 8000 +variable "ramo_git" { + description = "Ramo que a VM vai rodar" + type = string + default = "main" } -variable "porta_painel" { - type = number - default = 8501 +variable "google_api_key" { + description = "Chave do Gemini para o parecer. Passe por TF_VAR_google_api_key; vazia = template de reserva" + type = string + default = "" + sensitive = true # o Terraform não mostra o valor na tela } -variable "porta_banco" { - type = number - default = 5432 +variable "cliente_id_sal" { + description = "Chave de pseudonimização do grupo. Passe por TF_VAR_cliente_id_sal (opcional na VM)" + type = string + default = "" + sensitive = true } diff --git a/infra/versions.tf b/infra/versions.tf index ee82e0f..4aa7e81 100644 --- a/infra/versions.tf +++ b/infra/versions.tf @@ -1,14 +1,21 @@ +# versions.tf +# "Com quem o Terraform vai conversar": aqui, com a Google Cloud. +# O login vem do "gcloud auth application-default login"; nenhuma senha fica neste arquivo. +# (Substitui o versions.tf antigo, que usava o provider Docker local.) + terraform { required_version = ">= 1.6" + required_providers { - docker = { - source = "kreuzwerker/docker" - version = "~> 3.0" + google = { + source = "hashicorp/google" # plugin oficial da Google Cloud + version = ">= 6.0, < 8.0" # 6.x ou 7.x (evita surpresas de versão nova) } } } -# Docker local: nenhuma conta em nuvem, nenhum custo. -provider "docker" { - host = var.docker_host +provider "google" { + project = var.projeto_id + region = var.regiao + zone = var.zona }