diff --git a/PASSO_A_PASSO.md b/PASSO_A_PASSO.md new file mode 100644 index 0000000..c166fbd --- /dev/null +++ b/PASSO_A_PASSO.md @@ -0,0 +1,295 @@ +# ☁️ ACTA Platform — guia de implantação + +## 📌 Objetivo e escopo + +Este guia acompanha o [README](README.md): use o README para conhecer a plataforma e este arquivo para preparar e executar uma implantação do zero. As instruções abaixo refletem os scripts e manifests versionados neste repositório. + +O processo cria recursos cobrados na AWS e publica serviços acessíveis pela internet. Uma instância EC2 executa K3s em nó único; não há alta disponibilidade. Para produção crítica, avalie redundância, backups, recuperação, observabilidade e armazenamento remoto do estado Terraform antes de usar este desenho. + +## 🧭 Fluxo + +1. Preparar ferramentas, credenciais e configurações. +2. Validar o repositório e revisar o plano Terraform. +3. Provisionar a infraestrutura e instalar o cluster. +4. Configurar acesso ao GHCR e autenticação do Infisical. +5. Sincronizar os manifests pelo Argo CD. +6. Verificar workloads, certificados e URLs. + +## ✅ Preparar o ambiente + +Este procedimento usa **Windows PowerShell**. As ferramentas abaixo atendem a partes diferentes do fluxo; instale o conjunto necessário à tarefa que vai executar. Para provisionar e implantar a plataforma, são necessários todos os itens marcados como implantação. + +| Ferramenta | Necessária para | Como confirmar | +| --- | --- | --- | +| AWS CLI | Confirmar a conta AWS antes do Terraform. | `aws --version` | +| Terraform | Validar e criar a VPC, EC2, Security Group e Elastic IP. A CI fixa a versão 1.13.3. | `terraform version` | +| kubectl | Renderizar manifests, configurar o cluster e acompanhar workloads. O Docker Desktop também pode instalá-lo. | `kubectl version --client` | +| OpenSSH Client | Conectar à EC2 e enviar o bootstrap do K3s. | `ssh -V` | +| GitHub CLI | Confirmar acesso ao GHCR e gerar a credencial temporária de pull usada no cluster. | `gh --version` e `gh auth status` | +| yq | Necessária apenas para reproduzir localmente a checagem estrutural da Application Argo CD feita pela CI. | `yq --version` | +| kubeconform | Necessária apenas para validar localmente os schemas dos manifests Kubernetes. | `kubeconform -v` | +| Git | Clonar o repositório e trabalhar com branches e Pull Requests. | `git --version` | + +Terraform deve ser versão 1.5 ou superior, conforme `required_version`; para reproduzir a CI, use Terraform 1.13.3. As versões de ferramentas e actions usadas na CI estão em [`.github/workflows/teste-infra.yml`](.github/workflows/teste-infra.yml). Docker não é necessário para implantar imagens que já estão publicadas; ele só é necessário se você for construir imagens localmente. + +### Instalar ferramentas no Windows + +Abra o PowerShell como seu usuário. O `winget` instala os pacotes disponíveis na fonte configurada no Windows: + +```powershell +winget install --id Git.Git --exact +winget install --id Amazon.AWSCLI --exact +winget install --id Hashicorp.Terraform --exact +winget install --id Kubernetes.kubectl --exact +winget install --id GitHub.cli --exact +winget install --id MikeFarah.yq --exact +winget install --id YannHamon.kubeconform --exact +``` + +O OpenSSH Client pode ser instalado em **Configurações > Sistema > Recursos opcionais > Exibir recursos**. Em seguida, procure **OpenSSH Client** e instale. Se o comando `ssh` já existir, essa etapa está concluída. Feche e abra o PowerShell para atualizar o `PATH`, depois execute os comandos da tabela para confirmar cada instalação. + +### Acessos e recursos necessários para implantar + +Antes do provisionamento, prepare: + +- credenciais AWS com permissão para criar os recursos declarados em `infra/aws/main.tf`; +- um EC2 Key Pair existente na região de destino e sua chave privada local; +- o IPv4 público de quem administrará o cluster, em formato `/32` para restringir SSH e API Kubernetes; +- credenciais Universal Auth do Infisical e os segredos exigidos pelos manifests em `apps/config/`; +- imagens das APIs e do worker atualmente declarados, publicadas nos registros configurados; +- GitHub CLI autenticado com permissão `read:packages` no pacote privado `ghcr.io/appacta/acta-import-api`. + +Não inclua tokens, segredos, arquivos de estado ou chave privada nos comandos, no Git ou neste guia. Para validar Terraform e manifests sem criar recursos, não é necessário autenticar na AWS nem configurar os segredos das aplicações. + +## 1. Baixar e validar o repositório + +Clone o repositório e entre na pasta: + +```powershell +git clone https://github.com/AppActa/acta-platform.git +Set-Location acta-platform +``` + +Confirme as ferramentas necessárias ao fluxo que você executará. Para implantação completa, verifique todas as linhas da tabela acima: + +```powershell +aws --version +terraform version +kubectl version --client +ssh -V +gh --version +``` + +Selecione o perfil AWS nomeado desta execução e confira o alvo antes de criar recursos. Defina `AWS_PROFILE` na sessão para que o script e o provider do Terraform usem a mesma identidade: + +```powershell +$AwsProfile = "" +$env:AWS_PROFILE = $AwsProfile +aws sts get-caller-identity --profile $AwsProfile +aws configure get region --profile $AwsProfile +gh auth status +``` + +A conta e região retornadas devem ser as pretendidas. Mantenha o perfil selecionado nessa mesma sessão ao executar `deploy.ps1`. Não prossiga se não puder confirmar o alvo. + +Valide o código sem provisionar: + +```powershell +terraform -chdir=infra/aws fmt -check +terraform -chdir=infra/aws init -backend=false -input=false +terraform -chdir=infra/aws validate +kubectl kustomize apps +``` + +Para validar os schemas Kubernetes, instale `kubeconform` e execute: + +```powershell +kubectl kustomize apps | kubeconform -strict -summary -skip InfisicalSecret,ClusterIssuer - +``` + +Para reproduzir a validação estrutural da Application Argo CD, instale `yq` e execute a mesma expressão usada pela CI: + +```powershell +yq -e '.apiVersion == "argoproj.io/v1alpha1" and .kind == "Application" and .metadata.namespace == "argocd" and .spec.source.repoURL == "https://github.com/AppActa/acta-platform.git" and .spec.source.targetRevision == "main" and .spec.source.path == "apps" and .spec.destination.server == "https://kubernetes.default.svc" and .spec.destination.namespace == "acta-prod" and .spec.syncPolicy.automated.prune == true and .spec.syncPolicy.automated.selfHeal == true' argocd/prod.yaml +``` + +Essas verificações são estáticas: não criam recursos e não comprovam conectividade AWS, disponibilidade de serviços ou funcionamento de integrações externas. + +## 2. Configurar Terraform + +Crie `infra/aws/terraform.tfvars` localmente. Substitua todos os placeholders antes de continuar: + +```hcl +aws_region = "" +key_name = "" +admin_cidr = "/32" +instance_type = "t3.medium" +``` + +O Key Pair precisa existir na mesma região. Coloque a chave privada correspondente em `.aws/labsuser.pem`, caminho esperado pelo script de deploy. Restrinja permissões do arquivo à sua conta de usuário. `terraform.tfvars`, estado e chave são arquivos locais ignorados pelo Git. + +Confira seu IP público por um meio confiável e use somente esse endereço com sufixo `/32`; não libere SSH ou API Kubernetes para `0.0.0.0/0`. + +## 3. Revisar e aplicar a infraestrutura + +O script executa `aws sts get-caller-identity`, inicializa e valida Terraform, mostra o plano e pede a confirmação literal `APLICAR`. Antes de iniciar, confirme que a chave privada está em `.aws/labsuser.pem`, pois este caminho está fixo no script. Na raiz do repositório: + +```powershell +.\infra\aws\deploy.ps1 +``` + +Leia o plano antes de confirmar. Confira conta, região, quantidade de recursos, ações de substituição ou remoção e custos estimados. O script aplica o Terraform somente após a confirmação. + +Para inspecionar o estado e os outputs depois da criação: + +```powershell +terraform -chdir=infra/aws output +terraform -chdir=infra/aws output api_urls +``` + +Guarde o arquivo de estado Terraform em local persistente e seguro. Este repositório usa estado local em `infra/aws/terraform.tfstate`; perder o estado pode impedir atualizações seguras da infraestrutura. Não compartilhe o estado: ele pode conter dados sensíveis. O `.gitignore` exclui o estado e os planos Terraform do Git; ainda assim, confira `git status` antes de publicar alterações. + +## 4. Acompanhar bootstrap do cluster + +Durante a execução, o script aguarda a porta SSH, executa `infra/aws/bootstrap-k3s.sh` e configura K3s, Argo CD, cert-manager, Helm, Infisical Secrets Operator e o namespace `acta-prod`. Em seguida, salva o kubeconfig em `~/.kube/acta-aws.yaml` e configura `KUBECONFIG` para a sessão atual. O script continua diretamente às etapas de GHCR, Infisical, GitOps e rollout; não o interrompa após o bootstrap. + +Se o script terminar antes das etapas posteriores, defina o kubeconfig na sessão e confira o cluster: + +```powershell +$env:KUBECONFIG = Join-Path $env:USERPROFILE ".kube\acta-aws.yaml" +kubectl get nodes +kubectl get pods -n argocd +kubectl get pods -n cert-manager +kubectl get pods -n infisical-operator-system +``` + +O nó precisa estar `Ready`; os componentes precisam estar disponíveis antes de avançar. + +## 5. Configurar acesso ao GHCR e Infisical + +O script usa GitHub CLI para validar acesso ao pacote privado `ghcr.io/appacta/acta-import-api` e cria o Secret `ghcr-pull` no cluster. Verifique antes: + +```powershell +gh auth status +gh api /orgs/AppActa/packages/container/acta-import-api --jq .name +``` + +A autenticação deve ter permissão de leitura de pacotes. Não copie nem registre o token retornado por `gh auth token`. + +O script solicita o Client ID e Client Secret do Infisical. Insira-os somente nos prompts interativos quando solicitados; o campo do segredo é oculto. O valor é enviado ao Kubernetes como Secret `infisical-universal-auth`. Configure previamente no Infisical os segredos exigidos pelos manifests existentes em `apps/config/`, nos caminhos e ambiente referenciados neles. Obtenha cada valor diretamente no projeto Infisical autorizado para o ambiente de destino; use placeholders em exemplos locais e nunca copie os valores para o Git ou para este guia. Para conferir a sincronização, execute `kubectl get infisicalsecret -n acta-prod` e confirme que os Secrets referenciados pelos Deployments foram criados. + +### Estado de AI e MCP neste checkout + +Os manifests `apps/ai.yaml`, `apps/mcp.yaml`, `apps/config/ai-secrets.yaml` e `apps/config/mcp-secrets.yaml` estão listados em `apps/kustomization.yaml`. Antes de sincronizá-los, confirme que a imagem MCP está publicada no GHCR e que os caminhos e nomes referenciados existem no Infisical. O script de implantação aguarda os seis Deployments e os certificados, verifica `/health` do MCP e imprime o endereço público do protocolo MCP. + +Para preparar essa integração, os caminhos planejados nos manifests são `/ai` e `/mcp`, no ambiente `prod`; confirme que existem no projeto autorizado e que contêm os nomes exigidos pelo código antes de sincronizar os Secrets. Não registre valores reais em arquivos, comandos ou documentação. A chave compartilhada `ACTA_MCP_API_KEY` precisa ser a mesma nos dois serviços, pois o AI a envia como Bearer e o MCP a valida; confirme essa igualdade diretamente no ambiente antes de habilitar a integração. + +## 6. Sincronizar aplicações com Argo CD + +O script aplica `argocd/prod.yaml`, constrói os hosts a partir do Elastic IP e os configura na Application `acta-prod`. Para o MCP, ele usa `https://mcp..sslip.io/mcp` em `ACTA_MCP_URL`; o sufixo `/mcp` corresponde ao endpoint Streamable HTTP exposto pelo código de `acta-mcp`. `/health` é somente a rota de disponibilidade. O endereço depende do IP público retornado pelo Terraform e é impresso pelo script. O Argo CD acompanha a branch `main` e reconcilia os manifests de `apps/`. + +Confira o estado: + +```powershell +kubectl get application acta-prod -n argocd +kubectl get applications -n argocd +``` + +A sincronização automatizada usa `prune` e `selfHeal`: recursos removidos do Git podem ser removidos do cluster, e alterações manuais podem ser revertidas. + +### Retomar uma instância existente + +Use este procedimento quando a EC2 que já hospeda o cluster estiver parada. Ele retoma a instância existente; não recria recursos, altera o estado Terraform, executa bootstrap ou sincroniza o Argo CD. + +Confirme a conta, região e o único alvo pelas tags do projeto antes de iniciar: + +```powershell +$AwsProfile = $env:AWS_PROFILE +$AwsRegion = aws configure get region --profile $AwsProfile +aws sts get-caller-identity --profile $AwsProfile +aws ec2 describe-instances --profile $AwsProfile --region $AwsRegion ` + --filters "Name=tag:Project,Values=ACTA" "Name=tag:Name,Values=acta-prod-k3s" ` + --query 'Reservations[].Instances[].{Id:InstanceId,State:State.Name,PublicIp:PublicIpAddress,Key:KeyName}' --output table +``` + +Confira a conta e selecione o ID exato da instância esperada. Se a consulta não retornar exatamente um alvo esperado, pare. Inicie somente quando o estado for `stopped`: + +```powershell +$InstanceId = "" +aws ec2 start-instances --profile $AwsProfile --region $AwsRegion --instance-ids $InstanceId +aws ec2 wait instance-running --profile $AwsProfile --region $AwsRegion --instance-ids $InstanceId +aws ec2 wait instance-status-ok --profile $AwsProfile --region $AwsRegion --instance-ids $InstanceId +``` + +Depois, use o IP atual mostrado pela consulta e a chave privada correspondente ao Key Pair confirmado para verificar o cluster por SSH: + +```powershell +$PrivateKey = "" +$PublicIp = "" +ssh -i $PrivateKey "ubuntu@$PublicIp" "sudo kubectl get nodes" +ssh -i $PrivateKey "ubuntu@$PublicIp" "sudo kubectl get deployments,pods -n acta-prod" +``` + +Confirme o nó `Ready` e os seis Deployments declarados no Kustomize conforme a seção 7. Se probes, Pods ou eventos indicarem falha, registre o estado e siga o diagnóstico da etapa ativa; não repita bootstrap, aplique Terraform nem force sincronização GitOps como tentativa genérica de recuperação. + +## 7. Verificar serviços e certificados + +O script aguarda os Deployments e certificados e faz requisições HTTPS às APIs. Se estiver verificando manualmente: + +```powershell +kubectl rollout status deployment/acta-pg-api -n acta-prod --timeout=15m +kubectl rollout status deployment/acta-mongo-api -n acta-prod --timeout=15m +kubectl rollout status deployment/acta-import-api -n acta-prod --timeout=15m +kubectl rollout status deployment/acta-import-worker -n acta-prod --timeout=15m +kubectl rollout status deployment/acta-ai -n acta-prod --timeout=15m +kubectl rollout status deployment/acta-mcp -n acta-prod --timeout=15m +kubectl get certificates -n acta-prod +kubectl get pods,services,ingress -n acta-prod +terraform -chdir=infra/aws output api_urls +``` + +Aguarde os certificados ficarem `Ready` e os seis Deployments declarados concluírem o rollout. Use as URLs reportadas pelos outputs do Terraform para testar as APIs públicas. O script também imprime a URL do MCP; para chamadas de protocolo, use o endereço terminado em `/mcp`, e reserve `/health` para a verificação de disponibilidade: + +- PostgreSQL API: `/api/v1/health` +- Mongo API: `/api/v1/health` +- Import API: `/docs` +- MCP: `https://mcp..sslip.io/mcp` + +Exemplo para verificar uma URL: + +```powershell +$urls = terraform -chdir=infra/aws output -json api_urls | ConvertFrom-Json +Invoke-WebRequest "$($urls.pg_api)/api/v1/health" +Invoke-WebRequest "$($urls.mongo_api)/api/v1/health" +Invoke-WebRequest "$($urls.import_api)/docs" +$publicIp = (terraform -chdir=infra/aws output -raw public_ip).Trim() +$mcpHost = "mcp.$($publicIp.Replace('.', '-')).sslip.io" +Invoke-WebRequest "https://$mcpHost/health" +``` + +Uma resposta HTTP de sucesso confirma apenas a disponibilidade do endpoint consultado. Ela não valida o fluxo completo de importação. Faça um teste funcional controlado apenas com dados e arquivos autorizados, avaliando os efeitos no banco e no armazenamento de arquivos. + +## 🩺 Diagnóstico rápido + +| Sintoma | Verificação inicial | +| --- | --- | +| AWS aponta para conta ou região inesperada | Confira `aws sts get-caller-identity`, perfil ativo e `aws_region` em `terraform.tfvars`. | +| Terraform informa recurso existente ou divergência | Preserve o estado original e revise `terraform plan`; não apague o estado nem recursos manualmente. | +| SSH não conecta | Confirme o IP administrativo em `admin_cidr`, a chave privada e o Key Pair da região. | +| Pods ficam em `ImagePullBackOff` | Confirme acesso GHCR, validade do login da CLI e presença do Secret `ghcr-pull` na ServiceAccount. | +| `InfisicalSecret` não fica pronto | Confira credenciais Universal Auth, projeto, ambiente, caminho e nomes dos segredos referenciados nos manifests. | +| Certificado não fica `Ready` | Confira DNS/host gerado, portas públicas 80/443 e Ingress; consulte `kubectl describe certificate -n acta-prod` e `kubectl get events -n cert-manager`. | +| API retorna erro de dependência | Confira os Secrets sincronizados e os logs do Deployment afetado com `kubectl logs -n acta-prod deployment/`. | + +## 🧹 Encerrar o ambiente + +Recursos AWS continuam gerando custos após o fim de credenciais temporárias ou da sessão de laboratório. Para remover recursos, consulte o plano Terraform no mesmo workspace e só aplique a destruição quando houver autorização explícita. A remoção da EC2 não apaga automaticamente bancos ou serviços gerenciados fora desta configuração. + +## 📚 Referências + +- [README do ACTA Platform](README.md): visão geral, arquitetura, tecnologias e fluxo GitOps. +- [Workflow de validação da infraestrutura](.github/workflows/teste-infra.yml). +- [Script de provisionamento e implantação](infra/aws/deploy.ps1). +- [Bootstrap do cluster](infra/aws/bootstrap-k3s.sh). +- [Variáveis Terraform](infra/aws/variables.tf) e [recursos AWS](infra/aws/main.tf). +- [Application Argo CD](argocd/prod.yaml) e [manifests Kubernetes](apps/). \ No newline at end of file diff --git a/README.md b/README.md index ff32a03..d6215a9 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ O fluxo separa duas responsabilidades: 1. o Terraform cria a rede, a instância EC2 e o acesso ao cluster; 2. o Argo CD acompanha a pasta [`apps/`](apps/) na branch `main` e reconcilia os workloads no namespace `acta-prod`. -Atualmente, o estado GitOps inclui `acta-pg-api`, `acta-mongo-api`, `acta-import-api` e o worker de importação. As imagens são obtidas do GitHub Container Registry com tags imutáveis baseadas no SHA de cada serviço. +O estado GitOps inclui AI, MCP, as APIs e o worker de importação. O deploy configura o AI para acessar o MCP em `https://mcp..sslip.io/mcp`. As imagens e os segredos necessários devem estar disponíveis no GHCR e no Infisical. ## ✨ Capacidades @@ -63,7 +63,7 @@ instance_type = "t3.medium" O arquivo `terraform.tfvars`, estados, planos, credenciais AWS e chaves privadas são ignorados pelo Git. Nunca versione esses arquivos. -Os manifests `InfisicalSecret` contêm apenas referências. Os valores reais de banco, Firebase, Cloudinary, Redis e tokens devem permanecer no Infisical. +Os manifests `InfisicalSecret` contêm somente referências. Os valores reais de banco, Firebase, Cloudinary, Redis e tokens devem permanecer no Infisical. Antes de sincronizar AI e MCP, confirme que os caminhos planejados e os nomes referenciados existem no projeto Infisical autorizado. Não copie valores para o Git ou para a documentação. ## 🧪 Validação local @@ -88,7 +88,7 @@ Esses comandos verificam formatação e configuração estática. Eles não conf > [!CAUTION] > O processo cria recursos cobráveis na AWS. Revise o `terraform plan`, a conta selecionada, a região e os custos antes de confirmar a aplicação. -Na raiz do repositório, execute: +O procedimento detalhado, incluindo pré-requisitos, verificações e diagnóstico, está em [`PASSO_A_PASSO.md`](PASSO_A_PASSO.md). Na raiz do repositório, execute: ```powershell .\infra\aws\deploy.ps1 @@ -103,7 +103,7 @@ O script: 5. cria o Secret de autenticação do Infisical a partir de entradas interativas; 6. aplica [`argocd/prod.yaml`](argocd/prod.yaml) e ajusta os hosts `sslip.io` ao Elastic IP; 7. aguarda sincronização, workloads e certificados; -8. consulta os endpoints públicos e exibe o estado final. +8. consulta os endpoints públicos da PG API, Mongo API, Import API e a rota `/health` do MCP e exibe o endereço `/mcp` do MCP. O kubeconfig obtido do servidor é salvo em `~/.kube/acta-aws.yaml`. A chave privada esperada pelo script fica em `.aws/labsuser.pem`; esse caminho é local e está ignorado pelo Git. @@ -134,12 +134,14 @@ O Argo CD usa `prune` para remover recursos excluídos do estado desejado e `sel | Componente | Porta | Verificação de disponibilidade | | --- | ---: | --- | +| `acta-ai` | 8200 | `GET /health` (interno ao cluster; sem Ingress) | +| `acta-mcp` | 8000 | `GET /health` público; protocolo MCP em `/mcp` | | `acta-pg-api` | 8080 | `GET /api/v1/health` | | `acta-mongo-api` | 8081 | `GET /api/v1/health` | | `acta-import-api` | 8000 | `GET /docs` | | `acta-import-worker` | — | Processo RQ sem Service HTTP | -Cada workload usa imagem imutável do GHCR, requests e limits de CPU/memória. As APIs possuem probes de inicialização, prontidão e vida configuradas nos respectivos manifests. +Os manifests das APIs PG, Mongo e Import e do worker definem requests e limits de CPU/memória. AI e MCP têm Services internos e probes; as imagens e referências Infisical precisam estar disponíveis para os Deployments iniciarem corretamente. Durante a implantação, `infra/aws/deploy.ps1` deriva o host MCP do IP público do Terraform, configura `ACTA_MCP_URL` no Deployment do AI para `https://mcp..sslip.io/mcp` e imprime a URL resultante. O cliente AI envia a chave Bearer configurada e o contexto de usuário/empresa esperado pelo MCP. ## 🔐 Segredos e acesso de rede @@ -170,9 +172,12 @@ flowchart LR K3S --> PG[acta-pg-api] K3S --> MG[acta-mongo-api] + K3S --> AI[acta-ai] + AI --> |HTTPS /mcp| MCP[acta-mcp] K3S --> IM[acta-import-api e worker] NET[Internet] --> ING[Traefik e cert-manager] + ING --> MCP ING --> PG ING --> MG ING --> IM @@ -188,6 +193,7 @@ Terraform prepara a infraestrutura e o cluster. Ele não substitui o Argo CD: o │ └── teste-infra.yml # CI de validação da infraestrutura ├── apps/ │ ├── config/ # referências de segredos no Infisical +│ ├── ai.yaml, mcp.yaml # Deployments e Services de AI/MCP │ ├── *-api.yaml # Deployments e Services │ ├── ingress.yaml # HTTPS, hosts e certificados │ └── kustomization.yaml # agrega o estado de produção @@ -207,4 +213,4 @@ Terraform prepara a infraestrutura e o cluster. Ele não substitui o Argo CD: o - [Repositório](https://github.com/AppActa/acta-platform) · [Licença MIT](LICENSE) · `acta.institutojef@gmail.com` - Contribuições: use *issues* e *pull requests*; há um [`PULL_REQUEST_TEMPLATE.md`](PULL_REQUEST_TEMPLATE.md). -- Autoria: Equipe ACTA. +- Autoria: Equipe ACTA. \ No newline at end of file diff --git a/apps/ai.yaml b/apps/ai.yaml new file mode 100644 index 0000000..4354231 --- /dev/null +++ b/apps/ai.yaml @@ -0,0 +1,90 @@ +apiVersion: apps/v1 +kind: Deployment + +metadata: + name: acta-ai + annotations: + secrets.infisical.com/auto-reload: "true" + labels: + app.kubernetes.io/name: acta-ai + app.kubernetes.io/part-of: acta + +spec: + replicas: 1 + revisionHistoryLimit: 1 + + selector: + matchLabels: + app.kubernetes.io/name: acta-ai + + template: + metadata: + labels: + app.kubernetes.io/name: acta-ai + app.kubernetes.io/part-of: acta + + spec: + containers: + - name: acta-ai + image: ghcr.io/appacta/acta-ai:c308b670d003b36585f53eea084a4b228356aea5 + imagePullPolicy: IfNotPresent + + ports: + - name: http + containerPort: 8200 + protocol: TCP + + envFrom: + - secretRef: + name: acta-ai-env + + env: + - name: PORT + value: "8200" + + # deploy.ps1 substitui o template pela url do mcp + - name: ACTA_MCP_URL + value: https://mcp.0-0-0-0.sslip.io/mcp + + startupProbe: + tcpSocket: + port: http + periodSeconds: 10 + failureThreshold: 60 + + readinessProbe: + httpGet: + path: /health + port: http + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + + livenessProbe: + httpGet: + path: /health + port: http + periodSeconds: 20 + timeoutSeconds: 5 + failureThreshold: 3 +--- +apiVersion: v1 +kind: Service + +metadata: + name: acta-ai + labels: + app.kubernetes.io/name: acta-ai + app.kubernetes.io/part-of: acta + +spec: + type: ClusterIP + + selector: + app.kubernetes.io/name: acta-ai + + ports: + - name: http + port: 8200 + targetPort: http + protocol: TCP \ No newline at end of file diff --git a/apps/config/ai-secrets.yaml b/apps/config/ai-secrets.yaml new file mode 100644 index 0000000..7f474f9 --- /dev/null +++ b/apps/config/ai-secrets.yaml @@ -0,0 +1,36 @@ +apiVersion: secrets.infisical.com/v1alpha1 +kind: InfisicalSecret + +metadata: + name: acta-ai-secrets + +spec: + hostAPI: https://app.infisical.com/api + resyncInterval: 60 + + authentication: + universalAuth: + credentialsRef: + secretName: infisical-universal-auth + secretNamespace: acta-prod + secretsScope: + projectId: 051dcf5e-00ed-4d46-8b36-baad0a621f5d + envSlug: prod + secretsPath: /ai + recursive: false + + managedKubeSecretReferences: + - secretName: acta-ai-env + secretNamespace: acta-prod + creationPolicy: Owner + + template: + includeAllSecrets: false + data: + OPENAI_API_KEY: "{{ .OPENAI_API_KEY.Value }}" + JEV_API_KEY: "{{ .JEV_API_KEY.Value }}" + MONGODB_URI: "{{ .MONGODB_URI.Value }}" + GEMINI_API_KEY: "{{ .GEMINI_API_KEY.Value }}" + QDRANT_CLUSTER_ENDPOINT: "{{ .QDRANT_CLUSTER_ENDPOINT.Value }}" + QDRANT_API_KEY: "{{ .QDRANT_API_KEY.Value }}" + ACTA_MCP_API_KEY: "{{ .ACTA_MCP_API_KEY.Value }}" \ No newline at end of file diff --git a/apps/config/import-api-secrets.yaml b/apps/config/import-api-secrets.yaml index cebf6bc..458c778 100644 --- a/apps/config/import-api-secrets.yaml +++ b/apps/config/import-api-secrets.yaml @@ -32,7 +32,6 @@ spec: CLOUDINARY_API_KEY: "{{ .CLOUDINARY_API_KEY.Value }}" CLOUDINARY_API_SECRET: "{{ .CLOUDINARY_API_SECRET.Value }}" ACTA_PG_API_URL: "{{ .ACTA_PG_API_URL.Value }}" - ACTA_IA_TOKEN: "{{ .ACTA_IA_TOKEN.Value }}" REDIS_HOST: "{{ .REDIS_HOST.Value }}" REDIS_PORT: "{{ .REDIS_PORT.Value }}" REDIS_DB: "{{ .REDIS_DB.Value }}" diff --git a/apps/config/mcp-secrets.yaml b/apps/config/mcp-secrets.yaml new file mode 100644 index 0000000..897fab0 --- /dev/null +++ b/apps/config/mcp-secrets.yaml @@ -0,0 +1,38 @@ +apiVersion: secrets.infisical.com/v1alpha1 +kind: InfisicalSecret + +metadata: + name: acta-mcp-secrets + +spec: + hostAPI: https://app.infisical.com/api + resyncInterval: 60 + + authentication: + universalAuth: + credentialsRef: + secretName: infisical-universal-auth + secretNamespace: acta-prod + secretsScope: + projectId: 051dcf5e-00ed-4d46-8b36-baad0a621f5d + envSlug: prod + secretsPath: /mcp + recursive: false + + managedKubeSecretReferences: + - secretName: acta-mcp-env + secretNamespace: acta-prod + creationPolicy: Owner + + template: + includeAllSecrets: false + data: + ACTA_MCP_API_KEY: "{{ .ACTA_MCP_API_KEY.Value }}" + DATABASE_URL: "{{ .DATABASE_URL.Value }}" + MONGODB_URI: "{{ .MONGODB_URI.Value }}" + GEMINI_API_KEY: "{{ .GEMINI_API_KEY.Value }}" + QDRANT_CLUSTER_ENDPOINT: "{{ .QDRANT_CLUSTER_ENDPOINT.Value }}" + QDRANT_API_KEY: "{{ .QDRANT_API_KEY.Value }}" + CLOUDINARY_CLOUD_NAME: "{{ .CLOUDINARY_CLOUD_NAME.Value }}" + CLOUDINARY_API_KEY: "{{ .CLOUDINARY_API_KEY.Value }}" + CLOUDINARY_API_SECRET: "{{ .CLOUDINARY_API_SECRET.Value }}" \ No newline at end of file diff --git a/apps/config/pg-api-secrets.yaml b/apps/config/pg-api-secrets.yaml index 68ee68b..52ee819 100644 --- a/apps/config/pg-api-secrets.yaml +++ b/apps/config/pg-api-secrets.yaml @@ -32,6 +32,8 @@ spec: DB_PASSWORD: "{{ .DB_PASSWORD.Value }}" FIREBASE_PROJECT_ID: "{{ .FIREBASE_PROJECT_ID.Value }}" CORS_ALLOWED_ORIGINS: "{{ .CORS_ALLOWED_ORIGINS.Value }}" + BREVO_SENDER_EMAIL: "{{ .BREVO_SENDER_EMAIL.Value }}" + BREVO_API_KEY: "{{ .BREVO_API_KEY.Value }}" - secretName: firebase-admin secretNamespace: acta-prod @@ -40,4 +42,4 @@ spec: template: includeAllSecrets: false data: - service-account.json: "{{ .FIREBASE_SERVICE_ACCOUNT_JSON.Value }}" + service-account.json: "{{ .FIREBASE_SERVICE_ACCOUNT_JSON.Value }}" \ No newline at end of file diff --git a/apps/ingress.yaml b/apps/ingress.yaml index 0f9df39..41baa7a 100644 --- a/apps/ingress.yaml +++ b/apps/ingress.yaml @@ -82,4 +82,28 @@ spec: tls: - hosts: - import-api.0-0-0-0.sslip.io - secretName: import-api-tls \ No newline at end of file + secretName: import-api-tls +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: mcp + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod +spec: + ingressClassName: traefik + rules: + - host: mcp.0-0-0-0.sslip.io + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: acta-mcp + port: + number: 8000 + tls: + - hosts: + - mcp.0-0-0-0.sslip.io + secretName: mcp-tls \ No newline at end of file diff --git a/apps/kustomization.yaml b/apps/kustomization.yaml index 47e0da1..23de701 100644 --- a/apps/kustomization.yaml +++ b/apps/kustomization.yaml @@ -4,10 +4,14 @@ kind: Kustomization namespace: acta-prod resources: + - ai.yaml + - mcp.yaml - pg-api.yaml - mongo-api.yaml - import-api.yaml - ingress.yaml + - config/ai-secrets.yaml + - config/mcp-secrets.yaml - config/pg-api-secrets.yaml - config/mongo-api-secrets.yaml - config/import-api-secrets.yaml \ No newline at end of file diff --git a/apps/mcp.yaml b/apps/mcp.yaml new file mode 100644 index 0000000..82a2420 --- /dev/null +++ b/apps/mcp.yaml @@ -0,0 +1,96 @@ +apiVersion: apps/v1 +kind: Deployment + +metadata: + name: acta-mcp + annotations: + secrets.infisical.com/auto-reload: "true" + labels: + app.kubernetes.io/name: acta-mcp + app.kubernetes.io/part-of: acta + +spec: + replicas: 1 + revisionHistoryLimit: 1 + + selector: + matchLabels: + app.kubernetes.io/name: acta-mcp + + template: + metadata: + labels: + app.kubernetes.io/name: acta-mcp + app.kubernetes.io/part-of: acta + + spec: + containers: + - name: acta-mcp + image: ghcr.io/appacta/acta-mcp:sha-62ed32c186a82454c1cb911be5383fcc7d16024b + imagePullPolicy: IfNotPresent + + ports: + - name: http + containerPort: 8000 + protocol: TCP + + envFrom: + - secretRef: + name: acta-mcp-env + + env: + - name: ACTA_ENV + value: production + - name: ACTA_AUTH_MODE + value: api_key + - name: ACTA_HOST + value: 0.0.0.0 + - name: PORT + value: "8000" + - name: MONGODB_DATABASE + value: acta + - name: QDRANT_FAQ_COLLECTION + value: acta_faq + + startupProbe: + tcpSocket: + port: http + periodSeconds: 10 + failureThreshold: 60 + + readinessProbe: + httpGet: + path: /health + port: http + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + + livenessProbe: + httpGet: + path: /health + port: http + periodSeconds: 20 + timeoutSeconds: 5 + failureThreshold: 3 +--- +apiVersion: v1 +kind: Service + +metadata: + name: acta-mcp + labels: + app.kubernetes.io/name: acta-mcp + app.kubernetes.io/part-of: acta + +spec: + type: ClusterIP + + selector: + app.kubernetes.io/name: acta-mcp + + ports: + - name: http + port: 8000 + targetPort: http + protocol: TCP \ No newline at end of file diff --git a/infra/aws/deploy.ps1 b/infra/aws/deploy.ps1 index 0ab70f3..71830ff 100644 --- a/infra/aws/deploy.ps1 +++ b/infra/aws/deploy.ps1 @@ -80,6 +80,7 @@ $hosts = @{ "pg-api" = "pg-api.$ipHost.sslip.io" "mongo-api" = "mongo-api.$ipHost.sslip.io" "import-api" = "import-api.$ipHost.sslip.io" + "mcp" = "mcp.$ipHost.sslip.io" } # troca os hosts genéricos pelos endereços finais @@ -97,6 +98,17 @@ $patches = foreach ($name in $hosts.Keys) { } } +# configura a ia para chamar o endpoint mcp público deste deploy +$mcpUrl = "https://$($hosts['mcp'])/mcp" +$patches += @{ + target = @{ group = "apps"; version = "v1"; kind = "Deployment"; name = "acta-ai" } + patch = @" +- op: replace + path: /spec/template/spec/containers/0/env/1/value + value: $mcpUrl +"@ +} + $applicationPatch = @{ spec = @{ source = @{ @@ -113,16 +125,20 @@ kubectl rollout status deployment/acta-pg-api -n acta-prod --timeout=15m kubectl rollout status deployment/acta-mongo-api -n acta-prod --timeout=15m kubectl rollout status deployment/acta-import-api -n acta-prod --timeout=15m kubectl rollout status deployment/acta-import-worker -n acta-prod --timeout=15m +kubectl rollout status deployment/acta-ai -n acta-prod --timeout=15m +kubectl rollout status deployment/acta-mcp -n acta-prod --timeout=15m kubectl wait certificate --all -n acta-prod --for=condition=Ready --timeout=10m # chama os endereços públicos para confirmar que estão respondendo Invoke-WebRequest "https://$($hosts['pg-api'])/api/v1/health" -UseBasicParsing Invoke-WebRequest "https://$($hosts['mongo-api'])/api/v1/health" -UseBasicParsing Invoke-WebRequest "https://$($hosts['import-api'])/docs" -UseBasicParsing +Invoke-WebRequest "https://$($hosts['mcp'])/health" -UseBasicParsing # mostra o estado final da implantação kubectl get applications -n argocd kubectl get pods,secrets,ingress,certificate -n acta-prod Write-Host "Implantacao concluida" +Write-Host "MCP: https://$($hosts['mcp'])/mcp" terraform -chdir=infra/aws output api_urls \ No newline at end of file