From c09f66da55128e056d0e1c0a7d7b2f078fdf27cc Mon Sep 17 00:00:00 2001 From: baohua Date: Thu, 5 Mar 2026 22:23:06 -0800 Subject: [PATCH] Add practical examples --- 21_case_devops/21.7_practical_examples.md | 877 ++++++++++++++++++++++ 21_case_devops/README.md | 1 + 2 files changed, 878 insertions(+) create mode 100644 21_case_devops/21.7_practical_examples.md diff --git a/21_case_devops/21.7_practical_examples.md b/21_case_devops/21.7_practical_examples.md new file mode 100644 index 0000000..37f7a2d --- /dev/null +++ b/21_case_devops/21.7_practical_examples.md @@ -0,0 +1,877 @@ +## 21.7 实战案例:Go/Rust/数据库/微服务 + +本节通过实际项目案例演示如何为不同类型的应用构建最优化的 Docker 镜像,以及如何使用 Docker Compose 构建完整的开发和生产环境。 + +### 21.7.1 Go 应用的最小化镜像构建 + +Go 语言因其编译为静态二进制和快速启动而特别适合容器化。以下展示如何构建极小的 Go 应用镜像。 + +#### 超小 Go Web 服务 + +**应用代码(main.go):** + +```go +package main + +import ( + "fmt" + "log" + "net/http" + "os" +) + +func healthHandler(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + fmt.Fprintf(w, `{"status":"healthy","version":"1.0.0"}`) +} + +func helloHandler(w http.ResponseWriter, r *http.Request) { + hostname, _ := os.Hostname() + w.Header().Set("Content-Type", "application/json") + fmt.Fprintf(w, `{"message":"Hello from %s","version":"1.0.0"}`, hostname) +} + +func main() { + http.HandleFunc("/health", healthHandler) + http.HandleFunc("/hello", helloHandler) + http.HandleFunc("/", helloHandler) + + port := ":8080" + log.Printf("Server starting on %s", port) + + if err := http.ListenAndServe(port, nil); err != nil { + log.Fatalf("Server failed: %v", err) + } +} +``` + +**多阶段 Dockerfile:** + +```dockerfile +# Stage 1: 构建阶段 +FROM golang:1.20-alpine AS builder + +WORKDIR /build + +# 安装构建依赖 +RUN apk add --no-cache git ca-certificates tzdata + +# 复制模块文件(利用缓存) +COPY go.mod go.sum ./ +RUN go mod download + +# 复制源代码 +COPY . . + +# 构建静态二进制 +# -ldflags="-w -s" 去除调试符号减小体积 +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \ + -a -installsuffix cgo \ + -ldflags="-w -s -X main.Version=1.0.0 -X main.BuildTime=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" \ + -o app . + +# Stage 2: 运行阶段(scratch 镜像) +FROM scratch + +# 复制 CA 证书(用于 HTTPS) +COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ + +# 复制时区数据(用于时间处理) +COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo + +# 复制应用二进制 +COPY --from=builder /build/app /app + +EXPOSE 8080 + +# 使用绝对路径作为 ENTRYPOINT +ENTRYPOINT ["/app"] +``` + +**构建和测试:** + +```bash +# 构建镜像 +docker build -t go-app:latest . + +# 检查镜像大小 +docker images go-app + +# 运行容器 +docker run -d -p 8080:8080 --name go-demo go-app:latest + +# 测试应用 +curl http://localhost:8080/health | jq . + +# 进入容器验证 +docker exec go-demo ls -la / +# 只包含 /app 和系统必要文件 + +# 镜像大小通常 < 10MB(相比 golang:1.20-alpine 的 ~1GB) +docker history go-app:latest +``` + +**go.mod 和 go.sum 示例:** + +``` +module github.com/example/go-app + +go 1.20 + +require ( + // 如果需要依赖 +) +``` + +#### 带依赖的 Go 应用 + +**应用代码(使用 Gin 框架):** + +```go +package main + +import ( + "github.com/gin-gonic/gin" + "log" +) + +func main() { + router := gin.Default() + + router.GET("/health", func(c *gin.Context) { + c.JSON(200, gin.H{ + "status": "ok", + }) + }) + + router.GET("/api/users", func(c *gin.Context) { + c.JSON(200, gin.H{ + "users": []string{"alice", "bob"}, + }) + }) + + log.Fatal(router.Run(":8080")) +} +``` + +**优化的 Dockerfile:** + +```dockerfile +FROM golang:1.20-alpine AS builder + +WORKDIR /src + +RUN apk add --no-cache git ca-certificates tzdata + +COPY go.mod go.sum ./ +RUN go mod download + +COPY . . + +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -a -installsuffix cgo \ + -ldflags="-w -s" \ + -o app . + +# 最终镜像 +FROM alpine:3.17 + +RUN apk add --no-cache ca-certificates tzdata + +WORKDIR /root/ + +COPY --from=builder /src/app . + +EXPOSE 8080 + +CMD ["./app"] +``` + +### 21.7.2 Rust 应用的最小化镜像构建 + +Rust 因其性能和安全性在系统级应用中备受青睐。 + +**应用代码(main.rs):** + +```rust +use actix_web::{web, App, HttpServer, HttpResponse}; +use std::sync::Mutex; + +#[actix_web::main] +async fn main() -> std::io::Result<()> { + println!("Starting server on 0.0.0.0:8080"); + + HttpServer::new(|| { + App::new() + .route("/health", web::get().to(health)) + .route("/hello", web::get().to(hello)) + }) + .bind("0.0.0.0:8080")? + .run() + .await +} + +async fn health() -> HttpResponse { + HttpResponse::Ok().json(serde_json::json!({ + "status": "healthy" + })) +} + +async fn hello() -> HttpResponse { + HttpResponse::Ok().json(serde_json::json!({ + "message": "Hello from Rust" + })) +} +``` + +**Cargo.toml:** + +```toml +[package] +name = "rust-app" +version = "0.1.0" +edition = "2021" + +[[bin]] +name = "rust-app" +path = "src/main.rs" + +[dependencies] +actix-web = "4.4" +tokio = { version = "1.35", features = ["full"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +``` + +**多阶段构建 Dockerfile:** + +```dockerfile +# Stage 1: 编译 +FROM rust:1.75-alpine AS builder + +RUN apk add --no-cache musl-dev + +WORKDIR /src + +COPY Cargo.* ./ +COPY src ./src + +# 构建优化的发布版本 +RUN cargo build --release + +# Stage 2: 运行镜像 +FROM alpine:3.17 + +RUN apk add --no-cache ca-certificates + +COPY --from=builder /src/target/release/rust-app /app + +EXPOSE 8080 + +CMD ["/app"] +``` + +**构建和验证:** + +```bash +docker build -t rust-app:latest . +docker run -d -p 8080:8080 rust-app:latest +curl http://localhost:8080/health | jq . + +# Rust 应用通常比 Go 更小:5-20MB(取决于依赖) +docker images rust-app +``` + +### 21.7.3 数据库容器化最佳实践 + +#### PostgreSQL 生产部署 + +**自定义 PostgreSQL 镜像:** + +```dockerfile +FROM postgres:16-alpine + +# 安装额外工具 +RUN apk add --no-cache \ + postgresql-contrib \ + pg-stat-monitor \ + curl + +# 复制初始化脚本 +COPY init-db.sql /docker-entrypoint-initdb.d/ +COPY health-check.sh / + +RUN chmod +x /health-check.sh + +HEALTHCHECK --interval=10s --timeout=5s --start-period=40s --retries=3 \ + CMD /health-check.sh + +EXPOSE 5432 +``` + +**初始化脚本(init-db.sql):** + +```sql +-- 创建自定义用户 +CREATE USER appuser WITH PASSWORD 'secure_password'; + +-- 创建数据库 +CREATE DATABASE myappdb OWNER appuser; + +-- 创建扩展 +\c myappdb + +CREATE EXTENSION IF NOT EXISTS uuid-ossp; +CREATE EXTENSION IF NOT EXISTS hstore; +CREATE EXTENSION IF NOT EXISTS pg_trgm; + +-- 创建表 +CREATE TABLE users ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + username VARCHAR(255) NOT NULL UNIQUE, + email VARCHAR(255) NOT NULL UNIQUE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- 创建索引 +CREATE INDEX idx_users_username ON users (username); +CREATE INDEX idx_users_email ON users (email); + +-- 授予权限 +GRANT CONNECT ON DATABASE myappdb TO appuser; +GRANT USAGE ON SCHEMA public TO appuser; +GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO appuser; +GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO appuser; +``` + +**健康检查脚本(health-check.sh):** + +```bash +#!/bin/bash + +PGPASSWORD=$POSTGRES_PASSWORD pg_isready \ + -h localhost \ + -U $POSTGRES_USER \ + -d $POSTGRES_DB \ + -p 5432 > /dev/null 2>&1 + +exit $? +``` + +**Docker Compose 配置:** + +```yaml +version: '3.9' + +services: + postgres: + build: + context: . + dockerfile: Dockerfile.postgres + container_name: postgres-db + environment: + POSTGRES_DB: myappdb + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres_password + POSTGRES_INITDB_ARGS: "--encoding=UTF8 --locale=en_US.UTF-8" + volumes: + - postgres_data:/var/lib/postgresql/data + - ./backups:/backups + ports: + - "5432:5432" + networks: + - backend + restart: unless-stopped + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + + # 备份服务 + backup: + image: postgres:16-alpine + depends_on: + - postgres + environment: + PGPASSWORD: postgres_password + volumes: + - ./backups:/backups + command: | + sh -c 'while true; do + pg_dump -h postgres -U postgres -d myappdb > /backups/backup_$$(date +%Y%m%d_%H%M%S).sql + echo "Backup completed at $$(date)" + sleep 86400 + done' + networks: + - backend + +volumes: + postgres_data: + driver: local + +networks: + backend: + driver: bridge +``` + +**性能优化配置:** + +```yaml +version: '3.9' + +services: + postgres: + image: postgres:16-alpine + environment: + POSTGRES_DB: myappdb + command: + - "postgres" + - "-c" + - "max_connections=200" + - "-c" + - "shared_buffers=256MB" + - "-c" + - "effective_cache_size=1GB" + - "-c" + - "maintenance_work_mem=64MB" + - "-c" + - "checkpoint_completion_target=0.9" + - "-c" + - "wal_buffers=16MB" + - "-c" + - "default_statistics_target=100" + - "-c" + - "random_page_cost=1.1" + - "-c" + - "effective_io_concurrency=200" + - "-c" + - "work_mem=1310kB" + - "-c" + - "min_wal_size=1GB" + - "-c" + - "max_wal_size=4GB" + - "-c" + - "max_worker_processes=4" + - "-c" + - "max_parallel_workers_per_gather=2" + - "-c" + - "max_parallel_workers=4" + - "-c" + - "max_parallel_maintenance_workers=2" + volumes: + - postgres_data:/var/lib/postgresql/data + ports: + - "5432:5432" + +volumes: + postgres_data: +``` + +#### MySQL/MariaDB 部署 + +```dockerfile +FROM mariadb:11 + +# 复制自定义配置 +COPY my.cnf /etc/mysql/conf.d/custom.cnf + +# 初始化脚本 +COPY init.sql /docker-entrypoint-initdb.d/ + +EXPOSE 3306 + +HEALTHCHECK --interval=30s --timeout=10s --retries=3 \ + CMD mariadb-admin ping -h localhost || exit 1 +``` + +**自定义 my.cnf:** + +```ini +[mysqld] +# 性能优化 +max_connections = 200 +default_storage_engine = InnoDB +innodb_buffer_pool_size = 1GB +innodb_log_file_size = 256MB +query_cache_type = 0 +query_cache_size = 0 + +# 日志配置 +log_error = /var/log/mysql/error.log +slow_query_log = 1 +slow_query_log_file = /var/log/mysql/slow.log +long_query_time = 2 + +# 复制配置 +server_id = 1 +log_bin = mysql-bin +binlog_format = ROW +``` + +#### Redis 缓存部署 + +```dockerfile +FROM redis:7-alpine + +# 复制 Redis 配置 +COPY redis.conf /usr/local/etc/redis/redis.conf + +# 使用配置文件启动 +CMD ["redis-server", "/usr/local/etc/redis/redis.conf"] + +EXPOSE 6379 + +HEALTHCHECK --interval=5s --timeout=3s --retries=5 \ + CMD redis-cli ping || exit 1 +``` + +**redis.conf 配置:** + +```conf +# 绑定地址 +bind 0.0.0.0 + +# 端口 +port 6379 + +# 密码保护 +requirepass your_secure_password + +# 内存管理 +maxmemory 512mb +maxmemory-policy allkeys-lru + +# 持久化 +save 900 1 +save 300 10 +save 60 10000 + +# AOF 持久化 +appendonly yes +appendfsync everysec + +# 日志 +loglevel notice +logfile "" + +# 客户端输出缓冲限制 +client-output-buffer-limit normal 0 0 0 +client-output-buffer-limit slave 256mb 64mb 60 +client-output-buffer-limit pubsub 32mb 8mb 60 +``` + +### 21.7.4 微服务架构的 Docker Compose 编排 + +**三层微服务架构示例:** + +```yaml +version: '3.9' + +services: + # 前端服务 + frontend: + build: + context: ./frontend + dockerfile: Dockerfile + container_name: frontend + ports: + - "3000:3000" + environment: + REACT_APP_API_URL: http://localhost:8000 + NODE_ENV: production + depends_on: + - api + networks: + - frontend-network + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3000"] + interval: 30s + timeout: 10s + retries: 3 + + # API 服务 + api: + build: + context: ./api + dockerfile: Dockerfile + container_name: api + ports: + - "8000:8000" + environment: + DATABASE_URL: postgresql://appuser:password@postgres:5432/myappdb + REDIS_URL: redis://redis:6379 + LOG_LEVEL: info + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + networks: + - frontend-network + - backend-network + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8000/health"] + interval: 30s + timeout: 10s + retries: 3 + deploy: + resources: + limits: + cpus: '1' + memory: 512M + reservations: + cpus: '0.5' + memory: 256M + + # PostgreSQL 数据库 + postgres: + image: postgres:16-alpine + container_name: postgres + environment: + POSTGRES_DB: myappdb + POSTGRES_USER: appuser + POSTGRES_PASSWORD: password + volumes: + - postgres_data:/var/lib/postgresql/data + - ./db/init.sql:/docker-entrypoint-initdb.d/init.sql + networks: + - backend-network + restart: unless-stopped + healthcheck: + test: ["CMD-SHELL", "pg_isready -U appuser -d myappdb"] + interval: 10s + timeout: 5s + retries: 5 + + # Redis 缓存 + redis: + image: redis:7-alpine + container_name: redis + command: redis-server --appendonly yes --requirepass redispass + volumes: + - redis_data:/data + networks: + - backend-network + restart: unless-stopped + healthcheck: + test: ["CMD", "redis-cli", "--raw", "incr", "ping"] + interval: 10s + timeout: 5s + retries: 5 + + # Nginx 反向代理 + nginx: + image: nginx:alpine + container_name: nginx + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro + - ./nginx/conf.d:/etc/nginx/conf.d:ro + - ./ssl:/etc/nginx/ssl:ro + depends_on: + - frontend + - api + networks: + - frontend-network + restart: unless-stopped + healthcheck: + test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/health"] + interval: 30s + timeout: 10s + retries: 3 + +volumes: + postgres_data: + driver: local + redis_data: + driver: local + +networks: + frontend-network: + driver: bridge + backend-network: + driver: bridge +``` + +**nginx.conf 配置:** + +```nginx +upstream frontend { + server frontend:3000; +} + +upstream api { + server api:8000; +} + +server { + listen 80; + server_name localhost; + client_max_body_size 100M; + + # 健康检查端点 + location /health { + access_log off; + return 200 "OK\n"; + add_header Content-Type text/plain; + } + + # 前端应用 + location / { + proxy_pass http://frontend; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # API 接口 + location /api/ { + proxy_pass http://api/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_redirect off; + + # WebSocket 支持 + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + + # 静态资源缓存 + location ~* ^.+\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + proxy_pass http://frontend; + expires 30d; + add_header Cache-Control "public, immutable"; + } +} +``` + +### 21.7.5 使用 VS Code Dev Containers + +Dev Containers 让整个开发环境容器化,提升团队一致性。 + +**.devcontainer/devcontainer.json:** + +```json +{ + "name": "Python Dev Environment", + "image": "mcr.microsoft.com/devcontainers/python:3.11", + + "features": { + "ghcr.io/devcontainers/features/docker-in-docker:2": {}, + "ghcr.io/devcontainers/features/git:1": {} + }, + + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python", + "ms-python.vscode-pylance", + "ms-python.pylint", + "charliermarsh.ruff", + "ms-vscode-remote.remote-containers" + ], + "settings": { + "python.linting.enabled": true, + "python.linting.pylintEnabled": true, + "python.formatting.provider": "black", + "[python]": { + "editor.formatOnSave": true, + "editor.defaultFormatter": "ms-python.python" + } + } + } + }, + + "postCreateCommand": "pip install -r requirements.txt && pip install pytest black pylint", + + "forwardPorts": [8000, 5432, 6379], + "portsAttributes": { + "8000": { + "label": "Application", + "onAutoForward": "notify" + }, + "5432": { + "label": "PostgreSQL", + "onAutoForward": "ignore" + }, + "6379": { + "label": "Redis", + "onAutoForward": "ignore" + } + }, + + "mounts": [ + "source=${localEnv:HOME}/.ssh,target=/home/vscode/.ssh,readonly" + ], + + "remoteUser": "vscode" +} +``` + +**.devcontainer/Dockerfile:** + +```dockerfile +FROM mcr.microsoft.com/devcontainers/python:3.11 + +# 安装额外工具 +RUN apt-get update && apt-get install -y \ + postgresql-client \ + redis-tools \ + curl \ + git \ + && rm -rf /var/lib/apt/lists/* + +# 创建虚拟环境 +RUN python -m venv /opt/venv +ENV PATH="/opt/venv/bin:$PATH" + +WORKDIR /workspace +``` + +**docker-compose 用于 Dev Containers:** + +```yaml +# .devcontainer/docker-compose.yml +version: '3.9' + +services: + app: + build: + context: . + dockerfile: Dockerfile + environment: + DATABASE_URL: postgresql://dev:dev@postgres:5432/myapp + REDIS_URL: redis://redis:6379 + volumes: + - ..:/workspace:cached + ports: + - "8000:8000" + depends_on: + - postgres + - redis + + postgres: + image: postgres:16-alpine + environment: + POSTGRES_USER: dev + POSTGRES_PASSWORD: dev + POSTGRES_DB: myapp + volumes: + - postgres_data:/var/lib/postgresql/data + + redis: + image: redis:7-alpine + +volumes: + postgres_data: +``` diff --git a/21_case_devops/README.md b/21_case_devops/README.md index cdc9f15..45b1a42 100644 --- a/21_case_devops/README.md +++ b/21_case_devops/README.md @@ -8,4 +8,5 @@ * [Drone Demo](21.4_drone_demo.md) * [在 IDE 中使用 Docker](21.5_ide.md) * [VS Code](21.6_vsCode.md) +* [实战例子](21.7_practical_examples.md) * [本章小结](summary.md)