diff --git a/.project-docs/30-worklog/tasks/20260814-go-image-build-fix-7d4b9e2c.md b/.project-docs/30-worklog/tasks/20260814-go-image-build-fix-7d4b9e2c.md new file mode 100644 index 0000000..83775f6 --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260814-go-image-build-fix-7d4b9e2c.md @@ -0,0 +1,40 @@ +# Task: Make Go image build resilient to mirror failures + +## Identity + +- Task ID: 20260814-go-image-build-fix-7d4b9e2c +- Mode: Feature +- Branch: main +- Worktree: /Users/brother7/Documents/AI/NianAIGC +- Base commit: 550edcdc059073d9bea96d4b577d4fafeaefdf74 +- Owner: dsh +- Status: Ready for Integration + +## Scope + +- Fix the CI image build failure where the build machine's registry accelerator (mirror.aliyuncs.com) returned 500 on a `golang:1.21-alpine` blob: make the base images overridable and add a golang-image-free build path. + +## Intent And Constraints + +- Keep the standard multi-stage build as the default; no behavioral change for healthy registries. +- The runtime image must stay non-root, root-filesystem-read-only compatible, and identical across both build paths. + +## Outcome + +- `backend/Dockerfile`: added `GOLANG_IMAGE` and `RUNTIME_IMAGE` ARGs so a working mirror can be supplied via `--build-arg` without editing the file. +- Added `backend/Dockerfile.runtime`: alpine-only image that copies a prebuilt static `zhinian-api.linux` binary, for build environments that cannot pull the golang builder image at all. +- Documented all three build paths (standard, mirror override, prebuilt binary) in `backend/README.md` and `docs/DEPLOYMENT.md`. + +## Verification + +- `CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags="-s -w" -o zhinian-api.linux ./cmd/zhinian-api` succeeds on this machine: 12.3 MB statically linked stripped ELF x86-64, confirming the prebuilt-binary path is usable. +- Docker image assembly itself must run on a machine with Docker; both Dockerfiles follow the same runtime stage as the already-merged image definition. + +## Follow-ups + +- On the CI machine, either switch the registry accelerator in `/etc/docker/daemon.json` or use the prebuilt-binary path. +- Re-run the failing Gitea build to confirm. + +## Promotion Candidates + +- None. diff --git a/backend/Dockerfile b/backend/Dockerfile index 8b68be9..ea947c4 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,8 +1,20 @@ # syntax=docker/dockerfile:1 # Build context: the repository backend/ directory. # docker build -f backend/Dockerfile -t REGISTRY/PROJECT/zhinian-go-api:TAG backend/ +# +# GOLANG_IMAGE and RUNTIME_IMAGE are overridable for build environments whose +# registry mirror cannot serve the default docker.io images: +# docker build -f backend/Dockerfile \ +# --build-arg GOLANG_IMAGE=/golang:1.21-alpine \ +# --build-arg RUNTIME_IMAGE=/alpine:3.20 \ +# -t REGISTRY/PROJECT/zhinian-go-api:TAG backend/ +# +# If no mirror can serve the golang image at all, use backend/Dockerfile.runtime +# with a prebuilt static binary instead (see backend/README.md). +ARG GOLANG_IMAGE=golang:1.21-alpine +ARG RUNTIME_IMAGE=alpine:3.20 -FROM golang:1.21-alpine AS build +FROM ${GOLANG_IMAGE} AS build WORKDIR /src ENV CGO_ENABLED=0 GOOS=linux COPY go.mod go.sum ./ @@ -10,7 +22,7 @@ RUN go mod download COPY . . RUN go build -trimpath -ldflags="-s -w" -o /out/zhinian-api ./cmd/zhinian-api -FROM alpine:3.20 +FROM ${RUNTIME_IMAGE} RUN apk add --no-cache ca-certificates tzdata \ && addgroup -S -g 10001 zhinian \ && adduser -S -D -H -u 10001 -G zhinian zhinian \ diff --git a/backend/Dockerfile.runtime b/backend/Dockerfile.runtime new file mode 100644 index 0000000..801a7d0 --- /dev/null +++ b/backend/Dockerfile.runtime @@ -0,0 +1,24 @@ +# syntax=docker/dockerfile:1 +# Runtime-only image for build environments whose registry mirror cannot serve +# the golang builder image. Build the static binary first (any machine with +# Go 1.21, no Docker required), then assemble this image from alpine only: +# +# cd backend +# CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ +# go build -trimpath -ldflags="-s -w" -o zhinian-api.linux ./cmd/zhinian-api +# docker build -f backend/Dockerfile.runtime \ +# -t REGISTRY/PROJECT/zhinian-go-api:TAG backend/ +# +# RUNTIME_IMAGE is overridable the same way as in Dockerfile. +ARG RUNTIME_IMAGE=alpine:3.20 + +FROM ${RUNTIME_IMAGE} +RUN apk add --no-cache ca-certificates tzdata \ + && addgroup -S -g 10001 zhinian \ + && adduser -S -D -H -u 10001 -G zhinian zhinian \ + && mkdir -p /var/lib/zhinian \ + && chown -R zhinian:zhinian /var/lib/zhinian +COPY zhinian-api.linux /usr/local/bin/zhinian-api +USER 10001:10001 +EXPOSE 8080 +ENTRYPOINT ["zhinian-api"] diff --git a/backend/README.md b/backend/README.md index b2010bf..5d1f1b1 100644 --- a/backend/README.md +++ b/backend/README.md @@ -37,6 +37,35 @@ npm run go:vet npm run go:build ``` +## Container images + +Standard multi-stage build (needs the `golang` builder image): + +```bash +docker build -f backend/Dockerfile -t REGISTRY/PROJECT/zhinian-go-api:TAG backend/ +``` + +Mirror-friendly build (override the base images when the registry accelerator +cannot serve docker.io images): + +```bash +docker build -f backend/Dockerfile \ + --build-arg GOLANG_IMAGE=/golang:1.21-alpine \ + --build-arg RUNTIME_IMAGE=/alpine:3.20 \ + -t REGISTRY/PROJECT/zhinian-go-api:TAG backend/ +``` + +Prebuilt-binary build (no `golang` image at all; compile the static binary on +any Go 1.21 machine, then assemble from `alpine` only): + +```bash +cd backend +CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -trimpath -ldflags="-s -w" -o zhinian-api.linux ./cmd/zhinian-api +docker build -f backend/Dockerfile.runtime \ + -t REGISTRY/PROJECT/zhinian-go-api:TAG backend/ +``` + The runner defaults to `CGO_ENABLED=0` for reproducible cross-platform builds. To exercise the local foundation manually without changing the existing Next server, use a different port: diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md index 5edd741..80a8ef2 100644 --- a/docs/DEPLOYMENT.md +++ b/docs/DEPLOYMENT.md @@ -25,10 +25,23 @@ RDS/服务商凭据,仅共享会话密钥);Go 工作负载 `zhinian-go-api` 独 Node Worker,`worker.yaml` 已弃用保留)。Ingress 按路径分流:页面 → Web, 后端路径 → Go,`/api/internal/worker` → 无端点 deny Service。 -Go 镜像构建: +Go 镜像构建(三选一,详见 [`backend/README.md`](./backend/README.md)): ```bash +# 标准多阶段构建 docker build -f backend/Dockerfile -t REGISTRY/PROJECT/zhinian-go-api:TAG backend/ + +# 镜像加速器无法拉取 golang 镜像时,用 --build-arg 换源 +docker build -f backend/Dockerfile \ + --build-arg GOLANG_IMAGE=<可用镜像源>/golang:1.21-alpine \ + --build-arg RUNTIME_IMAGE=<可用镜像源>/alpine:3.20 \ + -t REGISTRY/PROJECT/zhinian-go-api:TAG backend/ + +# 完全绕开 golang 镜像:先在任意有 Go 1.21 的机器编译静态二进制,再只基于 alpine 组装 +cd backend +CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -trimpath -ldflags="-s -w" -o zhinian-api.linux ./cmd/zhinian-api +docker build -f backend/Dockerfile.runtime -t REGISTRY/PROJECT/zhinian-go-api:TAG backend/ ``` Go 首次启动时从 `zhinian-go-bootstrap` Secret 读取