# es-migrate **Repository Path**: kml/es-migrate ## Basic Information - **Project Name**: es-migrate - **Description**: Cross-version Elasticsearch index migration tool (GUI + CLI + single-file executable) - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-26 - **Last Updated**: 2026-08-26 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README
```
每次启动都会生成任务 ID。停止、崩溃或失败后,用完全相同的迁移参数并追加:
```bash
python es_migrate.py ... --job-id <任务ID>
```
已完成索引不会重跑,运行中的索引会安全回到 pending 并按 `_id` 覆盖重试。
默认运行数据位于 `~/.es_migrate/`:
- `state.db`:任务、索引状态、checkpoint 和审计事件
- `reports/<任务ID>.json`:完整结构化报告
- `dlq/*.jsonl`:不可恢复或重试耗尽的 Bulk 文档
任务查询和报告导出:
```bash
python es_migrate_jobs.py list
python es_migrate_jobs.py show <任务ID>
python es_migrate_jobs.py export <任务ID> --output ./audit.json
python es_migrate_jobs.py diagnostics <任务ID> \
--output ./support.zip \
--config ~/.es_migrate_gui.json
```
诊断包只包含系统信息、脱敏审计报告、脱敏 GUI 配置及内部 SHA-256 清单;
明确排除凭证、SQLite 状态库、DLQ 文档和索引内容。
### 常用参数
| 参数 | 默认 | 说明 |
|------|------|------|
| `--mode` | `auto` | `auto` / `reindex` / `scroll` |
| `--on-conflict` | `merge` | `recreate`(删了重建)/ `skip` / `force` / `merge`(按 _id 覆盖) |
| `--batch-size` | 1000 | scroll 单批大小 |
| `--slices` | `auto` | `_reindex` 切片数(auto 由 ES 决定) |
| `--swap-aliases` | 关 | 迁移完做原子别名切换 |
| `--target-suffix` | `""` | 目标索引加后缀 |
| `--live-sync-rounds` | `2` | 初次全量后的最大在线追平轮数 |
| `--live-sync-interval` | `2` | 追平轮次之间等待秒数 |
| `--incremental-field` | 空 | 更新水位字段,必须为日期或数值类型 |
| `--incremental-overlap-ms` | `60000` | 水位回看窗口 |
| `--concurrency` | `1` | 并行迁移索引数 |
| `--docs-per-second` | `0` | 全任务写入限速,0 为不限速 |
| `--bulk-retries` | `5` | 可恢复 Bulk 子项最大重试次数 |
| `--bulk-max-mb` | `10` | 单个 Bulk 请求的编码后大小上限 |
| `--verify-sample-size` | `200` | 收敛时进行 `_source` 哈希抽检的文档数 |
| `--verify-full-content` | 关 | 常量内存流式比较全部 ID、routing 与 `_source` 内容指纹 |
| `--production-safe-mode` | 关 | 强制新索引、预检、遇错即停、全量校验和运行时熔断 |
| `--plan-only` | 关 | 仅预检并生成不可变执行计划,不创建索引或写入 |
| `--approve-plan CODE` | 空 | 批准与当前任务、集群和参数绑定的生产计划 |
| `--continuous-writes` | 关 | 声明源持续写入;安全模式要求可靠水位字段 |
| `--no-runtime-guard` | 关 | 关闭目标资源熔断(生产不推荐) |
| `--max-heap-percent` | `85` | 自动暂停写入的目标节点 Heap 阈值 |
| `--min-disk-free-percent` | `10` | 自动暂停写入的最小磁盘可用比例 |
| `--safety-pause-timeout` | `300` | 熔断等待上限,超时后安全终止 |
| `--job-id` | 自动生成 | 恢复已有持久任务 |
| `--state-db` | `~/.es_migrate/state.db` | 任务状态库 |
| `--no-preflight` | 关 | 跳过预检(生产不推荐) |
| `--no-verify` | 关 | 跳过源/目标计数校验(不推荐) |
| `--exclude` | 系统索引 | 可多次追加排除模式 |
| `--include-system` | 关 | 含 `.kibana*` 等 |
| `--dry-run` | 关 | 只读不写 |
| `--stop-on-error` | 关 | 任一索引失败立即停 |
### 快照插件与安装教程
GUI 测试连接后点击“📸 快照安装指南”,选择源/目标集群、仓库类型和部署方式,
工具会按实际 ES 版本生成教程,但不会自动安装插件、修改配置或重启节点。
也可以使用 CLI:
```bash
python es_snapshot_guide.py \
--version 7.17.28 \
--repository s3 \
--deployment docker
```
版本规则:
- ES 8.0+ 已内置 `repository-s3`、`repository-gcs` 和
`repository-azure`,不要重复安装。
- ES 7.x 及更早的自建集群使用上述云仓库时,需要在每个节点安装与 ES
版本完全一致的插件,并逐节点滚动重启。
- `fs` 共享文件系统仓库不需要插件,但所有 master/data 节点必须配置相同
`path.repo` 并访问同一挂载路径。
- `repository-hdfs` 仍是独立插件;Elastic Cloud 不能按自建节点教程操作。
- 仓库注册后必须执行 `_verify`,并通过重命名恢复做真实恢复演练。
### 阿里云 OSS / 腾讯云 COS 快照
连接测试后点击“☁ OSS / COS 快照”,可以检测两端插件和已有仓库、注册源端
可写仓库、验证仓库、创建所选索引快照、查询状态、注册目标只读仓库以及安全恢复。
- 阿里云托管 ES 使用 `repository-oss`;自建 ES 8.x 的仓库参数自动使用
`oss.client.*` 前缀。
- 腾讯云 COS 根据 ES 主版本自动选择旧版扁平参数或 ES 8.x 的
`cos.client` 嵌套结构。
- 快照异步提交,不会让界面等待到超时;只有状态为 `SUCCESS` 才允许恢复。
- 目标端始终以只读方式注册同一仓库,并在恢复前重新注册以刷新仓库缓存。
- 恢复默认添加 `restored_` 前缀、排除 global state 和 aliases,禁止覆盖生产索引。
- 工具不会删除进行中的快照;关闭窗口只停止查看,不会中止 ES 服务端任务。
- 仓库类型、部署方式、Endpoint/Region、Bucket、Base Path、凭证、限速、快照名和
恢复前缀会随 GUI 主配置自动保存,下次打开快照窗口直接回填。
### 多节点集群地址
源和目标均可填写多个属于同一集群的节点 URL,使用逗号、分号或换行分隔;
GUI 也提供“编辑节点列表”。连接测试会并行探测所有节点并校验
`cluster_uuid`,混入不同集群会直接拒绝。只要至少一个节点健康即可继续,运行期间
遇到网络故障、502、503、504 或 429 会自动切换到下一节点。单个集群最多 20 个地址;
已有高可用负载均衡地址时仍建议只填写负载均衡地址。
### 环境变量(鉴权备选)
```bash
export ES_SRC_USER=admin
export ES_SRC_PASS='...'
export ES_SRC_API_KEY='...'
export ES_SRC_CA=/path/to/ca.pem
export ES_TGT_USER=admin
export ES_TGT_PASS='...'
export ES_TGT_API_KEY='...'
export ES_TGT_CA=/path/to/ca.pem
```
---
## 🖥️ GUI 用法
启动:
```bash
python es_migrate_gui.py
```
界面分 5 个 tab:
1. **① 连接** — 源 / 目标节点列表、用户名密码、API Key、CA 证书;支持集群身份校验、自动故障切换、分别或并行测试,以及 OSS/COS 快照
2. **② 索引** — 从源 ES 获取并多选索引,支持搜索、全选、反选;也可使用全部 / 模式选择
3. **③ 选项** — 模式、批大小、冲突策略、并发、限速、水位字段、重试、任务恢复 ID
4. **④ 进度** — 计划校验码审批、实时进度、取消、恢复、日志与脱敏诊断包
5. **⑤ 说明** — 版本说明 + 跨大版本原理解释
> 配置自动保存到 `~/.es_migrate_gui.json`,下次启动自动加载。OSS/COS 仓库配置与
> 访问凭证也保存在该文件中;程序会在 macOS/Linux 上将文件权限设为 `0600`。
---
## 🛠 打包
### 打包 Windows 单文件 exe
#### 方式 1:一键脚本(最简单)
在 Windows 上:
```cmd
:: 安装 Python 3.9+ (勾选 Add to PATH)
:: 下载代码,进入目录
build_windows.bat
```
产物:`dist\es_migrate.exe`(约 30-50 MB),双击即用,**无需任何 Python 环境**。
脚本会自动创建隔离的 `build_venv_windows`,校验 Python/Tk/SQLite、安装依赖、
执行语法检查和全部测试,再构建以下产物:
- `dist\es_migrate.exe`
- `dist\es_migrate-windows-x64.zip`
- `dist\SHA256SUMS.txt`
- `dist\RELEASE.json`
- `dist\SBOM.spdx.json`
可用以下方式指定构建 Python:
```cmd
set PYTHON=C:\path\to\python.exe
build_windows.bat
```
#### 方式 2:手动 PyInstaller
```cmd
python -m pip install -r requirements.txt
python -m PyInstaller --noconfirm --clean --onefile --windowed ^
--name es_migrate ^
--hidden-import=es_migrate ^
--hidden-import=migration_store ^
--collect-data=certifi ^
es_migrate_gui.py
```
#### 方式 3:Windows spec 文件
```cmd
python -m PyInstaller --noconfirm --clean es_migrate_windows.spec
```
Windows spec 已预配置:
- 排除大依赖(numpy/pandas/matplotlib 等)→ 减小体积
- 启用 UPX 压缩
- `console=False` → 无控制台窗口
#### 方式 4:GitHub Actions 自动构建
```bash
git tag v1.0.0
git push origin v1.0.0
```
CI 会在 `windows-latest` x64 runner 上运行测试、执行相同的一键脚本、启动打包
后的 GUI 做 5 秒冒烟测试,并上传 exe、zip、SHA-256 与发布元数据。Tag 构建会自动附加
到 Release;也可在 Actions 页面用 `workflow_dispatch` 手动触发。
本地商业签名:
```cmd
set REQUIRE_SIGNING=1
set WINDOWS_CERT_PATH=C:\secure\release.pfx
set WINDOWS_CERT_PASSWORD=<从安全环境注入>
set WINDOWS_TIMESTAMP_URL=http://timestamp.digicert.com
build_windows.bat
```
脚本会定位 `signtool.exe`、执行 Authenticode SHA-256 时间戳签名并验证签名,
再生成 ZIP、SHA-256、`RELEASE.json` 和 SPDX 2.3 SBOM。CI 的 Tag 发布强制签名,需要配置
`WINDOWS_CERT_BASE64` 与 `WINDOWS_CERT_PASSWORD` 两个 GitHub Secrets;普通
手动构建可以生成明确标记为 unsigned 的开发产物。
---
### 打包 macOS .app
#### 方式 1:一键脚本(最简单)
在 macOS 上:
```bash
bash build_macos.sh
```
产物:
- `dist/es_migrate.app`(arm64 原生)
- `dist/es_migrate-macos-arm64.zip`(分发包)
- `dist/SHA256SUMS.txt`
- `dist/RELEASE.json`
- `dist/SBOM.spdx.json`
`build_macos.sh` 会完成:
1. 创建干净 venv(避免污染系统 site-packages,否则会拖入几百 MB 无关包)
2. 只装 `pyinstaller / requests / tqdm`
3. PyInstaller 打包成 .app
4. 写入统一产品版本并签名
5. 可选 Developer ID、公证与 stapling
6. 生成 ZIP、SHA-256、机器可读发布元数据和 SPDX 2.3 SBOM
商业签名与公证:
```bash
REQUIRE_SIGNING=1 \
MACOS_SIGN_IDENTITY="Developer ID Application: Example Corp (TEAMID)" \
APPLE_ID="release@example.com" \
APPLE_TEAM_ID="TEAMID" \
APPLE_APP_PASSWORD="" \
bash build_macos.sh
```
未提供商业证书时只生成明确标记为 `adhoc` 的开发/内部分发版本;设置
`REQUIRE_SIGNING=1` 后,缺少 Developer ID 或公证凭据会直接终止构建。
#### 方式 2:手动 PyInstaller
```bash
python3 -m venv build_venv
source build_venv/bin/activate
pip install pyinstaller requests tqdm
pyinstaller --windowed --name es_migrate \
--osx-bundle-identifier com.mavis.esmigrate \
--hidden-import=es_migrate es_migrate_gui.py
codesign --force --deep --sign - dist/es_migrate.app
```
#### 方式 3:Intel Mac(x86_64)
上面的 build 默认是 arm64(Apple Silicon)。在 Intel Mac 上跑同一个脚本,PyInstaller 会自动产出 x86_64 版本(架构跟随构建机)。
如果要同时支持两种架构(universal binary),需要分别在 arm64 和 x86_64 上 build,再 `lipo` 合并(PyInstaller 不直接支持一键出 universal)。
#### Mac 签名说明
macOS 签名分三档:
| 级别 | 命令 | 谁能用 | 用户体验 |
|------|------|--------|----------|
| **不签名** | (无) | 仅本机 | 双击 → "es_migrate 已损坏" 弹窗(其实没损坏,是 Gatekeeper 拦截) |
| **Ad-hoc 签名**(默认) | `codesign --sign -` | 仅本机 | 双击正常;首次运行可能需到「系统设置 → 隐私与安全性」点"仍要打开"一次 |
| **Developer ID + 公证** | `codesign --sign "Developer ID Application: ..."` + `xcrun notarytool submit` | 任何 Mac | 双击直接运行,无任何弹窗 |
**本工具默认用 ad-hoc 签名**,够用场景:
- 自己在 Mac 上跑
- 团队内网分发,同事首次「右键 → 打开」即可
**要发到公网 / App Store**,需要:
1. Apple Developer Program 账号($99/年)
2. 开发者证书 + 描述文件
3. 提交苹果公证(`xcrun notarytool`),可能等几分钟到几小时
如果你的 Mac 装了 Xcode,也可以这样强制打开(绕过签名):
```bash
xattr -dr com.apple.quarantine dist/es_migrate.app
```
---
## ⚠️ 注意事项
1. **7.8 → 9.x 的 mapping 兼容**:脚本会去掉 `_doc` 类型包装、剔除已废弃字段。但**字段类型本身不兼容**的情况(如 7.x 的 `keyword` 在 9.x 完全保留 — OK;7.x 的 `text` 字段在 9.x 默认仍 OK)。如有自定义 analyzer / runtime field,需人工 review。
2. **shard 数**:保留源端可移植设置;目标集群节点数、磁盘水位和分片规划仍需上线前容量评审。
3. **持续写入边界**:配置水位字段后可识别新增及更新(包括总数不变),前提是每次更新都同步更新该字段。通用搜索 API仍无法可靠传播物理删除;严格 RPO=0 必须使用业务双写、CDC/CCR、删除墓碑,或在最终切换前短暂冻结写入并补偿删除。
4. **别名边界**:别名切换只在目标集群内部原子执行,不能替代跨集群客户端地址切换。
5. **规模边界**:本工具支持流式、限速和多索引并发,不会把全量文档放入内存;但单个超大索引仍由一个读取流处理。十亿级数据应先基准压测,并对 snapshot/restore、CCR 或分布式 worker 做方案比较。
6. **继续未完成**:状态持久化到 SQLite。已完成索引跳过;中断中的索引从本索引安全重扫,增量水位持久保存。同一任务由租约保护,不能被两个进程同时恢复。DLQ 修复后应以同任务或受控补偿流程重放。
7. **打包体积**:单文件 exe 通常 30-50 MB。开启 UPX 压缩可降到 ~20 MB(在 spec 里 `upx=True` 已默认开启)。
8. **杀毒软件**:部分 Windows 杀软对 PyInstaller 打包的 exe 误报。可在 spec 中加 `--key` 加密(PyInstaller 不提供强加密,杀软可加白名单更稳)。
9. **Data Stream**:当前版本会识别 Data Stream 并终止预检,不会把 backing index 静默当普通索引迁移。完整迁移需要同时处理 component/index template、ILM、写索引与 rollover 语义。
10. **内容校验边界**:生产安全模式的全量指纹会再次读取全部文档,并以常量内存比较双重顺序无关 SHA-256 聚合;这提供极强的工程一致性证据,但源端仍在变化时可能安全失败。最终 RPO=0 仍依赖 CCR/CDC 或停写边界。
11. **熔断边界**:运行时熔断是迁移侧的附加保护,不替代 Elasticsearch 自身磁盘水位、容量规划、监控告警和限流策略。
## 🏭 生产与商业化边界
当前代码已经是一套可审计、可恢复、可限流的**单机迁移执行器/桌面运维工具**。
它适合受控生产迁移,但不应被描述成已经完成的多租户商业 SaaS。商业平台还需
在执行器之外建设:服务端 API 与调度器、分布式 worker/租约与高可用、RBAC/SSO、
密钥托管、审批流、监控告警、租户配额、计费、灾备,以及真实规模的长期压测。
生产上线前至少完成:
1. 在脱敏副本上验证 mapping、分析器插件、模板、ILM 和数据流策略。
2. 按峰值写入量压测吞吐、429、磁盘水位、恢复时间和 DLQ 补偿。
3. 明确 RPO/RTO、删除传播方案、最终切换窗口与回滚路径。
4. 从小索引灰度,校验报告通过后再扩大并发;不要一开始就使用最大并发。
---
## 📺 输出示例
```
____ _____ _
| ___\/ ____| | | ES 批量索引迁移工具
| |__ | (___ ___| |_ ___ _ __ ___ __ _ __ _ ___
|___/ \___ \ / _ \ __/ _ \ '_ ` _ \ / _` |/ _` |/ _ \
__ _ ____) | __/ || __/ | | | | | (_| | (_| | __/
|_____|_____/ \___|\__\___|_| |_| |_|\__,_|\__, |\___|
__/ |
|___/
16:20:11 [INFO] source version: 7.8.0
16:20:11 [INFO] target version: 9.0.0
16:20:11 [INFO] source=... (v7.x) target=... (v9.x)
16:20:11 [INFO] found 12 indices: ['logs-2024.01', 'logs-2024.02', ...]
16:20:11 [INFO] migration mode = scroll
indices: 100%|████████████| 12/12 [08:23<00:00, 41.9s/it]
docs: 23.4M/23.4M [08:23<00:00, 46.5kdoc/s]
16:20:42 [INFO] ════════════════════════════════════════════════════════════════════
16:20:42 [INFO] 📋 Migration Summary
16:20:42 [INFO] ════════════════════════════════════════════════════════════════════
16:20:42 [INFO] ✓ ok 12
16:20:42 [INFO] ✗ failed 0
16:20:42 [INFO] docs 23,456,789
16:20:42 [INFO] elapsed 502.8s
16:20:42 [INFO] ════════════════════════════════════════════════════════════════════
```
---
## ☕ 赞助
如果这个工具帮你省下了迁移的时间,欢迎请作者喝杯咖啡 ☕
支付宝 · 微信(长按识别 / 扫码)
---
## 📜 License
MIT — [LICENSE](LICENSE),随意用,欢迎 PR。