• Русский
  • PermissionDenied в Gitaly, вызванный рассинхронизацией времени

    Описание проблемы

    • При переходе на страницы, связанные с репозиторием, в GitLab UI возвращается HTTP 500.

    • В логах pod Gitaly присутствует ключевое слово:

      finished unary call with code PermissionDenied
    • Системное время pod Gitaly и pod webservice / sidekiq отличается на несколько секунд или более.

    Корневая причина

    Gitaly выполняет аутентификацию своих клиентов (webservice, sidekiq) с помощью JWT, в claims которого входит временная метка. Если часы узлов расходятся, на стороне Gitaly JWT считается истекшим или еще не вступившим в силу, и RPC отклоняется с PermissionDenied. На практике это обычно срабатывает при рассинхронизации более чем примерно на 30 секунд, но точный допустимый порог зависит от настроек допустимого отклонения в библиотеке JWT.

    Распространенные причины рассинхронизации времени:

    • На узлах кластера не настроен NTP, либо служба NTP не запущена.
    • NTP настроен, но не может получить доступ к внешнему источнику времени, например из-за блокировки firewall.
    • Узел работает на оборудовании с неточным RTC (real-time clock) и ни разу не синхронизировался.

    Диагностика

    1. Подтвердите наличие ключевого слова PermissionDenied в логах Gitaly:

      kubectl logs -n <NAMESPACE> <gitaly-pod> | grep "PermissionDenied"
    2. Сравните время, которое сообщает pod Gitaly, и 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 эти pod могут оказаться на любом узле).
    • Разрешение на перезапуск компонентов GitLab.

    Важные замечания

    • Изменение системного времени на работающем узле — чувствительная операция. По возможности выполняйте ее в окне обслуживания.
    • Значительные обратные скачки времени могут нарушить работу баз данных и распределенных систем. Предпочтительнее плавная корректировка времени, а не резкое изменение, например с помощью date -s.

    Шаги

    1. Настройте NTP на каждом узле кластера и убедитесь, что служба активна. Конкретный инструмент (chrony, systemd-timesyncd, ntpd и т. д.) зависит от вашей ОС и выходит за рамки этого документа. После внесения изменений убедитесь на каждом узле, что системные часы синхронизированы с внешним источником времени.

    2. Перезапустите компоненты GitLab, чтобы после выравнивания времени они выпустили новые JWT. Подберите имена workload в соответствии с вашим 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.
      • Значения date -u, выполненные внутри pod Gitaly и webservice, теперь отличаются менее чем на одну секунду.

    Рекомендации

    • Для быстрой проверки рассинхронизации по всему кластеру выполните date -u на каждом узле и сравните результаты; любой выбивающийся узел является кандидатом на проблему.