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.
This commit is contained in:
commit
201f6c1054
5 files changed
+372
No files matched your search
@@ -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.
|
||||
@@ -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>/` 目录下,包含一个带 `name` 与 `description` frontmatter 的 `SKILL.md`:
|
||||
|
||||
```
|
||||
skills/
|
||||
└── runtime-image-select/
|
||||
├── SKILL.md # skill 主体(必需)
|
||||
├── references/ # 补充参考文档
|
||||
└── scripts/ # 辅助脚本
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
@@ -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-<name> 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-<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 同构):
|
||||
|
||||
```dockerfile
|
||||
FROM livebook:8418/images/runtime-<name>:<tag>
|
||||
COPY <CI 构建好的产物> <目标路径>
|
||||
CMD [...]
|
||||
```
|
||||
|
||||
Node 系模式(注意与通用模式不同——COPY 的是源码不是产物):
|
||||
|
||||
```dockerfile
|
||||
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,确认后再写
|
||||
@@ -0,0 +1,61 @@
|
||||
# runtime-base 镜像矩阵详情(摘自 runtime-base 仓库 README,2026-09 版)
|
||||
|
||||
> 按需读取:SKILL.md 第 1 步拿不准预装内容/环境变量时再查本文件。
|
||||
> 镜像全名:`$INNEV_IMAGE_PREFIX/runtime-<name>:<tag>`,默认前缀 `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 的现成片段)。
|
||||
+58
@@ -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
|
||||
Reference in new issue
Block a user