远程 Mac 自托管 Runner 上 docker pull 常同时打 ghcr.io 与企业 Harbor/ACR:层并发 × 多 Job 放大 RTT;宽权限或过期令牌引发 401/403;429/5xx 线性重试易与限流对撞。下文用可执行参数表固定并发层数、docker login TTL、HTTP 退避、磁盘水位,并附端点对照、最小权限、CI 占位命令。延伸阅读 Git 与 Docker 拉取加速指南;博客列表、首页 免登录。
场景与风险清单
同一宿主机上多 Job 共享出境带宽、TLS 与磁盘;层并发默认在弱网并非越快越好,易触发重置与 429。
风险清单(排障优先级从高到低)
- 凭据漂移:PAT 撤销、组织 SSO 未授权、机器人密码轮转未同步到 CI 密钥。
- 半拉取层:磁盘满或进程被杀后残留不完整层,后续 pull 表现为「卡住」或反复校验失败。
- 代理与 NO_PROXY 不一致:内网 Registry 误走代理,或外网 GHCR 被企业代理截断认证头。
- 端点选错:镜像名指向公网 ghcr 却在境内未做近源或反向缓存,导致大层长时间占用 CI 预算。
先固定表中起点,再按 pull 耗时与失败码单次只调一维。
| 参数 | 保守起点(共享出口 / 弱网) | 激进上限(独占带宽 / 低延迟) | 调参判据 |
|---|---|---|---|
并发拉取层数(Docker:max-concurrent-downloads;BuildKit 场景同步限制并行层) |
2~3 | 5~8 | 429/TLS 异常则降;RTT 低再升。 |
docker login 令牌 TTL(机器人 / fine-grained / 流水线 OIDC) |
7~30 天(人类可读轮换)/ 单次 Job 15~120 分钟(短时) | 90 天(仅配合自动轮换审计) | 长 TTL 泄露面大;短 TTL 配密钥管理或 OIDC。 |
| HTTP 429 退避阶梯(秒,含全抖动 ±20%) | 2 → 6 → 15 → 40(最多 4 次) | 同左(429 不建议更激进) | 优先 Retry-After;否则阶梯并错峰 Job。 |
| HTTP 5xx / 连接重置退避阶梯(秒) | 1 → 3 → 8 → 20(最多 4 次) | 1 → 2 → 4 → 8(低延迟内网 Registry) | 同源连续 5xx:换只读副本或维护窗外重试。 |
| 磁盘水位阈值(拉取前自检) | 可用空间 < 18 GiB 预警;< 12 GiB 禁止新 pull | 大镜像仓库可调高预警线 | 先 prune / 清 BuildKit 缓存再构建。 |
认证与最小权限
结论:优先 OIDC→GHCR;私服用机器人只读 + 项目令牌,忌管理员个人 PAT 跑 CI。
| 凭据类型 | 适用 | 最小权限建议 | 轮换节奏 |
|---|---|---|---|
| GitHub PAT(classic) | 遗留脚本、无 OIDC 的自托管 | 仅 read:packages + 绑定 IP 允许清单(若平台支持) |
≤30 天人工审计;泄露即全量吊销 |
| Fine-grained PAT / GitHub App 安装令牌 | 组织仓库、多仓只读拉取 | 仓库级 Contents read + Packages read | 7~30 天或按需;配合密钥管理自动注入 |
| Harbor Robot / ACR Token | 企业私服 | 仅 pull,禁用 push/delete;按项目命名空间拆分 | 与参数表 TTL 对齐;CI 只注入当前 Job |
共享机忌长期全局 docker login;用 DOCKER_CONFIG 沙箱目录,Job 结束即删。
跨境网络与镜像端点选型对照表
直连 ghcr 与企业缓存/内网副本在合规、延迟、失效模式上互斥取舍,须对齐出口策略。
| 端点策略 | 何时优先 | 主要代价 | 与参数表关系 |
|---|---|---|---|
直连 ghcr.io |
海外 Runner、已购优质出境、镜像体积小 | 境内弱网抖动、峰值 429 | 并发层数取保守;429 退避严格执行 |
| 企业反向代理 / Pull-through Cache | 多团队重复拉同一基础镜像;需审计与 DLP | 缓存一致性、首次冷拉仍慢 | 命中后可上调层并发;仍需磁盘水位阈值 |
| 镜像近源域名(同仓多域名) | 合规要求数据不经某区域 | DNS 与证书管理复杂度高 | 证书轮换纳入 FAQ;代理规则单独测 |
| 只在内网可用的私服副本 | 强隔离生产构建 | 同步滞后导致 digest「看似最新」 | 构建前用 manifest inspect 校验 digest |
CI 集成步骤(含示例命令占位)
口令、域名、digest 为占位;入库前删调试输出。
推荐顺序
- ① 导出
DOCKER_CONFIG到 Job 沙箱目录,避免污染共享用户。 - ② 磁盘自检:可用 GiB 低于阈值则 prune 后退出或降级为缓存命中构建。
- ③
docker login使用短时令牌;禁止在日志中打印。 - ④ 配置并发层数与外层退避脚本包裹
docker pull/docker buildx build。 - ⑤ 记录耗时与 HTTP 状态到构建摘要,便于回归对比。
# 0) 沙箱配置目录
export DOCKER_CONFIG="${CI_PROJECT_DIR:-$PWD}/.docker-job-$$"
mkdir -p "$DOCKER_CONFIG"
# 1) 磁盘水位(占位:按平台解析可用空间,与参数表 12/18 GiB 阈值比较)
# test "$(...)" -lt 12 && exit 1
# 2) 登录 GHCR(占位:从密钥管理注入)
# echo "$GITHUB_CR_PAT" | docker login ghcr.io -u YOUR_GH_USER --password-stdin
# 3) 守护进程并发(需与运维约定生效方式;此处为示意)
# jq 或 daemon.json 管理 max-concurrent-downloads=3
# 4) 拉取(占位镜像坐标)
# docker pull ghcr.io/OWNER/IMAGE@sha256:YOUR_DIGEST
# 5) 外层退避伪代码(429/5xx)
# pull_with_backoff() { for s in 2 6 15 40; do docker pull ... && return; sleep $((s+RANDOM%20)); done; exit 1; }
页内已含 HowTo 与 FAQPage JSON-LD,与步骤、FAQ 对齐。
FAQ(拉取卡住 / 证书 / 代理)
层下载「卡住」很久不动?
先降并发层数并停止同机其它大 pull;检查是否磁盘触顶;查看 daemon 日志里是否有反复 206/416 或 TLS 重协商。必要时删除对应层的部分数据后重试。
企业私服证书链不被信任?
导入企业根/中间 CA到系统信任库或 Docker 指定信任目录(依版本与安装方式而定);避免在生产全局开启 insecure-registry,除非网络已物理隔离并有审计。
强制走代理时 GHCR 间歇 403?
核对代理是否剥离 Authorization;为内网 Registry 配置 NO_PROXY;代理并发连接上限低时,与参数表的并发层数一起下调。