# server/Dockerfile — onesvm-browser-server 多阶段构建(Contract §5-A5)。 # # 架构说明(docker-image-transfer 规则):生产 amd64;Mac(arm64)构建时 # Makefile/部署脚本传 --platform linux/amd64;本机冒烟可 --platform linux/arm64。 # # 运行镜像二选一(默认 distroless;若生产节点拉取失败转 alpine): # ① gcr.io/distroless/base-debian12:nonroot —— 无 shell,~20MB,符合 ≤60MB 目标; # ② alpine:3.20 —— 有 shell,healthcheck 更方便,体积 ~13MB + 二进制。 # 本仓选 ①(distroless):镜像最小、攻击面最小;healthcheck 走二进制内建探针 # (-healthz-probe,见 cmd/browser-server main.go 同包说明)。 # # 构建示例: # docker build --platform linux/amd64 -t onesvm/browser-server:dev -f server/Dockerfile . # ---- 阶段一:构建(goproxy.cn 镜像加速;CGO 关闭纯静态)---- FROM golang:1.27-alpine AS builder ENV GOPROXY=https://goproxy.cn,direct \ CGO_ENABLED=0 \ GOOS=linux WORKDIR /src # 先拷依赖清单做层缓存(go.mod/go.sum 变动才重新 download)。 COPY server/go.mod server/go.sum ./ RUN go mod download COPY server/ ./ # -trimpath 去路径、-s -w 去符号表(镜像 ≤60MB 目标);产出单二进制多角色。 RUN go build -trimpath -ldflags "-s -w" -o /out/browser-server ./cmd/browser-server \ && touch /out/.keep # 供 stage-2 借道 copy 一个属主为 nonroot 的文件以建 /data 目录 # ---- 阶段二:运行(distroless / nonroot)---- # 备选(若生产节点 distroless 拉不动):把下行换为 # FROM docker.m.daocloud.io/library/alpine:3.20 # 并补 entrypoint ["/browser-server"](alpine 下 CMD 形式不变)。 FROM gcr.io/distroless/base-debian12:nonroot COPY --from=builder /out/browser-server /browser-server # 数据卷属主预置:named volume 首挂时 Docker 以镜像目录属主初始化(copy-up), # 默认 root 属主会挡住 nonroot(65532) 写库;这里预建 /data 并授予 nonroot 属主, # 卷挂载后 65532 可建库文件与 mihomo provider 子目录(swarm/compose 同理)。 COPY --from=builder --chown=65532:65532 /out/.keep /data/.keep # 无 EXPOSE 需要(8640 由 stack ports/ingress 声明);entrypoint 直指二进制, # role 由 stack command 追加参数(-role=gateway|scheduler|proxymanager)。 ENTRYPOINT ["/browser-server"]