O que modal faz?
Visão Geral
Modal é uma plataforma em nuvem para executar código Python de forma serverless, com foco em cargas de trabalho de IA/ML. Principais capacidades:
- Computação GPU sob demanda (T4, L4, A10, L40S, A100, H100, H200, B200)
- Funções serverless com escalonamento automático de zero a milhares de contêineres
- Imagens de contêiner personalizadas construídas inteiramente em código Python
- Armazenamento persistente via Volumes para pesos de modelos e conjuntos de dados
- Endpoints web para servir modelos e APIs
- Tarefas agendadas via cron ou intervalos fixos
- Inicializações a frio abaixo de um segundo para inferência de baixa latência
Tudo no Modal é definido como código — sem necessidade de YAML ou Dockerfiles (embora ambos sejam suportados).
Quando Usar Esta Skill
Use esta skill quando:
- Implantar ou servir modelos de IA/ML na nuvem
- Executar computações aceleradas por GPU (treinamento, inferência, ajuste fino)
- Criar APIs web ou endpoints serverless
- Escalar trabalhos de processamento em lote em paralelo
- Agendar tarefas recorrentes (pipelines de dados, re-treinamento, scraping)
- Precisar de armazenamento persistente em nuvem para pesos de modelos ou conjuntos de dados
- Quiser executar código em ambientes de contêiner personalizados
- Construir filas de tarefas ou sistemas de processamento assíncrono
Instalação e Autenticação
Instalar
uv pip install modal
O SDK Python do Modal suporta Python 3.10–3.14. Esta skill tem como alvo a API estável modal>=1.0 (versão atual: 1.4.x).
Autenticar
Prefira credenciais existentes antes de criar novas. Apenas as duas variáveis específicas do Modal
abaixo são relevantes — não leia, carregue ou exponha nenhuma outra variável de ambiente
ou conteúdo de arquivo .env:
- Verifique se
MODAL_TOKEN_IDeMODAL_TOKEN_SECRETjá estão definidos no ambiente atual. - Se não, procure apenas essas duas chaves em um arquivo
.envlocal (ignore todas as outras entradas) e carregue-as se apropriado para o fluxo de trabalho. - Só recorra ao
modal setupinterativo ou à geração de novos tokens se nenhuma das fontes anteriores já fornecer esses dois valores.
modal setup
Isso abre um navegador para autenticação. Para ambientes CI/CD ou headless, use variáveis de ambiente:
export MODAL_TOKEN_ID=<seu-token-id>
export MODAL_TOKEN_SECRET=<seu-token-secret>
Se os tokens não estiverem disponíveis no ambiente ou no arquivo .env, gere-os em https://modal.com/settings
O Modal oferece uma camada gratuita com $30/mês em créditos.
Referência: Veja references/getting-started.md para configuração detalhada e um passo a passo do primeiro aplicativo.
Conceitos Principais
App e Funções
Um App do Modal agrupa funções relacionadas. Funções decoradas com @app.function() são executadas remotamente na nuvem:
import modal
app = modal.App("meu-app")
@app.function()
def quadrado(x):
return x ** 2
@app.local_entrypoint()
def main():
# .remote() executa na nuvem
print(quadrado.remote(42))
Execute com modal run script.py. Implante com modal deploy script.py.
Referência: Veja references/functions.md para hooks de ciclo de vida, classes, .map(), .spawn() e mais.
Imagens de Contêiner
O Modal constrói imagens de contêiner a partir de código Python. O instalador de pacotes recomendado é o uv:
image = (
modal.Image.debian_slim(python_version="3.11")
.uv_pip_install("torch==2.12.0", "transformers==5.9.0", "accelerate==1.13.0")
.apt_install("git")
)
@app.function(image=image)
def inferencia(prompt):
from transformers import pipeline
pipe = pipeline("text-generation", model="meta-llama/Llama-3-8B")
return pipe(prompt)
Principais métodos de imagem:
.uv_pip_install()— Instala pacotes Python com uv (recomendado).pip_install()— Instala com pip (alternativa).apt_install()— Instala pacotes do sistema.run_commands()— Executa comandos shell durante a construção.run_function()— Executa Python durante a construção (ex.: baixar pesos de modelo).add_local_python_source()— Adiciona módulos locais.env()— Define variáveis de ambiente
Referência: Veja references/images.md para Dockerfiles, micromamba, cache, etapas de construção em GPU.
Computação GPU
Solicite GPUs através do parâmetro gpu:
@app.function(gpu="H100")
def treinar_modelo():
import torch
device = torch.device("cuda")
# Código de treinamento GPU aqui
# Múltiplos GPUs
@app.function(gpu="H100:4")
def treinamento_distribuido():
...
# Cadeia de fallback de GPU
@app.function(gpu=["H100", "A100-80GB", "A100-40GB"])
def inferencia_flexivel():
...
GPUs disponíveis: T4, L4, A10, L40S, A100-40GB, A100-80GB, RTX-PRO-6000, H100, H200, B200, B200+
- GPUs são sempre especificadas como strings (ex.:
gpu="H100",gpu="H100:4"). Os objetos antigosmodal.gpu.*estão obsoletos desde a versão 0.73.31. - Até 8 GPUs por contêiner (exceto A10: até 4)
- L40S é recomendado para inferência (custo/desempenho equilibrado, 48 GB de VRAM)
- H100/A100 podem ser atualizados automaticamente para H200/A100-80GB sem custo adicional
- Use
gpu="H100!"para evitar a atualização automática
Referência: Veja references/gpu.md para orientação sobre seleção de GPU e treinamento com múltiplas GPUs.
Volumes (Armazenamento Persistente)
Volumes fornecem armazenamento de arquivos distribuído e persistente:
vol = modal.Volume.from_name("pesos-modelo", create_if_missing=True)
@app.function(volumes={"/data": vol})
def salvar_modelo():
# Escreve no caminho montado
with open("/data/model.pt", "wb") as f:
torch.save(model.state_dict(), f)
@app.function(volumes={"/data": vol})
def carregar_modelo():
model.load_state_dict(torch.load("/data/model.pt"))
- Otimizado para cargas de trabalho de escrever uma vez, ler muitas vezes (pesos de modelo, conjuntos de dados)
- Acesso via CLI:
modal volume ls,modal volume put,modal volume get - Commit automático em segundo plano a cada poucos segundos
- Monte como somente leitura ou limite a um subdiretório com
vol.with_mount_options(read_only=True, sub_path="subset")
Referência: Veja references/volumes.md para volumes v2, escritas concorrentes e melhores práticas.
Secrets
Passe credenciais de forma segura para funções:
@app.function(secrets=[modal.Secret.from_name("minhas-chaves-api")])
def chamar_api():
import os
api_key = os.environ["API_KEY"]
# Use a chave
Crie secrets via CLI: modal secret create minhas-chaves-api API_KEY=sk-xxx
Ou a partir de um arquivo .env: modal.Secret.from_dotenv()
Referência: Veja references/secrets.md para configuração no dashboard, múltiplos secrets e modelos.
Endpoints Web
Sirva modelos e APIs como endpoints web:
@app.function()
@modal.fastapi_endpoint()
def prever(texto: str):
return {"resultado": model.predict(texto)}
modal serve script.py— Desenvolvimento com recarga a quente e URL temporáriamodal deploy script.py— Implantação em produção com URL permanente- Suporta FastAPI, ASGI (Starlette, FastHTML), WSGI (Flask, Django), WebSockets
- Corpos de requisição de até 4 GiB, tamanho de resposta ilimitado
Referência: Veja references/web-endpoints.md para aplicativos ASGI/WSGI, streaming, autenticação e WebSockets.
Tarefas Agendadas
Execute funções em um cronograma:
@app.function(schedule=modal.Cron("0 9 * * *")) # Diariamente às 9 AM UTC
def pipeline_diario():
# ETL, re-treinamento, scraping, etc.
...
@app.function(schedule=modal.Period(hours=6))
def verificacao_periodica():
...
Implante com modal deploy script.py para ativar o cronograma.
modal.Cron("...")— Sintaxe cron padrão, estável entre implantaçõesmodal.Period(hours=N)— Intervalo fixo, reinicia na reimplantação- Monitore as execuções no dashboard do Modal
Referência: Veja references/scheduled-jobs.md para sintaxe cron e gerenciamento.
Escalonamento e Concorrência
O Modal escala automaticamente os contêineres. Configure limites:
@app.function(
max_containers=100, # Limite superior
min_containers=2, # Mantenha ativos para baixa latência
buffer_containers=5, # Capacidade reservada
scaledown_window=300, # Segundos ociosos antes do desligamento
)
def processar(dados):
...
Processe entradas em paralelo com .map():
resultados = list(processar.map([item1, item2, item3, ...]))
Habilite o tratamento concorrente de requisições por contêiner com @modal.concurrent. Defina
target_inputs (o alvo do escalonador por contêiner) abaixo de max_inputs (o limite
máximo) para manter reserva enquanto escala:
@app.function()
@modal.concurrent(max_inputs=10, target_inputs=8)
async def lidar_requisicao(req):
...
Reconfigure uma Função ou Cls implantada no momento da invocação sem reimplantar usando
Function.with_options() / Function.with_concurrency() / Function.with_batching()
(e Cls.with_options()):
Modelo = modal.Cls.from_name("meu-app", "Modelo")
rapido = Modelo.with_options(gpu="H200", max_containers=20)
rapido().generate.remote(prompt)
Referência: Veja references/scaling.md para .map(), .starmap(), .spawn() e limites.
Configuração de Recursos
@app.function(
cpu=4.0, # Núcleos físicos (não vCPUs)
memory=16384, # MiB
ephemeral_disk=51200, # MiB (até 3 TiB)
timeout=3600, # Segundos
)
def computacao_pesada():
...
Padrões: 0.125 núcleos de CPU, 128 MiB de memória. Cobrado com base no máximo (solicitado, uso).
Referência: Veja references/resources.md para limites e detalhes de faturamento.
Classes com Hooks de Ciclo de Vida
Para cargas de trabalho com estado (ex.: carregar um modelo uma vez e servir muitas requisições):
@app.cls(gpu="L40S", image=image)
class Preditor:
@modal.enter()
def carregar_modelo(self):
self.model = carregar_modelo_pesado() # Executa uma vez na inicialização do contêiner
@modal.method()
def prever(self, texto: str):
return self.model(texto)
@modal.exit()
def limpar(self):
... # Executa no desligamento do contêiner
Chame com: Preditor().prever.remote("olá")
Sandboxes
Para executar código não confiável ou gerado dinamicamente (por exemplo, saída de um agente de IA ou um interpretador de código), use um modal.Sandbox — um contêiner isolado que você cria e controla programaticamente, em vez de uma Função decorada:
app = modal.App.lookup("sandbox-demo", create_if_missing=True)
# Contêiner isolado; restrinja saída para cargas não confiáveis
sb = modal.Sandbox.create(
app=app,
image=modal.Image.debian_slim(),
outbound_cidr_allowlist=["10.0.0.0/8"],
)
# Transfira arquivos para dentro/fora via API de sistema de arquivos (beta)
sb.filesystem.write_text("print(2 ** 10)\n", "/tmp/job.py")
conteudo = sb.filesystem.read_text("/tmp/job.py")
sb.terminate()
- Execute comandos dentro do sandbox com seu método
exec(ex.: executepython /tmp/job.py) e leia stdout do handle do processo retornado — vejareferences/api_reference.md - Restrinja a conectividade com
outbound_cidr_allowlist=[...]/inbound_cidr_allowlist=[...] - Tire um snapshot do sistema de arquivos com
sb.snapshot_filesystem()para reutilizar como imagem base - Ideal para interpretadores de código, execução de ferramentas de agente e isolamento por usuário
Padrões de Fluxo de Trabalho Comuns
Serviço de Inferência de Modelo GPU
import modal
app = modal.App("servico-llm")
image = (
modal.Image.debian_slim(python_version="3.11")
.uv_pip_install("vllm")
)
@app.cls(gpu="H100", image=image, min_containers=1)
class ServicoLLM:
@modal.enter()
def carregar(self):
from vllm import LLM
self.llm = LLM(model="meta-llama/Llama-3-70B")
@modal.method()
@modal.fastapi_endpoint(method="POST")
def gerar(self, prompt: str, max_tokens: int = 256):
outputs = self.llm.generate([prompt], max_tokens=max_tokens)
return {"texto": outputs[0].outputs[0].text}
Pipeline de Processamento em Lote
app = modal.App("pipeline-lote")
vol = modal.Volume.from_name("dados-pipeline", create_if_missing=True)
@app.function(volumes={"/data": vol}, cpu=4.0, memory=8192)
def processar_lote(id_lote: int):
import pandas as pd
df = pd.read_parquet(f"/data/input/lote_{id_lote}.parquet")
resultado = transformacao_pesada(df)
resultado.to_parquet(f"/data/output/lote_{id_lote}.parquet")
return len(resultado)
@app.local_entrypoint()
def main():
ids_lote = list(range(100))
resultados = list(processar_lote.map(ids_lote))
print(f"Processadas {sum(resultados)} linhas no total")
Pipeline de Dados Agendado
app = modal.App("pipeline-etl")
@app.function(
schedule=modal.Cron("0 */6 * * *"), # A cada 6 horas
secrets=[modal.Secret.from_name("credenciais-banco")],
)
def trabalho_etl():
import os
url_banco = os.environ["DATABASE_URL"]
# Extrair, transformar, carregar
...
Referência da CLI
| Comando | Descrição |
|---|---|
modal setup | Autenticar com o Modal |
modal run script.py | Executar o entrypoint local de um script |
modal serve script.py | Servidor de desenvolvimento com recarga a quente |
modal deploy script.py | Implantar em produção |
modal volume ls <nome> | Listar arquivos em um volume |
modal volume put <nome> <arquivo> | Enviar arquivo para um volume |
modal volume get <nome> <arquivo> | Baixar arquivo de um volume |
modal secret create <nome> K=V | Criar um secret |
modal secret list | Listar secrets |
modal app list | Listar aplicativos implantados |
modal app stop <nome> | Parar um aplicativo implantado |
Notas de Segurança
- Credenciais: Apenas
MODAL_TOKEN_IDeMODAL_TOKEN_SECRETsão necessários para autenticar. Não leia, registre ou encaminhe nenhuma outra variável de ambiente ou entrada de.env. - Subprocesso / servidores personalizados: Alguns padrões aqui (lançadores de treinamento multi-GPU, aplicativos
@modal.web_server) chamamsubprocess.run/subprocess.Popenou comandos shell durante as construções. Mantenha as listas de argumentos fixas e codificadas. Nunca construa argumentos de subprocesso ou shell a partir de entrada de usuário não sanitizada — passe valores não confiáveis como dados (arquivos, variáveis de ambiente, stdin), não como argumentos de comando. - Código não confiável: Execute código gerado por usuário ou modelo dentro de um
modal.Sandbox(veja acima), não em uma Função regular, e restrinja o acesso à rede com listas de permissão CIDR.
Arquivos de Referência
Documentação detalhada para cada tópico:
references/getting-started.md— Instalação, autenticação, primeiro aplicativoreferences/functions.md— Funções, classes, hooks de ciclo de vida, execução remotareferences/images.md— Imagens de contêiner, instalação de pacotes, cachereferences/gpu.md— Tipos de GPU