Help us learn about your current experience with the documentation. Take the survey.

在 Kubernetes 上运行 Gitaly

  • 版本:免费版、专业版、旗舰版
  • 产品:GitLab.com、GitLab 自管版、GitLab 专属版
  • 状态:有限可用性

在 Kubernetes 上运行 Gitaly 存在可用性方面的权衡,因此在规划生产环境时,请考虑这些权衡并相应地设定预期。 本文档描述了如何最大限度地减少和规划现有局限性的指导。

不支持 Gitaly Cluster (Praefect)。有关在 Kubernetes 上运行 Gitaly 的更多信息,请参阅 epic 6127

背景

根据设计,Gitaly(非集群模式)是一个单点故障服务(SPoF)。数据源自并由单个实例提供服务。 对于 Kubernetes,当 StatefulSet pod 发生轮转时(例如,在升级、节点维护或驱逐期间),轮转会导致由该 pod 或实例提供的数据服务中断。

云原生混合架构(Gitaly VM)中,Linux 软件包(Omnibus) 通过以下方式掩盖了这个问题:

  1. 原地升级 Gitaly 二进制文件。
  2. 执行优雅重载。

这种方法不适用于基于容器的生命周期,因为在容器生命周期中,容器或 pod 需要完全关闭并作为新的容器或 pod 启动。

Gitaly Cluster (Praefect) 通过在实例间复制数据来解决数据和服务的高可用性问题。然而,由于现有问题和设计约束在基于容器的平台上被放大,Gitaly Cluster (Praefect) 不适合在 Kubernetes 中运行。

为了支持云原生部署,Gitaly(非集群模式)是唯一的选择。 通过利用合适的 Kubernetes 和 Gitaly 功能与配置,您可以最大限度地减少服务中断并提供良好的用户体验。

要求

本页信息假定:

  • Kubernetes 版本等于或高于 1.29
  • Kubernetes 节点的 runc 版本等于或高于 1.1.9
  • Kubernetes 节点使用 cgroup v2。不支持原生、混合 v1 模式。仅支持systemd 风格的 cgroup 结构(Kubernetes 默认)。
  • Pod 可以访问节点挂载点 /sys/fs/cgroup
  • Containerd 版本为 2.1.0 或更高。
  • Pod 的 init 容器(init-cgroups)对 /sys/fs/cgroup 具有 root 用户的文件系统权限。用于将 pod 的 cgroup 委托给 Gitaly 容器(用户 git,UID 1000)。
  • cgroups 文件系统未使用 nsdelegate 标志挂载。更多信息,请参阅 Gitaly 问题 6480

指导

在 Kubernetes 中运行 Gitaly 时,您必须:

在 Containerd 中启用 cgroup_writable 字段

Gitaly 的 cgroup 支持要求非特权容器对 cgroups 具有可写访问权限。Containerd v2.1.0 引入了 cgroup_writable 配置选项。启用此选项后,可确保 cgroups 文件系统以读写权限挂载。

要启用此字段,请在将部署 Gitaly 的节点上执行以下步骤。如果 Gitaly 已部署,则必须在修改配置后重新创建 pod。

  1. 修改位于 /etc/containerd/config.toml 的 Containerd 配置文件,以包含 cgroup_writable 字段:

    [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
    runtime_type = "io.containerd.runc.v2"
    cgroup_writable = true
  2. 重启 Kubelet 和 Containerd 服务:

    sudo systemctl restart kubelet
    sudo systemctl restart containerd

    如果服务重启时间过长,这些命令可能会将节点标记为 NotReady。

处理 pod 中断

pod 可能因多种原因发生轮转。了解并规划服务生命周期有助于最大限度地减少中断。

例如,对于 Gitaly,Kubernetes StatefulSet 会在 spec.template 对象更改时发生轮转,这可能发生在 Helm Chart 升级(标签或镜像标签)或 pod 资源请求或限制更新期间。

本节重点介绍常见的 pod 中断情况及其处理方法。

安排维护窗口

由于该服务并非高可用,某些操作可能会导致短暂的服务中断。安排维护窗口可以预示潜在的服务中断,并有助于设定预期。您应对以下情况使用维护窗口:

  • GitLab Helm Chart 升级和重新配置。
  • Gitaly 配置更改。
  • Kubernetes 节点维护窗口。例如,升级和打补丁。将 Gitaly 隔离到其自己的专用节点池可能有所帮助。

使用 PriorityClass

使用 PriorityClass 为 Gitaly pod 分配比其他 pod 更高的优先级,以帮助应对节点饱和压力、驱逐优先级和调度延迟:

  1. 创建一个优先级类:

    apiVersion: scheduling.k8s.io/v1
    kind: PriorityClass
    metadata:
      name: gitlab-gitaly
    value: 1000000
    globalDefault: false
    description: "GitLab Gitaly priority class"
  2. 将该优先级类分配给 Gitaly pod:

    gitlab:
      gitaly:
        priorityClassName: gitlab-gitaly

向节点自动伸缩发出信号以防止驱逐

节点自动伸缩工具会根据需要添加和移除 Kubernetes 节点,以调度 pod 并优化成本。

在缩减规模事件期间,Gitaly pod 可能会被驱逐以优化资源使用。通常可以使用注解来控制此行为并排除工作负载。例如,对于 Cluster Autoscaler:

gitlab:
  gitaly:
    annotations:
      cluster-autoscaler.kubernetes.io/safe-to-evict: "false"

处理资源争用和饱和

由于 Git 操作的不可预测性,Gitaly 服务的资源使用情况可能难以预测。并非所有仓库都相同,其大小会严重影响性能和资源使用,尤其是对于单体仓库

在 Kubernetes 中,不受控制的资源使用可能导致内存不足(OOM)事件,这会迫使平台终止 pod 并杀死其所有进程。 Pod 终止会引发两个重要问题:

  • 数据/仓库损坏
  • 服务中断

本节重点介绍如何减小影响范围并保护整体服务。

限制 Git 进程的资源使用

隔离 Git 进程可以确保单个 Git 调用不会消耗所有服务和 pod 资源,从而提供安全保障。

Gitaly 可以使用 Linux 控制组 (cgroups) 来对资源使用施加更小的、每个仓库的配额。

您应将 cgroup 配额维持在整体 pod 资源分配之下。 CPU 不是关键问题,因为它只会降低服务速度。然而,内存饱和可能导致 pod 终止。在 pod 请求和 Git cgroup 分配之间保留 1 GiB 的内存缓冲区是一个安全的起点。缓冲区的大小取决于流量模式和仓库数据。

例如,当 pod 内存请求为 15 GiB 时,为 Git 调用分配 14 GiB:

gitlab:
  gitaly:
    cgroups:
      enabled: true
      # 所有仓库 cgroup 的总限制,不包括 Gitaly 进程
      memoryBytes: 15032385536 # 14GiB
      cpuShares: 1024
      cpuQuotaUs: 400000 # 4 cores
      # 每个仓库的限制,共 50 个仓库 cgroup
      repositories:
        count: 50
        memoryBytes: 7516192768 # 7GiB
        cpuShares: 512
        cpuQuotaUs: 200000 # 2 cores

更多信息,请参阅 Gitaly 配置文档

合理配置 Pod 资源

Gitaly pod 的资源配置至关重要,参考架构提供了一些作为起点的指导。然而,不同的仓库和使用模式会消耗不同程度的资源。 您应监控资源使用情况并随时间进行相应调整。

内存是 Kubernetes 中最敏感的资源,因为内存耗尽会触发 pod 终止。 使用 cgroups 隔离 Git 调用有助于限制仓库操作的资源使用,但这并不包括 Gitaly 服务本身。 与之前关于 cgroup 配额的建议一致,在整体 Git cgroup 内存分配和 pod 内存请求之间添加一个缓冲区以提高安全性。

首选 pod 的 Guaranteed 服务质量 类(资源请求与限制匹配)。使用此设置,pod 不易受到资源争用的影响,并且保证不会因其他 pod 的消耗而被驱逐。

资源配置示例:

gitlab:
  gitaly:
    resources:
      requests:
        cpu: 4000m
        memory: 15Gi
      limits:
        cpu: 4000m
        memory: 15Gi

    init:
      resources:
        requests:
          cpu: 50m
          memory: 32Mi
        limits:
          cpu: 50m
          memory: 32Mi

配置并发限制

您可以使用并发限制来帮助保护服务免受异常流量模式的影响。更多信息,请参阅 并发配置文档如何监控限制

隔离 Gitaly pod

当运行多个 Gitaly pod 时,您应将它们调度到不同的节点上,以分散故障域。这可以使用 pod 反亲和性来强制执行。例如:

gitlab:
  gitaly:
    antiAffinity: hard

优化 pod 轮转时间

本节介绍了通过缩短 pod 开始提供服务所需的时间,来减少维护事件或计划外基础设施事件期间停机时间的优化领域。

持久卷权限

随着数据量的增长(Git 历史记录和更多仓库),pod 启动并就绪所需的时间会越来越长。

在 pod 初始化期间,作为持久卷挂载的一部分,文件系统权限和所有权会显式设置为容器的 uidgid。 此操作默认运行,并且由于存储的 Git 数据包含许多小文件,可能会显著减慢 pod 的启动速度。

此行为可以通过 fsGroupChangePolicy 属性进行配置。仅当卷根目录的 uidgid 与容器规范不匹配时,才使用此属性执行该操作:

gitlab:
  gitaly:
    securityContext:
      fsGroupChangePolicy: OnRootMismatch

健康检查

Gitaly pod 在就绪探针成功后开始提供服务。默认的探针时间较为保守,以覆盖大多数用例。 减少 readinessProbeinitialDelaySeconds 属性可以更早地触发探针,从而加速 pod 就绪。例如:

gitlab:
  gitaly:
    statefulset:
      readinessProbe:
        initialDelaySeconds: 2
        periodSeconds: 10
        timeoutSeconds: 3
        successThreshold: 1
        failureThreshold: 3

Gitaly 优雅关闭超时

默认情况下,在终止时,Gitaly 会提供 1 分钟的超时时间,以便进行中的请求能够完成。 虽然乍看之下有益,但此超时设置:

  • 减慢了 pod 的轮转速度。
  • 在关闭过程中通过拒绝请求来降低可用性。

在基于容器的部署中,更好的方法是依赖客户端重试逻辑。您可以使用 gracefulRestartTimeout 字段重新配置超时时间。 例如,设置为 1 秒的优雅超时:

gitlab:
  gitaly:
    gracefulRestartTimeout: 1

监控磁盘使用情况

对于长期运行的 Gitaly 容器,请定期监控磁盘使用情况,因为如果未启用日志轮转,日志文件的增长可能会导致存储问题。