Kubernetes에서 Gitaly
Status: Experiment
Kubernetes에서 Gitaly를 실행하는 것은 가용성의 트레이드 오프가 있으므로, 프로덕션 환경을 계획할 때 이러한 트레이드 오프를 고려하고 그에 맞게 기대치를 설정하세요.
이 문서는 기존 한계를 최소화하고 계획하는 방법에 대한 안내를 제공합니다.
Gitaly Cluster(Praefect)는 지원되지 않습니다. Kubernetes에서 Gitaly를 실행하는 방법에 대한 자세한 내용은 epic 6127를 참조하세요.
맥락
설계상 Gitaly(비 클러스터)는 단일 실패 지점 서비스(SPoF)입니다. 데이터는 단일 인스턴스에서 소싱되고 제공됩니다.
Kubernetes의 경우, StatefulSet 포드가 회전할 때(예: 업그레이드, 노드 유지 관리 또는 퇴거 중) 회전으로 인해 포드 또는 인스턴스에서 제공하는 데이터에 대한 서비스 중단이 발생합니다.
Cloud Native Hybrid 설정(Gitaly VM)에서는 Linux 패키지(Omnibus)가 문제를 다음과 같이 해결합니다:
- Gitaly 바이너리를 제자리에서 업그레이드합니다.
- 정상적인 재로드를 수행합니다.
이 접근 방식은 컨테이너나 포드가 완전히 종료되고 새로운 컨테이너나 포드로 시작해야 하는 컨테이너 기반 수명 주기에는 적합하지 않습니다.
Gitaly Cluster(Praefect)는 인스턴스 간에 데이터를 복제하여 데이터 및 서비스 고가용성 측면을 해결합니다. 그러나 Gitaly Cluster는 기존 문제 및 설계 제약으로 인해 Kubernetes에서 실행하는 데 적합하지 않습니다.
Cloud Native 배포를 지원하기 위해 Gitaly(비 클러스터)가 유일한 선택입니다.
적절한 Kubernetes 및 Gitaly 기능 및 구성을 활용하면 서비스 중단을 최소화하고 우수한 사용자 경험을 제공할 수 있습니다.
요구 사항
이 페이지의 정보는 다음을 전제로 합니다:
- Kubernetes 버전이
1.29
이상입니다. - Kubernetes 노드
runc
버전이1.1.9
이상입니다. - Kubernetes 노드 cgroup v2. 네이티브, 하이브리드 v1 모드는 지원되지 않습니다. 오직
systemd
스타일 cgroup 구조만 지원됩니다(Kubernetes 기본값). - 포드가 노드의 마운트 포인트
/sys/fs/cgroup
에 액세스해야 합니다. - 포드 init 컨테이너(
init-cgroups
)가/sys/fs/cgroup
에서root
사용자 파일 시스템 권한에 액세스해야 합니다. Gitaly 컨테이너에 포드 cgroup을 위임하는 데 사용됩니다
(사용자git
, UID1000
).
안내
Kubernetes에서 Gitaly를 실행할 때 다음을 수행해야 합니다:
포드 중단 문제 해결
포드는 여러 가지 이유로 회전할 수 있습니다. 서비스 수명 주기를 이해하고 계획하는 것이 중단을 최소화하는 데 도움이 됩니다.
예를 들어, Gitaly의 경우 Kubernetes StatefulSet
는 Helm Chart 업그레이드(레이블 또는 이미지 태그) 또는 포드 리소스 요청 또는 한계 업데이트 중에 발생할 수 있는 spec.template
객체 변경 시 회전합니다.
이 섹션에서는 일반적인 포드 중단 사례와 이를 해결하는 방법에 중점을 둡니다.
유지 관리 창 일정
서비스의 가용성이 높지 않기 때문에 특정 작업은 짧은 서비스 중단을 초래할 수 있습니다. 유지 관리 창 일정을 설정하면 잠재적인 서비스 중단을 알리고 기대치를 설정하는 데 도움이 됩니다. 다음 용도로 유지 관리 창을 사용해야 합니다:
- GitLab 헬름 차트 업그레이드 및 재구성.
- Gitaly 구성 변경.
- 쿠버네티스 노드 유지 관리 창. 예를 들어, 업그레이드 및 패치. Gitaly를 전용 노드 풀로 격리하는 것이 도움이 될 수 있습니다.
PriorityClass
사용
PriorityClass를 사용하여 Gitaly 포드에 다른 포드보다 높은 우선 순위를 지정하여 노드 포화 압력, 추방 우선 순위 및 스케줄링 대기 시간을 개선하세요:
-
우선 순위 클래스를 생성하세요:
apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata: name: gitlab-gitaly value: 1000000 globalDefault: false description: "GitLab Gitaly priority class"
-
Gitaly 포드에 우선 순위 클래스를 지정하세요:
gitlab: gitaly: priorityClassName: gitlab-gitaly
추방 방지를 위한 노드 자동 확장 신호
노드 자동 확장 도구는 필요에 따라 쿠버네티스 노드를 추가 및 제거하여 포드를 스케줄링하고 비용을 최적화합니다.
다운스케일링 이벤트 중에 Gitaly 포드는 리소스 사용 최적화를 위해 추방될 수 있습니다. 주석은 일반적으로 이 동작을 제어하고 작업 부하를 제외하는 데 사용됩니다. 예를 들어, 클러스터 오토스케일러와 함께:
gitlab:
gitaly:
annotations:
cluster-autoscaler.kubernetes.io/safe-to-evict: "false"
리소스 경합 및 포화 해결
Gitaly 서비스의 리소스 사용은 Git 작업의 예측 불가능한 특성 때문에 예측할 수 없습니다. 모든 리포지토리가 동일하지 않으며 크기는 성능과 리소스 사용에 큰 영향을 미칩니다, 특히 모노레포에 대해서는 더욱 그렇습니다.
쿠버네티스에서 통제되지 않은 리소스 사용은 메모리 부족(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 코어
# 리포지토리별 제한, 50 리포지토리 cgroups
repositories:
count: 50
memoryBytes: 7516192768 # 7GiB
cpuShares: 512
cpuQuotaUs: 200000 # 2 코어
자세한 내용은 Gitaly 구성 문서를 참조하세요.
적절한 크기의 Pod 리소스
Gitaly pod의 크기를 정하는 것은 중요하며, 참조 아키텍처는 시작점으로서 몇 가지 지침을 제공합니다. 그러나 서로 다른 리포지토리와 사용 패턴은 다양한 정도의 리소스를 소비합니다.
리소스 사용량을 모니터링하고 시간에 따라 조정해야 합니다.
메모리는 Kubernetes에서 가장 민감한 리소스입니다. 메모리가 부족해지면 pod가 종료될 수 있습니다.
Git 호출을 cgroup으로 격리하기는 리포지토리 작업에 대한 리소스 사용을 제한하는 데 도움이 되지만, Gitaly 서비스 자체는 포함되지 않습니다.
cgroup 쿼터에 대한 이전 권장 사항에 따라, 전체 Git cgroup 메모리 할당과 pod 메모리 요청 사이에 여유를 두어 안전성을 향상시키세요.
Guaranteed
서비스 품질 클래스의 pod가 선호됩니다 (리소스 요청이 제한과 일치). 이 설정을 사용하면 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
동시성 속도 제한 구성
cgroup을 사용하는 것 외에도, 동시성 제한을 사용하여 서비스가 비정상적인 트래픽 패턴으로부터 보호받도록 추가적인 도움을 줄 수 있습니다. 추가 정보는 동시성 구성 문서와 제한 사항 모니터링 방법을 참조하세요.
Gitaly pods 격리
여러 Gitaly pod를 실행할 때, 실패 도메인을 분산시키기 위해 서로 다른 노드에 스케줄해야 합니다. 이는 pod 안티 친화성을 사용하여 강제할 수 있습니다. 예를 들어:
gitlab:
gitaly:
antiAffinity: hard
pod 회전 시간 최적화
이 섹션에서는 유지 관리 이벤트 또는 계획되지 않은 인프라 이벤트 동안 다운타임을 줄이기 위해 pod가 트래픽을 제공하는 시작 시간을 단축하는 최적화 영역을 다룹니다.
영구 볼륨 권한
데이터 크기가 커짐에 따라 (Git 역사 및 기타 리포지토리), pod는 시작되고 준비되는 데 점점 더 많은 시간이 걸립니다.
pod 초기화 동안, 영구 볼륨 마운트의 일환으로 파일 시스템 권한 및 소유권이 컨테이너 uid
및 gid
로 명시적으로 설정됩니다.
이 작업은 기본적으로 실행되며, 저장된 Git 데이터에 많은 작은 파일이 포함되어 있기 때문에 pod 시작 시간을 상당히 지연시킬 수 있습니다.
이 동작은
fsGroupChangePolicy
속성으로 구성할 수 있습니다. 이 속성을 사용하여 볼륨 루트 uid
또는 gid
가 컨테이너 사양과 불일치할 경우에만 작업을 수행하세요:
gitlab:
gitaly:
securityContext:
fsGroupChangePolicy: OnRootMismatch
헬스 프로브
Gitaly pod는 준비 프로브가 성공한 후에 트래픽을 제공하기 시작합니다. 기본 프로브 시간은 대부분의 사용 사례를 커버하기 위해 보수적입니다.
readinessProbe
의 initialDelaySeconds
속성을 줄이면 프로브가 더 빨리 시작되어 pod 준비가 가속화됩니다. 예를 들어:
gitlab:
gitaly:
statefulset:
readinessProbe:
initialDelaySeconds: 2
periodSeconds: 10
timeoutSeconds: 3
successThreshold: 1
failureThreshold: 3
Gitaly 우아한 종료 타임아웃
기본적으로 Gitaly 종료 시, 진행 중인 요청이 완료될 수 있도록 1분의 타임아웃을 부여합니다.
처음에는 유익해 보이지만, 이 타임아웃은:
- 파드 회전 속도를 저하시킵니다.
- 종료 프로세스 중 요청을 거부하여 가용성을 감소시킵니다.
컨테이너 기반 배포에서 더 나은 접근 방식은 클라이언트 측 재시도 로직에 의존하는 것입니다. gracefulRestartTimeout
필드를 사용하여 타임아웃을 재구성할 수 있습니다.
예를 들어, 1초의 우아한 타임아웃을 부여하려면:
gitlab:
gitaly:
gracefulRestartTimeout: 1