Files

15 KiB
Raw Permalink Blame History

name, description
name description
runtime-image 在具体项目中探测技术栈并按 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-<name> matrix), write a project Dockerfile, or audit/fix an existing Dockerfile. 边界:构建/推送业务镜像归项目 CI 或 xgent-image-push;修改 runtime 镜像本身(加镜像/改版本)归 runtime-base 仓库维护,不用本 skill。

runtime-image — 按项目语言与环境选运行时镜像

依据 runtime-base 仓库的镜像矩阵(10 个镜像:Linux 7 个 Alpine 系 + Wine 3 个),在项目里完成「探测栈 → 选型 → 写 Dockerfile → 真机验证」。两种工作模式:

  • 新建模式:项目没有 Dockerfile,从零选型生成
  • 评估修正模式:项目已有 Dockerfile(或用户要求"看看这个 Dockerfile"),先审计再修正

核心理念(推理链,所有约束都挂在这上面):runtime 系列只负责把编译好的代码跑起来——构建/编译由项目自己的 CI 完成,镜像内无编译器/SDK(Node 系唯一例外:容器启动时才 install+build,自带 node-gyp 链)。因此项目 Dockerfile 恒为单段:FROM runtime-<name> → 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-<name>:<tag>。两路都不通(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-<name>:<tag>——前缀由前置探测给出(内网 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 同构):

FROM livebook:8418/images/runtime-<name>:<tag>
COPY <CI 构建好的产物> <目标路径>
CMD [...]

Node 系模式(注意与通用模式不同——COPY 的是源码不是产物):

FROM livebook:8418/images/runtime-node:<tag>
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 <app>:test . 成功
  2. docker run 起容器,进程活着(Node 系首启会 install+build,启动慢是正常的,看日志等到 listening)
  3. 有端口的验证端口可达 / 健康检查通过

环境不允许构建验证(宿主机没登记 livebook:8418 为 insecure-registry、无 docker)→ 显式声明"未验证",并把"待人工补"列进汇报。

第 5 步:汇报

固定结构:

  • 模式:新建 / 评估修正
  • 镜像源:内网 / 外网(探测结论,脚本输出佐证)
  • 探测结论:语言/栈 + 证据文件路径
  • 选型:runtime-<name>:<tag> + 一句话理由
  • 【评估修正模式】审计结果:必须修 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,确认后再写