add: 本地开发 compose 编排、构建脚本与数据库初始化
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
```
|
||||
Executable
+45
@@ -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
|
||||
@@ -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
|
||||
Executable
+36
@@ -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
|
||||
Reference in New Issue
Block a user