由时钟偏差导致的 Gitaly PermissionDenied

问题描述

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

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

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

根本原因

Gitaly 使用包含时间戳声明的 JWT 对其客户端(webservice、sidekiq)进行身份验证。如果节点时钟发生漂移,Gitaly 端会将 JWT 视为已过期或尚未生效,并以 PermissionDenied 拒绝 RPC。实际情况下,超过 ~30 秒的漂移通常会触发此问题,但确切的容差取决于 JWT 库的宽限期设置。

时钟漂移的常见原因:

  • 集群节点未配置 NTP,或 NTP 服务未运行。
  • 已配置 NTP,但无法访问上游时间源(例如被防火墙阻止)。
  • 节点运行在实时硬件时钟(RTC)不准确的硬件上,并且从未进行过同步。

故障排除

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

    kubectl logs -n <NAMESPACE> <gitaly-pod> | grep "PermissionDenied"
  2. 比较 Gitaly pod 和客户端 pod(webservice 或 sidekiq)报告的时间。相差数秒以上则需要引起怀疑:

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

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

    在每个节点上检查系统时钟是否已与 NTP 源同步。如果 NTP 未处于活动状态,则该节点是产生漂移的源头。

解决方案

前提条件

  • 具备对所有集群节点的 SSH 访问权限(重新调度后,这些 pod 可能由任意节点承载)。
  • 具备重启 GitLab 组件的权限。

注意事项

  • 更改运行中节点的系统时间是一项敏感操作。请尽可能在维护窗口期间执行。
  • 大幅向后调整时间可能会干扰数据库和分布式系统。应优先采用逐步调整,而不是使用 date -s 之类的硬跳变。

步骤

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

  2. 在时钟对齐后重启 GitLab 组件,使其签发新的 JWT。请根据您的发行版调整工作负载名称:

    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 pod 内执行的 date -u 现在相差小于一秒。

提示

  • 如需快速检查整个集群的时间偏差,请在每个节点上运行 date -u 并进行比较;任何偏离值较大的节点都可能是问题节点。

GitLab 是 GitLab Inc. 的商标。

Alauda 是独立供应商。本产品与 GitLab Inc. 没有关联,也未获得其认可或赞助。所有商标均归其各自所有者所有,仅用于识别目的。