由时钟偏移导致的 Gitaly PermissionDenied

问题描述

  • 访问 GitLab UI 中与仓库相关的页面时返回 HTTP 500。

  • Gitaly pod 日志包含以下关键字:

    finished unary call with code PermissionDenied
  • Gitaly pod 的系统时间与 webservice / sidekiq pods 的系统时间相差数秒或更多。

根本原因

Gitaly 使用包含时间戳声明的 JWT 来对其客户端(webservicesidekiq)进行身份验证。如果节点时钟发生漂移,Gitaly 侧会将该 JWT 视为已过期或尚未生效,从而拒绝该 RPC 并返回 PermissionDenied。在实践中,超过约 30 秒的漂移通常会触发此问题,但具体容差取决于 JWT library 的 leeway 设置。

时钟漂移的常见原因包括:

  • 集群节点未配置 NTP,或者 NTP 服务未运行。
  • 已配置 NTP,但无法连接到上游时间源(例如被防火墙阻止)。
  • 节点运行在硬件上,该硬件的 real-time clock (RTC) 不准确,并且从未完成同步。

排查

  1. 在 Gitaly 日志中确认 PermissionDenied 关键字:

    kubectl logs -n <NAMESPACE> <gitaly-pod> | grep "PermissionDenied"
  2. 比较 Gitaly pod 和客户端 pod(webservicesidekiq)报告的时间。相差超过几秒就值得怀疑:

    kubectl exec -n <NAMESPACE> <gitaly-pod>     -- date -u
    kubectl exec -n <NAMESPACE> <webservice-pod> -- date -u
  3. 确定受影响 pods 所在的节点,然后直接检查主机时钟:

    kubectl get pods -n <NAMESPACE> -o wide | grep -E "gitaly|webservice"

    在每个节点上,检查系统时钟是否与 NTP 源同步。如果 NTP 未启用,则该节点就是漂移的来源。

解决方案

前提条件

  • 可通过 SSH 访问所有集群节点(重新调度后,任何节点都可能承载这些 pods)。
  • 具有重启 GitLab 组件的权限。

注意事项

  • 在运行中的节点上修改系统时间是一项敏感操作。尽可能在维护窗口内执行。
  • 将时间大幅回拨可能会影响数据库和分布式系统。优先使用平滑调整,而不是类似 date -s 这样的硬切换。

步骤

  1. 在每个集群节点上配置 NTP,并确保服务处于活动状态。具体使用的工具(chrony、systemd-timesyncd、ntpd 等)取决于你的 OS,本文不作展开。修改完成后,确认每个节点上的系统时钟都已与上游时间源同步。

  2. 重启 GitLab 组件,以便在时钟对齐后重新生成新的 JWT。根据你的 release 调整工作负载名称:

    kubectl -n <NAMESPACE> rollout restart statefulset <RELEASE>-gitaly
    kubectl -n <NAMESPACE> rollout restart deployment <RELEASE>-webservice-default
    kubectl -n <NAMESPACE> rollout restart deployment <RELEASE>-sidekiq-all-in-1-v2
  3. 验证修复结果:

    • Gitaly 日志不再输出 PermissionDenied 条目。
    • GitLab UI 中的仓库页面返回 200。
    • 在 Gitaly 和 webservice pods 内执行的 date -u 现在相差不到一秒。

提示

  • 若要快速检查整个集群的时钟偏移,可在每个节点上运行 date -u 并进行比较;任何异常节点都可能是候选问题节点。