refactor: build.sh 改用 buildx bake 并行构建
This commit is contained in:
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
## 前置条件
|
## 前置条件
|
||||||
|
|
||||||
- Docker
|
- Docker(需要 buildx)
|
||||||
- Go 1.25+(构建镜像时在容器内编译,本机不强制)
|
- Python 3(构建脚本用于生成 bake 定义)
|
||||||
|
|
||||||
## 使用
|
## 使用
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ docker compose ps
|
|||||||
docker compose down
|
docker compose down
|
||||||
```
|
```
|
||||||
|
|
||||||
构建脚本会扫描 `app/` 下所有 `api`、`rpc`、`mq` 入口,生成 `juwan/<service>-<type>:dev` 镜像。编译失败的服务会跳过,不影响其他服务。
|
构建脚本会扫描 `app/` 下所有 `api`、`rpc`、`mq` 入口,通过 `docker buildx bake` 并行构建所有服务镜像,生成 `juwan/<service>-<type>:dev`。
|
||||||
|
|
||||||
如需只启动部分服务:
|
如需只启动部分服务:
|
||||||
|
|
||||||
|
|||||||
+48
-21
@@ -2,33 +2,31 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||||
IMAGE_PREFIX="juwan"
|
IMAGE_PREFIX="${IMAGE_PREFIX:-juwan}"
|
||||||
IMAGE_TAG="${1:-dev}"
|
IMAGE_TAG="${1:-dev}"
|
||||||
|
|
||||||
cd "$ROOT_DIR"
|
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
|
bakefile=$(mktemp "${TMPDIR:-/tmp}/juwan-bake-XXXXXX")
|
||||||
service_type=$(basename "$service_dir")
|
mv "$bakefile" "${bakefile}.json"
|
||||||
service_name=$(basename "$(dirname "$service_dir")")
|
bakefile="${bakefile}.json"
|
||||||
|
|
||||||
entry_file=$(grep -rl "package main" "$service_dir"/*.go 2>/dev/null | head -n 1 || true)
|
python3 - "$IMAGE_PREFIX" "$IMAGE_TAG" "$bakefile" <<'PYEOF'
|
||||||
[[ -z "$entry_file" ]] && continue
|
import json, os, subprocess, sys, glob
|
||||||
|
|
||||||
config_file=$(ls "$service_dir/etc/"*.yaml 2>/dev/null | head -n 1 || true)
|
prefix, tag, outpath = sys.argv[1], sys.argv[2], sys.argv[3]
|
||||||
config_name="${config_file:+$(basename "$config_file")}"
|
|
||||||
config_name="${config_name:-config.yaml}"
|
|
||||||
|
|
||||||
image_name="${IMAGE_PREFIX}/${service_name}-${service_type}:${IMAGE_TAG}"
|
dockerfile_tpl = """\
|
||||||
echo "--- $image_name ---"
|
# syntax=docker/dockerfile:1.7
|
||||||
|
|
||||||
cat > Dockerfile.tmp <<EOF
|
|
||||||
FROM golang:1.25-alpine AS builder
|
FROM golang:1.25-alpine AS builder
|
||||||
RUN apk add --no-cache tzdata
|
RUN apk add --no-cache tzdata
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
COPY go.mod go.sum ./
|
COPY go.mod go.sum ./
|
||||||
RUN go mod download
|
RUN --mount=type=cache,target=/go/pkg/mod go mod download
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN go build -ldflags="-s -w" -o /app/main ./$service_dir
|
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
|
FROM alpine:latest
|
||||||
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||||
@@ -36,10 +34,39 @@ COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /usr/share/zoneinfo/Asia/S
|
|||||||
ENV TZ=Asia/Shanghai
|
ENV TZ=Asia/Shanghai
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=builder /app/main /app/main
|
COPY --from=builder /app/main /app/main
|
||||||
COPY $service_dir/etc /app/etc
|
COPY {service_dir}/etc /app/etc
|
||||||
CMD ["./main", "-f", "etc/$config_name"]
|
CMD ["./main", "-f", "etc/{config_name}"]
|
||||||
EOF
|
"""
|
||||||
|
|
||||||
docker build -f Dockerfile.tmp -t "$image_name" . && echo "OK: $image_name" || echo "FAIL: $image_name"
|
targets = {}
|
||||||
rm -f Dockerfile.tmp
|
for service_dir in sorted(glob.glob("app/*/*")):
|
||||||
done
|
service_type = os.path.basename(service_dir)
|
||||||
|
if service_type not in ("api", "rpc", "mq"):
|
||||||
|
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"))
|
||||||
|
config_name = os.path.basename(yamls[0]) if yamls else "config.yaml"
|
||||||
|
service_name = os.path.basename(os.path.dirname(service_dir))
|
||||||
|
target_name = f"{service_name}-{service_type}"
|
||||||
|
|
||||||
|
targets[target_name] = {
|
||||||
|
"dockerfile-inline": dockerfile_tpl.format(service_dir=service_dir, config_name=config_name),
|
||||||
|
"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