量产线安装 (factory.sh)
用于产线上批量处理 100 ~ 1000 台设备。操作员只需在一个界面填完 prompt, EDGE_ID / hostname / 凭据 / Docker / cosign verify / 容器启动即全部自动完成。 每台 30 秒。
如果是单台设备 / 现场安装(SI 人员),请先阅读 快速安装(一行命令)。 量产线与现场安装的差异仅在于入口,结果完全相同。
0. 一览
sudo -i
mkdir -p /home/kopens
curl -fsSL https://product.kopens.io/plantpulse-edge/plantpulse-edge-docker.tar.gz \
| tar xz -C /home/kopens
cd /home/kopens/plantpulse-edge-docker/bin
sudo bash factory.sh
factory.sh 会自动完成:
- LAN1 NIC MAC 自动识别 — 取 MAC 后 6 位生成
EDGE_<HEX6>(例如:EDGE_3F8A21)。 - 主机名同步 —
hostname也一并改为edge-<hex6>(RFC 1123,小写+连字符)。 - 5 个字段的 prompt — SITE_ID / SERVER_HOST / SERVER_API_KEY / LAN1 NIC / LAN2 NIC。
- 委派给
install.sh— Docker / cosign / random secret / app.properties / 容器启动。 - 输出 factory-label.txt — 用于贴在设备上。打印后封箱。
- 追加 factory-inventory.csv — 产线末端回收 → 上传至公司 inventory DB。
1. ID / hostname 自动生成
EDGE_ID — EDGE_<HEX6>
LAN1 NIC eth1 MAC = 00:1a:2b:3f:8a:21
─────
마지막 6 자 (HEX6) = 3F8A21
EDGE_ID = EDGE_3F8A21
hostname = edge-3f8a21
Why MAC?
- hostname 可能随每次 OS install 变化(cloud-init /
hostnamectl),且可能冲突 - MAC 固化在硬件中,可保证每台设备唯一
- 24-bit 后 6 位 → 约 1670 万种组合。1000 台量产的冲突概率约 0.003%(按 Birthday paradox 评估是安全的)
强制遵守域规则(产品仓库的 CLAUDE.md):
| 标识符 | 正则表达式 | 示例 |
|---|---|---|
EDGE_ID | ^EDGE_[A-Z0-9_]{1,60}$ | EDGE_3F8A21 |
SITE_ID | ^SITE_[A-Z0-9_]{1,60}$ | SITE_FAB1_042 |
| hostname (RFC 1123) | 小写 + 连字符 | edge-3f8a21 |
factory.sh 会先校验上述 regex 再继续 — 未通过则立即 abort。
MAC 冲突 / 无 NIC 的设备
- 冲突可能性 — 若
/data1/factory-inventory.csv中已存在相同 EDGE_ID 则立即 abort (防止静默重复)。可自动检出同一台设备被 provisioning 两次或 MAC 冲突的情况。 - 无 MAC 的设备(虚拟 NIC 等) —
derive_edge_id_from_mac失败 → install.sh 回退为基于 hostname 的方式。产线上几乎不会发生。
2. Prompt 的 5 个字段
操作员在产线上输入:
══════════════════════════════════════════════════════════════════════
PlantPulse Edge — Factory Provisioning
══════════════════════════════════════════════════════════════════════
Detected NIC eth1 MAC : 00:1a:2b:3f:8a:21
Auto EDGE_ID : EDGE_3F8A21
Auto hostname : edge-3f8a21
══════════════════════════════════════════════════════════════════════
Site ID (^SITE_[A-Z0-9_]+) [SITE_00001]: SITE_FAB1_042
Platform server host [192.168.0.41]: 10.10.0.5
Platform API key []: kFa7bGz8pQrN3xMv9wT2sLcEjY1uV6oH
LAN1 NIC (PLC) [eth1]: ← Enter = default
LAN2 NIC (WAN) [eth2]: ← Enter = default
══════════════════════════════════════════════════════════════════════
Summary
EDGE_ID : EDGE_3F8A21
hostname : edge-3f8a21
SITE_ID : SITE_FAB1_042
SERVER_HOST : 10.10.0.5
SERVER_API_KEY : kFa7…(32 chars)
LAN1 NIC (PLC) : eth1
LAN2 NIC (WAN) : eth2
══════════════════════════════════════════════════════════════════════
Proceed with install.sh? [y/N]
按下 y 后,install.sh 运行 5 ~ 10 分钟(含首次镜像 pull 3 GB),随后输出标签。
3. 自动生成的凭据 (G2)
install.sh 为每台设备生成不同的 random 密码(详见下载包中的 plantpulse-edge-docker/README.md 的 Secrets 一节):
| 变量 | 长度 | 位置 |
|---|---|---|
ADMIN_PASS (UI 登录) | random 14位+! | /etc/kopens/credentials.txt |
API_KEY (REST) | random 16位 | 上述文件 + env.sh.generated |
MQTT_PASS (HiveMQ) | random 14位+! | 上述文件 |
OPCUA_PASS | random 14位+! | 上述文件 |
CERT_PASS (SSL keystore) | random 14位+! | 上述文件 |
/etc/kopens/env.sh.generated(chmod 0600 root)是所有后续脚本(upgrade.sh / restart.sh)
都会 source 的 sidecar 文件 — 重启后仍保持相同凭据。
4. 镜像签名 verification (G3)
install.sh 会自动安装 cosign + 部署 cosign.pub,并在 docker pull 之后立即自动 verify。
[install] Installing cosign ...
[install] OK — cosign installed (cosign version 2.4.1)
[install] cosign.pub installed — image verification enabled
[install] docker pull — docker.kopens.io/pe/plantpulse-edge:2026-20260523
[install] OK — Pull complete
[cosign] OK — signature verified for docker.kopens.io/pe/plantpulse-edge:2026-20260523
如果 cosign.pub 仍是 placeholder(运维团队尚未生成 keypair),则仅输出 WARN,安装继续进行。
参见下载包中的 plantpulse-edge-docker/README.md 的 Cosign 一节。
5. 标签 + 库存清单
/etc/kopens/factory-label.txt — 用于贴在设备上
安装结束后会打印到屏幕并保存为 chmod 0600 文件。产线人员用标签打印机打印后封箱:
══════════════════════════════════════════════════════════════════════
PlantPulse Edge — Factory Label
══════════════════════════════════════════════════════════════════════
EDGE_ID : EDGE_3F8A21
hostname : edge-3f8a21
SITE_ID : SITE_FAB1_042
build_date : 2026-05-19 14:25:33
──────────────────────────────────────────────────────────────────────
LAN1 (PLC) : eth1 MAC=00:1a:2b:3f:8a:21
LAN2 (WAN) : eth2 MAC=00:1a:2b:3f:8a:22
──────────────────────────────────────────────────────────────────────
UI admin : admin / aBcD1234EfGh!
REST API : kFa7bGz8pQrN3xMv9wT2sLcEjY1uV6oH
(full credentials → /etc/kopens/credentials.txt — root only)
══════════════════════════════════════════════════════════════════════
/data1/factory-inventory.csv — 用于产线回收
每台设备追加一行。产线末端由操作员回收 → 上传至公司 inventory DB:
edge_id,hostname,site_id,server_host,lan1_iface,lan1_mac,lan2_iface,lan2_mac,build_date,iso8601
EDGE_3F8A21,edge-3f8a21,SITE_FAB1_042,10.10.0.5,eth1,00:1a:2b:3f:8a:21,eth2,00:1a:2b:3f:8a:22,2026-05-19 14:25:33,2026-05-19T05:25:33Z
EDGE_5B7C03,edge-5b7c03,SITE_FAB1_043,10.10.0.5,eth1,00:1a:2b:5b:7c:03,eth2,00:1a:2b:5b:7c:04,2026-05-19 14:28:12,2026-05-19T05:28:12Z
6. 产线末端批量回收
# 라인 마지막 박스 처리 후 회수 워크스테이션에서
scp root@edge-3f8a21:/data1/factory-inventory.csv ./inventory-line1-$(date +%F).csv
# 회수한 CSV 들을 사내 inventory DB 로 업로드 (예: scripts/import-inventory.py)
python3 import-inventory.py inventory-line1-2026-05-19.csv
也可以添加 hook,在设备上电时自动 push 到 platform 侧(Phase 1 — 引入公司内部 provisioning REST API 时)。
7. 常见陷阱
| 现象 | 原因 | 处理 |
|---|---|---|
EDGE_ID '...' fails ^EDGE_[A-Z0-9_]{1,60}$ | LAN1 NIC 的 MAC 非 ASCII 的虚拟 NIC | 如 LAN1_IFACE=enp1s0 bash factory.sh 一样显式指定 |
already_in_inventory: EDGE_3F8A21 already provisioned | 同一台设备重复执行或 MAC 冲突 | 用 vi /data1/factory-inventory.csv 删除对应行后重新执行。若为 MAC 冲突则停线并上报 |
cosign verify FAILED (COSIGN_REQUIRE=1) | image 未签名 或 cosign.pub 错误 | 确认构建服务器是否漏调用 sign-image.sh,并更新 cosign.pub |
hostnamectl set-hostname failed | 在 container 内执行 / 无权限 | factory.sh 强制 root — sudo bash factory.sh |
| 看不到 prompt(标签未打印) | install.sh 中途失败 | 确认 journalctl -u plantpulse-edge.service -n 50 后参见故障排查 |
8. 相关文档
plantpulse-edge-docker/README.md— 运行场景(量产 / SI / CI)一节(随发布包一起提供)plantpulse-edge-docker/README.md— Cosign 一节(keypair 生成 · rollout · rotation)- 快速安装(单台设备,现场 SI 用)
- 安装后检查清单
- 隔离网络 / Airgap 安装