From 97f4e88e3c399101d65152ca1d5eaeacc1a6be3b Mon Sep 17 00:00:00 2001 From: nitai Date: Wed, 10 Jun 2026 12:18:00 -0300 Subject: [PATCH 1/2] =?UTF-8?q?blog:=20MLflow=20no=20DGB=20=E2=80=94=20pla?= =?UTF-8?q?taforma=20de=20MLOps=20atr=C3=A1s=20do=20IAP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Post sobre o novo servidor MLflow compartilhado (Cloud Run + IAP), a lib dgb-mlflow (JWT auto-assinado), os dois caminhos da arquitetura (metadados via IAP, artefatos direto no GCS), e os gotchas de borda. Embute o deck de 20 slides via iframe e linka a página de slides. --- .../2026-06-10-mlflow-plataforma-mlops.md | 151 ++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 docs/blog/posts/2026-06-10-mlflow-plataforma-mlops.md diff --git a/docs/blog/posts/2026-06-10-mlflow-plataforma-mlops.md b/docs/blog/posts/2026-06-10-mlflow-plataforma-mlops.md new file mode 100644 index 0000000..242f6e4 --- /dev/null +++ b/docs/blog/posts/2026-06-10-mlflow-plataforma-mlops.md @@ -0,0 +1,151 @@ +--- +date: 2026-06-10 +authors: + - nitai +categories: + - MLOps + - Data Science + - Infraestrutura +title: "MLflow no DGB: uma plataforma de experimentos atrás do IAP — e o JWT que a lib assina sozinha" +hide: + - toc +--- + +# MLflow no DGB: uma plataforma de experimentos atrás do IAP — e o JWT que a lib assina sozinha + +Até esta semana, cada experimento de ciência de dados do DGB vivia e morria na máquina de quem o rodou: métricas num notebook, o modelo num `.pkl` perdido numa pasta, e nenhum rastro compartilhado de "o que foi treinado, com quais dados, e quão bom ficou". A plataforma ganhou agora um **servidor MLflow compartilhado** — tracking de experimentos, Model Registry e ferramentas de GenAI — rodando em Cloud Run atrás do IAP, com uma biblioteca cliente que faz a autenticação sumir: `import dgb_mlflow; dgb_mlflow.configure()` e o resto é o `mlflow` de sempre. Este post conta o que foi construído, e o gotcha de autenticação que virou a peça central do desenho. + + + +!!! tip "Os slides desta entrega" + Há uma apresentação de 20 slides cobrindo arquitetura, uso e bastidores — + embutida abaixo e também publicada em + ****. + +
+ +
+ +

⛶ Abrir os slides em tela cheia →

+ +--- + +## O problema: experimentos sem rastro + +O time de DS trabalha em duas frentes — as **VMs de desenvolvimento** (uma por pessoa, na infra GCP) e os **computadores pessoais**. Sem um lugar comum, o estado real de um modelo era folclore: ninguém conseguia comparar duas execuções, recarregar a versão que tinha ido melhor, ou auditar com que dados um classificador foi treinado. Para a pesquisa de NLP do DGB (classificação temática, embeddings, GenAI sobre as notícias), isso é dívida que cresce a cada experimento. + +A meta foi dar à plataforma **um backend único** para experimentos, métricas, artefatos e modelos versionados — self-service, com governança de acesso e custo controlado, e que começasse com **uma linha de instalação**. + +## A arquitetura: dois caminhos + +O ponto central do desenho é que o cliente fala com **dois destinos diferentes**, e isso é de propósito: + +``` + código Python + dgb-mlflow + │ + │ (1) METADADOS (2) ARTEFATOS + │ Authorization: Bearer JWT leitura/escrita DIRETA + │ aud = /* (ADC, sem proxy do servidor) + ▼ │ + ┌─────────┐ run.invoker ┌──────────────┐ │ + │ IAP │ ───────────────► │ Cloud Run │ │ + └─────────┘ │ MLflow 3.13 │ │ + └──────┬───────┘ │ + │ metadados ▼ + ┌────────▼────────┐ ┌──────────────────────────┐ + │ Cloud SQL Postgres│ │ GCS │ + │ DB mlflow (privado)│ │ inspire-7-finep- │ + └─────────────────┘ │ mlflow-artifacts │ + └──────────────────────────┘ +``` + +- **Metadados** (experimentos, runs, registry, métricas) passam pelo IAP até o servidor MLflow no Cloud Run, que persiste no **Cloud SQL Postgres**. +- **Artefatos** (os modelos, que podem ser grandes) **não** passam pelo servidor: o cliente lê e grava **direto no GCS** via ADC. O servidor nunca vira gargalo de upload de modelo. + +O servidor não tem autenticação nativa do MLflow — **o IAP é a porta**. Quem está na lista de acesso entra; o resto recebe `403` antes mesmo de chegar ao container. + +## O gotcha que virou peça central: o JWT auto-assinado + +Aqui está a parte que custou a investigação e definiu a biblioteca cliente. IAP direto no Cloud Run é GA (sem load balancer, sem custo extra) — mas o cliente OAuth que o IAP gera é **gerenciado pelo Google**, e ele **recusa ID tokens OIDC programáticos**: toda tentativa de autenticar via `id_token` devolvia `401 Invalid JWT audience`. + +A saída não foi um client OAuth próprio, e sim um **JWT auto-assinado**: a service account assina o próprio token (via `iam.signJwt`), com a audience igual à **URL do servidor seguida de `/*`** — não um "IAP client id". Esse token, injetado no header `Authorization` a cada request via o mecanismo de `request_header_provider` do MLflow, passa pelo IAP (resposta `200` confirmada). + +O ponto é que **o cientista de dados não vê nada disso**. A biblioteca **`dgb-mlflow`** esconde a complexidade inteira: + +```python +import dgb_mlflow, mlflow + +dgb_mlflow.configure() # resolve URL + assina o JWT do IAP; nada mais +mlflow.set_experiment("meu-experimento") +with mlflow.start_run(): + mlflow.log_param("lr", 0.01) + mlflow.log_metric("acc", 0.97) + mlflow.log_artifact("modelo.pkl") # vai direto ao GCS +``` + +Instala-se em uma linha, fixada na release: + +```bash +pip install "git+https://github.com/destaquesgovbr/ml-platform.git@v0.1.0#subdirectory=client" +``` + +Na dev VM, a credencial vem automática da service account da VM; no PC, basta um `gcloud auth application-default login`. O código é idêntico nos dois — muda só de onde sai a credencial. + +## As pegadinhas de borda (que viraram features) + +Construir atrás do IAP rendeu uma sequência de bordas que só apareceram com tráfego real: + +- **Conta `@gmail` é barrada no login de browser do IAP.** Vários da equipe usam conta externa à org, e o IAP bloqueia o login interativo delas na UI. A plataforma ganhou um **proxy local** (`scripts/iap_ui_proxy.py`) que injeta o JWT assinado e serve a UI em `http://localhost:5000` — acesso ao painel sem depender do login do navegador. +- **MLflow 3.x bloqueia o header `Host`.** A versão 3.x recusa hosts `*.run.app` com `Invalid Host header` (proteção contra DNS rebinding). Como o IAP já é a fronteira de segurança, a correção foi liberar o host explicitamente (`MLFLOW_SERVER_ALLOWED_HOSTS`) ([infra#195](https://github.com/destaquesgovbr/infra/pull/195)). +- **Postgres público virou privado.** O backend de metadados foi migrado para **IP privado via Direct VPC egress** ([infra#193](https://github.com/destaquesgovbr/infra/pull/193)), num piloto que abriu o caminho para tirar o IP público do resto da instância — rastreado em [infra#194](https://github.com/destaquesgovbr/infra/issues/194). +- **Scale-to-zero.** O Cloud Run roda com `min-instances=0`: sem tráfego, custa zero; o primeiro request paga um cold start. IAP é grátis. + +## O que acompanha o servidor + +A entrega não foi só o servidor; foi uma plataforma utilizável de ponta a ponta: + +- **Biblioteca `dgb-mlflow`** — pacote Python instalável via git, construída com **TDD** (32 testes, mocks de `google.auth`, sem rede real), publicada na release **v0.1.0**. +- **Projetos de exemplo que rodam de verdade** (testados E2E contra o servidor): um **tradicional** (classificação de notícias gov.br com sklearn — tracking + Model Registry, com um caminho BERT opcional) e um de **GenAI** (tracing com `@mlflow.trace`, avaliação com `mlflow.models.evaluate` e prompt registry, provider plugável). +- **Documentação** — 6 tutoriais PT-BR (getting-started PC e VM, como funciona o IAP, Model Registry, GenAI, troubleshooting) no repo, mais o [tutorial no site de docs](https://destaquesgovbr.github.io/docs/modulos/mlflow/). +- **CI/CD** — 57 testes automatizados como gate, build/deploy da imagem por Workload Identity Federation, e build do pacote com release versionada. Tudo verde. +- **Infra como código** — todo o servidor (Cloud Run + IAP + DB + bucket + IAM) foi entregue por PRs no Terraform ([infra#190](https://github.com/destaquesgovbr/infra/pull/190), [#191](https://github.com/destaquesgovbr/infra/pull/191), [#192](https://github.com/destaquesgovbr/infra/pull/192), [#193](https://github.com/destaquesgovbr/infra/pull/193), [#195](https://github.com/destaquesgovbr/infra/pull/195)). + +Um detalhe de governança que vale o registro: o acesso começou como uma **lista de e-mails** no Terraform, mas passou a aceitar também um **Google Group** ([infra#196](https://github.com/destaquesgovbr/infra/pull/196)). Onboarding de um novo membro deixou de exigir um PR de infra — basta entrar no grupo. + +## Antes e depois + +| | Antes | Depois | +|---|---|---| +| Onde vivem os experimentos | máquina de cada um, sem rastro | backend compartilhado (Cloud SQL) | +| Modelos | `.pkl` solto numa pasta | Model Registry versionado | +| Comparar runs | impossível entre pessoas | UI única de tracking | +| Autenticação | — | IAP + JWT auto-assinado (transparente) | +| Artefatos grandes | — | direto no GCS, sem proxy | +| Começar a usar | — | uma linha de `pip install` | +| Custo ocioso | — | zero (`min-instances=0`) | + +## Números + +| Métrica | Valor | +|--------|-------| +| Servidor | MLflow **3.13.0** · Cloud Run · `southamerica-east1` | +| Pacote cliente | `dgb-mlflow` **v0.1.0** · Python 3.11+ | +| Testes (gate CI) | **57** automatizados · 32 no cliente (TDD) | +| Projetos de exemplo | 2 — tradicional (sklearn/BERT) e GenAI | +| Tutoriais | 6 PT-BR no repo + 1 no site de docs | +| PRs de infra | #190 · #191 · #192 · #193 · #195 · #196 | +| Backend de metadados | Cloud SQL Postgres (IP privado) | +| Artefatos | `gs://inspire-7-finep-mlflow-artifacts` | + +## Lições + +1. **IAP no Cloud Run recusa OIDC programático — assine o seu próprio JWT.** O cliente OAuth gerenciado pelo Google não aceita `id_token`; um JWT auto-assinado (`iam.signJwt`) com `aud = /*` passa. Foi o que destravou o acesso de máquina, e a descoberta valeu por toda a investigação. +2. **A complexidade de auth pertence à biblioteca, não ao usuário.** Encapsular o JWT, o ADC e a montagem da URL atrás de `configure()` é o que faz a plataforma ser adotada: o cientista de dados escreve `mlflow` puro e nunca toca no IAP. +3. **Separe metadados de artefatos.** Deixar o cliente gravar modelos direto no GCS (sem proxy do servidor) tira o servidor do caminho crítico de upload — essencial quando os modelos são grandes. +4. **A borda só aparece com a conta real.** O bloqueio de `@gmail` no login e o `Host` recusado pelo MLflow 3.x não estavam em nenhum tutorial; apareceram ao exercitar o caminho de verdade, e cada um virou uma feature (proxy, allowed-hosts). +5. **Privatize por etapas.** Migrar só o Postgres do MLflow para IP privado foi um piloto de baixo risco que validou o Direct VPC egress antes de propor o mesmo para a instância inteira. + +A plataforma de experimentos agora existe e é uma linha de install. O próximo passo do arco é levar mais da pesquisa de NLP do DGB ([Epic docs#37](https://github.com/destaquesgovbr/docs/issues/37)) para cima dela — e tirar o IP público do Postgres de vez ([infra#194](https://github.com/destaquesgovbr/infra/issues/194)). O código, os exemplos e os tutoriais estão em **[destaquesgovbr/ml-platform](https://github.com/destaquesgovbr/ml-platform)**. From ee863bde30386120e2fd2c6a2182b4d8f9d45f6d Mon Sep 17 00:00:00 2001 From: nitai Date: Sat, 20 Jun 2026 19:52:48 -0300 Subject: [PATCH 2/2] =?UTF-8?q?docs:=20adiciona=20m=C3=B3dulo=20Gobus=20MC?= =?UTF-8?q?P?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/modulos/gobus-mcp.md | 97 +++++++++++++++++++++++++++++++++++++++ mkdocs.yml | 1 + 2 files changed, 98 insertions(+) create mode 100644 docs/modulos/gobus-mcp.md diff --git a/docs/modulos/gobus-mcp.md b/docs/modulos/gobus-mcp.md new file mode 100644 index 0000000..20ec365 --- /dev/null +++ b/docs/modulos/gobus-mcp.md @@ -0,0 +1,97 @@ +# Gobus MCP + +Servidor **MCP (Model Context Protocol)** que expõe o acervo do Destaques Gov.BR — ~300 mil artigos, grafo de entidades NER canonicalizadas e analytics por agência — como tools, resources e prompts consumíveis diretamente por LLMs. + +!!! info "Repositório" + **GitHub**: [destaquesgovbr/gobus-mcp](https://github.com/destaquesgovbr/gobus-mcp) + + **URL (produção)**: [destaquesgovbr-gobus-mcp-klvx64dufq-rj.a.run.app](https://destaquesgovbr-gobus-mcp-klvx64dufq-rj.a.run.app) + +## Visão Geral + +O Gobus MCP é a camada de acesso ao acervo do Gov.BR para agentes de IA. Um cliente MCP (Claude Desktop, Claude Code, ou qualquer cliente que implemente o protocolo) conecta ao servidor e passa a ter acesso às 300 mil publicações do portal como contexto — sem precisar conhecer GraphQL, Postgres ou Typesense. + +Toda leitura de dados passa pela [graphql-api](graphql-api.md): o servidor não abre conexões diretas aos backends. Isso centraliza rate-limiting, autenticação, analytics e validação de schema em um único ponto. + +```mermaid +flowchart LR + Claude["Claude
(Desktop / Code / LLM)"] + MCP["Gobus MCP
(Cloud Run)"] + API["graphql-api"] + PG[("Postgres")] + TS[("Typesense")] + NEO[("Neo4j")] + + Claude <-->|"MCP (stdio ou HTTP)"| MCP + MCP <-->|"HTTP GraphQL"| API + API --> PG + API --> TS + API --> NEO +``` + +## Capacidades + +| Categoria | Quantidade | Exemplos | +|-----------|:----------:|----------| +| Tools | 7 | `search_news`, `get_entity_profile`, `get_entity_network`, `get_agency_analytics`, `detect_trends` | +| Resources | 3 | `gobus://agencies`, `gobus://themes`, `gobus://platform-stats` | +| Prompts | 4 | `monitor_agency`, `trace_entity`, `weekly_digest`, `draft_press_release` | + +As tools retornam Markdown formatado — pensado para ser lido diretamente pelo LLM sem parsing adicional. + +## Papel na plataforma + +O Gobus MCP é a interface de IA do Destaques Gov.BR: + +- **Assessoras de comunicação** usam via Claude para gerar briefings diários e comparar a cobertura entre agências. +- **Pesquisadores e jornalistas** rastreiam trajetórias de entidades (pessoas, políticas, leis) e mapeiam redes institucionais. +- **Cidadãos** recebem boletins semanais resumidos em linguagem acessível. +- **Equipe de dados** usa os prompts compostos para automatizar análises recorrentes. + +## Stack + +| Camada | Tecnologia | +|--------|-----------| +| Protocolo | FastMCP 3.x (MCP) | +| Transport | stdio (local) · HTTP streamable-http (Cloud Run) | +| Cliente GraphQL | httpx async | +| Config | pydantic-settings (prefixo `GOBUS_`) | +| Deploy | Cloud Run, env vars via Terraform | + +## Conectar ao servidor + +=== "Claude Desktop / Code" + + Adicione ao `.mcp.json` do projeto ou ao settings global: + + ```json + { + "mcpServers": { + "gobus": { + "transport": "http", + "url": "https://destaquesgovbr-gobus-mcp-klvx64dufq-rj.a.run.app/mcp/" + } + } + } + ``` + +=== "Local (stdio)" + + ```json + { + "mcpServers": { + "gobus": { + "command": "python", + "args": ["-m", "gobus_mcp"], + "env": { + "GOBUS_GRAPHQL_URL": "http://localhost:8000/graphql" + } + } + } + } + ``` + +!!! tip "Documentação profunda" + A referência completa — todas as tools com parâmetros e exemplos, resources, prompts, arquitetura de transport e guia de deploy — vive co-localizada no repo do serviço: + + **→ [Documentação do Gobus MCP](https://destaquesgovbr.github.io/gobus-mcp/)** diff --git a/mkdocs.yml b/mkdocs.yml index 2ddbacf..2ecbc57 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -71,6 +71,7 @@ nav: - GrowthBook: modulos/growthbook.md - Streamlit: modulos/spaces-streamlit.md - MLflow: modulos/mlflow.md + - Gobus MCP: modulos/gobus-mcp.md - Apresentações: - Visão Geral: apresentacoes/index.md - MLflow no DGB: apresentacoes/mlflow-no-dgb/index.md