Gitaly Cluster (Praefect)
GitLab v19.4Offering: GitLab Self-Managed
요약
GitLab에서 Git 스토리지는 Gitaly 서비스를 통해 제공되며, GitLab 운영에 필수적입니다. Gitaly는 다음을 위해 클러스터 구성으로 실행할 수 있습니다. 이 구성에서는 모든 Git 리포지터리를 클러스터의 여러 Gitaly 노드에 저장할 수 있습니다.
GitLab에서 Git 스토리지는 Gitaly 서비스를 통해 제공되며, GitLab 운영에 필수적입니다. 사용자, 리포지터리, 활동이 늘어나면 다음과 같은 방법으로 Gitaly를 적절히 확장하는 것이 중요합니다.
- 리소스 고갈로 Git, Gitaly, GitLab 애플리케이션 성능이 저하되기 전에 Git이 사용할 수 있는 CPU 및 메모리 리소스를 늘립니다.
- 스토리지 한도에 도달해 쓰기 작업이 실패하기 전에 사용 가능한 스토리지를 늘립니다.
- 단일 장애점을 제거해 내결함성을 높입니다. 서비스 저하가 프로덕션으로 변경 사항을 배포하는 일을 막는다면 Git은 미션 크리티컬로 간주해야 합니다.
Gitaly는 다음을 위해 클러스터 구성으로 실행할 수 있습니다.
- Gitaly 서비스 확장.
- 내결함성 향상.
이 구성에서는 모든 Git 리포지터리를 클러스터의 여러 Gitaly 노드에 저장할 수 있습니다.
Gitaly Cluster (Praefect)를 사용하면 다음과 같은 방식으로 내결함성이 높아집니다.
- 웜 스탠바이 Gitaly 노드로 쓰기 작업을 복제합니다.
- Gitaly 노드 장애를 감지합니다.
- Git 요청을 사용 가능한 Gitaly 노드로 자동 라우팅합니다.
Gitaly Cluster (Praefect)에 대한 기술 지원은 GitLab Premium 및 Ultimate 고객으로 한정됩니다.
다음은 Gitaly Cluster (Praefect)가 제공하는 가상 스토리지 storage-1에 액세스하도록 설정된 GitLab을 보여줍니다.

이 예시에서는 다음과 같습니다.
- 리포지터리는
storage-1이라는 가상 스토리지에 저장됩니다. - Gitaly 노드 세 개가
storage-1액세스를 제공합니다.gitaly-1,gitaly-2,gitaly-3입니다. - 세 Gitaly 노드는 서로 분리된 세 개의 해시 스토리지 위치에 데이터를 공유합니다.
- 복제 인수는
3입니다. 각 리포지터리의 복사본이 3개 유지됩니다.
단일 노드 장애를 가정한 Gitaly Cluster (Praefect)의 가용성 목표는 다음과 같습니다.
-
복구 지점 목표(Recovery Point Objective, RPO): 1분 미만.
쓰기는 비동기적으로 복제됩니다. 새로 승격된 기본 노드로 복제되지 않은 쓰기는 모두 손실됩니다. 장애가 발생한 노드에서 진행 중이던 읽기 작업은 모두 종료됩니다.
강력한 일관성은 일부 상황에서 손실을 방지합니다.
-
복구 시간 목표(Recovery Time Objective, RTO): 10초 미만. 각 Praefect 노드가 매초 실행하는 상태 확인으로 중단을 감지합니다. 페일오버는 각 Praefect 노드에서 상태 확인이 연속 10회 실패해야 일어납니다.
RPO와 RTO 개선은 에픽 8903에서 제안되고 있습니다.
클러스터 전체 장애가 발생하면 재해 복구 계획을 실행해야 합니다. 이 계획은 앞에서 설명한 RPO와 RTO에 영향을 줄 수 있습니다.
Gitaly Cluster (Praefect) 배포 전#
Gitaly Cluster (Praefect)는 내결함성이라는 이점을 제공하지만, 그만큼 설치와 관리가 복잡해집니다. Gitaly Cluster (Praefect)를 배포하기 전에 다음을 참고합니다.
- 기존 알려진 문제.
- 스냅샷 백업 및 복구.
- Gitaly Cluster (Praefect)가 최선의 구성인지 확인하기 위한 구성 지침과 리포지터리 스토리지 옵션.
아직 Gitaly Cluster (Praefect)로 마이그레이션하지 않았다면 두 가지 선택지가 있습니다.
- 샤딩된 Gitaly 인스턴스.
- Gitaly Cluster (Praefect).
질문이 있으면 담당 Customer Success Manager 또는 고객 지원팀에 문의합니다.
이미 Gitaly Cluster (Praefect)를 사용하면서 문제나 제한 사항을 겪고 있다면, 복원 또는 복구에 대한 즉각적인 도움을 받기 위해 고객 지원팀에 문의합니다.
알려진 문제#
다음 표는 Gitaly Cluster (Praefect) 사용에 영향을 주는 현재 알려진 문제를 정리한 것입니다. 이 문제들의 현재 상태는 참조된 이슈와 에픽을 참고합니다.
| 문제 | 요약 | 방지 방법 |
|---|---|---|
| Gitaly Cluster (Praefect) + Geo - 실패한 동기화 재시도 문제 | Geo 보조 사이트에서 Gitaly Cluster (Praefect)를 사용하면, 동기화에 실패한 리포지터리가 Geo의 재동기화 시도에서도 계속 실패할 수 있습니다. 이 상태에서 복구하려면 지원팀의 도움을 받아 수동 단계를 실행해야 합니다. | GitLab 15.0에서 15.2까지는 Geo 기본 사이트에서 gitaly_praefect_generated_replica_paths 기능 플래그를 활성화합니다. GitLab 15.3에서는 이 기능 플래그가 기본적으로 활성화됩니다. |
| 업그레이드 후 마이그레이션이 적용되지 않아 Praefect가 데이터베이스에 데이터를 삽입할 수 없음 | 완료된 마이그레이션이 데이터베이스에 최신 상태로 반영되지 않으면 Praefect 노드가 표준 작업을 수행할 수 없습니다. | Praefect 데이터베이스가 실행 중이고 모든 마이그레이션이 완료되었는지 확인합니다. 예를 들어 다음 명령은 적용된 모든 마이그레이션 목록을 표시해야 합니다. sudo -u git -- /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml sql-migrate-status. 지원팀이 업그레이드 계획을 검토할 수 있도록 업그레이드 지원 요청을 고려합니다. |
| 실행 중인 클러스터에서 스냅샷으로 Gitaly Cluster (Praefect) 노드 복원 | Gitaly Cluster (Praefect)는 일관된 상태로 실행되므로, 뒤처진 노드 하나를 투입하면 클러스터가 그 노드의 데이터를 다른 노드의 데이터와 조정할 수 없습니다. | 백업 스냅샷에서 Gitaly Cluster (Praefect) 노드 하나만 복원하지 않습니다. 백업에서 복원해야 한다면 다음과 같이 진행합니다. 1. GitLab을 종료합니다. 2. 모든 Gitaly Cluster (Praefect) 노드를 동시에 스냅샷합니다. 3. Praefect 데이터베이스의 덤프를 만듭니다. |
| Kubernetes, Amazon ECS 또는 유사 환경에서 실행할 때의 제한 사항 | Kubernetes에서 Gitaly Cluster (Praefect)는 베타이며 일반 공개되지 않았고, Gitaly에는 알려진 제한 사항이 있습니다. 자세한 내용은 Kubernetes의 Gitaly Cluster와 에픽 6127을 참고합니다. Amazon ECS 또는 유사 환경에서는 Gitaly Cluster (Praefect)를 지원하지 않습니다. | 프로덕션 배포에는 GitLab의 참조 아키텍처를 사용합니다. |
Praefect가 쓰기를 기록하기 전에 PostReceiveHook이 호출됨 |
경쟁 조건으로 인해 쓰기가 모든 노드에 복제되기 전에 PostReceiveHook이 실행될 수 있습니다. CI/CD 파이프라인이 아직 쓰기를 받지 못한 복제본을 대상으로 하면, 이 경쟁 조건 때문에 파이프라인이 couldn't find remote ref refs/merge-requests/$iid/{head,merge} 오류로 실패합니다. 자세한 내용은 이슈 5406을 참고합니다 |
job 전체를 다시 시도하거나 fetch sources Stage만 다시 시도합니다. 자세한 내용은 job stages attempts를 참고합니다. |
| HPA 자동 스케일링으로 스토리지 이동이 조용히 실패할 수 있음 | Sidekiq Pod에 Horizontal Pod Autoscaler(HPA)를 사용하면 job 실행 중 Pod 스케일링 때문에 리포지터리 스토리지 이동이 조용히 실패할 수 있습니다. | 리포지터리 스토리지 이동을 수행하기 전에 minReplicas = maxReplicas로 설정해 HPA를 고정 복제본으로 구성하고, 마이그레이션 중 스케일링이 일어나지 않게 합니다. |
스냅샷 백업 및 복구#
Gitaly Cluster (Praefect)는 스냅샷 백업을 지원하지 않습니다. 스냅샷 백업은 Praefect 데이터베이스가 디스크 스토리지와 동기화되지 않는 문제를 일으킬 수 있습니다. 복원 과정에서 Praefect가 Gitaly 디스크 정보의 복제 메타데이터를 재구성하는 방식 때문에 공식 백업 및 복원 Rake 태스크를 사용해야 합니다.
증분 백업 방법을 사용하면 Gitaly Cluster (Praefect) 백업 속도를 높일 수 있습니다.
두 방법을 모두 사용할 수 없다면 복원 도움을 받기 위해 고객 지원팀에 문의합니다.
Geo와의 비교#
Gitaly Cluster (Praefect)와 Geo는 서로 다른 유형의 이중화를 제공합니다.
- Gitaly Cluster (Praefect)의 이중화는 데이터 스토리지에 내결함성을 제공하며 사용자에게는 보이지 않습니다.
- Geo의 이중화는 GitLab 인스턴스 전체에 대한 복제(사용자에게 보입니다)와 재해 복구를 제공합니다. Geo는 Git 데이터를 포함해 여러 데이터 유형을 복제합니다.
다음 표는 Gitaly Cluster (Praefect)와 Geo의 주요 차이점을 정리한 것입니다.
| 도구 | 노드 | 위치 | 지연 허용 | 페일오버 | 일관성 | 이중화 제공 대상 |
|---|---|---|---|---|---|---|
| Gitaly Cluster (Praefect) | 여러 개 | 단일 | 1초 미만, 이상적으로는 한 자릿수 밀리초 | 자동 | 강력함 | Git의 데이터 스토리지 |
| Geo | 여러 개 | 여러 개 | 최대 1분 | 수동 | 최종적 | GitLab 인스턴스 전체 |
자세한 내용은 다음을 참고합니다.
가상 스토리지#
가상 스토리지를 사용하면 GitLab에서 리포지터리 스토리지 하나만 두고 리포지터리 관리를 단순화할 수 있습니다.
Gitaly Cluster (Praefect)의 가상 스토리지는 대부분 직접 Gitaly 스토리지 구성을 대체할 수 있습니다. 다만 각 리포지터리를 여러 Gitaly 노드에 저장하는 데 필요한 추가 스토리지 공간을 부담해야 합니다. 직접 Gitaly 스토리지 대신 Gitaly Cluster (Praefect) 가상 스토리지를 사용하는 이점은 다음과 같습니다.
- 각 Gitaly 노드에 모든 리포지터리의 복사본이 있으므로 내결함성이 향상됩니다.
- 읽기 부하가 Gitaly 노드에 분산되므로, 샤드별 최대 부하에 맞춘 과다 프로비저닝 필요성이 줄어들어 리소스 활용도가 향상됩니다.
- 읽기 부하가 Gitaly 노드에 분산되므로 성능을 위한 수동 재분배가 필요하지 않습니다.
- 모든 Gitaly 노드가 동일하므로 관리가 더 단순합니다.
리포지터리 복제본 수는 복제 인수를 사용해 구성할 수 있습니다.
모든 리포지터리에 같은 복제 인수를 적용하는 것은 비경제적일 수 있습니다. 매우 큰 GitLab 인스턴스에 더 큰 유연성을 제공하기 위한 가변 복제 인수는 이 이슈에서 추적하고 있습니다.
표준 Gitaly 스토리지와 마찬가지로 가상 스토리지도 샤딩할 수 있습니다.
다중 가상 스토리지#
Gitaly Cluster (Praefect) 배포에서는 여러 가상 스토리지를 구성할 수 있습니다. 이를 통해 다음을 할 수 있습니다.
- 성능 특성이 다른 별도의 클러스터로 리포지터리를 구성합니다.
- 리포지터리 그룹별로 서로 다른 복제 인수를 적용합니다.
- 인프라의 각 부분을 독립적으로 확장합니다.
가상 스토리지는 GitLab 서버의 gitlab_rails['repositories_storages']에서 구성합니다. 이
해시의 각 항목이 별개의 가상 스토리지를 나타냅니다. Praefect 구성은 어느 Gitaly 노드가 각
가상 스토리지를 제공하는지 정의합니다. 서로 다른 가상 스토리지에 있는 리포지터리는 완전히 독립적이며
가상 스토리지 사이에 복제되지 않습니다.
예를 들어 다음과 같이 구성할 수 있습니다.
storage-1: 복제 인수가 3인 중요한 프로덕션 리포지터리용 가상 스토리지.storage-2: 복제 인수가 2인 덜 중요한 리포지터리용 가상 스토리지.
각 가상 스토리지에는 자체 Gitaly 노드 세트가 필요합니다.
소스 코드 보기
%%{init: { "fontFamily": "GitLab Sans" }}%%
graph TD
accTitle: Multiple virtual storages
accDescr: Example of multiple virtual storages, one with a replication factor of three and one with a replication factor of two.
GitLab[[GitLab server]]
Storage1[(storage-1<br/>Praefect cluster)]
Storage2[(storage-2<br/>Praefect cluster)]
GitLab --> Storage1
GitLab --> Storage2
Storage1 --> G1[Gitaly node 1]
Storage1 --> G2[Gitaly node 2]
Storage1 --> G3[Gitaly node 3]
Storage2 --> G4[Gitaly node 4]
Storage2 --> G5[Gitaly node 5]</code></pre></details></div>
구성 방법은 다중 가상 스토리지 구성을 참고합니다.
혼합 구성#
GitLab이 다음을 조합해 사용하도록 구성할 수 있습니다.
- 독립 실행형 Gitaly 인스턴스(직접 Gitaly 스토리지).
- Gitaly Cluster (Praefect) 가상 스토리지.
혼합 구성은 다음과 같은 경우에 사용할 수 있습니다.
- 독립 실행형 Gitaly에서 Gitaly Cluster (Praefect)로 점진적으로 마이그레이션하는 경우.
- 일부 리포지터리에만 고가용성이 필요한 경우.
- 중요한 리포지터리에만 Gitaly Cluster (Praefect)를 사용해 비용을 최적화하려는 경우.
혼합 구성에서는 각 스토리지를 GitLab에서 독립적으로 구성합니다.
- 독립 실행형 Gitaly 스토리지는 Gitaly 노드에 직접 연결됩니다.
- Gitaly Cluster (Praefect) 스토리지는 Praefect 로드 밸런서에 연결됩니다.
GitLab은 독립 실행형인지 클러스터인지와 무관하게 구성된 모든 스토리지를 동등하게 취급합니다. 새 리포지터리를
생성할 때 GitLab은 구성된 스토리지 가중치와 사용 가능한 용량을 기준으로 스토리지를 선택합니다.
Mermaid 다이어그램 (15줄)소스 코드 보기
%%{init: { "fontFamily": "GitLab Sans" }}%%
graph TD
accTitle: Mixed configuration
accDescr: Example result of mixed configuration, with a Gitaly Cluster (Praefect) and standalone Gitaly configured together.
GitLab[[GitLab server]]
Praefect[(Praefect cluster)]
Standalone1[(Standalone gitaly)]
GitLab -->|cluster storage| Praefect
GitLab -->|default storage| Standalone1
Praefect --> G1[Gitaly node 1]
Praefect --> G2[Gitaly node 2]
Praefect --> G3[Gitaly node 3]</code></pre></details></div>
자세한 내용은 다음을 참고합니다.
- 구성 예시는 혼합 구성을 참고합니다.
- 마이그레이션 지침은 기존 GitLab 인스턴스에서 TCP 사용을 참고합니다.
스토리지 레이아웃#
Warning
스토리지 레이아웃은 Gitaly Cluster (Praefect)의 내부 세부 사항이며 릴리스 사이에 안정적으로 유지된다고 보장하지 않습니다.
여기에 있는 정보는 참고와 디버깅에 도움을 주기 위한 것입니다. 디스크에서 리포지터리를 직접
변경하는 것은 지원하지 않으며, 오류가 발생하거나 변경 사항이 덮어써질 수 있습니다.
Gitaly Cluster (Praefect) 가상 스토리지는 단일 스토리지처럼 보이는 추상화를 제공하지만 실제로는 여러
물리적 스토리지로 구성됩니다. Gitaly Cluster (Praefect)는 모든 작업을 각 물리적 스토리지에 복제해야 합니다.
작업은 일부 물리적 스토리지에서는 성공하고 다른 스토리지에서는 실패할 수 있습니다.
부분적으로 적용된 작업은 다른 작업에 문제를 일으키고 시스템을 복구할 수 없는 상태로 남길 수 있습니다.
이런 유형의 문제를 피하려면 각 작업이 완전히 적용되거나 전혀 적용되지 않아야 합니다. 이러한 작업의 속성을
원자성이라고 합니다.
GitLab은 리포지터리 스토리지의 스토리지 레이아웃을 제어합니다. GitLab은 리포지터리를 생성, 삭제,
이동할 위치를 리포지터리 스토리지에 지시합니다. 이러한 작업이 여러 물리적 스토리지에 적용될 때 원자성 문제가 발생합니다.
예를 들면 다음과 같습니다.
- GitLab이 복제본 중 하나를 사용할 수 없는 상태에서 리포지터리를 삭제합니다.
- 이후 GitLab이 그 리포지터리를 다시 생성합니다.
그 결과 삭제 시점에 사용할 수 없었던 오래된 복제본이 충돌을 일으켜 리포지터리 재생성을
막을 수 있습니다.
이러한 원자성 문제는 과거에 다음 영역에서 여러 문제를 일으켰습니다.
- Gitaly Cluster (Praefect)를 사용한 보조 사이트로의 Geo 동기화.
- 백업 복원.
- 리포지터리 스토리지 사이의 리포지터리 이동.
Gitaly Cluster (Praefect)는 부분적으로 적용된 작업 때문에 발생할 수 있는 충돌을 방지하는 특수한 레이아웃으로
디스크에 리포지터리를 저장해 이러한 작업의 원자성을 보장합니다.
클라이언트 생성 복제본 경로#
리포지터리는 Gitaly 클라이언트가 정한 상대 경로에 스토리지에 저장됩니다. 이러한 경로는
@cluster 접두사로 시작하지 않는다는 점으로 식별할 수 있습니다. 상대 경로는
해시 스토리지 스키마를 따릅니다.
Praefect 생성 복제본 경로#
Gitaly Cluster (Praefect)가 리포지터리를 생성할 때 고유하고 영구적인 ID인 리포지터리 ID 를 그 리포지터리에 할당합니다. 리포지터리 ID는
Gitaly Cluster (Praefect) 내부용이며 GitLab의 다른 ID와는 관련이 없습니다. 리포지터리가 Gitaly Cluster (Praefect)에서 제거된 뒤 다시
추가되면 새 리포지터리 ID가 할당되고, Gitaly Cluster (Praefect) 관점에서는 다른 리포지터리가 됩니다. 리포지터리 ID 시퀀스는
항상 증가하지만 시퀀스에 빈 구간이 생길 수 있습니다.
리포지터리 ID는 클러스터의 각 리포지터리에 대해 복제본 경로 라는 고유한 스토리지 경로를 도출하는 데 사용됩니다. 한 리포지터리의
복제본은 모두 스토리지의 같은 복제본 경로에 저장됩니다. 복제본 경로는 상대 경로 와 구별됩니다.
- 상대 경로는 Gitaly 클라이언트가 가상 스토리지와 함께 리포지터리를 식별하는 데 사용하는 이름이며, 그 클라이언트에게만 고유합니다.
- 복제본 경로는 물리적 스토리지에 있는 실제 물리 경로입니다.
Praefect는 클라이언트 요청을 처리할 때 RPC 안의 리포지터리를 가상 식별자 (virtual storage, relative path)에서 물리 리포지터리
식별자 (storage, replica_path)로 변환합니다.
복제본 경로의 형식은 다음과 같습니다.
- 오브젝트 풀은
@cluster/pools/<xx>/<xx>/<repository ID> 입니다. 오브젝트 풀은 다른 리포지터리와 다른 디렉터리에 저장됩니다.
하우스키핑 과정에서 정리되지 않도록 Gitaly가 오브젝트 풀을 식별할 수 있어야 합니다. 오브젝트 풀을 정리하면 연결된
리포지터리에서 데이터 손실이 발생할 수 있습니다.
- 그 외 리포지터리는
@cluster/repositories/<xx>/<xx>/<repository ID> 입니다
예를 들면 @cluster/repositories/6f/96/54771 입니다.
복제본 경로의 마지막 구성 요소 54771이 리포지터리 ID입니다. 이 값으로 디스크에서 리포지터리를 식별할 수 있습니다.
<xx>/<xx>는 리포지터리 ID 문자열 표현의 SHA256 해시 중 앞 네 자리 16진수입니다. 이
자릿수는 일부 파일 시스템에서 문제를 일으킬 수 있는 지나치게 큰 디렉터리를 피하기 위해 리포지터리를
하위 디렉터리에 고르게 분산하는 데 사용됩니다. 이 예시에서 54771은
6f960ab01689464e768366d3315b3d3b2c28f38761a58a70110554eb04d582f7로 해싱되므로 앞 네 자리는 6f와 96입니다.
디스크에서 리포지터리 식별#
praefect metadata 하위 명령으로 다음을 수행합니다.
- 메타데이터 저장소에서 리포지터리의 가상 스토리지와 상대 경로를 조회합니다. 해시 스토리지 경로를 확보한 뒤에는 Rails
콘솔로 프로젝트 경로를 조회할 수 있습니다.
- 다음 중 하나로 클러스터에서 리포지터리가 저장된 위치를 찾습니다.
- 가상 스토리지와 상대 경로.
- 리포지터리 ID.
디스크의 리포지터리에는 Git 구성 파일에 프로젝트 경로도 들어 있습니다. 리포지터리의 메타데이터가 삭제된
경우에도 이 구성 파일로 프로젝트 경로를 판단할 수 있습니다.
해시 스토리지 문서의 지침을 따릅니다.
작업의 원자성#
Gitaly Cluster (Praefect)는 PostgreSQL 메타데이터 저장소와 스토리지 레이아웃을 사용해 리포지터리 생성,
삭제, 이동 작업의 원자성을 보장합니다. 디스크 작업은 여러 스토리지에 걸쳐 원자적으로 적용할 수 없습니다. 그러나 PostgreSQL은
메타데이터 작업의 원자성을 보장합니다. Gitaly Cluster (Praefect)는 실패하는 작업이 항상 메타데이터를 일관된 상태로
남기도록 작업을 모델링합니다. 성공한 작업 뒤에도 디스크에 오래된 상태가 남아 있을 수 있습니다. 이는 예상된
상황이며 남은 상태가 이후 작업을 방해하지는 않지만, 정리가 수행될 때까지 디스크 공간을 불필요하게
사용할 수 있습니다.
스토리지에 남은 리포지터리를 정리하는 백그라운드 크롤러
작업이 진행되고 있습니다.
리포지터리 생성#
리포지터리를 생성할 때 Praefect는 다음을 수행합니다.
- PostgreSQL에서 리포지터리 ID를 예약합니다. 이 과정은 원자적이며 두 생성 작업이 같은 ID를 받지 않습니다.
- 리포지터리 ID에서 도출한 복제본 경로에 Gitaly 스토리지의 복제본을 생성합니다.
- 디스크에 리포지터리가 성공적으로 생성된 뒤 메타데이터 레코드를 생성합니다.
동시에 두 작업이 같은 리포지터리를 생성하더라도 스토리지의 서로 다른 디렉터리에 저장되므로 충돌하지
않습니다. 먼저 완료된 작업이 메타데이터 레코드를 생성하고, 다른 작업은 "already exists" 오류로 실패합니다.
실패한 생성 작업은 스토리지에 리포지터리를 남깁니다. 스토리지에 남은 리포지터리를 정리하는
백그라운드 크롤러 작업이 진행되고 있습니다.
리포지터리 ID는 PostgreSQL의 repositories_repository_id_seq에서 생성됩니다. 앞의 예시에서 실패한 작업은
리포지터리를 생성하지 못한 채 리포지터리 ID 하나를 가져갔습니다. 실패한 리포지터리 생성이 리포지터리 ID에 빈 구간을 남기는 것은 예상된 결과입니다.
리포지터리 삭제#
리포지터리는 메타데이터 레코드를 제거해 삭제합니다. 메타데이터 레코드가 삭제되는 즉시 리포지터리는 논리적으로 존재하지 않게 됩니다.
PostgreSQL이 제거의 원자성을 보장하며, 동시에 실행된 삭제는 "not found" 오류로 실패합니다. 메타데이터 레코드를 성공적으로
삭제한 뒤 Praefect는 스토리지에서 복제본 제거를 시도합니다. 이 작업은 실패해 스토리지에 잔여 상태를 남길 수 있습니다.
잔여 상태는 이후에 정리됩니다.
리포지터리 이동#
Gitaly와 달리 Gitaly Cluster (Praefect)는 스토리지에서 리포지터리를 이동하지 않고, 메타데이터 저장소에 있는
리포지터리의 상대 경로를 갱신해 가상으로만 이동합니다.
구성 요소#
Gitaly Cluster (Praefect)는 여러 구성 요소로 이루어집니다.
- 요청을 분산하고 Praefect 노드에 내결함성 있는 액세스를 제공하는
로드 밸런서.
- 클러스터를 관리하고 Gitaly 노드로 요청을 라우팅하는 Praefect 노드.
- 클러스터 메타데이터를 유지하는 PostgreSQL 데이터베이스와, Praefect의 데이터베이스
연결을 풀링하는 데 권장되는 PgBouncer.
- 리포지터리 스토리지와 Git 액세스를 제공하는 Gitaly 노드.
아키텍처#
Praefect는 Gitaly의 라우터이자 트랜잭션 관리자이며, Gitaly Cluster (Praefect)를 실행하는 데
필요한 구성 요소입니다.

자세한 내용은 Gitaly 고가용성(HA) 설계를 참고합니다.
기능#
Gitaly Cluster (Praefect)는 다음 기능을 제공합니다.
- Gitaly 노드 사이의 분산 읽기.
- 보조 복제본의 강력한 일관성.
- 중복성을 높이기 위한 리포지터리 복제 인수.
- 기본 Gitaly 노드에서 보조 Gitaly 노드로의
자동 페일오버.
- 복제 큐가 비어 있지 않은 경우 발생할 수 있는 데이터 손실 보고.
제안된 개선 사항은 에픽 1489에서 확인할 수 있으며,
읽기의 수평 분산도 여기에 포함됩니다.
분산 읽기#
Gitaly Cluster (Praefect)는 가상 스토리지에 구성된 Gitaly 노드 전체에 읽기 작업을
분산할 수 있습니다.
ACCESSOR 옵션이 표시된 모든 RPC는 최신 상태이고 정상인 Gitaly 노드로 리디렉션됩니다.
예를 들어 GetBlob이 그렇습니다.
여기에서 "최신 상태"는 다음을 뜻합니다.
- 이 Gitaly 노드에 예약된 복제 작업이 없습니다.
- 마지막 복제 작업이 완료 상태입니다.
다음 경우에는 기본 노드가 요청을 처리하도록 선택됩니다.
- 최신 상태인 노드가 없습니다.
- 노드 선택 중 다른 오류가 발생합니다.
멀티 기가바이트 모노레포처럼 크고 변경이 잦은 리포지터리에서는 Praefect가 보조 노드로 복제하는 속도보다 변경이 더 빨리 들어오면 기본 노드가 대부분 또는 모든 요청을 처리할 수 있습니다.
이 경우 CI/CD job과 그 외 리포지터리 트래픽이 기본 노드의 용량에 병목이 생깁니다.
Prometheus를 사용해 읽기 분산을 모니터링할 수 있습니다.
강력한 일관성#
Gitaly Cluster (Praefect)는 정상이고 최신 상태인 모든 복제본에 변경 사항을 동기적으로 기록해 강력한 일관성을 제공합니다. 트랜잭션 시점에
복제본이 오래되었거나 정상이 아니면 그 복제본에는 비동기적으로 복제됩니다.
강력한 일관성이 기본 복제 방식입니다. 일부 작업은 여전히 강력한 일관성 대신 복제 job(최종 일관성)을
사용합니다. 자세한 내용은
강력한 일관성 에픽을 참고합니다.
강력한 일관성을 사용할 수 없으면 Gitaly Cluster (Praefect)는 최종 일관성을 보장합니다. 이 경우
Gitaly Cluster (Praefect)는 기본 Gitaly 노드에 쓰기가 발생한 뒤 모든 쓰기를 보조 Gitaly 노드로 복제합니다.
강력한 일관성 모니터링에 대한 자세한 내용은
Gitaly Cluster (Praefect) 모니터링을 참고합니다.
복제 인수#
복제 인수는 Gitaly Cluster (Praefect)가 특정 리포지터리에 대해 유지하는 복사본 수입니다. 복제 인수가
높으면 다음과 같습니다.
- 중복성과 읽기 워크로드 분산이 더 좋아집니다.
- 스토리지 비용이 더 높아집니다.
기본적으로 Gitaly Cluster (Praefect)는 가상 스토리지의 모든 스토리지로 리포지터리를
복제합니다.
구성 정보는 복제 인수 구성을 참고합니다.
Gitaly Cluster (Praefect) 업그레이드#
Gitaly Cluster (Praefect)를 업그레이드하려면
무중단 업그레이드 문서를 따릅니다.
Gitaly Cluster (Praefect)를 이전 버전으로 롤백#
Gitaly Cluster (Praefect)를 이전 버전으로 롤백해야 한다면 일부 Praefect 데이터베이스 마이그레이션을 되돌려야 할 수 있습니다.
Praefect 노드가 여러 개라고 가정할 때 Gitaly Cluster (Praefect)를 롤백하는 방법은 다음과 같습니다.
-
모든 Praefect 노드에서 Praefect 서비스를 중지합니다.
gitlab-ctl stop praefect
-
Praefect 노드 중 하나에서 GitLab 패키지를 이전 버전으로 롤백합니다.
-
롤백한 노드에서 Praefect 마이그레이션 상태를 확인합니다.
sudo -u git -- /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml sql-migrate-status
-
APPLIED 칼럼에 unknown migration 이 표시된 마이그레이션 수를 셉니다.
-
롤백하지 않은 Praefect 노드에서 되돌릴 마이그레이션을 검증하기 위해 롤백을 시험 실행합니다.
은 롤백한 노드가 보고한 unknown 마이그레이션 수입니다.
sudo -u git -- /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml sql-migrate
-
결과가 올바르면 같은 명령을 -f 옵션과 함께 실행해 마이그레이션을 되돌립니다.
sudo -u git -- /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml sql-migrate -f
-
나머지 Praefect 노드에서 GitLab 패키지를 롤백하고 Praefect 서비스를 다시 시작합니다.
gitlab-ctl start praefect
Gitaly Cluster (Praefect)로 마이그레이션#
Warning
Gitaly Cluster (Praefect)에는 몇 가지 알려진 문제가 있습니다. 계속하기 전에 다음 정보를 검토합니다.
Gitaly Cluster (Praefect)로 마이그레이션하기 전에 다음을 수행합니다.
- Gitaly Cluster (Praefect) 배포 전을 검토합니다.
- 개선 사항과 버그 수정을 활용하기 위해 가능한 최신 GitLab 버전으로 업그레이드합니다.
Gitaly Cluster (Praefect)로 마이그레이션하는 방법은 다음과 같습니다.
- 필요한 스토리지를 생성합니다.
리포지터리 스토리지 권장 사항을 참고합니다.
- Gitaly Cluster (Praefect)를 생성하고 구성합니다.
- 아직 그렇게 구성되지 않았다면 기존 Gitaly 인스턴스가 TCP를 사용하도록 구성합니다.
- 리포지터리를 이동합니다. Gitaly Cluster (Praefect)로 마이그레이션하려면 Gitaly Cluster (Praefect) 밖에
저장된 기존 리포지터리를 이동해야 합니다. 자동 마이그레이션은 없지만 GitLab API로 이동 작업을
예약할 수 있습니다.
default 리포지터리 스토리지를 사용하지 않더라도 이 스토리지는 반드시 구성해야 합니다.
이 제한 사항에 대한 자세한 내용을 참고합니다.
Kubernetes의 Gitaly 차트에서 마이그레이션하려면
전용 마이그레이션 지침을 따릅니다.
Gitaly Cluster (Praefect)에서 마이그레이션#
Gitaly Cluster (Praefect)의 제한 사항과 장단점이 환경에 맞지 않는다면, Gitaly Cluster (Praefect)에서
샤딩된 Gitaly 인스턴스로 마이그레이션할 수 있습니다.
- 새 Gitaly 서버를 생성하고 구성합니다.
- 새로 만든 스토리지로 리포지터리를 이동합니다. 샤드별 또는
그룹별로 이동할 수 있으므로 여러 Gitaly 서버에 분산할 수 있습니다.
Kubernetes의 Gitaly Cluster#
Gitaly Cluster (Praefect)는 인스턴스 전체에 데이터를 복제해 데이터와 서비스의 고가용성 문제를 해결합니다.
다만 Kubernetes의 Gitaly Cluster는 현재 베타입니다. 일반 공개로 전환하기 전에 이 알려진 문제와 관련 항목을 해결하고 있습니다.