Kubernetes에서 Gitaly 실행

Tier: Free, Premium, Ultimate Offering: GitLab.com, Self-managed, GitLab Dedicated Status: Experiment

Kubernetes에서 Gitaly를 실행하는 것은 가용성에 대한 트레이드 오프가 있으므로, 프로덕션 환경을 계획하고 기대를 설정할 때 이러한 트레이드 오프를 고려해야 합니다. 이 문서는 기존 제약 사항을 최소화하고 계획하는 방법에 대한 설명과 지침을 제공합니다.

Gitaly 클러스터(Praefect)는 지원되지 않습니다. Kubernetes에서 Gitaly를 실행하는 자세한 내용은 epic 6127를 참조하세요.

컨텍스트

설계상 Gitaly(클러스터 없음)는 단일 장애점 서비스(SPoF)입니다. 데이터는 단일 인스턴스에서 가져오고 제공됩니다. Kubernetes에서는 StatefulSet 파드가 회전될 때(예: 업그레이드 시, 노드 유지보수 시 또는 제명 경우) 해당 파드 또는 인스턴스가 제공하는 데이터에 대한 서비스 중단이 발생합니다.

클라우드 네이티브 하이브리드 설정( Gitaly VM)에서는 Linux 패키지(Omnibus)가 다음과 같은 문제에 대한 해결을 가리킵니다:

  1. Gitaly 이진 파일을 그대로 업그레이드합니다.
  2. 안전한 리로드를 수행합니다.

동일한 방식이 컨테이너 기반 라이프사이클에는 적합하지 않습니다. 컨테이너나 파드가 완전히 종료되고 새로운 컨테이너나 파드로 시작해야 하는 이유 때문입니다.

Gitaly 클러스터(Praefect)는 데이터 및 서비스 고가용성 측면을 복제하여 해결합니다. 그러나 Kubernetes에서 실행하기에는 기존 문제 및 디자인 제한이 컨테이너 기반 플랫폼에 의해 확대되므로 Gitaly 클러스터가 부적합합니다.

클라우드 네이티브 배포를 지원하기 위해 Gitaly(클러스터 없음)가 유일한 선택지입니다. 적절한 Kubernetes 및 Gitaly 기능 및 구성을 활용하여 서비스 중단을 최소화하고 사용자 경험을 향상시킬 수 있습니다.

요구 사항

이 페이지에 있는 정보는 다음을 가정합니다:

  • 1.29 이상의 Kubernetes 버전입니다.
  • 1.1.9 이상의 Kubernetes 노드 runc 버전입니다.
  • Kubernetes 노드 cgroup v2입니다. Native, hybrid v1 모드는 지원되지 않습니다. 오직 systemd-스타일 cgroup 구조가 지원됩니다(Kubernetes 기본값).
  • Pod가 노드 마운트 포인트 /sys/fs/cgroup에 접근합니다.
  • Pod init 컨테이너(init-cgroups)가 /sys/fs/cgrouproot 사용자 파일 시스템 권한에 접근합니다. Pod cgroup을 Gitaly 컨테이너에 위임하는 데 사용됩니다(git 사용자, UID 1000).

안내

Kubernetes에서 Gitaly를 실행할 때 다음을 해야 합니다:

파드 중단 처리

파드는 여러 이유로 회전될 수 있습니다. 서비스 라이프사이클을 이해하고 계획하면 서비스 중단을 최소화할 수 있습니다.

예를 들어, Gitaly의 경우 Kubernetes StatefulSetspec.template 객체 변경으로 회전할 수 있습니다. 이는 Helm 차트 업그레이드(labels 또는 이미지 태그)나 파드 리소스 요청이나 제한 업데이트 중에 발생할 수 있습니다.

이 섹션은 흔한 파드 중단 사례 및 이에 대한 대처 방법에 중점을 둡니다.

유지 보수 창을 예약

서비스가 고가용성이 아니기 때문에 특정 작업은 잠시 서비스 중단을 발생할 수 있습니다. 유지 보수 창을 예약하면 잠재적인 서비스 중단을 신호화하고 기대치를 설정하는 데 도움이 됩니다. 다음과 같은 경우에 유지 보수 창을 사용해야 합니다:

  • GitLab Helm 차트 업그레이드 및 재구성
  • Gitaly 구성 변경
  • Kubernetes 노드 유지 보수 창. 예를 들어, 업그레이드 및 패치. Gitaly를 별도의 전용 노드 풀로 분리하는 것이 도움이 될 수 있습니다.

PriorityClass 사용

PriorityClass를 사용하여 다른 파드보다 더 높은 우선순위를 할당하여 노드 포화 압력, 퇴직 우선 순위 및 스케줄링 지연에 도움을 줄 수 있습니다.

  1. 우선순위 클래스 생성:

    apiVersion: scheduling.k8s.io/v1
    kind: PriorityClass
    metadata:
      name: gitlab-gitaly
    value: 1000000
    globalDefault: false
    description: "GitLab Gitaly priority class"
    
  2. 우선순위 클래스를 Gitaly 파드에 할당:

    gitlab:
      gitaly:
        priorityClassName: gitlab-gitaly
    

노드 오토스케일링에 퇴직 방지 신호 보내기

노드 오토스케일링 도구는 필요에 따라 Kubernetes 노드를 추가하거나 제거하여 파드를 예약하고 비용을 최적화합니다.

다운 스케일링 이벤트 중에 Gitaly 파드는 자원 사용을 최적화하기 위해 퇴직될 수 있습니다. 어노테이션은 일반적으로 이 동작을 제어하고 작업 부담을 제외하는 데 사용됩니다. 예를 들어, Cluster Autoscaler의 경우:

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

자원 경합 및 포화 처리

Gitlay 서비스 자원 사용은 Git 작업의 불확실한 성격으로 예측할 수 없을 수 있습니다. 모든 저장소가 동일하지 않고 크기는 성능과 자원 사용에, 특히 monorepos의 경우에 큰 영향을 미칩니다.

Kubernetes에서 제어되지 않는 자원 사용은 Out Of Memory (OOM) 이벤트로 이어질 수 있으며, 이는 플랫폼이 파드를 종료하고 해당 프로세스를 모두 종료합니다. 파드 종료는 두 가지 중요한 문제를 제기합니다:

  • 데이터/저장소 손상
  • 서비스 중단

이 섹션은 영향의 범위를 줄이고 전체 서비스를 보호하는 데 중점을 두며 자원 사용을 제한합니다.

Git 프로세스 자원 사용 제한

Git 프로세스를 격리하면 단일 Git 호출이 전체 서비스 및 파드 자원을 모두 사용할 수 없도록 안전을 제공합니다.

Gitaly는 Linux Control Groups (cgroups)를 사용하여 리소스 사용에 대한 작은 저장소당 할당량을 부과할 수 있습니다.

전반적인 파드 자원 할당보다 cgroup 할당량을 낮게 유지해야 합니다. CPU는 서비스 속도만 느리게 할 뿐입니다. 그러나 메모리 포화는 파드 종료로 이어질 수 있습니다. 파드 요청과 Git cgroup 할당 사이에 1 GiB 메모리 버퍼는 안전한 시작점입니다. 버퍼 크기 조정은 트래픽 패턴 및 저장소 데이터에 따라 달라집니다.

예를 들어, 15 GiB의 파드 메모리 요청에는 14 GiB가 Git 호출에 할당됩니다:

gitlab:
  gitaly:
    cgroups:
      enabled: true
      # Gitaly 프로세스 제외, 모든 저장소 cgroups에 대한 총 제한
      memoryBytes: 15032385536 # 14GiB
      cpuShares: 1024
      cpuQuotaUs: 400000 # 4 cores
      # 저장소 당 제한, 50개 저장소 cgroups
      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의 Garanteed Quality of Service 클래스는 우선합니다(리소스 요청이 한도와 일치). 이 설정으로 인해 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

동시성 속도 제한 구성

cgroups를 사용하는 것 외에도, 동시성 제한을 사용하여 서비스를 비정상적인 트래픽 패턴으로부터 보다 효과적으로 보호할 수 있습니다. 자세한 정보는 동시성 구성 설명서제한 모니터링 방법을 참조하세요.

Gitaly pods 격리

여러 개의 Gitaly pod을 실행할 때, 다른 노드에 스케줄링하여 장애 도메인을 분산시키세요. 이는 pod 안티 어피니티를 사용하여 강제로 시행할 수 있습니다. 예를 들어:

gitlab:
  gitaly:
    antiAffinity: hard

Pod 회전 시간 최적화

이 섹션은 유지 보수 이벤트 또는 계획되지 않은 인프라 이벤트 중에 다운 타임을 줄이기 위한 최적화 영역을 다룹니다.

지속적인 볼륨 권한

데이터 크기가 증가함에 따라(pod 초기화 및 더 많은 저장소에 대한 Git 히스토리), pod는 시작하여 트래픽을 제공하는 데 점점 더 많은 시간이 걸립니다.

지속적인 볼륨 마운트를 통한 pod 초기화 중에 파일 시스템 권한 및 소유권이 명시적으로 컨테이너 uidgid로 설정됩니다. 이 작업은 기본적으로 실행되며, 저장된 Git 데이터에는 많은 수의 작은 파일이 포함되어 있기 때문에 pod 시작 시간이 크게 느려질 수 있습니다.

이 동작은 fsGroupChangePolicy 속성을 사용하여 구성할 수 있습니다. 이 속성을 사용하여 볼륨 루트 uid 또는 gid가 컨테이너 스펙과 일치하지 않는 경우에만 작업을 수행하세요:

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