# 排查从 GitHub Enterprise Server 到 GHE.com 的实时迁移问题

有关迁移可能遇到的问题的建议。

如果迁移遇到问题，请检查迁移状态 `gh elm migration status --migration-id MIGRATION-ID` 并查看错误信息。

## 状态和建议的措施

| 地位                         | Meaning               | 建议的操作                                   |
| -------------------------- | --------------------- | --------------------------------------- |
| **创建**                     | 迁移已创建，但尚未启动           |                                         |
| `gh elm migration start`运行 |                       |                                         |
| 已排队 \*\*\*\*               | 迁移正在等待开始              | Wait                                    |
| **出口**                     | 正在从源导出数据              | 通过 `gh elm migration status` 进行监控       |
| **处理**                     | 导出的数据正在导入到目标          | 通过 `gh elm migration status` 进行监控       |
| **准备切换**                   | 初始迁移已完成，迁移已准备就绪，可进行切换 | 准备就绪后，运行 `gh elm migration cutover`     |
| **切换中**                    | 源存储库已存档，其余更改将应用于目标    | 监控;状态将转换为 **“已完成”**                     |
| **Completed**              | 迁移已成功完成               | 验证目标存储库并回收模拟对象                          |
| **失败**                     | 迁移遇到无法恢复的失败           | 调查错误（请参阅下文）                             |
| **已暂停**                    | 迁移已暂停                 | 检查暂停原因并解决问题（请参阅下文）                      |
| **已终止**                    | 迁移已取消                 | N/A                                     |
| **已降级**                    | 目标无法访问                | 检查GitHub企业服务器设备与 GHE.com 之间的网络连接（请参阅下文） |

## 迁移状态为“失败”

当无法恢复的错误阻止迁移继续时，迁移将进入 **“失败** ”状态。 这不同于单个资源导入失败—迁移失败意味着迁移本身无法继续。

若要分析，请运行 `gh elm migration status --migration-id MIGRATION-ID` 并查看响应中的错误详细信息。 每次失败都会包含格式为`(Correlation ID for Support: UUID)`的关联 ID。 如果联系 GitHub 支持，请提供此 ID，以便支持团队可以进行调查。

解决基础问题后，使用 `gh elm migration cancel --migration-id MIGRATION-ID` 中止失败的迁移并启动新的迁移。

## 迁移状态为“已暂停”

当问题需要干预后，迁移会进入 **暂停** 状态，然后才能继续。 运行 `gh elm migration status --migration-id MIGRATION-ID` 并检查暂停原因。

常见的暂停原因：

* **凭据过期**：其中一个 personal access tokens (classic) 凭据已过期。 创建一个具有所需作用域的新令牌，并用 `gh elm credential update` 更新它。 然后重启迁移。
* **速率限制**：迁移达到 API 速率限制。 等待几分钟，然后重启。

若要在解决基础问题后重启暂停的迁移，请执行以下操作：

```shell
gh elm migration start --migration-id MIGRATION-ID
```

## 迁移状态为“已降级”

**降级**状态意味着设备上的迁移服务GitHub Enterprise Server无法访问目标企业。 迁移在源端继续，但目标状态未知。

检查GitHub Enterprise Server设备与GHE.com子域之间的网络连接，然后再次运行`gh elm migration status --migration-id MIGRATION-ID`。 状态响应包括与目标最后一次成功联系的时间戳，这有助于评估连接问题发生的时间。

## 迁移卡在“导出”阶段

如果迁移仍处于 **导出** 状态，且 30 分钟或更多时间没有进度更改，导出程序可能会停滞不前。

1. 运行 `gh elm migration status --migration-id MIGRATION-ID` 并记下资源计数是否发生更改。

2. 如果计数值没有变化，请检查设备到目标端的网络连通性。

3. 查看设备上的导出程序日志 GitHub Enterprise Server （需要 SSH 管理员访问权限）：

   ```shell copy
   journalctl -t elm-exporter-backfiller --since "1 hour ago" | tail -50
   journalctl -t elm-exporter-sender --since "1 hour ago" | tail -50
   ```

4. 如果导出程序任务崩溃，它应会自动恢复。 如果未完成，请联系 GitHub 支持。

## Git 同步未完成

如果 `gh elm migration status` 显示初始 Git 推送在较长时间内未完成，请检查 Git 同步器日志：

```shell copy
journalctl -t elm-exporter-git-syncer --since "2 hours ago"
```

查找:

* **`connection refused`**：设备与目标之间的 GitHub Enterprise Server 网络问题。 检查防火墙规则和 DNS 解析。
* \*\*`authentication failed`\*\*personal access token (classic)：可能缺少所需的范围或可能已过期。
* **`remote: error`**：目标端可能正在拒绝推送。 请联系 GitHub 支持，并提供错误详情。

## 某些资源无法导入

单个资源可能无法导入，而不会导致整体迁移失败。 在 `gh elm migration status --migration-id MIGRATION-ID` 的输出中可以看到失败资源的计数。

只有在所有自动重试都用尽后，才会显示失败的资源，因此在无需干预的情况下，你看到的任何失败都会被确认为无法解决。 查看状态响应中的错误详细信息：在补全或实时更新中，每个失败的资源都会显示 `"state": "failed"`。

如果失败资源的数量和类型可以接受，就可以进行切换。 否则，中止迁移，解决基础问题，然后启动新的迁移。

## 切换失败，源存储库不可用

如果在源存储库已归档后切换失败，ELM 服务将尝试取消归档该存储库。 如果此操作失败，存储库管理员可以取消存储库的存档。 请参阅“[存档仓库](/zh/enterprise-server@3.21/repositories/archiving-a-github-repository/archiving-repositories#unarchiving-a-repository)”。

请注意，取消存档存储库将导致实例上的额外负载，因为存储库中的所有问题和拉取请求都将在 Elasticsearch 中重新编制索引。

源存储库取消存档后，您可以使用 `gh elm migration cutover --migration-id MIGRATION-ID` 重试切换，或者使用 `gh elm migration cancel --migration-id MIGRATION-ID` 中止迁移，并在准备就绪后开始新的迁移。

## 由于强制推送，必须重新启动迁移

如果在迁移正在进行时有人强制推送到源存储库的默认分支，则源和目标之间的 Git 同步会中断。 强制推送会以无法增量合并的方式重写提交历史记录。

如果发生这种情况，请使用 `gh elm migration cancel --migration-id MIGRATION-ID` 中止迁移，并启动新的迁移。 在重启之前，请与团队沟通，当迁移处于活动状态时，不允许强制推送到默认分支。

## 迁移访问令牌遭到拒绝

如果迁移失败并出现身份验证错误，请检查：

* 源令牌和目标令牌都是 personal access tokens (classic)。
  Fine-grained personal access tokens 不受支持。
* 如果目标组织强制实施 SAML 单一登录，则必须对令牌进行 SSO 授权。
* 这两个令牌都具有 [使用企业实时迁移迁移存储库](/zh/enterprise-server@3.21/migrations/elm/migrate-your-repository#4-configure-the-live-migration-secrets) 中指定的范围。

如果最近轮换了令牌，迁移过程会自动获取新的凭据。 无需运行 `ghe-config-apply` 或重启迁移服务。

## GitHub CLI 访问令牌被拒绝

Enterprise Live Migrations 使用两组凭据。 本部分适用于在步骤 2 中创建并由本地存储的`gh elm configure`**操作员令牌**。

操作员必须为每个端点使用一个 personal access token (classic)：

* **源操作器令牌**必须在GitHub Enterprise Server上创建。
* 必须在\*\*\*\* 上创建GHE.com。
* 这两个令牌都具有 [使用企业实时迁移迁移存储库](/zh/enterprise-server@3.21/migrations/elm/migrate-your-repository#2-create-the-tokens-used-by-the-operator-who-will-perform-the-migration) 中指定的范围。
* 令牌所有者必须是相应企业的管理员。 选择范围不会授予用户管理访问权限。
* Fine-grained personal access tokens 不受支持。

### 常见响应

| 响应                                                 | Meaning                                                      | 纠正方法                                                                                                                                                    |
| -------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `401 Bad credentials`                              | 终结点无法对令牌进行身份验证。 尚未评估授权范围。                                    | 检查令牌是否已过期或已吊销，是否已完全复制令牌，以及源令牌和目标令牌是否已交换。 确认每个令牌都是在其使用所在的主机上创建的。                                                                                         |
| `403 Forbidden`                                    | 令牌已经过身份验证，但其用户或范围未授权该操作。                                     | 将 personal access token (classic) 与 `admin:enterprise` 一起使用。 确认令牌所有者是企业管理员。 如果 SAML SSO 适用，请为 SSO 授权令牌。                                                 |
| `Resource not accessible by personal access token` | 不支持令牌类型或权限。 这通常发生在使用 fine-grained personal access token 时。   | 将其替换为具有 `admin:enterprise` 的 personal access token (classic)。                                                                                           |
| `404 Not Found`                                    | 请求可能使用了错误的 API URL，或者 Enterprise Live Migrations 可能未为目标企业启用。 | 对于 GHE.com，请使用租户 API 的 URL，例如 `https://api.SUBDOMAIN.ghe.com`，末尾不要带斜杠。 也请验证源 API 的 URL。 如果这两个 URL 都正确，请联系 GitHub 支持 以确认 Enterprise Live Migrations 已启用。 |

### 独立验证令牌

在将每个令牌与 `/user` 一起使用之前，先针对 Enterprise Live Migrations 终结点进行测试。 这些命令打印响应标头，但放弃响应正文。

对于源（GitHub Enterprise Server）令牌：

```shell
curl --silent --show-error --output /dev/null --dump-header - \
  --header "Authorization: Bearer $SOURCE_OPERATOR_TOKEN" \
  "$SOURCE_API_URL/user"
```

对于目标词元：

```shell
curl --silent --show-error --output /dev/null --dump-header - \
  --header "Authorization: Bearer $TARGET_OPERATOR_TOKEN" \
  "$TARGET_API_URL/user"
```

每个请求都应返回 `200 OK`。
`X-OAuth-Scopes` 响应标头应包含 `admin:enterprise`。

如果 `/user` 返回的是 `200 OK`，而 Enterprise Live Migrations 命令返回的是 `401 Bad credentials`，则 CLI 可能存储了不同的令牌或 URL。 再次运行 `gh elm configure` ，并仔细地将每个令牌与其相应的终结点相关联。

操作员令牌由 Enterprise Live Migrations CLI 本地存储。 轮换操作员令牌后，再次运行 `gh elm configure` 或使用相应的命令行选项提供替换凭据。

这不同于步骤 4 中配置的迁移服务令牌。 更新后的迁移服务凭据会自动生效，不需要 `ghe-config-apply`，也不需要重启迁移服务。

不要在日志、屏幕截图、支持捆绑包或支持请求中包含访问令牌。 如果问题仍然存在，请向 GitHub 支持 提供 HTTP 状态、端点主机名、迁移 ID、带时区的时间戳以及任何关联 ID（如有），但不要提供令牌。

## 源 GHES URL 被拒绝

Enterprise Live Migrations 需要 GitHub Enterprise Server URL 才能使用 HTTPS。 如果 URL 配置为 HTTP，迁移将在预检验证阶段失败。

## 收集日志以获取支持

联系 GitHub 支持 时，最有用的信息包括：

1. **支持包**（首选）：在`ghe-support-bundle -u`设备上运行GitHub Enterprise Server。 这会自动捕获所有 Enterprise Live Migrations 日志。
2. **迁移状态输出**： `gh elm migration status --migration-id MIGRATION-ID`
3. **迁移 ID** 和大约失败时间（带时区）
4. **错误消息中的任何关联 ID**

如果不支持捆绑包，可以手动收集日志：

```shell copy
journalctl -t elm-exporter-migration-manager --since "24 hours ago" > migration-manager.log
journalctl -t elm-exporter-backfiller --since "24 hours ago" > backfiller.log
journalctl -t elm-exporter-sender --since "24 hours ago" > sender.log
journalctl -t elm-exporter-git-syncer --since "24 hours ago" > git-syncer.log
```