diff --git a/README.md b/README.md index 1ffac0e..5104a48 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ pnpm dev ``` 脚本从环境变量读取 SWR 凭据(`SWR_REGISTRY`、`SWR_NAMESPACE`、`SWR_USERNAME`、`SWR_PASSWORD`), -单架构(`linux/amd64`)构建并推送到华为云 SWR(SWR 不支持 buildx 多架构 OCI manifest,脚本使用 `docker build` + `docker push` 分步执行)。 +多架构(`linux/amd64,linux/arm64`)构建并推送到华为云 SWR:分架构推送后用 `docker manifest` 合并为 `latest`(标准 Docker manifest list,SWR 可正常解析)。 Dockerfile 基础镜像使用国内可访问的 DaoCloud 镜像源(`docker.m.daocloud.io/library/node:22-alpine`,Docker Hub 在国内不可达)。 diff --git a/build-and-push.sh b/build-and-push.sh index f5a8e69..e66da39 100755 --- a/build-and-push.sh +++ b/build-and-push.sh @@ -4,9 +4,10 @@ # # 遵循团队部署规范: # - 镜像推送到华为云 SWR,tag 固定为 latest -# - 单架构构建 linux/amd64(服务器主流架构;SWR 不支持 buildx 多架构 OCI manifest) -# - --provenance=false 禁用 SLSA 证明,避免额外 manifest 导致 SWR 解析失败 -# - docker build + docker push 分步执行,与 SWR 兼容性更好 +# - 多架构:linux/amd64 + linux/arm64 +# - 分架构推送(latest-amd64 / latest-arm64),再用 docker manifest +# 合并为 latest(标准 Docker manifest list,SWR 可正常解析) +# - --provenance=false 禁用 SLSA 证明,避免额外 manifest # - 登录凭据一律从环境变量读取,绝不写入仓库 # # 使用前先设置环境变量(示例): @@ -28,22 +29,49 @@ set -euo pipefail IMAGE_NAME="arcticnewone" IMAGE_TAG="latest" FULL_IMAGE="${SWR_REGISTRY}/${SWR_NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG}" +ARCHS="amd64 arm64" +BUILDER_NAME="multiarch-builder" -# 前置检查:docker +# 前置检查:docker / buildx / manifest if ! command -v docker >/dev/null 2>&1; then echo "错误: 未找到 docker 命令,请先安装 Docker" >&2 exit 1 fi +if ! docker buildx version >/dev/null 2>&1; then + echo "错误: 未找到 docker buildx,请启用 Buildx(Docker 20.10+ 默认内置)" >&2 + exit 1 +fi +if ! docker manifest --help >/dev/null 2>&1; then + echo "错误: 当前 Docker 不支持 docker manifest 命令" >&2 + exit 1 +fi # 1. 登录 SWR(凭据来自环境变量,不写入仓库) -echo ">>> [1/3] 登录 SWR: ${SWR_REGISTRY}" +echo ">>> [1/4] 登录 SWR: ${SWR_REGISTRY}" echo "${SWR_PASSWORD}" | docker login "${SWR_REGISTRY}" -u "${SWR_USERNAME}" --password-stdin -# 2. 构建 linux/amd64 镜像(禁用 provenance,产物为单一 manifest) -echo ">>> [2/3] 构建镜像: ${FULL_IMAGE} (linux/amd64)" -docker build --platform linux/amd64 --provenance=false -t "${FULL_IMAGE}" . +# 2. 确保支持多架构的 builder 存在 +echo ">>> [2/4] 准备多架构 builder: ${BUILDER_NAME}" +if ! docker buildx inspect "${BUILDER_NAME}" >/dev/null 2>&1; then + docker buildx create --name "${BUILDER_NAME}" +fi -# 3. 推送镜像 -echo ">>> [3/3] 推送镜像: ${FULL_IMAGE}" -docker push "${FULL_IMAGE}" -echo ">>> 推送完成: ${FULL_IMAGE}" +# 3. 分别构建并推送各架构镜像(禁用 provenance,产物为单一 manifest) +for ARCH in ${ARCHS}; do + echo ">>> [3/4] 构建并推送 linux/${ARCH}: ${FULL_IMAGE}-${ARCH}" + docker buildx build \ + --builder "${BUILDER_NAME}" \ + --platform "linux/${ARCH}" \ + --provenance=false \ + --push \ + -t "${FULL_IMAGE}-${ARCH}" \ + . +done + +# 4. 合并为多架构 manifest list 并推送(标准 Docker manifest list) +echo ">>> [4/4] 合并并推送多架构 manifest: ${FULL_IMAGE}" +docker manifest create "${FULL_IMAGE}" \ + --amend "${FULL_IMAGE}-amd64" \ + --amend "${FULL_IMAGE}-arm64" +docker manifest push "${FULL_IMAGE}" +echo ">>> 推送完成: ${FULL_IMAGE} (linux/amd64 + linux/arm64)" diff --git a/deploy/README.md b/deploy/README.md index de46771..9cdc8ed 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -15,7 +15,7 @@ ## 镜像构建与推送 -镜像由项目根目录的 `build-and-push.sh` 构建并推送到华为云 SWR(单架构 `linux/amd64`,tag 统一为 `latest`): +镜像由项目根目录的 `build-and-push.sh` 构建并推送到华为云 SWR(多架构 `linux/amd64,linux/arm64`,tag 统一为 `latest`): ```bash # 1. 设置环境变量(凭据绝不写入仓库) @@ -30,7 +30,7 @@ export SWR_PASSWORD="<密码>" 推送完成后镜像地址:`swr.cn-north-4.myhuaweicloud.com/virtheart/arcticnewone:latest`。 -说明:SWR 不支持 buildx 多架构推送的 OCI manifest 格式,脚本改为 `docker build` + `docker push` 分步执行,并禁用 provenance(`--provenance=false`),保证镜像可被 SWR 正常解析。 +说明:SWR 不支持 buildx 多架构一步推送产生的 OCI manifest 格式。脚本改为分架构推送(`latest-amd64` / `latest-arm64`),再用 `docker manifest create` + `docker manifest push` 合并为标准的 Docker manifest list(`latest`),并禁用 provenance(`--provenance=false`),保证 SWR 可正常解析。 ## 部署前置条件