refactor: 更好的构建脚本
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
## 前置条件
|
||||
|
||||
- Docker(需要 buildx)
|
||||
- Python 3(构建脚本用于生成 bake 定义)
|
||||
- Python 3(构建脚本)
|
||||
|
||||
## 使用
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
cd deploy/dev
|
||||
|
||||
# 1. 构建所有微服务镜像
|
||||
./build.sh
|
||||
# 默认 8 并行,可通过环境变量 BAKE_BATCH_SIZE 调整
|
||||
python3 build.py
|
||||
|
||||
# 2. 启动
|
||||
docker compose up -d
|
||||
@@ -61,7 +62,7 @@ docker compose up -d postgres redis snowflake player-rpc player-api
|
||||
管理员账户通过环境变量配置,`users-rpc` 启动时自动初始化:
|
||||
|
||||
| 变量 | 说明 | 默认值 |
|
||||
| ---------------- | ------------ | ----------------- |
|
||||
| -------------- | ------------ | --------------- |
|
||||
| ADMIN_USERNAME | 管理员用户名 | admin |
|
||||
| ADMIN_PASSWORD | 管理员密码 | admin123 |
|
||||
| ADMIN_EMAIL | 管理员邮箱 | admin@juwan.dev |
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build all microservice images via docker buildx bake."""
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from glob import glob
|
||||
from pathlib import Path
|
||||
|
||||
ROOT_DIR = Path(__file__).resolve().parent.parent.parent
|
||||
IMAGE_PREFIX = os.environ.get("IMAGE_PREFIX", "juwan")
|
||||
IMAGE_TAG = sys.argv[1] if len(sys.argv) > 1 else "dev"
|
||||
BAKE_BATCH_SIZE = int(os.environ.get("BAKE_BATCH_SIZE", "8"))
|
||||
|
||||
DOCKERFILE_TPL = """\
|
||||
# syntax=docker/dockerfile:1.7
|
||||
FROM golang:1.25-alpine AS builder
|
||||
RUN apk add --no-cache tzdata
|
||||
WORKDIR /build
|
||||
COPY go.mod go.sum ./
|
||||
RUN --mount=type=cache,target=/go/pkg/mod go mod download
|
||||
COPY . .
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \\
|
||||
--mount=type=cache,target=/root/.cache/go-build \\
|
||||
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}"]
|
||||
"""
|
||||
|
||||
DOCKERFILE_NO_CONFIG_TPL = """\
|
||||
# syntax=docker/dockerfile:1.7
|
||||
FROM golang:1.25-alpine AS builder
|
||||
RUN apk add --no-cache tzdata
|
||||
WORKDIR /build
|
||||
COPY go.mod go.sum ./
|
||||
RUN --mount=type=cache,target=/go/pkg/mod go mod download
|
||||
COPY . .
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \\
|
||||
--mount=type=cache,target=/root/.cache/go-build \\
|
||||
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
|
||||
CMD ["./main"]
|
||||
"""
|
||||
|
||||
|
||||
def discover_targets():
|
||||
targets = {}
|
||||
for service_dir in sorted(glob(str(ROOT_DIR / "app" / "*" / "*"))):
|
||||
service_dir_rel = Path(service_dir).relative_to(ROOT_DIR).as_posix()
|
||||
service_type = Path(service_dir).name
|
||||
if service_type not in ("api", "rpc", "mq", "adapter"):
|
||||
continue
|
||||
go_files = glob(os.path.join(service_dir, "*.go"))
|
||||
if not go_files or not any("package main" in open(f).read() for f in go_files):
|
||||
continue
|
||||
|
||||
yamls = glob(os.path.join(service_dir, "etc", "*.yaml"))
|
||||
service_name = Path(service_dir).parent.name
|
||||
target_name = f"{service_name}-{service_type}"
|
||||
|
||||
if yamls:
|
||||
config_name = Path(yamls[0]).name
|
||||
dockerfile = DOCKERFILE_TPL.format(
|
||||
service_dir=service_dir_rel, config_name=config_name
|
||||
)
|
||||
else:
|
||||
dockerfile = DOCKERFILE_NO_CONFIG_TPL.format(service_dir=service_dir_rel)
|
||||
|
||||
targets[target_name] = {
|
||||
"dockerfile-inline": dockerfile,
|
||||
"tags": [f"{IMAGE_PREFIX}/{target_name}:{IMAGE_TAG}"],
|
||||
}
|
||||
return targets
|
||||
|
||||
|
||||
def main():
|
||||
os.chdir(ROOT_DIR)
|
||||
targets = discover_targets()
|
||||
if not targets:
|
||||
print("No targets found")
|
||||
sys.exit(1)
|
||||
|
||||
bake = {
|
||||
"group": {"default": {"targets": list(targets.keys())}},
|
||||
"target": targets,
|
||||
}
|
||||
|
||||
fd, bakefile = tempfile.mkstemp(prefix="juwan-bake-", suffix=".json")
|
||||
try:
|
||||
with os.fdopen(fd, "w") as f:
|
||||
json.dump(bake, f, indent=2)
|
||||
print(f"Generated {len(targets)} targets -> {bakefile}")
|
||||
|
||||
names = list(targets.keys())
|
||||
for i in range(0, len(names), BAKE_BATCH_SIZE):
|
||||
batch = names[i : i + BAKE_BATCH_SIZE]
|
||||
subprocess.run(
|
||||
["docker", "buildx", "bake", "--load", "-f", bakefile, *batch],
|
||||
check=True,
|
||||
)
|
||||
finally:
|
||||
os.unlink(bakefile)
|
||||
|
||||
|
||||
main()
|
||||
@@ -1,99 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
IMAGE_PREFIX="${IMAGE_PREFIX:-juwan}"
|
||||
IMAGE_TAG="${1:-dev}"
|
||||
|
||||
cd "$ROOT_DIR"
|
||||
|
||||
bakefile=$(mktemp "${TMPDIR:-/tmp}/juwan-bake-XXXXXX")
|
||||
mv "$bakefile" "${bakefile}.json"
|
||||
bakefile="${bakefile}.json"
|
||||
|
||||
python3 - "$IMAGE_PREFIX" "$IMAGE_TAG" "$bakefile" <<'PYEOF'
|
||||
import json, os, subprocess, sys, glob
|
||||
|
||||
prefix, tag, outpath = sys.argv[1], sys.argv[2], sys.argv[3]
|
||||
|
||||
dockerfile_tpl = """\
|
||||
# syntax=docker/dockerfile:1.7
|
||||
FROM golang:1.25-alpine AS builder
|
||||
RUN apk add --no-cache tzdata
|
||||
WORKDIR /build
|
||||
COPY go.mod go.sum ./
|
||||
RUN --mount=type=cache,target=/go/pkg/mod go mod download
|
||||
COPY . .
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=cache,target=/root/.cache/go-build \
|
||||
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}"]
|
||||
"""
|
||||
|
||||
dockerfile_no_config_tpl = """\
|
||||
# syntax=docker/dockerfile:1.7
|
||||
FROM golang:1.25-alpine AS builder
|
||||
RUN apk add --no-cache tzdata
|
||||
WORKDIR /build
|
||||
COPY go.mod go.sum ./
|
||||
RUN --mount=type=cache,target=/go/pkg/mod go mod download
|
||||
COPY . .
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=cache,target=/root/.cache/go-build \
|
||||
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
|
||||
CMD ["./main"]
|
||||
"""
|
||||
|
||||
targets = {}
|
||||
for service_dir in sorted(glob.glob("app/*/*")):
|
||||
service_dir = service_dir.replace("\\", "/")
|
||||
service_type = os.path.basename(service_dir)
|
||||
if service_type not in ("api", "rpc", "mq", "adapter"):
|
||||
continue
|
||||
go_files = glob.glob(os.path.join(service_dir, "*.go"))
|
||||
has_main = any("package main" in open(f).read() for f in go_files) if go_files else False
|
||||
if not has_main:
|
||||
continue
|
||||
yamls = glob.glob(os.path.join(service_dir, "etc", "*.yaml"))
|
||||
service_name = os.path.basename(os.path.dirname(service_dir))
|
||||
target_name = f"{service_name}-{service_type}"
|
||||
|
||||
if yamls:
|
||||
config_name = os.path.basename(yamls[0])
|
||||
dockerfile = dockerfile_tpl.format(service_dir=service_dir, config_name=config_name)
|
||||
else:
|
||||
dockerfile = dockerfile_no_config_tpl.format(service_dir=service_dir)
|
||||
|
||||
targets[target_name] = {
|
||||
"dockerfile-inline": dockerfile,
|
||||
"tags": [f"{prefix}/{target_name}:{tag}"],
|
||||
}
|
||||
|
||||
bake = {
|
||||
"group": {"default": {"targets": list(targets.keys())}},
|
||||
"target": targets,
|
||||
}
|
||||
|
||||
with open(outpath, "w") as f:
|
||||
json.dump(bake, f, indent=2)
|
||||
|
||||
print(f"Generated {len(targets)} targets -> {outpath}")
|
||||
PYEOF
|
||||
|
||||
docker buildx bake --load -f "$bakefile"
|
||||
rm -f "$bakefile"
|
||||
Reference in New Issue
Block a user