add: 本地开发 compose 编排、构建脚本与数据库初始化
This commit is contained in:
@@ -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
@@ -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
|
||||||
@@ -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