This page contains information related to upcoming products, features, and functionality. It is important to note that the information presented is for informational purposes only. Please do not rely on this information for purchasing or planning purposes. The development, release, and timing of any products, features, or functionality may be subject to change or delay and remain at the sole discretion of GitLab Inc.
Status Authors Coach DRIs Owning Stage Created
accepted -

Cells: HTTP 라우팅 서비스

이 문서는 Cells에서 사용되는 라우팅 서비스의 설계 목표와 아키텍처에 대해 설명합니다. 라우팅 서비스가 아키텍처에 어떻게 맞는지 더 잘 이해하려면 인프라 아키텍처를 살펴보세요.

목표

라우팅 레이어는 모든 Cells가 별도의 도메인으로 이동하는 대신(예: gitlab.com) 한 도메인(예: gitlab.com)에서 모든 Cells가 제공되는 일관된 사용자 경험을 제공하는 것을 목표로 합니다.

사용자는 https://gitlab.com을 사용하여 Cell로 활성화된 GitLab에 액세스할 수 있을 것입니다. URL 액세스에 따라 해당 정보를 제공할 수 있는 올바른 Cell로 투명하게 프록시될 것입니다. 예를 들어,

  • https://gitlab.com/users/sign_in로 향하는 모든 요청은 모든 Cells로 임의로 분산됩니다.
  • https://gitlab.com/gitlab-org/gitlab/-/tree/master로 향하는 모든 요청은 항상 특정 Cell 5로 이동합니다.
  • https://gitlab.com/my-username/my-project로 향하는 모든 요청은 항상 특정 Cell 1로 이동합니다.
  1. 기술.

    라우팅 서비스가 작성된 기술을 결정합니다. 선택은 성능이 가장 좋은 언어 및 라우팅 레이어의 예상된 방식 및 배포 위치에 따라 달라집니다. 서비스를 멀티 클라우드로 만들어야 하는 경우 CDN 제공업체에 배포해야 할 수도 있습니다. 그럼, 서비스는 CDN 제공업체와 호환되는 기술을 사용하여 작성해야 할 수도 있습니다.

    ADR 001

  2. Cell 검색.

    라우팅 서비스는 모든 Cells의 상태를 검색하고 모니터링할 수 있어야 합니다.

  3. 단일 도메인으로 여러 Cells와 상호 작용 가능.

    라우팅 서비스는 액세스하는 리소스에 따라 모든 요청을 Cells로 지능적으로 라우팅할 것입니다.

  4. 라우터 엔드포인트 분류.

    상태가 없는 라우팅 서비스는 하나의 Cell에서 엔드포인트에 대한 정보를 가져와 캐시할 것입니다. 우리는 들어오는 요청(지문)을 정확하게 설명하는 프로토콜을 구현해야 하므로 그것이 하나의 Cell에 의해 분류되고 그 결과를 캐싱할 수 있도록 할 수 있는 프로토콜도 구현해야 합니다. 또한 부정적인 캐시와 캐시 삭제 메커니즘을 구현해야 합니다.

  5. GraphQL 및 다른 모호한 엔드포인트.

    대부분의 엔드포인트는 조직입니다. (예: /api/graphql)처럼 엔드포인트가 모호하거나 샤딩 키가 페이로드에 심층에 저장된 있는 경우에는 대응하려면 어떻게 할지 결정해야 합니다.

  6. 작은.

    라우팅 서비스는 구성 및 규칙 기반으로 구축되며 어떠한 비즈니스 로직도 구현하지 않습니다. 초기 단계의 프로젝트 소스 코드의 최대 크기는 테스트를 포함하여 1,000줄입니다. 엄격한 한계는 라우팅 서비스가 특별한 로직을 갖지 않게 하고, 몇 일 안에 어떠한 기술로도 재작성될 수 있도록 하는 것입니다.

요구 사항

요구 사항 설명 우선 순위
검색 모든 Cells의 상태를 검색하고 모니터링할 수 있어야 함 높음
보안 인가된 Cells만 라우팅될 수 있어야 함 높음
단일 도메인 예: GitLab.com 높음
캐싱 성능을 위해 라우팅 정보를 캐시할 수 있어야 함 높음
낮은 지연 시간 50ms 이상의 증가된 지연 시간 높음
경로 기반 경로를 기반으로 라우팅 결정을 할 수 있어야 함 높음
복잡성 라우팅 서비스는 구성 및 작고 규칙 기반으로 해야 함 높음
롤링 라우팅 서비스는 혼합 버전이 실행 중인 Cells와 작동해야 함 높음
피처 플래그 기능을 켜고 끌 수 있으며 일부 릴리스할 수 있어야 함 높음
점진적 롤아웃 변경을 천천히 배포할 수 있어야 함 중간
상태 없음 데이터베이스가 필요하지 않으며 모든 라우팅 정보를 Cells가 제공함 중간
시크릿 기반 시크릿을 기반으로 라우팅 결정을 할 수 있어야 함 (예: JWT) 중간
관찰 가능성 기존의 관찰 도구를 사용할 수 있어야 함 낮음
셀프매니지드 셀프매니지드에서 최종적으로 사용할 수 있어야 함 낮음
지역별 다른 지역으로 요청을 라우팅할 수 있어야 함 낮음

낮은 지연 시간

라우팅 서비스의 목표 지연 시간은 50ms 미만이어야 합니다.

urgent: high 요청을 살펴보면 p50 수준에서 많은 여유가 없습니다. 추가 50 _ms_를 더하면 p95 수준에서도 SLO에 부합할 수 있습니다.

응용프로그램에는 3가지 기본 진입점이 있습니다. web, api, git. 각 서비스는 apdex 표준을 사용하여 지연에 기초한 서비스 수준 지표(SLI)가 할당됩니다. 이러한 SLI들을 위한 대응되는 서비스 수준 목표(SLO)는 대량의 요청에 대해 낮은 지연 시간이 필요합니다. 이 라우팅 레이어를 이러한 서비스의 앞단에 추가하더라도 SLI에 영향을 미치지 않도록 하는 것이 중요합니다. 라우팅 레이어는 이러한 서비스들의 프록시이며, 우리는 요청 흐름 전체(Edge 네트워크 및 로드 밸런서와 같은 컴포넌트 포함)에 대한 포괄적인 SLI 모니터링 시스템이 없기 때문에 web, git, api에 대한 SLI를 대상으로 사용합니다.

우리가 사용하는 주요 SLI는 rails requests입니다. 요청 긴급성에 따라 다양한 satisfied 목표(ms)가 있습니다:

긴급성 시간 (ms)
:high 250 ms
:medium 500 ms
:default 1000 ms
:low 5000 ms

분석

우리가 가지고 있는 여유를 계산하는 방법은 다음과 같습니다:

\mathrm{여유}\ {ms} = \mathrm{만족}\ {ms} - \mathrm{지속}\ {ms}

web:

목표 지속 시간 백분위수 여유
5000 ms p99 4000 ms
5000 ms p95 4500 ms
5000 ms p90 4600 ms
5000 ms p50 4900 ms
1000 ms p99 500 ms
1000 ms p95 740 ms
1000 ms p90 840 ms
1000 ms p50 900 ms
500 ms p99 0 ms
500 ms p95 60 ms
500 ms p90 100 ms
500 ms p50 400 ms
250 ms p99 140 ms
250 ms p95 170 ms
250 ms p90 180 ms
250 ms p50 200 ms

분석은 https://gitlab.com/gitlab-org/gitlab/-/issues/432934#note_1667993089에서 수행되었습니다.

api:

목표 지속 시간 백분위수 여유
5000 ms p99 3500 ms
5000 ms p95 4300 ms
5000 ms p90 4600 ms
5000 ms p50 4900 ms
1000 ms p99 440 ms
1000 ms p95 750 ms
1000 ms p90 830 ms
1000 ms p50 950 ms
500 ms p99 450 ms
500 ms p95 480 ms
500 ms p90 490 ms
500 ms p50 490 ms
250 ms p99 90 ms
250 ms p95 170 ms
250 ms p90 210 ms
250 ms p50 230 ms

분석은 https://gitlab.com/gitlab-org/gitlab/-/issues/432934#note_1669995479에서 수행되었습니다.

git:

목표 지속 시간 백분위수 여유
5000 ms p99 3760 ms
5000 ms p95 4280 ms
5000 ms p90 4430 ms
5000 ms p50 4900 ms
1000 ms p99 500 ms
1000 ms p95 750 ms
1000 ms p90 800 ms
1000 ms p50 900 ms
500 ms p99 280 ms
500 ms p95 370 ms
500 ms p90 400 ms
500 ms p50 430 ms
250 ms p99 200 ms
250 ms p95 230 ms
250 ms p90 240 ms
250 ms p50 240 ms

분석은 https://gitlab.com/gitlab-org/gitlab/-/issues/432934#note_1671385680에서 수행되었습니다.

비목표

아직 정의되지 않았습니다.

제안

Routing Service는 다음 설계 지침을 구현합니다.

  1. 간단함:
    • 라우팅 서비스는 요청을 버퍼에 저장하지 않습니다.
    • 라우팅 서비스는 요청 헤더를 기반으로 단일 셀로만 프록시를 할 수 있습니다.
  2. 상태가 없음:
    • 라우팅 서비스에 영구 리포지터리가 없습니다.
    • 라우팅 서비스는 다중 수준 캐시를 사용합니다: 인메모리, 외부 공유 캐시.
  3. 제로 트러스트:
    • 라우팅 서비스는 프록시되는 각 요청에 서명합니다.
    • 신뢰는 JWT 토큰 또는 상호 인증 체계를 사용하여 확립됩니다.
    • 셀은 제로 트러스트 모델을 따르는 한 공개 인터넷을 통해 사용할 수 있습니다.
  4. 구성 기반:
    • 라우팅 서비스는 정적 항목 디렉터리으로 구성됩니다.
    • 라우팅 서비스 구성은 서비스 배포의 일부로 적용됩니다.
  5. 규칙 기반:
    • 라우팅 서비스는 모든 셀에서 수집된 라우팅 규칙과 함께 배포됩니다.
    • 라우팅 서비스는 다른 버전의 GitLab에서 생성된 규칙 디렉터리을 지원합니다.
    • 규칙은 헤더, 헤더 내용 또는 라우트 경로와 같은 모든 기준으로 일치할 수 있습니다.
  6. 중립적:
    • 라우팅 서비스는 조직과 같은 고수준 개념을 알지 못합니다.
    • 분류는 규칙에서 제공된 명세에 따라 이루어집니다. 해당 명세는 셔딩 키를 찾기 위해 사용됩니다.
    • 셔딩 키 결과가 캐시됩니다.
    • 한 가지 캐시된 셔딩 키는 많은 유사한 요청을 처리하는 데 사용됩니다.

다음 다이어그램은 사용자 요청이 DNS를 통해 클라우드플레어 워커로 배포된 라우팅 서비스로 경로 지정되고 라우터가 요청을 보낼 셀을 선택하는 방법을 보여줍니다.

graph TD; user((사용자)); router[라우팅 서비스]; cell_us0{셀 US0}; cell_us1{셀 US1}; cell_eu0{셀 EU0}; cell_eu1{셀 EU1}; user-->router; router-->cell_eu0; router-->cell_eu1; router-->cell_us0; router-->cell_us1; subgraph 유럽 cell_eu0; cell_eu1; end subgraph 미국 cell_us0; cell_us1; end

라우팅 규칙

각 셀은 라우팅 서비스에서 사용될 사전 컴파일된 라우팅 규칙 디렉터리을 게시합니다:

  • 라우팅 규칙에는 요청 해석, 셔딩 키 찾기 및 라우팅 결정 방법이 설명됩니다.
  • 라우팅 규칙은 라우팅 서비스의 배포 중에 컴파일됩니다.
    • 배포 프로세스는 라우팅 서비스 구성에 포함된 각 셀에서 라우팅 규칙의 최신 버전을 가져옵니다.
    • 컴파워크플로는 모든 셀의 라우팅 규칙을 Merge합니다.
    • 충돌하는 규칙은 라우팅 서비스가 컴파일/시작되는 것을 방지합니다.
    • 각 라우팅 규칙 항목에는 Merge을 용이하게하기 위한 고유 식별자가 있습니다.
    • 라우팅 서비스는 규칙 디렉터리이 변경된 경우에만 다시 배포됩니다. 그러나 새로 추가된 대부분의 엔드포인트가 이전의 라우트 규칙을 이미 준수할 것으로 예상되기 때문에 이는 빈번히 발생하지 않을 것으로 예상됩니다.
  • 구성은 배포 중에 라우팅 규칙을 가져와야 하는 셀을 설명합니다.
  • 공개된 라우팅 규칙은 비밀을 기반으로 라우팅 결정을 수행할 수 있습니다. 예를 들어 세션 쿠키나 인증 토큰이 c100- 접두어를 가지면 모든 요청은 주어진 셀로 전달됩니다.
  • 셀은 /api/v4/internal/cells/route_rules.json에 라우팅 규칙을 게시합니다.
  • 셀이 특정 셀이 처리할 수 있는 엔드포인트만 포함합니다.
  • 셀은 셔딩 키에 기반한 동적 분류를 수행하도록 /api/v4/internal/cells/classify를 호출하도록 요청할 수 있습니다.
  • 라우팅 규칙은 prefix를 사용하여 분류를 가속화해야 합니다. 컴파일 기간 동안 라우팅 서비스는 모든 찾은 접두어를 결정 트리로 변환하여 후속 정규식 일치를 가속화해야 합니다.
  • 일부 접두어는 셀에 독립적이어야 합니다. 예를 들어 개인 액세스 토큰 접두어는 조직과 관련이 있고 셀과 관련이 없어야 합니다. 우리는 조직을 다른 셀로 옮기더라도 개인 액세스 토큰이나 다른 토큰을 변경하지 않고 싶습니다.
  • 라우팅 규칙은 비용이 많이 들고 규칙을 동적으로 구문 분석하고 평가하는 것을 피하기 위해 소스 코드로 컴파일하는 것이 이상적입니다.

라우팅 규칙 JSON 구조는 모든 매처를 설명합니다.

{
    "rules": [
        {
            "id": "<고유 식별자>",
            "cookies": {
                "<쿠키_이름>": {
                    "prefix": "<일치하는_접두어>",
                    "match_regex": "<정규표현식_일치>"
                },
                "<쿠키_이름2>": {
                    "prefix": "<일치하는_접두어>",
                    "match_regex": "<정규표현식_일치>"
                }
            },
            "headers": {
                "<헤더_이름>": {
                    "prefix": "<일치하는_접두어>",
                    "match_regex": "<정규표현식_일치>"
                },
                "<헤더_이름2>": {
                    "prefix": "<일치하는_접두어>",
                    "match_regex": "<정규표현식_일치>"
                }
            },
            "path": {
                "prefix": "<일치하는_접두어>",
                "match_regex": "<정규표현식_일치>"
            },
            "method": ["<허용된_메소드_디렉터리>"],
            
            // 많은 규칙이 일치하는 경우 어떤 규칙이 이기는지 정의
            "priority": 1000,
            
            // 요청을 수락하고 해당 셀로 프록시
            "action": "proxy",
            
            // 요청을 정규표현식 일치 그룹을 기반으로 분류
            "action": "classify",
            "classify": {
                "keys": ["일치하는_정규표현식_캡처_그룹_디렉터리"]
            }
        }
    ]
}

세션 쿠키와 비밀을 기반으로 라우팅 결정을 하는 데 사용되는 Cell 100에서 게시된 라우팅 규칙의 예입니다. 라우팅 규칙이 비밀 기반이므로 높은 우선순위가 할당되어야 하며 나머지 모든 매처보다 우선시되어야 합니다:

{
    "rules": [
        {
            "id": "t4mkd5ndsk58si6uwwz7rdavil9m2hpq",
            "cookies": {
                "_gitlab_session": {
                    "prefix": "c100-" // `c100-`로 접두어가 붙은 `_gitlab_session` 요청 수락
                }
            },
            "action": "proxy",
            "priority": 1000
        },
        {
            "id": "jcshae4d4dtykt8byd6zw1ecccl5dkts",
            "headers": {
                "GITLAB_TOKEN": {
                    "prefix": "C100_" // `C100_`로 접두어가 붙은 `GITLAB_TOKEN` 요청 수락
                }
            },
            "action": "proxy",
            "priority": 1000
        }
    ]
}

모든 셀에서 게시된 라우팅 규칙의 예로 경로에 따라 라우팅 결정을 하는 규칙입니다:

{
    "rules": [
        {
            "id": "c9scvaiwj51a75kzoh917uwtnw8z4ebl",
            "path": {
                "prefix": "/api/v4/projects/", //  매칭 가속화
                "match_regex": "^/api/v4/projects/(?<project_id_or_path_encoded>[^/]+)(/.*)?$"
            },
            "action": "classify",
            "classify": {
                "keys": ["project_id_or_path_encoded"]
            }
        }
    ]
}

분류

각 셀은 분류 엔드포인트를 구현합니다.

  • 분류 엔드포인트는 /api/v4/internal/cells/classify (또는 gRPC 엔드포인트)에 있습니다.
  • 분류 엔드포인트는 셔딩 키 디렉터리을 받습니다. 셔딩 키는 요청에서 디코딩되며 셀이 제공한 라우팅 규칙을 기반으로 결정됩니다.
  • 엔드포인트는 비슷한 요청이 분류를 수행하지 않고도 빠르게 처리될 수 있도록 캐시를 오염시키기 위해 다른 등가 셔딩 키를 반환합니다.
  • 라우팅 서비스는 셀의 상태를 추적하고 가중치, 셀의 상태 또는 정의된 기준에 따라 classify 요청을 셀에 수행합니다. 가중치는 셀 중 어느 셀이 셔딩 키를 분류하는 데 우선되는지 나타낼 것입니다.
  • 라우팅 서비스는 classify 호출을 일정 시간 동안 재시도합니다. 셀의 반복적인 classify 실패은 셀이 건강하지 않은 것을 나타냅니다.
  • classify 결과는 action에 관계없이 캐시됩니다. 거부된 분류는 찾을 수 없는 셔딩 키에 대한 과도한 요청을 방지하기 위해 캐시됩니다.
  • 캐시된 응답은 만료새로 고침으로 지정된 기간 동안 유지됩니다.
    • 만료는 사용되지 않는 경우 항목이 캐시에서 제거되는 시기를 정의합니다.
    • 새로 고침은 요청이 분류된 경우 지연 없이 제공되어야 하므로 비동기적으로 수행됩니다. 캐시가 항상 핫하고 최신 상태임을 보장하기 위해 새로 고침이 수행됩니다.

위 예제에 대한 설명:

  1. 라우터는 /api/v4/projects/1000/issues에 대한 요청을 확인합니다.
  2. 이 요청에 대해 위의 rule이 선택되어, classifyproject_id_or_path_encoded를 요청합니다.
  3. project_id_or_path_encoded1000으로 디코딩합니다.
  4. 캐시에서 project_id_or_path_encoded=1000이 어느 셀과 연결되었는지 확인합니다.
  5. 캐시에 셀이 없는 경우 /api/v4/internal/cells/classify로 요청을 보냅니다.
  6. Rails는 주어진 프로젝트를 소유한 셀과 캐시에 넣어야 할 모든 다른 등가 셔딩 키를 응답합니다.
  7. 라우팅 서비스는 구성에서 지정된 기간이나 응답에 맞게 캐시합니다.
# POST /api/v4/internal/cells/classify

```json
## 요청:
{
    "metadata": {
        "rule_id": "c9scvaiwj51a75kzoh917uwtnw8z4ebl",
        "headers": {
            "all_request_headers": "value"
        },
        "method": "GET",
        "path": "/api/v4/projects/100/issues"
    },
    "keys": {
        "project_id_or_path_encoded": 100
    }
}

## 응답:
{
    "action": "proxy",
    "proxy": {
        "name": "cell_1",
        "url": "https://cell1.gitlab.com"
    },
    "ttl": "10 minutes",
    "matched_keys": [ // 캐시에 넣어야 하는 모든 동등한  디렉터리
        { "project_id_or_path_encoded": 100 },
        { "project_id_or_path_encoded": "gitlab-org%2Fgitlab" },
        { "project_full_path": "gitlab-org/gitlab" },
        { "namespace_full_path": "gitlab-org" },
        { "namespace_id": 10 },
        { "organization_full_path": "gitlab-inc" },
        { "organization_id": 50 },
    ]
}

다음 코드는 샤딩 키를 찾지 못한 경우 부정적인 응답을 나타냅니다:

# POST /api/v4/internal/cells/classify
## 요청:
{
    "metadata": {
        "rule_id": "c9scvaiwj51a75kzoh917uwtnw8z4ebl",
        "headers": {
            "all_request_headers": "value"
        },
        "method": "GET",
        "path": "/api/v4/projects/100/issues"
    },
    "keys": {
        "project_id_or_path_encoded": 100
    }
}

## 응답:
{
    "action": "reject",
    "reject": {
        "http_status": 404
    },
    "cache": {
        "refresh": "10 minutes",
        "expiry": "10 minutes"
    },
    "matched_keys": [ // 캐시에 넣어야 하는 모든 동등한  디렉터리
        { "project_id_or_path_encoded": 100 },
    ]
}

구성

루팅 서비스는 다음과 유사한 구성을 사용할 것으로 예상됩니다:

[[cells]]
name=cell_1
url=https://cell1.gitlab.com
key=ABC123
classify_weight=100

[[cells]]
name=cell_2
url=https://cell2.gitlab.com
key=CDE123
classify_weight=1

[cache.memory.classify]
refresh_time=10 minutes
expiry_time=1 hour

[cache.external.classify]
refresh_time=30 minutes
expiry_time=6 hour

우리는 이러한 셀의 정적 디렉터리을 제공하는 것이 허용되는 것으로 가정합니다. 왜냐하면:

  1. 정적: 동적으로 프로비저닝되고 폐기되는 셀은 드물 것입니다.
  2. 충분하지만: 최대 100개의 셀까지도 이러한 디렉터리을 관리할 수 있습니다.
  3. 간단합니다: 우리는 서비스에서 견고한 서비스 검색을 구현할 필요가 없으며, 항상 이 디렉터리이 철저하다는 보장이 있습니다.

이 구성은 모든 셀과 URL, 신뢰되지 않는 키, 가중치, 요청을 얼마 동안 캐싱해야 하는지를 설명합니다. classify_weight는 셀이 다른 셀보다 얼마나 자주 분류 요청을 받아야 하는지를 정의합니다.

요청 흐름

  1. 두 개의 셀이 있습니다.
  2. gitlab-org는 최상위 네임스페이스이며 GitLab.com Public 조직의 Cell US0에 위치합니다.
  3. my-company는 최상위 네임스페이스이며 my-organization 조직의 Cell EU0에 위치합니다.

정적 라우팅을 수행하도록 라우터 구성

  1. Cell US0는 모든 다른 공개 프로젝트를 지원합니다.
  2. Cell은 모든 시크릿 및 세션 쿠키를 eu0_와 같은 접두사로 생성하도록 구성됩니다.
    1. 개인 액세스 토큰은 조직에 대해 스코프가 지정되며, 조직이 단일 셀의 일부이기 때문에 생성된 PAT는 셀 식별자와 접두사가 붙습니다.
    2. 세션 쿠키는 사용 중인 조직을 인코딩하는데, 조직이 단일 셀의 일부이기 때문에 세션 쿠키는 셀 식별자로 접두사가 붙습니다.
  3. Cell EU0는 비공개 조직, 그룹 및 프로젝트만 허용합니다.
  4. 명시적으로 접두사를 붙이지 않는 한 모든 요청은 Cell US0로 전달됩니다.

Cell US0:

{
    "rules": [
        {
            "id": "tjh147se67wadjzum7onwqiad2b75uft",
            "path": {
                "prefix": "/"
            },
            "action": "proxy",
            "priority": 1
        }
    ]
}

Cell EU0:

{
    "rules": [
        {
            "id": "t4mkd5ndsk58si6uwwz7rdavil9m2hpq",
            "cookies": {
                "_gitlab_session": {
                    "prefix": "eu0_"
                }
            },
            "path": {
                "prefix": "/"
            },
            "action": "proxy",
            "priority": 1000
        },
        {
            "id": "jcshae4d4dtykt8byd6zw1ecccl5dkts",
            "headers": {
                "GITLAB_TOKEN": {
                    "prefix": "eu0_"
                }
            },
            "path": {
                "prefix": "/"
            },
            "action": "proxy",
            "priority": 1000
        }
    ]
}

로그인된 상태로 /my-company/my-project로 진입

  1. 사용자가 조직을 my-company로 변경했기 때문에 세션 쿠키는 eu0_로 접두사가 붙었습니다.
  2. 사용자가 /my-company/my-project로 요청을 보내고, 쿠키가 eu0_로 접두사가 붙었기 때문에 Cell EU0로 이동합니다.
  3. Cell EU0은 올바른 응답을 반환합니다.
sequenceDiagram participant user as 사용자 participant router as 라우터 participant cell_eu0 as 셀 EU0 participant cell_eu1 as 셀 EU1 user->>router: GET /my-company/my-project<br/>_gitlab_session=eu0_uwwz7rdavil9 router->>cell_eu0: GET /my-company/my-project cell_eu0->>user: <h1>My Project...

로그인되지 않은 상태로 /my-company/my-project로 진입

  1. 사용자는 /my-company/my-project를 방문하는데, 세션 쿠키가 없기 때문에 요청은 Cell US0로 전달됩니다.
  2. 사용자가 로그인합니다.
  3. GitLab은 사용자의 기본 조직이 my-company임을 확인하고 사용자가 my-company와 상호 작용하도록 의도되었음을 나타내기 위해 eu0_로 접두사가 붙은 세션 쿠키를 할당합니다.
  4. 사용자가 다시 /my-company/my-project로 요청을 보내는데, 이번에는 Cell EU0로 프록시되는 세션 쿠키를 가지고 있습니다.
  5. Cell EU0은 올바른 응답을 반환합니다.
sequenceDiagram participant user as 사용자 participant router as 라우터 participant cell_us0 as 셀 US0 participant cell_eu0 as 셀 EU0 user->>router: GET /my-company/my-project router->>cell_us0: GET /my-company/my-project cell_us0->>user: HTTP 302 /users/sign_in?redirect=/my-company/my-project user->>router: GET /users/sign_in?redirect=/my-company/my-project router->>cell_us0: GET /users/sign_in?redirect=/my-company/my-project cell_us0-->>user: <h1>Sign in... user->>router: POST /users/sign_in?redirect=/my-company/my-project router->>cell_us0: POST /users/sign_in?redirect=/my-company/my-project cell_us0->>user: HTTP 302 /my-company/my-project<br/>_gitlab_session=eu0_uwwz7rdavil9 user->>router: GET /my-company/my-project<br/>_gitlab_session=eu0_uwwz7rdavil9 router->>cell_eu0: GET /my-company/my-project<br/>_gitlab_session=eu0_uwwz7rdavil9 cell_eu0->>user: <h1>My Project...

#### 마지막 단계 이후 `/gitlab-org/gitlab`으로 이동합니다.

사용자는 `/my-company/my-project`로 이동하고 세션 쿠키가 없기 때문에 요청이 `Cell US0`으로 전달됩니다.

```mermaid
sequenceDiagram
    participant user as 사용자
    participant router as 라우터
    participant cell_eu0 as Cell EU0
    participant cell_us0 as Cell US0
    user->>router: GET /gitlab-org/gitlab<br/>_gitlab_session=eu0_uwwz7rdavil9
    router->>cell_eu0: GET /gitlab-org/gitlab
    cell_eu0->>user: HTTP 404

분류에 따라 동적 라우팅을 수행하도록 라우터 구성

Cells는 요청을 분류할 수 있도록 라우트 규칙을 게시합니다.

Cell US0 및 EU0:

{
    "rules": [
        {
            "id": "tjh147se67wadjzum7onwqiad2b75uft",
            "path": {
                "prefix": "/",
                "regex": "^/(?top_level_group)[^/]+(/.*)?$",
            },
            "action": "classify",
            "classify": {
                "keys": ["top_level_group"]
            }
        },
        {
            "id": "jcshae4d4dtykt8byd6zw1ecccl5dkts",
            "path": {
                "prefix": "/"
            },
            "action": "proxy"
        }
    ]
}

Cell EU0에 로그인한 상태로 /my-company/my-project로 이동

  1. /my-company/my-project/을 방문합니다.
  2. 라우터가 샤딩 키 top_level_group=my-company를 디코딩합니다.
  3. 라우터가 해당 샤딩 키가 캐싱되어 있는지 확인합니다.
  4. 캐싱되어 있지 않기 때문에 분류 요청이 무작위 Cell로 /classify로 전송됩니다.
  5. 분류의 응답이 캐싱됩니다.
  6. 분류된 Cell로 요청이 프록시됩니다.
sequenceDiagram participant user as 사용자 participant router as 라우터 participant cache as 캐시 participant cell_us0 as Cell US0 participant cell_eu0 as Cell EU0 user->>router: GET /my-company/my-project router->>cache: CACHE_GET: top_level_group=my-company cache->>router: CACHE_NOT_FOUND router->>cell_us0: POST /api/v4/internal/cells/classify<br/>top_level_group=my-company cell_us0->>router: CLASSIFY: top_level_group=my-company, cell=cell_eu0 router->>cache: CACHE_SET: top_level_group=my-company, cell=cell_eu0 router->>cell_eu0: GET /my-company/my-project cell_eu0->>user: <h1>내 프로젝트...

로그인하지 않은 상태에서 /my-company/my-project로 이동

  1. /my-company/my-project/을 방문합니다.
  2. 라우터가 샤딩 키 top_level_group=my-company를 디코딩합니다.
  3. 라우터가 해당 샤딩 키가 캐싱되어 있는지 확인합니다.
  4. 캐싱되어 있지 않기 때문에 분류 요청이 무작위 Cell로 /classify로 전송됩니다.
  5. 분류의 응답이 캐싱됩니다.
  6. 분류된 Cell로 요청이 프록시됩니다.
  7. 프로젝트가 비공개이기 때문에 사용자가 로그인 화면으로 리디렉션됩니다.
  8. 로그인 페이지는 모든 Cells에서 처리되도록 정의되어 있어서 무작위 Cell로 프록시됩니다.
  9. 사용자가 로그인한 후 다시 /my-company/my-project/를 방문합니다.
  10. top_level_group=my-company가 올바른 Cell로 프록시됩니다.
sequenceDiagram participant user as 사용자 participant router as 라우터 participant cache as 캐시 participant cell_us0 as Cell US0 participant cell_eu0 as Cell EU0 user->>router: GET /my-company/my-project router->>cache: CACHE_GET: top_level_group=my-company cache->>router: CACHE_NOT_FOUND router->>cell_us0: POST /api/v4/internal/cells/classify<br/>top_level_group=my-company cell_us0->>router: CLASSIFY: top_level_group=my-company, cell=cell_eu0 router->>cache: CACHE_SET: top_level_group=my-company, cell=cell_eu0 router->>cell_eu0: GET /my-company/my-project cell_eu0->>user: HTTP 302 /users/sign_in?redirect=/my-company/my-project user->>router: GET /users/sign_in?redirect=/my-company/my-project router->>cell_us0: GET /users/sign_in?redirect=/my-company/my-project cell_us0-->>user: <h1>로그인... user->>router: POST /users/sign_in?redirect=/my-company/my-project router->>cell_eu0: POST /users/sign_in?redirect=/my-company/my-project cell_eu0->>user: HTTP 302 /my-company/my-project user->>router: GET /my-company/my-project router->>cache: CACHE_GET: top_level_group=my-company cache->>router: CACHE_FOUND: cell=cell_eu0 router->>cell_eu0: GET /my-company/my-project cell_eu0->>user: <h1>내 프로젝트...

마지막 단계 이후 /gitlab-org/gitlab으로 이동

  1. /gitlab-org가 캐시에 없기 때문에 분류되고 올바른 Cell로 전달됩니다.
sequenceDiagram participant user as 사용자 participant router as 라우터 participant cache as 캐시 participant cell_us0 as Cell US0 participant cell_eu0 as Cell EU0 user->>router: GET /gitlab-org/gitlab router->>cache: CACHE_GET: top_level_group=gitlab-org cache->>router: CACHE_NOT_FOUND router->>cell_us0: POST /api/v4/internal/cells/classify<br/>top_level_group=gitlab-org cell_us0->>router: CLASSIFY: top_level_group=gitlab-org, cell=cell_us0 router->>cache: CACHE_SET: top_level_group=gitlab-org, cell=cell_us0 router->>cell_us0: GET /gitlab-org/gitlab cell_us0->>user: <h1>내 프로젝트...

성능 및 신뢰성 고려 사항

  • 각 Cell이 모든 샤딩 키를 분류할 수 있을 것으로 예상됩니다.
  • 대체로 분류는 모든 필요한 데이터를 소유하고 있는 클러스터 전체 데이터 제공자에 의해 수행될 수 있습니다.
  • 게시된 라우팅 규칙을 통해 정적 기준을 정의할 수 있으며, 이는 라우팅 결정을 비밀 값으로만 만들어 줍니다. 결과적으로 라우팅 서비스는 요청 처리에 대한 지연 시간을 추가하지 않으며 뛰어난 내구성을 제공합니다.
  • 새로운 샤딩 키를 학습할 때 벌칙이 있을 것으로 예상됩니다. 그러나 샤딩 키의 저카디널리티로 인해 다층 캐시가 매우 높은 캐시 히트 비율을 제공할 것으로 예상됩니다. 샤딩 키는 사실상 리소스(조직, 그룹 또는 프로젝트)로 매핑되고 그런 것들은 한정적이기 때문입니다.

대안

요청 버퍼링

요청 버퍼링을 사용한 상태 없는 라우터 는 Cell이 요청을 다른 Cell로 리디렉션하기 위해 X-Gitlab-Cell-Redirect로 응답하는 방식을 설명합니다.

  • 이 방법은 전체 요청(헤더 + 본문)을 버퍼링해야 하기 때문에 메모리 사용량이 매우 큽니다.
  • 본 제안에서는 서로 다른 버전의 Cell을 실행할 수도 있는 혼합 배포를 다루기 어려운 점이 있습니다.
  • 이 제안은 디코딩된 샤딩 키가 아니라 요청을 기반으로 하기 때문에 더 많은 정보를 캐싱하는 것으로 보입니다.

학습 요청

Routes Learning을 사용한 Stateless Router는 이 문서와 유사한 방식을 설명합니다. 단, 루트 규칙 및 분류는 사전 점검 /api/v4/internal/cells/learn 형식으로 일괄적으로 수행됩니다.

  • 이로써 전체 루트 학습이 동적으로 이루어지며, Cells의 가용성에 의존합니다.
  • 본 제안은 Cells의 혼합 배포를 다루는 간단한 방법을 제공하지 않습니다. 여기서 Cells는 다른 버전을 실행할 수 있습니다.
  • 이 제안은 디코딩된 샤딩 키가 아닌 요청을 기반으로 하기 때문에, 정보를 캐싱하는 데 많은 노력이 필요할 것으로 보입니다.

자주 묻는 질문

  1. 라우팅 서비스가 규칙 세트를 어떻게 및 언제 컴파일하나요?

정의되지 않았습니다.

링크