mirror of
https://github.com/yeasy/docker_practice.git
synced 2026-03-11 04:14:38 +00:00
17 KiB
17 KiB
21.7 实战案例:Go/Rust/数据库/微服务
本节通过实际项目案例演示如何为不同类型的应用构建最优化的 Docker 镜像,以及如何使用 Docker Compose 构建完整的开发和生产环境。
21.7.1 Go 应用的最小化镜像构建
Go 语言因其编译为静态二进制和快速启动而特别适合容器化。以下展示如何构建极小的 Go 应用镜像。
超小 Go Web 服务
应用代码(main.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:
# 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"]
构建和测试:
# 构建镜像
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 框架):
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:
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):
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:
[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:
# 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"]
构建和验证:
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 镜像:
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):
-- 创建自定义用户
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):
#!/bin/bash
PGPASSWORD=$POSTGRES_PASSWORD pg_isready \
-h localhost \
-U $POSTGRES_USER \
-d $POSTGRES_DB \
-p 5432 > /dev/null 2>&1
exit $?
Docker Compose 配置:
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
性能优化配置:
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 部署
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:
[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 缓存部署
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 配置:
# 绑定地址
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 编排
三层微服务架构示例:
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 配置:
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:
{
"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:
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:
# .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: