Rust 的 DevOps 优势
Rust 编译出的是单一静态二进制文件,没有运行时依赖(不像 Node.js 需要 node_modules)。这让容器化极其简单:
Node.js 镜像:基础镜像(100MB) + node(50MB) + node_modules(200MB) = 350MB+
Rust 镜像: FROM scratch + 单一二进制 = 10-20MBDocker:多阶段构建
Rust 编译需要完整工具链(~2GB),但运行只需要二进制。多阶段构建分离编译和运行环境:
# Dockerfile(后端)
# ── 阶段 1:编译 ───────────────────────────────────────────────
FROM rust:1.78-slim AS builder
WORKDIR /app
# 先复制依赖文件,利用 Docker 缓存
# 只有 Cargo.toml 变化时才重新下载依赖
COPY Cargo.toml Cargo.lock ./
RUN mkdir src && echo "fn main() {}" > src/main.rs
RUN cargo build --release
RUN rm -rf src
# 再复制真正的源码,只重新编译应用代码
COPY src ./src
RUN touch src/main.rs && cargo build --release
# ── 阶段 2:运行 ───────────────────────────────────────────────
# scratch = 空镜像,什么都没有
FROM debian:bookworm-slim AS runtime
# 仅添加运行时必要的 CA 证书(HTTPS 请求需要)
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
# 非 root 用户运行(安全最佳实践)
RUN useradd -m appuser
USER appuser
WORKDIR /app
COPY --from=builder /app/target/release/blog-api .
EXPOSE 3000
CMD ["./blog-api"]# Dockerfile(前端 Next.js)
FROM node:22-alpine AS base
WORKDIR /app
FROM base AS deps
COPY package.json pnpm-lock.yaml ./
RUN corepack enable pnpm && pnpm install --frozen-lockfile
FROM base AS builder
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN pnpm build # Next.js standalone 输出
FROM base AS runner
ENV NODE_ENV=production
RUN addgroup --system nodejs && adduser --system nextjs
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
CMD ["node", "server.js"]Next.js 的 standalone 模式(output: 'standalone' in next.config.ts)会把所有依赖打包成可独立运行的目录,配合多阶段构建可以把前端镜像压缩到 ~100MB。
Docker Compose:本地开发环境
# docker-compose.yml
version: "3.9"
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: rustforge
POSTGRES_PASSWORD: development
POSTGRES_DB: rustforge_db
ports: ["5432:5432"]
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U rustforge"]
interval: 5s
timeout: 5s
retries: 5
backend:
build: ./backend
ports: ["3001:3000"]
environment:
DATABASE_URL: postgres://rustforge:development@postgres:5432/rustforge_db
JWT_SECRET: dev-secret-change-in-production
depends_on:
postgres:
condition: service_healthy
frontend:
build: ./frontend
ports: ["3000:3000"]
environment:
NEXT_PUBLIC_API_URL: http://localhost:3001
NEXT_PUBLIC_WS_URL: ws://localhost:3001
depends_on:
- backend
volumes:
postgres_data:# 一键启动完整开发环境
docker compose up -d
# 查看日志
docker compose logs -f backend
# 停止
docker compose downGitHub Actions:CI/CD 流水线
# .github/workflows/ci.yml
name: CI/CD
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
# ── 后端 ─────────────────────────────────────────────────────
backend-test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_PASSWORD: test
POSTGRES_DB: test_db
ports: ["5432:5432"]
options: >-
--health-cmd pg_isready
--health-interval 10s
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
components: clippy, rustfmt
# 缓存编译产物(大幅加速后续 CI)
- name: Cache Rust
uses: Swatinem/rust-cache@v2
with:
workspaces: backend
- name: Lint
working-directory: backend
run: |
cargo fmt --check
cargo clippy -- -D warnings
- name: Run tests
working-directory: backend
env:
DATABASE_URL: postgres://postgres:test@localhost:5432/test_db
run: cargo test
backend-build:
needs: backend-test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build & push Docker image
uses: docker/build-push-action@v5
with:
context: ./backend
push: true
tags: ghcr.io/${{ github.repository }}/backend:${{ github.sha }}
# ── 前端 ─────────────────────────────────────────────────────
frontend-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with: { version: 9 }
- uses: actions/setup-node@v4
with: { node-version: 22, cache: pnpm }
- run: pnpm install --frozen-lockfile
working-directory: frontend
- name: Type check
run: pnpm typecheck
working-directory: frontend
- name: Build
run: pnpm build
working-directory: frontend
# ── 部署 ─────────────────────────────────────────────────────
deploy:
needs: [backend-build, frontend-build]
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- name: Deploy to production
run: |
# SSH 到服务器或调用 Kubernetes API
echo "Deploying version ${{ github.sha }}"Kubernetes 基础:部署 Rust 服务
# k8s/backend-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: blog-api
spec:
replicas: 3 # 3 个副本,负载均衡
selector:
matchLabels:
app: blog-api
template:
metadata:
labels:
app: blog-api
spec:
containers:
- name: blog-api
image: ghcr.io/your-org/blog-api:latest
ports:
- containerPort: 3000
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef: # 从 K8s Secret 读取,不硬编码
name: db-secret
key: url
resources:
requests:
cpu: "100m"
memory: "64Mi"
limits:
cpu: "500m"
memory: "128Mi" # Rust 内存占用极低!
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: blog-api-svc
spec:
selector:
app: blog-api
ports:
- port: 80
targetPort: 3000Rust 在 K8s 的优势:Node.js 服务通常需要 256-512MB 内存限制,Rust 服务设 64-128MB 绰绰有余。在大规模集群里,这意味着显著更低的云账单。
健康检查与优雅关闭
生产环境必备:
// src/main.rs — 带健康检查和优雅关闭的服务器
use axum::{routing::get, Json, Router};
use serde_json::{json, Value};
use tokio::signal;
async fn health() -> Json<Value> {
Json(json!({ "status": "ok", "version": env!("CARGO_PKG_VERSION") }))
}
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/health", get(health))
// ... 其他路由
;
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app)
.with_graceful_shutdown(shutdown_signal()) // 优雅关闭
.await
.unwrap();
}
async fn shutdown_signal() {
let ctrl_c = async {
signal::ctrl_c().await.expect("failed to install Ctrl+C handler");
};
#[cfg(unix)]
let terminate = async {
signal::unix::signal(signal::unix::SignalKind::terminate())
.expect("failed to install signal handler")
.recv()
.await;
};
#[cfg(not(unix))]
let terminate = std::future::pending::<()>();
tokio::select! {
_ = ctrl_c => {},
_ = terminate => {},
}
println!("收到关闭信号,等待请求处理完毕...");
}监控与日志
# Cargo.toml
[dependencies]
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }use tracing::{info, warn, error, instrument};
use tracing_subscriber::EnvFilter;
#[tokio::main]
async fn main() {
// 结构化日志,支持 JSON 输出(Kubernetes 日志收集友好)
tracing_subscriber::fmt()
.with_env_filter(EnvFilter::from_default_env())
.json()
.init();
info!(version = env!("CARGO_PKG_VERSION"), "服务启动");
}
// #[instrument] 自动记录函数入参和执行时间
#[instrument(skip(pool))]
async fn get_user(pool: &PgPool, id: Uuid) -> Result<User, sqlx::Error> {
info!(%id, "查询用户");
let user = sqlx::query_as!(User, "SELECT * FROM users WHERE id = $1", id)
.fetch_one(pool)
.await?;
info!(%id, "查询成功");
Ok(user)
}# 运行时控制日志级别
RUST_LOG=blog_api=debug,sqlx=warn cargo run实战项目:完整 CI/CD 流水线
实战项目
完整 CI/CD 流水线
2 小时中级
为第 6 章的全栈项目配置完整的 CI/CD:单元测试、Docker 构建、自动部署到云服务器。
Docker 多阶段构建GitHub ActionsK8s Deployment健康检查结构化日志
gh repo create rustforge-board --public && git push -u origin main实战视频
DevOps:Docker 20MB 镜像 + CI/CD
0:00 / 0:00CC
Docker 多阶段构建、GitHub Actions、K8s。
本章小结
- ✓ Docker 多阶段构建(20MB Rust 镜像)
- ✓ Docker Compose 本地开发环境
- ✓ GitHub Actions CI/CD 流水线
- ✓ Kubernetes Deployment + Service
- ✓ 优雅关闭与健康检查
- ✓ 结构化日志(tracing)
最后一章:Rust + AI 集成——用 Rust 的性能优势驱动 AI 应用。