GitLab HTTPS 访问间歇性失败

问题描述

部分用户无法通过浏览器访问 GitLab,而其他用户可以访问——该失败是间歇性的。

  • 对同一域名多次执行 curl,结果不一致(有时成功,有时因 TLS 错误失败)。
  • 证书本身是有效的;刷新浏览器几次后可能最终成功。

根因

GitLab 域名解析到多个 IP 地址,但其中只有一个是真正的 LB VIP,或者是运行 Ingress Controller 的节点 IP。其他 IP 上没有对应的 TLS 证书。每次客户端解析时都可能命中不同的 IP:命中真正的 IP 时成功,命中错误的 IP 时会因为 TLS 握手错误而失败(当没有证书匹配 SNI 时,通常会出现 nginx-ingress 或其他基于 Go 的代理返回的 tls: internal error)。这就是故障看起来是间歇性的原因。

常见原因包括:

  • DNS 服务器上为同一域名配置了多个 A 记录。
  • 之前部署遗留的 IP 记录没有清理。

故障排查

  1. 检查 DNS 解析,确认是否返回了多个 IP:

    nslookup gitlab.example.com
    # 或者,输出更简洁:
    dig +short gitlab.example.com
    # 如果返回了多个 IP,请继续下一步。

    同时检查客户端的 /etc/hosts 中是否存在过期的静态条目。如果用户是从 Kubernetes 集群内部访问 GitLab,还需要检查 CoreDNS 的自定义记录。

  2. 通过 curl --resolve 强制指定解析,分别验证每个 IP。返回 tls: internal error 的那个 IP 就是错误的:

    curl -v --resolve gitlab.example.com:443:192.168.28.5 https://gitlab.example.com
    curl -v --resolve gitlab.example.com:443:192.168.xx.xx https://gitlab.example.com
  3. 检查错误 IP 返回的证书。其 subject 通常与 GitLab 域名不匹配:

    curl -v --resolve gitlab.example.com:443:<wrong-ip> https://gitlab.example.com 2>&1 \
      | grep -E "subject|issuer"

解决方案

前提条件

  • 可以访问托管 GitLab 域名的 DNS 管理平台。
  • 已识别出正确的 IP——通常是 LB VIP,或者运行 Ingress Controller 的节点 IP。如有不确定,请与网络/运维团队确认。

注意事项

在修改 DNS 记录之前,请务必确认已经识别出正确的 IP。删除正确的记录会导致所有用户都无法访问 GitLab。请在变更前记录原始配置,并在维护窗口内执行操作。

步骤

  1. 登录 DNS 管理平台,列出 GitLab 域名的所有 A 记录。

  2. 删除错误的 A 记录,只保留正确的 IP。

  3. 等待 DNS TTL 过期,或者在客户端手动清除本地 DNS 缓存:

    # Linux(现代 systemd)
    sudo resolvectl flush-caches
    # Linux(较旧的 systemd,已弃用)
    sudo systemd-resolve --flush-caches
    
    # macOS
    sudo dscacheutil -flushcache && sudo killall -HUP mDNSResponder
  4. 验证修复结果:

    • nslookup gitlab.example.com 现在只返回一个 IP。
    • 连续执行 10 次 curl -v https://gitlab.example.com,每次都成功。
    • 之前无法访问 GitLab 的用户在清理本地 DNS 缓存后恢复正常。

提示

  • curl -v 会直接打印证书的 subject / issuer,在诊断 TLS 问题时比切换到浏览器更快。
  • 如果 Ingress Controller 位于多个 load balancer node 之后,建议暴露一个统一的 VIP,而不是配置多个 A 记录。