Files
docker_practice/21_case_devops/21.7_practical_examples.md
2026-03-05 22:23:06 -08:00

878 lines
17 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 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:
```