add: 本地开发 compose 编排、构建脚本与数据库初始化

This commit is contained in:
zetaloop
2026-04-01 03:45:11 +08:00
parent e7970ac25f
commit 507d8c6fd9
7 changed files with 501 additions and 1 deletions
+14
View File
@@ -0,0 +1,14 @@
.git
.idea
.serena
.sisyphus
node_modules
deploy
backup
docs
*.log
# Go compiled binaries
app/*/api/api
app/*/rpc/rpc
app/*/mq/mq
+6 -1
View File
@@ -120,4 +120,9 @@ dist
# End of https://mrkandreev.name/snippets/gitignore-generator/#Node # End of https://mrkandreev.name/snippets/gitignore-generator/#Node
DockerFile DockerFile
.idea .idea
# Go compiled binaries
app/*/api/api
app/*/rpc/rpc
app/*/mq/mq
+32
View File
@@ -0,0 +1,32 @@
# PostgreSQL
PD_USERNAME=postgres
DB_PASSWORD=123456
DB_PORT=5432
DB_NAME=app
DB_URI=postgresql://postgres:123456@postgres:5432/app
# Redis
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_M_HOST=redis:6379
REDIS_S_HOST=redis:6379
# Kafka
KAFKA_BROKER=kafka:9092
# Email (placeholder)
EMAIL_SMTP_HOST=smtp.example.com
EMAIL_SMTP_PORT=465
EMAIL_SMTP_USERNAME=test@example.com
EMAIL_SMTP_PASSWORD=changeme
EMAIL_FROM_ADDRESS=test@example.com
EMAIL_FROM_NAME=juwan-dev
EMAIL_REPLY_TO=
# S3 (objectstory, placeholder)
S3_ENDPOINT=https://example.com
S3_ACCESS_KEY=changeme
S3_SECRET_KEY=changeme
S3_BUCKET_NAME=dev-bucket
S3_REGION=auto
+62
View File
@@ -0,0 +1,62 @@
# 本地开发环境
## 前置条件
- Docker
- Go 1.25+(构建镜像时在容器内编译,本机不强制)
## 使用
```bash
cd deploy/dev
# 1. 构建所有微服务镜像
./build.sh
# 2. 启动
docker compose up -d
# 3. 查看状态
docker compose ps
# 4. 停止
docker compose down
```
构建脚本会扫描 `app/` 下所有 `api``rpc``mq` 入口,生成 `juwan/<service>-<type>:dev` 镜像。编译失败的服务会跳过,不影响其他服务。
如需只启动部分服务:
```bash
docker compose up -d postgres redis snowflake player-rpc player-api
```
## 端口映射
| 服务 | 宿主机端口 |
| --------------- | ---------- |
| PostgreSQL | 15432 |
| Redis | 16379 |
| Kafka | 19092 |
| users-api | 18801 |
| player-api | 18802 |
| game-api | 18803 |
| shop-api | 18804 |
| order-api | 18805 |
| wallet-api | 18806 |
| community-api | 18807 |
| objectstory-api | 18808 |
| email-api | 18809 |
## 环境变量
编辑 `.env` 修改数据库密码、Kafka 地址等。默认值可直接用于本地开发。
## 数据库初始化
首次启动时 PostgreSQL 会自动执行 `desc/sql/` 下的建表语句。如需重新初始化,删除 volume 后重启:
```bash
docker compose down -v
docker compose up -d
```
+45
View File
@@ -0,0 +1,45 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
IMAGE_PREFIX="juwan"
IMAGE_TAG="${1:-dev}"
cd "$ROOT_DIR"
find app -mindepth 2 -maxdepth 2 -type d \( -name "api" -o -name "rpc" -o -name "mq" \) | sort | while read -r service_dir; do
service_type=$(basename "$service_dir")
service_name=$(basename "$(dirname "$service_dir")")
entry_file=$(grep -rl "package main" "$service_dir"/*.go 2>/dev/null | head -n 1 || true)
[[ -z "$entry_file" ]] && continue
config_file=$(ls "$service_dir/etc/"*.yaml 2>/dev/null | head -n 1 || true)
config_name="${config_file:+$(basename "$config_file")}"
config_name="${config_name:-config.yaml}"
image_name="${IMAGE_PREFIX}/${service_name}-${service_type}:${IMAGE_TAG}"
echo "--- $image_name ---"
cat > Dockerfile.tmp <<EOF
FROM golang:1.25-alpine AS builder
RUN apk add --no-cache tzdata
WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build -ldflags="-s -w" -o /app/main ./$service_dir
FROM alpine:latest
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /usr/share/zoneinfo/Asia/Shanghai
ENV TZ=Asia/Shanghai
WORKDIR /app
COPY --from=builder /app/main /app/main
COPY $service_dir/etc /app/etc
CMD ["./main", "-f", "etc/$config_name"]
EOF
docker build -f Dockerfile.tmp -t "$image_name" . && echo "OK: $image_name" || echo "FAIL: $image_name"
rm -f Dockerfile.tmp
done
+306
View File
@@ -0,0 +1,306 @@
services:
# ==================== 基础设施 ====================
postgres:
image: postgres:17-bookworm
container_name: juwan-postgres
restart: unless-stopped
environment:
POSTGRES_USER: ${PD_USERNAME}
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ${DB_NAME}
ports:
- "15432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
- ./init-db.sh:/docker-entrypoint-initdb.d/init-db.sh:ro
- ../../desc/sql:/docker-entrypoint-initdb.d/sql:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${PD_USERNAME} -d ${DB_NAME}"]
interval: 5s
timeout: 3s
retries: 10
redis:
image: redis:8-alpine
container_name: juwan-redis
restart: unless-stopped
ports:
- "16379:6379"
volumes:
- redisdata:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 10
kafka:
image: apache/kafka:4.0.1
container_name: juwan-kafka
restart: unless-stopped
environment:
KAFKA_NODE_ID: 1
KAFKA_PROCESS_ROLES: broker,controller
KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:9093
KAFKA_LISTENERS: PLAINTEXT://:9092,CONTROLLER://:9093
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
CLUSTER_ID: juwan-dev-kafka-cluster-id
ports:
- "19092:9092"
healthcheck:
test: ["CMD-SHELL", "/opt/kafka/bin/kafka-broker-api-versions.sh --bootstrap-server localhost:9092"]
interval: 10s
timeout: 5s
retries: 10
# ==================== 共享服务 ====================
snowflake:
image: juwan/snowflake-rpc:dev
container_name: juwan-snowflake
restart: unless-stopped
# ==================== RPC 层 ====================
user-rpc:
image: juwan/users-rpc:dev
container_name: juwan-user-rpc
restart: unless-stopped
env_file: .env
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
snowflake:
condition: service_started
user-verifications-rpc:
image: juwan/user_verifications-rpc:dev
container_name: juwan-user-verifications-rpc
restart: unless-stopped
env_file: .env
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
snowflake:
condition: service_started
user-rpc:
condition: service_started
player-rpc:
image: juwan/player-rpc:dev
container_name: juwan-player-rpc
restart: unless-stopped
env_file: .env
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
snowflake:
condition: service_started
game-rpc:
image: juwan/game-rpc:dev
container_name: juwan-game-rpc
restart: unless-stopped
env_file: .env
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
snowflake:
condition: service_started
shop-rpc:
image: juwan/shop-rpc:dev
container_name: juwan-shop-rpc
restart: unless-stopped
env_file: .env
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
snowflake:
condition: service_started
user-rpc:
condition: service_started
order-rpc:
image: juwan/order-rpc:dev
container_name: juwan-order-rpc
restart: unless-stopped
env_file: .env
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
snowflake:
condition: service_started
wallet-rpc:
image: juwan/wallet-rpc:dev
container_name: juwan-wallet-rpc
restart: unless-stopped
env_file: .env
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
snowflake:
condition: service_started
community-rpc:
image: juwan/community-rpc:dev
container_name: juwan-community-rpc
restart: unless-stopped
env_file: .env
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
snowflake:
condition: service_started
objectstory-rpc:
image: juwan/objectstory-rpc:dev
container_name: juwan-objectstory-rpc
restart: unless-stopped
env_file: .env
# ==================== API 层 ====================
users-api:
image: juwan/users-api:dev
container_name: juwan-users-api
restart: unless-stopped
env_file: .env
ports:
- "18801:8888"
depends_on:
user-rpc:
condition: service_started
user-verifications-rpc:
condition: service_started
player-api:
image: juwan/player-api:dev
container_name: juwan-player-api
restart: unless-stopped
env_file: .env
ports:
- "18802:8888"
depends_on:
player-rpc:
condition: service_started
game-api:
image: juwan/game-api:dev
container_name: juwan-game-api
restart: unless-stopped
env_file: .env
ports:
- "18803:8888"
depends_on:
game-rpc:
condition: service_started
shop-api:
image: juwan/shop-api:dev
container_name: juwan-shop-api
restart: unless-stopped
env_file: .env
ports:
- "18804:8888"
depends_on:
shop-rpc:
condition: service_started
order-api:
image: juwan/order-api:dev
container_name: juwan-order-api
restart: unless-stopped
env_file: .env
ports:
- "18805:8888"
depends_on:
order-rpc:
condition: service_started
player-rpc:
condition: service_started
shop-rpc:
condition: service_started
wallet-api:
image: juwan/wallet-api:dev
container_name: juwan-wallet-api
restart: unless-stopped
env_file: .env
ports:
- "18806:8888"
depends_on:
wallet-rpc:
condition: service_started
community-api:
image: juwan/community-api:dev
container_name: juwan-community-api
restart: unless-stopped
env_file: .env
ports:
- "18807:8888"
depends_on:
community-rpc:
condition: service_started
user-rpc:
condition: service_started
objectstory-api:
image: juwan/objectstory-api:dev
container_name: juwan-objectstory-api
restart: unless-stopped
env_file: .env
ports:
- "18808:8888"
depends_on:
objectstory-rpc:
condition: service_started
email-api:
image: juwan/email-api:dev
container_name: juwan-email-api
restart: unless-stopped
env_file: .env
ports:
- "18809:8888"
depends_on:
redis:
condition: service_healthy
kafka:
condition: service_healthy
# ==================== MQ ====================
email-mq:
image: juwan/email-mq:dev
container_name: juwan-email-mq
restart: unless-stopped
env_file: .env
depends_on:
kafka:
condition: service_healthy
volumes:
pgdata:
name: juwan-pgdata
redisdata:
name: juwan-redisdata
+36
View File
@@ -0,0 +1,36 @@
#!/usr/bin/env bash
set -e
SQL_DIR="/docker-entrypoint-initdb.d/sql"
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" -f "$SQL_DIR/common/update_updated_at_column.sql"
ordered=(
users/users.sql
users/user_follows.sql
users/user_preferences.sql
users/user_verifications.sql
game/games.sql
player/players.sql
player/player_services.sql
shop/shop.sql
shop/shop_players.sql
shop/shop_invitations.sql
order/00-orders.sql
order/order_state_log.sql
wallet/00-wallets.sql
wallet/wallet_transactions.sql
community/posts.sql
community/comments.sql
community/post_likes.sql
community/comment_likes.sql
dispute/disputes.sql
dispute/dispute_timeline.sql
review/reviews.sql
search/favorites.sql
)
for f in "${ordered[@]}"; do
[ -f "$SQL_DIR/$f" ] || continue
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" -f "$SQL_DIR/$f"
done