Docker/Postgres/pgvector/Redis
A entrega desta semana é uma stack local com API, PostgreSQL/pgvector e Redis, configuração de segredo e evidências de persistência. O ambiente precisa de Docker e Compose v2; o script usa Bash para escrever arquivos, inclusive no Windows via terminal compatível. Não execute em uma pasta que contenha infraestrutura de produção.
Você trabalhará com dois documentos sintéticos e uma senha de laboratório. O banco não será publicado no host, e o Redis é privado à rede Compose. A API mínima permite testar a composição de serviços, enquanto consultas executadas nos containers verificam os contratos dos dados. Não declare uma integração completa de RAG até implementar e testar a consulta a partir da API.
BashDockerPostgreSQLRedisAo terminar esta aula
- Volumes e migrações têm contratos próprios.
- Serviços precisam de testes além de status running.
- Embeddings exigem dimensão, versão e autorização.
Antes de continuar: Leitura: Docker/Postgres/pgvector/Redis
Preparar arquivos e registrar o ambiente
FundamentosCrie uma pasta exclusiva, adicione secrets/db_password.txt ao ignore e copie o script. Registre docker version e docker compose version. Revise o compose antes de subir: apenas a porta 3000 deve estar publicada, volumes devem apontar para diretórios de dados das imagens e a API monta código somente para leitura. Faça docker compose config para validar o arquivo, evitando divulgar qualquer configuração com segredo real. As tags usadas são uma conveniência didática; capture os digests das imagens para reproduzir exatamente o ambiente que você testou.
↗ What is a container?↗ Manage secrets securely in Docker Compose
Subir e verificar contratos concretos
FundamentosExecute docker compose up -d --wait e inspecione ps. Rode os comandos SQL, Redis e HTTP do exemplo, conservando as saídas. O contrato SQL exige extensão vector, duas linhas e filtragem por tenant; o Redis exige SET seguido de GET; a API exige JSON com alive:true. Se a extensão não existir, diagnostique a imagem ou a migração antes de criar um índice. Se a API não subir, confira arquivo montado e logs do processo. Um container running sem esses resultados não atende à entrega.
↗ pgvector: vector similarity search for Postgres↗ Redis streaming
mkdir -p secrets
printf '%s' 'senha-sintetica-do-lab' > secrets/db_password.txt
cat > compose.yaml <<'YAML'
services:
api:
image: node:22-alpine
working_dir: /app
volumes: ["./api.cjs:/app/api.cjs:ro"]
command: ["node", "api.cjs"]
ports: ["127.0.0.1:3000:3000"]
depends_on:
db: {condition: service_healthy}
redis: {condition: service_healthy}
db:
image: pgvector/pgvector:pg17
environment:
POSTGRES_USER: lab
POSTGRES_DB: lab
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets: [db_password]
volumes: ["pgdata:/var/lib/postgresql/data"]
healthcheck:
test: ["CMD-SHELL", "pg_isready -U lab -d lab"]
interval: 2s
timeout: 2s
retries: 20
redis:
image: redis:7-alpine
command: ["redis-server", "--appendonly", "yes"]
volumes: ["redisdata:/data"]
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 2s
timeout: 2s
retries: 20
secrets:
db_password:
file: ./secrets/db_password.txt
volumes:
pgdata:
redisdata:
YAML
cat > api.cjs <<'JS'
require('node:http').createServer((req,res)=>{
res.setHeader('Content-Type','application/json');
res.end(JSON.stringify({alive:true,path:req.url}));
}).listen(3000,'0.0.0.0');
JS
docker compose up -d --wait
docker compose exec -T db psql -U lab -d lab -v ON_ERROR_STOP=1 <<'SQL'
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE IF NOT EXISTS docs(id text PRIMARY KEY, tenant text NOT NULL, embedding vector(3));
INSERT INTO docs VALUES ('d1','t1','[1,0,0]'),('d2','t2','[0,1,0]') ON CONFLICT DO NOTHING;
SELECT id FROM docs WHERE tenant='t1' ORDER BY embedding <=> '[1,0,0]' LIMIT 1;
SQL
docker compose exec -T redis redis-cli SET lab:marker presente
docker compose exec -T redis redis-cli GET lab:marker
docker compose exec -T api node -e "fetch('http://localhost:3000/health').then(r=>r.json()).then(console.log)"
docker compose restart db redis
docker compose exec -T db pg_isready -U lab -d lab
docker compose exec -T db psql -U lab -d lab -c "SELECT count(*) FROM docs;"
docker compose exec -T redis redis-cli GET lab:markerVerifique também a dimensão: execute docker compose exec -T db psql -U lab -d lab -v ON_ERROR_STOP=1 -c "INSERT INTO docs VALUES ('bad-dimension','t1','[1,0]');". A operação deve retornar erro de dimensão e não adicionar a linha. Consulte novamente count(*) e confirme que continua dois. Essa falha é esperada e demonstra validação do tipo vector(3), não um incidente do banco.
Testar migração e falha transacional
FundamentosRepita a criação inicial e confirme que não duplica documentos. Em seguida abra uma transação que insere d3 e tenta inserir novamente d1 com a mesma chave; com ON_ERROR_STOP habilitado, o erro deve impedir uma conclusão silenciosa. Use ROLLBACK e confirme que d3 não persiste quando a operação é revertida. Escreva uma migração versionada separada que adiciona source_version e verifique o esquema no banco. Explique por que IF NOT EXISTS não basta para evoluir colunas existentes nem registrar a ordem das mudanças.
Verificar persistência e isolamento
FundamentosReinicie db e redis e aguarde readiness, repetindo count e GET. Depois faça down sem a opção de remover volumes, suba novamente e repita os testes. A expectativa permanece duas linhas e marker presente. Não use down -v para testar persistência, porque ele solicita apagar volumes do laboratório. Tente ler /run/secrets/db_password na API: o arquivo não deve estar montado ali. Isso mostra distribuição por serviço, sem provar criptografia no host. Inspecione logs para garantir que a senha sintética não foi impressa.
Diagnosticar dependências e fechar a entrega
FundamentosPare Redis e confirme que a API de liveness ainda responde; descreva por que isso não prova readiness para endpoints que dependem do cache. Pare db e observe a mesma distinção. Retome os serviços e refaça os contratos. Entregue compose, código, migrações, versões e saídas esperadas e observadas. Inclua um plano de integração da API com consultas autorizadas e um teste de restauração a implementar em seguida. A stack local demonstra setup, serviços e persistência básica, mas não substitui backup, controle de acesso de produção ou recuperação de desastre.
Exercício aplicado
Após recriar o container do banco, todos os documentos somem, mas /health continua respondendo. Explique a falha e mostre como verificar a correção.
- Confira o volume e o caminho de dados da imagem.
- Diferencie health do processo de readiness dos dados.
- Insira marcador, recrie sem remover volume e consulte.
- Registre migração, extensão e quantidade de documentos.
Abrir resolução comentada
A API mínima mede liveness e não consulta dados. O desaparecimento indica que o armazenamento não foi preservado no caminho correto ou que o volume foi removido. Corrija a montagem e execute a prova de retenção com marcador.
Persistência após recriação é uma evidência específica. Uma restauração de backup ainda é necessária para testar recuperação quando o volume ou host é perdido. Não transforme um resultado local de health em uma garantia de durabilidade.
mkdir -p secrets
printf '%s' 'senha-sintetica-do-lab' > secrets/db_password.txt
cat > compose.yaml <<'YAML'
services:
api:
image: node:22-alpine
working_dir: /app
volumes: ["./api.cjs:/app/api.cjs:ro"]
command: ["node", "api.cjs"]
ports: ["127.0.0.1:3000:3000"]
depends_on:
db: {condition: service_healthy}
redis: {condition: service_healthy}
db:
image: pgvector/pgvector:pg17
environment:
POSTGRES_USER: lab
POSTGRES_DB: lab
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets: [db_password]
volumes: ["pgdata:/var/lib/postgresql/data"]
healthcheck:
test: ["CMD-SHELL", "pg_isready -U lab -d lab"]
interval: 2s
timeout: 2s
retries: 20
redis:
image: redis:7-alpine
command: ["redis-server", "--appendonly", "yes"]
volumes: ["redisdata:/data"]
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 2s
timeout: 2s
retries: 20
secrets:
db_password:
file: ./secrets/db_password.txt
volumes:
pgdata:
redisdata:
YAML
cat > api.cjs <<'JS'
require('node:http').createServer((req,res)=>{
res.setHeader('Content-Type','application/json');
res.end(JSON.stringify({alive:true,path:req.url}));
}).listen(3000,'0.0.0.0');
JS
docker compose up -d --wait
docker compose exec -T db psql -U lab -d lab -v ON_ERROR_STOP=1 <<'SQL'
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE IF NOT EXISTS docs(id text PRIMARY KEY, tenant text NOT NULL, embedding vector(3));
INSERT INTO docs VALUES ('d1','t1','[1,0,0]'),('d2','t2','[0,1,0]') ON CONFLICT DO NOTHING;
SELECT id FROM docs WHERE tenant='t1' ORDER BY embedding <=> '[1,0,0]' LIMIT 1;
SQL
docker compose exec -T redis redis-cli SET lab:marker presente
docker compose exec -T redis redis-cli GET lab:marker
docker compose exec -T api node -e "fetch('http://localhost:3000/health').then(r=>r.json()).then(console.log)"
docker compose restart db redis
docker compose exec -T db pg_isready -U lab -d lab
docker compose exec -T db psql -U lab -d lab -c "SELECT count(*) FROM docs;"
docker compose exec -T redis redis-cli GET lab:markerComo conferir seu resultado
- API, SQL e Redis passam em testes separados.
- Vector(3) rejeita dimensão incompatível.
- Dados permanecem após down/up sem remover volumes.
- Segredo é montado apenas no serviço autorizado.
Aplique em um problema novo
Primeiro resolva sem consultar a resposta. Explique suas decisões e guarde a evidência. A conclusão de leitura é independente desta autoavaliação.
Confira seus pré-requisitos
- Distinguir imagem/container/volume.
- Interpretar readiness e persistência.
Postgres/Redis contêm marker=M9 em volumes nomeados. Recrie containers sem apagar volumes; depois migração adiciona campo obrigatório sem migrar linhas antigas. Docker não estava disponível na inspeção inicial.
Conferir raciocínio e critérios de domínio
O resultado esperado da recriação é marker M9 preservado; removê-lo indicaria volume errado/efêmero. O teste deve consultar o dado, não apenas HTTP 200.
A migração incompleta bloqueia promoção da API que exige o campo; readiness/liveness não compensam contrato quebrado.
Com Docker indisponível, somente configuração pôde ser validada e marcadores/recriação permanecem pendentes. Não afirmar que serviços executaram porque Compose parseou.
Evidências para autoavaliação ou revisão por pares
- Persistência observável: O resultado esperado da recriação é marker M9 preservado; removê-lo indicaria volume errado/efêmero. O teste deve consultar o dado, não apenas HTTP 200.
- Compatibilidade da migração: A migração incompleta bloqueia promoção da API que exige o campo; readiness/liveness não compensam contrato quebrado.
- Evidência pendente: Com Docker indisponível, somente configuração pôde ser validada e marcadores/recriação permanecem pendentes. Não afirmar que serviços executaram porque Compose parseou.
Um erro frequente
compose config executa os serviços.
Validar configuração é diferente de iniciar e consultar serviços.
Teste sua compreensão
Responda com suas palavras antes de abrir o comentário. Saber explicar uma decisão é parte do domínio.
1. localhost da API aponta para o banco?
2. Vetor próximo demonstra verdade da resposta?
Não.
Similaridade de representação não comprova suporte factual.
3. Compose secrets criptografa o arquivo local?
Não automaticamente.
Ele controla disponibilização por arquivo ao serviço; proteja a origem no host.
Seu progresso fica salvo neste navegador. Concluir a leitura não substitui demonstrar o domínio nos exercícios.
Referências e aprofundamento
Documentação oficial e trabalhos originais. As referências registram o escopo e as limitações para você conferir o que sustentam.
- What is a container?
Docker • consulta: 2026-10-06
DockerContêineres, isolamento de processos, portabilidade e comparação com máquinas virtuais.
Limites: Isolamento de contêiner não implica sandbox infalível; validar rede, mounts, identidade e permissões.
- Manage secrets securely in Docker Compose
Docker • consulta: 2026-10-06
DockerSecrets montados em arquivos e concessão explícita por serviço.
Limites: O arquivo de origem também precisa de proteção; suporte apresentado para Linux containers.
- Transactions
PostgreSQL • consulta: 2026-10-06
PostgreSQLTransações, atomicidade, persistência, COMMIT, ROLLBACK e savepoints.
Limites: Transação local não cria atomicidade entre banco, fila e serviços externos.
- pgvector: vector similarity search for Postgres
pgvector • consulta: 2026-10-06
PostgreSQLTipos vetoriais, busca exata/aproximada e índices HNSW/IVFFlat.
Limites: Busca aproximada troca recall por desempenho; medir plano de execução, filtros e recall.
- Redis streaming
Redis • consulta: 2026-10-06
RedisAPIsStreams, logs append-only, consumer groups, acknowledgments e distribuição entre workers.
Limites: Entrega at-least-once requer tratamento de duplicações; Redis Streams e uma fila de jobs têm semânticas diferentes.
- Cache-Aside Pattern
Microsoft • consulta: 2026-10-06
FundamentosCarregamento sob demanda, invalidação e expiração de cache.
Limites: Cache pode ficar desatualizado; não garante consistência entre origem e cópia.