16 KiB
16 KiB
资产管理系统后端骨架 - 技术需求 V1(Docker 全环境版)
执行目标:基于纯 Docker 环境构建可编译、可运行的最小化后端骨架,无宿主机依赖(无全局 Go/Protoc/Postgres/Redis 环境) 技术栈锁定:golang:1.26.1-alpine3.23、postgres:18.3-alpine3.23、redis:8.6.2-alpine、Docker/Docker Compose
一、核心调整说明
- 基础镜像版本统一:所有 Go 构建/运行、数据库、缓存镜像均替换为指定版本
- 纯 Docker 环境闭环:
- 宿主机无依赖(无需安装 Go/Protoc/Make 等)
- 所有构建/编译/运行操作均通过 Docker 容器完成
- 新增 Protoc 构建容器(解决 proto 文件编译依赖)
- 路径与权限优化:适配 Alpine 3.23 镜像特性,调整文件权限和执行逻辑
二、完整目录结构(./backend/ 根)
./backend/
├── gateway/
│ ├── cmd/
│ │ └── main.go
│ ├── internal/
│ │ ├── config/
│ │ │ └── config.go
│ │ ├── ws/
│ │ │ ├── hub.go
│ │ │ ├── client.go
│ │ │ └── message.go
│ │ └── router/
│ │ └── router.go
│ ├── go.mod
│ └── Dockerfile
├── services/
│ └── user-svc/
│ ├── cmd/
│ │ └── main.go
│ ├── internal/
│ │ ├── config/
│ │ │ └── config.go
│ │ ├── domain/
│ │ │ └── user.go
│ │ ├── repository/
│ │ │ └── repo.go
│ │ ├── service/
│ │ │ └── service.go
│ │ └── grpcserver/
│ │ └── server.go
│ ├── proto/
│ │ └── user.proto
│ ├── migrations/
│ │ └── 001_init.sql
│ ├── go.mod
│ └── Dockerfile
├── shared/
│ ├── proto/
│ │ ├── common/
│ │ │ └── common.proto
│ │ └── generate.go
│ └── pkg/
│ ├── logger/
│ │ └── logger.go
│ ├── errors/
│ │ └── errors.go
│ ├── database/
│ │ └── postgres.go
│ └── cache/
│ └── redis.go
├── scripts/
│ ├── dev-start.sh # 纯 Docker 启动脚本
│ ├── gen-proto.sh # Docker 内编译 proto
│ └── docker-proto-builder/ # Protoc 编译容器配置
│ └── Dockerfile
├── docker-compose.yml # 全服务编排(含 proto 编译、Go 构建)
├── docker-compose.dev.yml # 开发模式编排(挂载源码、热更新)
├── Makefile # 封装 Docker 命令(兼容无宿主机 Make 环境)
└── README.md # 纯 Docker 环境使用说明
三、核心文件调整(适配指定技术栈 + 纯 Docker 环境)
3.1 基础镜像统一替换
所有 Dockerfile/Compose 文件中的基础镜像替换为指定版本:
- Go 构建/运行:
golang:1.26.1-alpine3.23 - PostgreSQL:
postgres:18.3-alpine3.23 - Redis:
redis:8.6.2-alpine - 基础运行镜像:
alpine:3.23
3.2 ./backend/gateway/Dockerfile
# 构建阶段:使用指定 Go 版本
FROM golang:1.26.1-alpine3.23 AS builder
WORKDIR /build
# 安装依赖(Alpine 3.23 适配)
RUN apk add --no-cache git ca-certificates tzdata
ENV TZ=Asia/Shanghai
# 复制共享代码和网关代码
COPY ../shared /shared
COPY . /build
# 替换模块路径并构建(关闭 CGO 适配 Alpine)
RUN go mod download
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o gateway ./cmd
# 运行阶段:轻量 Alpine
FROM alpine:3.23
RUN apk add --no-cache ca-certificates tzdata
ENV TZ=Asia/Shanghai
WORKDIR /app
# 复制构建产物
COPY --from=builder /build/gateway .
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
# 权限优化(非 root 运行)
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
RUN chown -R appuser:appgroup /app
USER appuser
EXPOSE 8080
# 优雅退出配置
STOPSIGNAL SIGTERM
CMD ["./gateway"]
3.3 ./backend/services/user-svc/Dockerfile
# 构建阶段
FROM golang:1.26.1-alpine3.23 AS builder
WORKDIR /build
RUN apk add --no-cache git ca-certificates tzdata
ENV TZ=Asia/Shanghai
# 复制共享代码和用户服务代码
COPY ../../shared /shared
COPY . /build
RUN go mod download
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o user-svc ./cmd
# 运行阶段
FROM alpine:3.23
RUN apk add --no-cache ca-certificates tzdata
ENV TZ=Asia/Shanghai
WORKDIR /app
COPY --from=builder /build/user-svc .
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
# 非 root 运行
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
RUN chown -R appuser:appgroup /app
USER appuser
EXPOSE 50051
STOPSIGNAL SIGTERM
CMD ["./user-svc"]
3.4 ./backend/scripts/docker-proto-builder/Dockerfile
# Protoc 编译专用容器(解决宿主机无 Protoc 依赖)
FROM golang:1.26.1-alpine3.23
# 安装 Protoc 及 Go 插件
RUN apk add --no-cache protobuf git
RUN go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.34.0
RUN go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.5.1
# 配置环境变量
ENV PATH="$PATH:/go/bin"
ENV GOPATH="/go"
WORKDIR /proto
CMD ["protoc", "--version"]
3.5 ./backend/scripts/gen-proto.sh
#!/bin/sh
# 纯 Docker 环境编译 Proto 文件(无宿主机 Protoc 依赖)
set -e
# 进入项目根目录
SCRIPT_DIR=$(cd $(dirname $0)/.. && pwd)
cd $SCRIPT_DIR
# 构建 Protoc 编译容器并执行编译
docker build -t proto-builder:latest ./scripts/docker-proto-builder/
# 运行编译容器(挂载 proto 目录)
docker run --rm \
-v $SCRIPT_DIR/shared/proto:/proto \
-w /proto \
proto-builder:latest \
sh -c "for f in */*.proto; do \
protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
\$f; \
done"
echo "✅ Proto 文件编译完成(Docker 环境)"
3.6 ./backend/docker-compose.yml
version: '3.8'
services:
# Proto 编译服务(一次性执行)
proto-builder:
build: ./scripts/docker-proto-builder/
volumes:
- ./shared/proto:/proto
command: sh -c "/proto/../scripts/gen-proto.sh"
profiles: ["proto"]
# 网关服务
gateway:
build: ./gateway
ports:
- "8080:8080"
environment:
- WS_ADDR=:8080
- REDIS_ADDR=redis:6379
depends_on:
- redis
restart: unless-stopped
security_opt:
- no-new-privileges:true
networks:
- backend-network
# 用户服务
user-svc:
build: ./services/user-svc
ports:
- "50051:50051"
environment:
- DB_HOST=user-db
- DB_PORT=5432
- DB_USER=postgres
- DB_PASSWORD=postgres
- DB_NAME=user_db
- GRPC_ADDR=:50051
depends_on:
- user-db
restart: unless-stopped
security_opt:
- no-new-privileges:true
networks:
- backend-network
# PostgreSQL 数据库(指定版本)
user-db:
image: postgres:18.3-alpine3.23
ports:
- "5432:5432"
environment:
- POSTGRES_DB=user_db
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_INITDB_ARGS=--encoding=UTF8 --lc-collate=C --lc-ctype=C
volumes:
- user_data:/var/lib/postgresql/data
- ./services/user-svc/migrations:/docker-entrypoint-initdb.d
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -d user_db"]
interval: 5s
timeout: 3s
retries: 5
networks:
- backend-network
# Redis 缓存(指定版本)
redis:
image: redis:8.6.2-alpine
ports:
- "6379:6379"
command: redis-server --appendonly yes --requirepass ""
volumes:
- redis_data:/data
restart: unless-stopped
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
networks:
- backend-network
volumes:
user_data:
redis_data:
networks:
backend-network:
driver: bridge
3.7 ./backend/docker-compose.dev.yml(开发模式,挂载源码)
version: '3.8'
services:
gateway:
build: ./gateway
ports:
- "8080:8080"
environment:
- WS_ADDR=:8080
- REDIS_ADDR=redis:6379
depends_on:
- redis
volumes:
- ./gateway:/build
- ./shared:/shared
command: sh -c "go run cmd/main.go"
restart: on-failure
networks:
- backend-network
user-svc:
build: ./services/user-svc
ports:
- "50051:50051"
environment:
- DB_HOST=user-db
- DB_PORT=5432
- DB_USER=postgres
- DB_PASSWORD=postgres
- DB_NAME=user_db
- GRPC_ADDR=:50051
depends_on:
- user-db
volumes:
- ./services/user-svc:/build
- ./shared:/shared
command: sh -c "go run cmd/main.go"
restart: on-failure
networks:
- backend-network
# 复用主 Compose 的数据库/缓存服务
user-db:
extends:
file: docker-compose.yml
service: user-db
redis:
extends:
file: docker-compose.yml
service: redis
volumes:
user_data:
redis_data:
networks:
backend-network:
driver: bridge
3.8 ./backend/scripts/dev-start.sh
#!/bin/sh
set -e
# 纯 Docker 环境启动脚本(无宿主机依赖)
SCRIPT_DIR=$(cd $(dirname $0)/.. && pwd)
cd $SCRIPT_DIR
echo "======================================"
echo " Asset Helper Backend (Docker 环境) "
echo "======================================"
# 停止旧容器
echo "🔄 停止旧容器..."
docker-compose -f docker-compose.yml down -v 2>/dev/null || true
# 编译 Proto 文件(Docker 内执行)
echo "📝 编译 Proto 文件..."
./scripts/gen-proto.sh
# 启动开发环境
echo "🚀 启动开发容器..."
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up --build -d
# 等待服务就绪
echo "⌛ 等待服务初始化..."
sleep 10
# 检查服务状态
echo "✅ 服务状态检查:"
docker-compose -f docker-compose.yml ps
# 健康检查
echo "\n📊 健康检查:"
docker run --rm --network backend_backend-network curlimages/curl:8.7.1 -s http://gateway:8080/health || echo "⚠️ Gateway 服务未就绪"
# 输出访问信息
echo "\n📌 访问信息:"
echo "Gateway WebSocket: ws://localhost:8080/ws"
echo "Health Check: http://localhost:8080/health"
echo "User gRPC: localhost:50051"
echo "PostgreSQL: localhost:5432 (user: postgres, pass: postgres, db: user_db)"
echo "Redis: localhost:6379"
echo "\n💡 查看日志:docker-compose -f docker-compose.yml logs -f"
echo "💡 停止服务:docker-compose -f docker-compose.yml down -v"
3.9 ./backend/Makefile(兼容无宿主机 Make 环境)
.PHONY: dev build proto test down logs clean
# 兼容无宿主机 Make 环境:通过 Docker 执行核心操作
dev:
@echo "启动开发环境(纯 Docker)..."
@./scripts/dev-start.sh
build:
@echo "构建生产镜像..."
@docker-compose -f docker-compose.yml build
proto:
@echo "编译 Proto 文件(Docker 内)..."
@./scripts/gen-proto.sh
test:
@echo "执行健康检查..."
@docker run --rm --network backend_backend-network curlimages/curl:8.7.1 -s http://gateway:8080/health || echo "Gateway 服务未运行"
down:
@echo "停止并清理容器..."
@docker-compose -f docker-compose.yml -f docker-compose.dev.yml down -v
logs:
@echo "实时查看日志..."
@docker-compose -f docker-compose.yml -f docker-compose.dev.yml logs -f
clean: down
@echo "清理镜像和数据卷..."
@docker system prune -f
@docker volume rm backend_user_data backend_redis_data 2>/dev/null || true
3.10 ./backend/README.md(纯 Docker 环境说明)
# Asset Helper Backend
资产管理系统后端服务(纯 Docker 环境版)
## 核心特性
- 📦 全 Docker 环境:无需宿主机安装 Go/Protoc/Postgres/Redis 等依赖
- 🔒 安全配置:非 root 用户运行容器、权限最小化
- 🚀 开发/生产双模式:开发模式挂载源码,生产模式构建镜像
- 🛡️ 健康检查:自动检测服务状态,快速定位问题
## 环境要求
- Docker 20.10+
- Docker Compose 2.0+
- 无其他宿主机依赖(无需 Go/Protoc/Make/Postgres/Redis)
## 快速开始
```bash
# 进入后端目录
cd ./backend
# 启动开发环境(自动编译 Proto + 启动所有服务)
make dev
# 检查服务状态
make test
# 查看实时日志
make logs
# 停止并清理环境
make down
# 清理所有镜像/数据卷(彻底重置)
make clean
服务端口
| 服务 | 端口 | 说明 |
|---|---|---|
| gateway | 8080 | WebSocket + HTTP 入口 |
| user-svc | 50051 | gRPC 用户服务 |
| user-db | 5432 | PostgreSQL 数据库 |
| redis | 6379 | Redis 缓存 |
开发模式 vs 生产模式
开发模式(默认)
- 挂载本地源码到容器,支持实时修改
- 使用
docker-compose.dev.yml扩展配置 - 自动重启 Go 服务(代码修改后需手动重启)
生产模式
# 构建生产镜像
make build
# 启动生产环境(无源码挂载)
docker-compose -f docker-compose.yml up -d
关键操作
编译 Proto 文件
make proto
健康检查
make test
# 或直接执行
curl http://localhost:8080/health
测试 WebSocket
# 安装 wscat(或使用 Docker 版)
docker run --rm -it --network backend_backend-network jmalloc/websocket-client wss://gateway:8080/ws
> {"id":"1","type":"test","service":"user","action":"health"}
目录结构
./backend/ # 后端服务根目录
├── gateway/ # 网关服务(WebSocket + HTTP)
├── services/ # 微服务目录(user-svc 示例)
├── shared/ # 共享代码/Proto/工具包
├── scripts/ # 自动化脚本(Docker 环境适配)
├── docker-compose.yml # 生产环境编排
├── docker-compose.dev.yml # 开发环境编排
└── Makefile # 快捷命令封装
技术栈版本
- Go: 1.26.1-alpine3.23
- PostgreSQL: 18.3-alpine3.23
- Redis: 8.6.2-alpine
- Docker: 20.10+
- Docker Compose: 2.0+
## 四、验证标准(纯 Docker 环境)
### 4.1 执行命令
```bash
cd ./backend
make dev
4.2 预期结果
- 容器启动:
gateway/user-svc/user-db/redis4 个容器状态为Up - Proto 编译:无报错,
shared/proto/common/生成common.pb.go和common_grpc.pb.go - 健康检查:
make test返回{"status":"ok"} - 端口可访问:
ws://localhost:8080/ws可建立 WebSocket 连接localhost:5432可连接 PostgreSQLlocalhost:6379可连接 Redis
4.3 生产环境验证
# 构建生产镜像
make build
# 启动生产环境
docker-compose up -d
# 验证无源码挂载,服务正常运行
docker-compose ps
curl http://localhost:8080/health
五、关键优化点(适配纯 Docker 环境)
- Protoc 编译容器化:无需宿主机安装 Protoc,通过专用容器完成 proto 编译
- 依赖全内置:所有 Go 模块、系统依赖均在 Docker 内安装,无宿主机依赖
- 权限安全:容器内使用非 root 用户运行服务,降低安全风险
- 健康检查:PostgreSQL/Redis 增加健康检查,确保服务就绪后启动应用
- 日志与重启策略:配置合理的重启策略和日志输出,便于问题排查
- 网络隔离:使用自定义网络,避免端口冲突和网络安全问题
六、执行说明(给 Trae 的指令)
- 确保目标机器已安装 Docker 和 Docker Compose
- 将上述目录结构和文件完整生成到
./backend/目录 - 执行
cd ./backend && make dev启动环境 - 验证
make test返回{"status":"ok"}即完成基础架构搭建 - 后续业务逻辑开发可基于此骨架,通过挂载源码的开发模式迭代