commit 201f6c105416b770c916dabb0aa85cdfee12d4cb Author: sunjunzhao Date: Sun Sep 27 19:02:11 2026 +0800 feat: add runtime-image-select skill Base runtime image selection and Dockerfile generation/audit per the runtime-base image matrix, packaged as a skills.sh-compliant skill. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7ab6032 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 XGENT.ai + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..ec12490 --- /dev/null +++ b/README.md @@ -0,0 +1,39 @@ +# Images Skills + +[Agent Skills](https://skills.sh) 集合,可安装到 Claude Code、Cursor、Codex 等支持 skill 的 Agent 中。 + +## 安装 + +安装全部 skills: + +```bash +npx skills add gitea@livebook:images/skills.git +``` + +只安装指定 skill: + +```bash +npx skills add gitea@livebook:images/skills.git --skill runtime-image-select +``` + +## Skills 列表 + +| Skill | 说明 | +| --- | --- | +| [runtime-image-select](skills/runtime-image-select/SKILL.md) | 探测项目技术栈,按 runtime-base 镜像矩阵选型并生成/修正 Dockerfile | + +## 目录结构 + +每个 skill 位于 `skills//` 目录下,包含一个带 `name` 与 `description` frontmatter 的 `SKILL.md`: + +``` +skills/ +└── runtime-image-select/ + ├── SKILL.md # skill 主体(必需) + ├── references/ # 补充参考文档 + └── scripts/ # 辅助脚本 +``` + +## License + +[MIT](LICENSE) diff --git a/skills/runtime-image-select/SKILL.md b/skills/runtime-image-select/SKILL.md new file mode 100644 index 0000000..3159d54 --- /dev/null +++ b/skills/runtime-image-select/SKILL.md @@ -0,0 +1,193 @@ +--- +name: runtime-image-select +description: 在具体项目中探测技术栈并按 runtime-base 镜像矩阵选择正确的基础运行时镜像、生成/修正项目 Dockerfile;也能对已存在的 Dockerfile 做评估审计并修正。凡用户要求「写 Dockerfile / 容器化这个项目 / 选基础镜像 / FROM 哪个镜像 / 帮我打包成镜像 / 这个项目部署成容器 / 评估一下这个 Dockerfile / 帮我看看 Dockerfile 有没有问题 / 优化 / 评审 / 精简镜像」,或出现 容器起不来 / exec format error / 镜像太大 / pip 安装编译失败 / latest 镜像行为变了 等症状,或提到 runtime-node / runtime-pm2 / runtime-base / livebook:8418 / code.innev.cn / 镜像源 时,务必使用本 skill——即使用户没说「运行时镜像」四个字。Use whenever the user needs to pick a base runtime image (runtime- matrix), write a project Dockerfile, or audit/fix an existing Dockerfile. 边界:构建/推送业务镜像归项目 CI 或 xgent-image-push;修改 runtime 镜像本身(加镜像/改版本)归 runtime-base 仓库维护,不用本 skill。 +--- + +# runtime-image-select — 按项目语言与环境选运行时镜像 + +依据 runtime-base 仓库的镜像矩阵(10 个镜像:Linux 7 个 Alpine 系 + Wine 3 个),在项目里完成「探测栈 → 选型 → 写 Dockerfile → 真机验证」。两种工作模式: + +- **新建模式**:项目没有 Dockerfile,从零选型生成 +- **评估修正模式**:项目已有 Dockerfile(或用户要求"看看这个 Dockerfile"),先审计再修正 + +核心理念(推理链,所有约束都挂在这上面):**runtime 系列只负责把编译好的代码跑起来**——构建/编译由项目自己的 CI 完成,镜像内无编译器/SDK(Node 系唯一例外:容器启动时才 install+build,自带 node-gyp 链)。因此项目 Dockerfile 恒为单段:`FROM runtime-` → `COPY 产物` → `CMD`。 + +## 总流程 + +``` +前置 镜像源探测(脚本自动判内网/外网) +→ 第 0 步 探测技术栈(同时探测已有 Dockerfile → 有则转评估修正模式) +→ 第 1 步 选型对照 → 第 2 步 定 tag +→ 第 3 步 写/改 Dockerfile → 第 4 步 真机验证 → 第 5 步 汇报 +``` + +## 前置:镜像源探测(每次任务最先做,绝不可跳过) + +镜像源有两路,**由探测决定,不许凭经验猜**: + +| 源 | 前缀 | 适用 | 条件 | +|---|---|---|---| +| 内网 | `livebook:8418/images` | 内网机器,构建拉取最快 | ① daemon.json 已登记 `insecure-registries: ["livebook:8418"]`(明文 HTTP,不登记 docker 拒拉)② `http://livebook:8418/v2/` 可达(200/401 均算通) | +| 外网 | `code.innev.cn/images` | 外网/分发给同事使用 | 内网任一条件不满足时兜底 | + +执行 `scripts/detect-registry.sh`,stdout 最后一行即镜像前缀;之后所有 Dockerfile 的 `FROM` 统一写成 `<前缀>/runtime-:`。两路都不通(exit 1)→ die:停下报排查方向(内网机器确认同网段+登记+重启 docker;外网机器确认能访问 `https://code.innev.cn`),不硬写任何一个。 + +→ 验收:本次任务的镜像前缀有探测结论(内网/外网)背书,并记入第 5 步汇报。 + +## 第 0 步:探测技术栈(绝不可跳过) + +不许凭用户一句话定镜像——读项目文件坐实,再听用户补充部署需求。 + +| 探测到什么 | 判定 | +|---|---| +| `package.json`(无构建产物直出静态页) | Node 后端 → 再问:单进程还是多进程/需要 cluster? | +| `package.json` + 纯前端(react/vue/vite 静态产物) | **不走本系列**,CI 出静态文件由项目自己的 nginx 镜像托管 | +| `requirements.txt` / `pyproject.toml` | Python | +| `go.mod` / `Cargo.toml` | Go/Rust → 静态二进制 → `runtime-base` | +| `pom.xml` / `build.gradle` | Java → jar | +| `*.csproj` / `*.sln` | .NET → **必须再问一句**:现代 .NET(publish 产物)还是 Framework 4.x?两者走完全不同的镜像 | +| `CMakeLists` / `Makefile`(C/C++) | C/C++ → **必须确认 musl 动态链接**(见约束⑤) | +| MQL4 / MT4 终端需求 | `runtime-mt4` | +| 脚本/运维工具 | `runtime-base` | + +**同时探测已有 Dockerfile**:glob 项目内所有 `Dockerfile*`(含子目录、`.dockerignore`)。存在即转入「评估修正模式」(见第 2 步之后);不存在走新建模式顺次执行。多个 Dockerfile 逐一评估。 + +→ 验收:判定结论有文件证据(路径),拿不准的在选型前问清,不猜;是否已有 Dockerfile 有明确结论。 + +## 第 1 步:选型对照 + +| 项目类型 | FROM | 项目提供什么 | +|---|---|---| +| Node 后端(单进程) | `runtime-node` | 源码(**不含** node_modules);启动时 install+build | +| Node 后端(多进程) | `runtime-pm2` | 同上 + pm2 cluster(缺省 max 核数) | +| Python | `runtime-python` | 源码 + `venv/`(CI 装好依赖) | +| Go / Rust 静态二进制 | `runtime-base` | 编译好的二进制 | +| C/C++(musl 动态链接) | `runtime-cpp` | 编译好的二进制 | +| Java jar | `runtime-java` | `target/*.jar` | +| 现代 .NET publish 产物 | `runtime-dotnet` | `dotnet publish` 输出目录 | +| .NET Framework 4.x | `runtime-wine-dotnet` | `bin/Release/` 目录 | +| MT4 终端 | `runtime-mt4` | 不 COPY,运行时挂数据目录 | +| 脚本/工具 | `runtime-base` | 脚本文件 | + +镜像全名:`<前缀>/runtime-:`——前缀由前置探测给出(内网 `livebook:8418/images` / 外网 `code.innev.cn/images`),也可被 `INNEV_IMAGE_PREFIX` 显式覆盖(CI 场景)。矩阵详情(预装内容/环境变量/Wine 说明)读 `references/runtime-matrix.md`,按需读取。 + +die:探测结果不在矩阵内(如 Ruby/PHP)→ 停下报"无匹配运行时镜像",给两个去向(项目自建完整镜像 / 去 runtime-base 仓库加新镜像),**不硬塞一个近似的**。 + +→ 验收:选出的镜像在对照表中有明确一行,且该行的特殊前提已逐条确认(musl 还是 glibc / 现代 .NET 还是 4.x / 单进程还是多进程)。 + +## 第 2 步:定 tag + +三类 tag:`latest`(每周一随上游滚动重建)/ `短sha` / `版本标签`(如 v0.5.0)。 + +- 生产/交付项目 → 锁 `版本标签` 或 `短sha`(warn:用户坚持用 latest 时提醒滚动重建风险,不拦) +- 本地试跑 → latest 可接受 + +→ 验收:Dockerfile 里 FROM 的 tag 是显式选择的结果,不是顺手写的。 + +## 评估修正模式(第 0 步发现已有 Dockerfile 时走此分支) + +先做完第 1、2 步得出"应该长什么样"(正确镜像 + tag),再拿现状逐项对照审计。**只读审计,审计报告出来之前不改任何文件**。 + +### 审计清单(每条挂着硬约束编号,级别定义见下) + +| # | 检查项 | 违反 | 级别 | +|---|---|---|---| +| A1 | 多段构建 / 装了 SDK / 镜像内编译(`RUN mvn package`、`dotnet build`、`pip install` 源码编译) | ① | 必须修 | +| A2 | Node 系 COPY 了 node_modules / 缺 `.dockerignore` 排除 / 把 dist 产物当通用模式 COPY(Node 系模型是启动时 install+build) | ② | 必须修 | +| A3 | `ARG` 传令牌 / 密钥 COPY 进层 / `.npmrc` 含 token 落镜像层 | ③ | 必须修(安全项,优先处理) | +| A4 | `FROM ...:latest` 且是生产/交付项目 | ④ | 建议修 | +| A5 | glibc 环境(debian/ubuntu CI)构建的 C/C++ 产物 FROM alpine 系 | ⑤ | 必须修 | +| A6 | Go/Rust 二进制 FROM `golang`/`rust` 全镜像或其他带语言环境的镜像 | ⑥ | 建议修(换 `runtime-base`,体积常减一个数量级) | +| A7 | 前端静态产物用 node 镜像 `serve`/`http-server` 跑 | ⑦ | 建议修(换 nginx 托管) | +| A8 | 栈与 FROM 错配(Framework 4.x 用 `runtime-dotnet`、多进程需求用 `runtime-node` 单进程跑等) | 选型表 | 必须修 | +| A9 | 缺 `CMD`/`ENTRYPOINT`、`EXPOSE`、入口路径与 CI 产物路径对不上 | — | 提示 | +| A10 | FROM 前缀与前置探测结论不符(外网机器 FROM `livebook:8418` 拉不动;内网机器 FROM `code.innev.cn` 白白绕远) | ⑧ | 必须修 | + +级别定义:**必须修** = 会挂(起不来/行为错)或泄密;**建议修** = 能跑但体积/稳定性/可维护性受损;**提示** = 瑕疵。只发现"建议修/提示"且用户只要评估时,不改文件,出报告即止。 + +### 审计报告格式(固定) + +- 结论一行:当前 FROM 什么 → 应该 FROM 什么(一致则明说"选型正确") +- 问题表:级别 | 行号 | 问题 | 依据(约束编号) | 修法 +- 没问题的项也要说一句"已过"(A1-A9 逐条过,不许只报坏不报好——漏报和误报一样有害) + +报告摊开、用户确认要修 → 转第 3 步按模板改写(给 diff),再进第 4 步验证。用户说"只评估不动手"→ 报告即交付物。 + +## 第 3 步:写/改 Dockerfile + +默认动作:**先把选型结论 + 拟写的 Dockerfile 全文摊给用户确认,再落盘**(改已有 Dockerfile 时给 diff)。 + +模板(通用模式,python/java/dotnet/cpp/base/wine 同构): + +```dockerfile +FROM livebook:8418/images/runtime-: +COPY <目标路径> +CMD [...] +``` + +Node 系模式(注意与通用模式不同——COPY 的是源码不是产物): + +```dockerfile +FROM livebook:8418/images/runtime-node: +COPY . . # .dockerignore 必须排除 node_modules +``` + +Node 系运行参数在 `docker run` 时经环境变量注入(`APP_ENTRY`/`BUILD_CMD`/`NPM_REGISTRY`/`PM2_APP_NAME`/`PM2_INSTANCES` 等,全表见 references),**不落镜像层**。已有 Dockerfile 若用了多段构建/装了 SDK/ARG 传令牌,按红线逐条指出并改写为单段。 + +→ 验收:Dockerfile 单段、无编译步骤、无密钥;Node 系配套 `.dockerignore` 排除 node_modules。 + +## 第 4 步:真机验证 + +完成定义 = 真机构建并起容器冒烟,不是"Dockerfile 语法没问题": + +1. `docker build -t :test .` 成功 +2. `docker run` 起容器,进程活着(Node 系首启会 install+build,启动慢是正常的,看日志等到 listening) +3. 有端口的验证端口可达 / 健康检查通过 + +环境不允许构建验证(宿主机没登记 `livebook:8418` 为 insecure-registry、无 docker)→ 显式声明"未验证",并把"待人工补"列进汇报。 + +## 第 5 步:汇报 + +固定结构: +- 模式:新建 / 评估修正 +- 镜像源:内网 / 外网(探测结论,脚本输出佐证) +- 探测结论:语言/栈 + 证据文件路径 +- 选型:`runtime-:` + 一句话理由 +- 【评估修正模式】审计结果:必须修 N 条 / 建议修 N 条 / 提示 N 条(附问题表),用户是否授权修正 +- 写了什么:Dockerfile(新建/修改 diff)、`.dockerignore` +- 被跳过/未做的:如多进程需求未确认、CI 构建步骤缺失、用户只让评估没让修 +- 验证做到哪一步:build/run 结果,**没做就说没做** +- 待人工补:daemon.json 登记、CI 构建产物步骤、secret 配置 + +## 硬约束 + +- ① **编译不进 runtime 镜像**。为什么:镜像内无编译器/SDK(dotnet 无 SDK、java 无 JDK/Maven、python 无 musl 编译链),装了即失去"最小运行基座"意义;症状是构建期 `mvn/dotnet SDK not found`、pip 源码编译失败。唯一例外:Node 系启动时 install+build(自带 node-gyp 链,这是设计如此,不是漏洞)。 +- ② **Node 系不 COPY node_modules**。为什么:entrypoint 启动时 `pnpm install`(有 lockfile 走 `--frozen-lockfile`),COPY 进去会覆盖冲突且镜像膨胀;原生模块在容器内编译才匹配运行环境。 +- ③ **令牌不用 ARG 传、不落镜像层**。为什么:ARG 值留在 `docker history`;`NPM_TOKEN` 走 env 会出现在 `docker inspect` 输出。正确姿势:`NPM_TOKEN_FILE` 指 secret 文件(docker secret / k8s 挂载卷),令牌只写进容器内 `~/.npmrc`。 +- ④ **生产不用 latest**。为什么:`latest` 每周一 02:30 随上游滚动重建,同一 tag 下面内容会变——上次能跑不代表下次能跑。锁版本标签或短 sha。 +- ⑤ **C/C++ 仅 musl 动态链接适用 `runtime-cpp`**。为什么:Alpine 是 musl 系统,glibc 产物跑不起来,症状 `exec format error` 或 `not found`(二进制存在但加载器缺失)。glibc 产物请换基座或静态编译。 +- ⑥ **Go/Rust 没有独立镜像**。静态原生二进制运行时不需要语言环境,`FROM runtime-base` 即可——不要去找 `runtime-go`。 +- ⑦ **前端不走本系列**。React/Vue 等由 CI 构建静态文件,项目自己的 nginx 镜像托管。 +- ⑧ **镜像源由前置探测决定,不猜不写死**。为什么:内网 `livebook:8418` 是 LAN 明文 HTTP,外网根本够不着;外网 `code.innev.cn` 在内网绕远。FROM 写死任何一路,换个网络环境就拉不动。每次任务先跑 `scripts/detect-registry.sh`;CI 等需要固定的场景用 `INNEV_IMAGE_PREFIX` 显式指定,并在汇报里说明。 + +## 排障对照表 + +| 症状 | 原因 | 怎么办 | +|---|---|---| +| `exec format error` | glibc 二进制进 musl 镜像 / 架构不符 | 构建阶段改 musl 或静态编译;确认 buildx 架构 | +| 拉镜像 `http: server gave HTTP response to HTTPS client` | 宿主未登记 `insecure-registries: ["livebook:8418"]` | 改 `/etc/docker/daemon.json` 并重启 docker(人工);或按前置探测改走外网前缀 | +| 外网机器 FROM `livebook:8418` 超时/拒连 | 内网前缀只在内网可达 | FROM 换 `code.innev.cn/images/...`(重跑前置探测确认) | +| 外网拉取 401 unauthorized | code.innev.cn 仓库要求认证 | `docker login code.innev.cn`(人工,用 Gitea 账号) | +| pip 安装编译失败 | runtime-python 无编译链 | 构建阶段(CI)编译好 wheel/venv 再 COPY | +| .NET 程序在 runtime-dotnet 起不来 | Framework 4.x 程序走了现代 .NET 镜像 | 换 `runtime-wine-dotnet`,`xvfb-run -a wine` 启动 | +| 令牌出现在 `docker inspect` | NPM_TOKEN 走了 env | 改 NPM_TOKEN_FILE secret 挂载 | +| 上周能跑这周不能 | 用了 latest,周一滚动重建 | 锁版本标签/短 sha | +| Node 容器启动很慢 | 正常:启动时 install+build | 看日志等到 listening;别当挂起杀掉 | + +## 红线(任何情况下不违反) + +- 不凭猜测选型——第 0 步探测不到证据就问,不硬塞近似镜像 +- 不给项目 Dockerfile 加编译段/SDK(Node 系启动时构建除外) +- 不在 Dockerfile/镜像层里写任何令牌密钥;用户贴过来的令牌不落进任何文件,并提醒轮换 +- 不静默截断——跳过的需求(多进程/私有源/健康检查)必须出现在汇报里 +- 改已有 Dockerfile 前先给结论和 diff,确认后再写 diff --git a/skills/runtime-image-select/references/runtime-matrix.md b/skills/runtime-image-select/references/runtime-matrix.md new file mode 100644 index 0000000..7e33a4a --- /dev/null +++ b/skills/runtime-image-select/references/runtime-matrix.md @@ -0,0 +1,61 @@ +# runtime-base 镜像矩阵详情(摘自 runtime-base 仓库 README,2026-09 版) + +> 按需读取:SKILL.md 第 1 步拿不准预装内容/环境变量时再查本文件。 +> 镜像全名:`$INNEV_IMAGE_PREFIX/runtime-:`,默认前缀 `livebook:8418/images/`。 + +## Linux 侧(全部 Alpine,linux/amd64 + linux/arm64 双架构) + +| 镜像 | 基座 | 预装内容 | +|---|---|---| +| `runtime-base` | alpine:3.22 | bash/git/curl/jq/make/CA 证书/东八区时区;Go/Rust 静态二进制、脚本的运行地基 | +| `runtime-node` | node:24-alpine | Node 后端:启动时 pnpm install + build,`node $APP_ENTRY` 启动 | +| `runtime-pm2` | node:24-alpine | 同上 + pm2:`pm2-runtime` cluster 启动 | +| `runtime-python` | python:3.13-alpine | pip/setuptools/wheel + uv/pipx/virtualenv(无源码编译链——无 musl wheel 的包在构建阶段编译) | +| `runtime-dotnet` | dotnet/aspnet:10.0-alpine | ASP.NET Core 运行时,无 SDK | +| `runtime-cpp` | alpine:3.22 | libstdc++/libgcc 运行库(仅 musl 动态链接产物) | +| `runtime-java` | temurin:21-jre-alpine | JRE 21 LTS,无 JDK/Maven/Gradle | + +Node 系(node/pm2)额外预装:pnpm/yarn(ARG `PNPM_VERSION`/`YARN_VERSION` 可控版本)、python3/make/g++(node-gyp 编译链)——因为它们在容器启动时才 `pnpm install`,原生模块安装期编译直接可用。 + +## Windows 侧(Linux 容器 + Wine,仅 x86_64) + +| 镜像 | 基座 | 预装内容 | +|---|---|---| +| `runtime-wine-base` | debian:bookworm-slim | Wine(win32+win64) + winetricks + Xvfb + 中文字体 + 预初始化 wine 前缀 `/opt/wineprefix` | +| `runtime-wine-dotnet` | runtime-wine-base | 预装 .NET Framework 4.8(ARG `DOTNET_FX` 可切 verb) | +| `runtime-mt4` | runtime-wine-base | 预装 MetaTrader 4 + corefonts,Xvfb headless 启动 | + +为什么是 Wine 不是 Windows 容器:Windows 容器需要 Windows Server 宿主机,维护成本高;.NET Framework 4.x 与 MT4 在 Wine 下成熟稳定。基座选 Debian 不选 Alpine:musl 版 Wine 对 32 位(wow64)支持不完整。 + +## Node 系环境变量(docker run 时注入,不落镜像层) + +| 变量 | 作用 | 默认值 | +|---|---|---| +| `NPM_REGISTRY` | npm 仓库地址(含私有源) | 官方源 | +| `NPM_TOKEN` | 私有仓库令牌(走 env 会进 `docker inspect`,不推荐) | 无 | +| `NPM_TOKEN_FILE` | 令牌文件路径(docker secret / k8s 挂载卷,推荐,优先于 NPM_TOKEN) | 无 | +| `APP_DIR` | 应用目录 | `/app` | +| `BUILD_CMD` | 构建脚本名(package.json 无此脚本自动跳过) | `build` | +| `APP_ENTRY` | 构建产物入口文件 | `dist/main.js` | +| `PM2_APP_NAME` | 【pm2】应用名 | `app` | +| `PM2_INSTANCES` | 【pm2】cluster 实例数 | `max`(核数) | + +## 常用 CMD / COPY 范式 + +- Go:`COPY app /usr/local/bin/app` + `CMD ["app"]` +- Java:`COPY target/app.jar /app/app.jar` + `CMD ["java","-jar","/app/app.jar"]` +- .NET:`COPY out /app` + `CMD ["dotnet","/app/MyService.dll"]` +- wine-dotnet:`COPY bin/Release/ "$WINEPREFIX/drive_c/app/"` + `CMD ["xvfb-run","-a","wine","C:\\app\\MyService.exe"]` +- MT4:不 COPY;`docker run -v /srv/mt4/data:/opt/wineprefix/drive_c/"Program Files (x86)"/"MetaTrader 4"`,EA/指标放数据目录 `MQL4/Experts`、`MQL4/Indicators` 后重启容器 + +## 前置:拉取侧宿主机登记(人工) + +`/etc/docker/daemon.json` 加 `{ "insecure-registries": ["livebook:8418"] }` 并重启 docker(内网 registry 走 LAN 明文 HTTP)。 + +## Tag 策略 + +`latest`(每周一 02:30 北京时间随上游滚动重建)/ `短sha` / `版本标签`(v*)。生产锁版本标签或短 sha。 + +## 包管理国内镜像源 + +各 runtime Dockerfile 注释里有开启方式,也可在项目构建阶段 Dockerfile 自行配置(README 给了 crates.io rsproxy 与 Maven aliyun 的现成片段)。 diff --git a/skills/runtime-image-select/scripts/detect-registry.sh b/skills/runtime-image-select/scripts/detect-registry.sh new file mode 100755 index 0000000..864c00f --- /dev/null +++ b/skills/runtime-image-select/scripts/detect-registry.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +# detect-registry.sh — 探测可用镜像源,输出镜像前缀 +# 内网 livebook:8418 优先(LAN 明文 HTTP,快);外网 code.innev.cn 兜底。 +# 用法:scripts/detect-registry.sh +# 输出:stdout 最后一行为镜像前缀(如 livebook:8418/images);exit 1 = 两路都不通 +set -u + +IN_HOST="livebook:8418" +IN_PREFIX="livebook:8418/images" +OUT_HOST="code.innev.cn" +OUT_PREFIX="code.innev.cn/images" + +say() { printf '%s\n' "$*" >&2; } +ok() { say "✔ $*"; } +no() { say "✘ $*"; } +step() { say "▸ $*"; } + +# --- 检查 1:insecure-registries 是否登记 livebook:8418(内网是明文 HTTP,不登记 docker 拒拉)--- +insecure_ok=0 +if command -v docker >/dev/null 2>&1; then + if docker info 2>/dev/null | grep -qi "$IN_HOST"; then + insecure_ok=1 + fi +fi +if [ "$insecure_ok" -eq 0 ]; then + for f in /etc/docker/daemon.json "$HOME/.docker/daemon.json"; do + [ -f "$f" ] && grep -q "$IN_HOST" "$f" && insecure_ok=1 && break + done +fi +[ "$insecure_ok" -eq 1 ] && ok "insecure-registries 已登记 $IN_HOST" || no "insecure-registries 未登记 $IN_HOST" + +# --- 检查 2:内网 registry 连通性(/v2/ 返回 200 或 401 都算通)--- +in_code=$(curl -s -o /dev/null -w '%{http_code}' --connect-timeout 2 --max-time 4 "http://$IN_HOST/v2/" 2>/dev/null || echo 000) +if [ "$in_code" = 200 ] || [ "$in_code" = 401 ]; then + ok "内网 $IN_HOST 可达(HTTP ${in_code})" +else + no "内网 $IN_HOST 不可达(HTTP ${in_code})" +fi + +if [ "$insecure_ok" -eq 1 ] && { [ "$in_code" = 200 ] || [ "$in_code" = 401 ]; }; then + step "结论:走内网(构建拉取最快)" + printf '%s\n' "$IN_PREFIX" + exit 0 +fi + +# --- 兜底:外网 --- +step "内网不可用,尝试外网 $OUT_HOST" +out_code=$(curl -s -o /dev/null -w '%{http_code}' --connect-timeout 3 --max-time 6 "https://$OUT_HOST/v2/" 2>/dev/null || echo 000) +if [ "$out_code" = 200 ] || [ "$out_code" = 401 ]; then + ok "外网 $OUT_HOST 可达(HTTP ${out_code})" + say "⚠ 走外网:拉取速度不如内网;若外网仓库要求认证,先 docker login $OUT_HOST(人工)" + printf '%s\n' "$OUT_PREFIX" + exit 0 +fi + +no "外网 $OUT_HOST 也不可达(HTTP ${out_code})" +say "两路都不通。排查:内网机器确认与 $IN_HOST 同网段且 daemon.json 已登记并重启 docker;外网机器确认能访问 https://$OUT_HOST" +exit 1