Introdução
O mundo DevOps está sendo silenciosamente transformado por Rust. Enquanto Go dominou a primeira onda de ferramentas cloud-native (Docker, Kubernetes, Terraform), uma nova geração de ferramentas de infraestrutura está sendo construída em Rust, aproveitando sua performance superior, consumo mínimo de memória e segurança de memória. Projetos como Firecracker (microVMs da AWS Lambda), Bottlerocket (OS para containers da Amazon), Vector (pipeline de observabilidade) e Turbopack (bundler da Vercel) demonstram que Rust é a escolha ideal para software de infraestrutura que precisa ser rápido, confiável e eficiente.
Para engenheiros DevOps e SRE, Rust também oferece vantagens práticas: binários estáticos sem dependências de runtime facilitam deployment, imagens Docker minúsculas reduzem tempo de pull e consumo de storage, e a ausência de garbage collector garante latência previsível — essencial para proxies, load balancers e agentes de monitoramento.
Ferramentas de Infraestrutura Escritas em Rust
| Ferramenta | Categoria | Descrição |
|---|---|---|
| Firecracker | Virtualização | MicroVMs da AWS — motor por trás do Lambda e Fargate |
| Bottlerocket | Sistema Operacional | OS minimalista para containers, mantido pela Amazon |
| Vector | Observabilidade | Pipeline de dados unificado (logs, métricas, traces) |
| Tikv | Banco de dados | Key-value store distribuído (usado pelo PingCAP/TiDB) |
| Linkerd2-proxy | Service Mesh | Data plane do Linkerd, proxy ultrarrápido |
| nushell | Shell | Shell moderno com output estruturado |
| just | Build | Task runner (alternativa a Makefile) |
| watchexec | Automação | Executor de comandos baseado em file watchers |
| delta | Git | Visualizador de diffs com syntax highlighting |
| gitoxide | Git | Implementação de Git em Rust (usado pelo cargo) |
Otimização de Imagens Docker para Rust
Uma das maiores vantagens de Rust para DevOps é a capacidade de criar imagens Docker extremamente pequenas. Veja como otimizar:
Dockerfile Multi-Stage Otimizado
# === Stage 1: Build ===
FROM rust:1.85-bookworm AS builder
# Criar projeto vazio para cache de dependências
WORKDIR /app
RUN cargo init --name meu-servico
COPY Cargo.toml Cargo.lock ./
RUN cargo build --release && rm -rf src
# Copiar código real e compilar
COPY src ./src
RUN touch src/main.rs && cargo build --release
# === Stage 2: Runtime ===
FROM debian:bookworm-slim
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# Criar usuário não-root
RUN useradd --create-home --shell /bin/bash app
USER app
COPY --from=builder /app/target/release/meu-servico /usr/local/bin/
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
ENTRYPOINT ["meu-servico"]
Build Estático com musl (Imagem Scratch)
Para imagens ainda menores, compile com musl e use uma imagem scratch (vazia):
FROM rust:1.85-bookworm AS builder
RUN rustup target add x86_64-unknown-linux-musl
RUN apt-get update && apt-get install -y musl-tools
WORKDIR /app
COPY . .
RUN cargo build --release --target x86_64-unknown-linux-musl
# Imagem final: apenas o binário, sem sistema operacional
FROM scratch
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/meu-servico /
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
EXPOSE 8080
ENTRYPOINT ["/meu-servico"]
Comparação de Tamanhos
| Abordagem | Tamanho da Imagem |
|---|---|
rust:1.85 (sem multi-stage) | ~1.5 GB |
| Debian slim + binário | ~80 MB |
| Alpine + binário musl | ~15 MB |
| Scratch + binário musl | ~5-10 MB |
| Distroless + binário | ~20 MB |
Compare com uma imagem Node.js típica (~300MB) ou Python (~400MB). Rust permite reduzir o tamanho em 95%.
GitHub Actions para Projetos Rust
Pipeline CI/CD Completo
name: CI/CD Rust
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
CARGO_TERM_COLOR: always
RUSTFLAGS: "-Dwarnings"
jobs:
check:
name: Verificação de Qualidade
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- uses: Swatinem/rust-cache@v2
- name: Verificar formatação
run: cargo fmt --all -- --check
- name: Executar Clippy (linter)
run: cargo clippy --all-targets --all-features
test:
name: Testes
runs-on: ubuntu-latest
needs: check
services:
postgres:
image: postgres:16
env:
POSTGRES_PASSWORD: teste
POSTGRES_DB: app_teste
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: Executar testes
run: cargo test --all-features
env:
DATABASE_URL: postgres://postgres:teste@localhost:5432/app_teste
- name: Testes de integração
run: cargo test --test '*' -- --ignored
env:
DATABASE_URL: postgres://postgres:teste@localhost:5432/app_teste
build-and-push:
name: Build e Push Docker
runs-on: ubuntu-latest
needs: test
if: github.ref == 'refs/heads/main'
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Login no GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build e push
uses: docker/build-push-action@v6
with:
push: true
tags: |
ghcr.io/${{ github.repository }}:latest
ghcr.io/${{ github.repository }}:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
Exemplo Prático: Ferramenta de Monitoramento de Infraestrutura
Vamos construir uma CLI que verifica a saúde de múltiplos serviços e exibe um dashboard no terminal.
[package]
name = "infra-monitor"
version = "0.1.0"
edition = "2021"
[dependencies]
tokio = { version = "1", features = ["full"] }
reqwest = { version = "0.12", features = ["json"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
clap = { version = "4", features = ["derive"] }
colored = "2"
anyhow = "1"
chrono = "0.4"
use anyhow::Result;
use chrono::Local;
use clap::Parser;
use colored::*;
use serde::Deserialize;
use std::time::{Duration, Instant};
#[derive(Parser)]
#[command(name = "infra-monitor")]
#[command(about = "Monitor de saúde de infraestrutura")]
struct Cli {
/// Arquivo de configuração com endpoints
#[arg(short, long, default_value = "endpoints.json")]
config: String,
/// Intervalo entre verificações (segundos)
#[arg(short, long, default_value_t = 30)]
intervalo: u64,
/// Executar uma vez e sair
#[arg(short = '1', long)]
uma_vez: bool,
}
#[derive(Debug, Deserialize)]
struct Endpoint {
nome: String,
url: String,
#[serde(default = "timeout_padrao")]
timeout_ms: u64,
#[serde(default)]
esperar_status: Option<u16>,
}
fn timeout_padrao() -> u64 {
5000
}
#[derive(Debug)]
struct ResultadoVerificacao {
nome: String,
url: String,
status: StatusVerificacao,
latencia: Duration,
}
#[derive(Debug)]
enum StatusVerificacao {
Ok(u16),
StatusInesperado(u16),
Timeout,
Erro(String),
}
async fn verificar_endpoint(
client: &reqwest::Client,
endpoint: &Endpoint,
) -> ResultadoVerificacao {
let inicio = Instant::now();
let resultado = client
.get(&endpoint.url)
.timeout(Duration::from_millis(endpoint.timeout_ms))
.send()
.await;
let latencia = inicio.elapsed();
let status = match resultado {
Ok(resp) => {
let codigo = resp.status().as_u16();
match endpoint.esperar_status {
Some(esperado) if codigo != esperado => {
StatusVerificacao::StatusInesperado(codigo)
}
_ if codigo >= 200 && codigo < 400 => {
StatusVerificacao::Ok(codigo)
}
_ => StatusVerificacao::StatusInesperado(codigo),
}
}
Err(e) if e.is_timeout() => StatusVerificacao::Timeout,
Err(e) => StatusVerificacao::Erro(e.to_string()),
};
ResultadoVerificacao {
nome: endpoint.nome.clone(),
url: endpoint.url.clone(),
status,
latencia,
}
}
fn exibir_resultados(resultados: &[ResultadoVerificacao]) {
let agora = Local::now().format("%Y-%m-%d %H:%M:%S");
println!("\n{}", format!("═══ Health Check — {} ═══", agora).bold().cyan());
let mut ok_count = 0;
let mut fail_count = 0;
for r in resultados {
let (icone, status_texto) = match &r.status {
StatusVerificacao::Ok(codigo) => {
ok_count += 1;
("OK".green().bold(), format!("{}", codigo).green())
}
StatusVerificacao::StatusInesperado(codigo) => {
fail_count += 1;
("WARN".yellow().bold(), format!("{}", codigo).yellow())
}
StatusVerificacao::Timeout => {
fail_count += 1;
("TIMEOUT".red().bold(), "timeout".red())
}
StatusVerificacao::Erro(msg) => {
fail_count += 1;
("ERRO".red().bold(), msg.as_str().red())
}
};
let latencia_texto = if r.latencia.as_millis() > 1000 {
format!("{:.1}s", r.latencia.as_secs_f64()).red()
} else if r.latencia.as_millis() > 300 {
format!("{}ms", r.latencia.as_millis()).yellow()
} else {
format!("{}ms", r.latencia.as_millis()).green()
};
println!(
" [{}] {} — {} ({})",
icone,
r.nome.white().bold(),
status_texto,
latencia_texto
);
}
println!(
"\n Resumo: {} {} | {} {}",
ok_count.to_string().green().bold(),
"saudáveis".green(),
fail_count.to_string().red().bold(),
"com problemas".red()
);
}
#[tokio::main]
async fn main() -> Result<()> {
let cli = Cli::parse();
let config_conteudo = std::fs::read_to_string(&cli.config)?;
let endpoints: Vec<Endpoint> = serde_json::from_str(&config_conteudo)?;
let client = reqwest::Client::builder()
.user_agent("infra-monitor/0.1.0")
.build()?;
loop {
let mut handles = Vec::new();
for endpoint in &endpoints {
let client = client.clone();
let endpoint_clone = Endpoint {
nome: endpoint.nome.clone(),
url: endpoint.url.clone(),
timeout_ms: endpoint.timeout_ms,
esperar_status: endpoint.esperar_status,
};
handles.push(tokio::spawn(async move {
verificar_endpoint(&client, &endpoint_clone).await
}));
}
let mut resultados = Vec::new();
for handle in handles {
resultados.push(handle.await?);
}
exibir_resultados(&resultados);
if cli.uma_vez {
break;
}
tokio::time::sleep(Duration::from_secs(cli.intervalo)).await;
}
Ok(())
}
Arquivo de Configuração (endpoints.json)
[
{
"nome": "API Principal",
"url": "https://api.meusite.com.br/health",
"timeout_ms": 3000,
"esperar_status": 200
},
{
"nome": "Frontend",
"url": "https://meusite.com.br",
"timeout_ms": 5000
},
{
"nome": "Banco de Dados (proxy)",
"url": "http://db-proxy:8080/health",
"timeout_ms": 2000,
"esperar_status": 200
}
]
Empresas Usando Rust em DevOps e Infraestrutura
- Amazon Web Services: Firecracker (Lambda, Fargate), Bottlerocket OS, S3 components, IAM services
- Cloudflare: Pingora (proxy HTTP que substitui o Nginx), Workers runtime, ferramentas de rede
- Datadog: Agent components para alta performance em coleta de métricas
- Timber/Vector: Pipeline unificado de observabilidade usado por milhares de empresas
- Vercel: Turbopack (bundler), turborepo components
- 1Password: Infraestrutura de backend e criptografia
- Fly.io: Components do runtime de microVMs
- Linkerd: Data plane proxy do service mesh
Como Começar
- Aprenda Rust: Tutorial de primeiros passos e tratamento de erros
- Configure Docker: Siga nosso guia de Docker para Rust para builds otimizados
- Configure CI/CD: Use nosso guia de GitHub Actions para pipelines Rust
- Construa CLIs: Muitas ferramentas DevOps começam como CLIs — veja Rust para CLIs
- Async Rust: Essencial para ferramentas de networking — receita de async/await
- HTTP requests: Aprenda a fazer requisições HTTP com reqwest
Conclusão
Rust está se consolidando como a linguagem de escolha para a próxima geração de ferramentas de infraestrutura. Se Go trouxe Docker e Kubernetes, Rust está trazendo microVMs, proxies de alta performance, pipelines de observabilidade e sistemas operacionais para containers. Para engenheiros DevOps e SRE, aprender Rust é um investimento que se paga tanto em ferramentas melhores quanto em oportunidades profissionais crescentes.
Veja Também
- Guia: Docker para Rust — Otimize suas imagens Docker
- Guia: GitHub Actions para Rust — Configure CI/CD profissional
- Rust para CLI — Construa ferramentas de linha de comando
- Rust para Microsserviços — gRPC, filas e deploy em produção
- Rust para Desenvolvimento Web — APIs e backends com Axum
- Receita: Variáveis de Ambiente — Configuração de aplicações
- Receita: Fazer Requisição HTTP — Comunicação entre serviços