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. As with all projects, the items mentioned on this page are subject to change or delay. The development, release, and timing of any products, features, or functionality remain at the sole discretion of GitLab Inc.
Status Authors Coach DRIs Owning Stage Created
rejected -

이 제안은 루팅 서비스 제안에 의해 대체되었습니다.

이 문서는 진행 중인 작업이며 Cells 디자인의 매우 초기 상태를 나타냅니다. 중요한 측면들이 문서화되어 있지 않지만, 향후 추가할 예정입니다. 이는 Cells를 위한 하나의 가능한 아키텍처이며, 이를 구현할 접근 방법을 결정하기 전에 대안과 대조할 것입니다. 이 설명서는 우리가 이 접근 방법을 선택하지 않기로 결정하더라도, 그 이유를 문서화할 수 있도록 유지될 것입니다.

제안: 라우트 학습을 사용한 상태 없는 라우터

gitlab_users, gitlab_routes, gitlab_admin과 관련된 테이블을 분해하여 모든 Cells 사이에서 공유할 수 있도록 하고, 모든 Cell이 사용자를 인증하고 요청을 올바른 Cell로 경로 지정할 수 있도록 할 것입니다. Cells는 소유하지 않은 리소스에 대한 요청을 받을 수 있지만, 올바른 Cell로 다시 리디렉션하는 방법을 알고 있습니다.

라우터는 상태를 유지하지 않으며 routes 데이터베이스를 읽지 않으므로 모든 데이터베이스 상호 작용은 여전히 Rails 모놀리스에서 발생합니다. 이 아키텍처는 지역을 지원하여 낮은 트래픽 데이터베이스를 지역 간에 복제할 수 있도록 허용합니다.

사용자는 직접적으로 Cells의 개념에 노출되지 않지만, 대신 선택한 조직에 따라 다른 데이터를 볼 수 있습니다. 조직은 응용 프로그램에서 격리를 강제하고 어떤 요청이 어떤 셀로 경로 지정될지 결정할 수 있도록 도입되는 새로운 엔터티가 될 것입니다. 왜냐하면 조직은 단일 Cell에만 있을 수 있기 때문입니다.

차이점

이 제안과 요청 버퍼링을 사용한 제안과의 주요 차이점은 이 제안이 사전 API 요청(/api/v4/internal/cells/learn)을 사용하여 요청 본문을 올바른 Cell로 리디렉트하는 것입니다. 이는 각 요청이 정확히 한 번만 처리되도록 전송되지만, URI가 사용되어 어떤 Cell로 경로 지정해야 하는지 디코딩하는 데 사용됩니다.

다이어그램에서 요약

이 그림은 사용자 요청이 DNS를 통해 가장 가까운 라우터로 경로 지정되고, 라우터가 요청을 보낼 셀을 선택하는 방법을 보여줍니다.

graph TD; user((User)); dns[DNS]; router_us(Router); router_eu(Router); cell_us0{Cell US0}; cell_us1{Cell US1}; cell_eu0{Cell EU0}; cell_eu1{Cell EU1}; user-->dns; dns-->router_us; dns-->router_eu; subgraph 유럽 router_eu-->cell_eu0; router_eu-->cell_eu1; end subgraph 미국 router_us-->cell_us0; router_us-->cell_us1; end

더 자세한 내용

이 그림은 라우터가 실제로 어떤 셀에 요청을 보낼 수 있는지를 보여줍니다. 사용자는 지리적으로 가장 가까운 라우터를 받게 됩니다.

graph TD; user((User)); dns[DNS]; router_us(Router); router_eu(Router); cell_us0{Cell US0}; cell_us1{Cell US1}; cell_eu0{Cell EU0}; cell_eu1{Cell EU1}; user-->dns; dns-->router_us; dns-->router_eu; subgraph 유럽 router_eu-->cell_eu0; router_eu-->cell_eu1; end subgraph 미국 router_us-->cell_us0; router_us-->cell_us1; end router_eu-.->cell_us0; router_eu-.->cell_us1; router_us-.->cell_eu0; router_us-.->cell_eu1;

훨씬 더 자세한 내용

이 그림은 데이터베이스를 보여줍니다. gitlab_usersgitlab_routes는 미국 지역에만 존재하지만 다른 지역으로 복제됩니다. 복제는 다이어그램을 읽기가 너무 어려워서 화살표가 없습니다.

graph TD; user((User)); dns[DNS]; router_us(Router); router_eu(Router); cell_us0{Cell US0}; cell_us1{Cell US1}; cell_eu0{Cell EU0}; cell_eu1{Cell EU1}; db_gitlab_users[(gitlab_users 기본)]; db_gitlab_routes[(gitlab_routes 기본)]; db_gitlab_users_replica[(gitlab_users 복제본)]; db_gitlab_routes_replica[(gitlab_routes 복제본)]; db_cell_us0[(gitlab_main/gitlab_ci Cell US0)]; db_cell_us1[(gitlab_main/gitlab_ci Cell US1)]; db_cell_eu0[(gitlab_main/gitlab_ci Cell EU0)]; db_cell_eu1[(gitlab_main/gitlab_ci Cell EU1)]; user-->dns; dns-->router_us; dns-->router_eu; subgraph 유럽 router_eu-->cell_eu0; router_eu-->cell_eu1; cell_eu0-->db_cell_eu0; cell_eu0-->db_gitlab_users_replica; cell_eu0-->db_gitlab_routes_replica; cell_eu1-->db_gitlab_users_replica; cell_eu1-->db_gitlab_routes_replica; cell_eu1-->db_cell_eu1; end subgraph 미국 router_us-->cell_us0; router_us-->cell_us1; cell_us0-->db_cell_us0; cell_us0-->db_gitlab_users; cell_us0-->db_gitlab_routes; cell_us1-->db_gitlab_users; cell_us1-->db_gitlab_routes; cell_us1-->db_cell_us1; end router_eu-.->cell_us0; router_eu-.->cell_us1; router_us-.->cell_eu0; router_us-.->cell_eu1;

변경 사항 요약

  1. 사용자 데이터에 관련된 테이블(프로필 설정, 인증 자격 증명, 개인 액세스 토큰 포함)이 gitlab_users 스키마로 분해됨
  2. routes 테이블이 gitlab_routes 스키마로 분해됨
  3. application_settings (그 외 몇 가지 인스턴스 레벨 테이블)이 gitlab_admin 스키마로 분해될 것으로 예상됨
  4. routes 테이블에 routes.cell_id 열이 추가됨
  5. 새로운 Router 서비스가 만들어져 요청을 어느 셀로 라우팅할지 선택함
  6. 라우터가 새 요청을 받으면 /api/v4/internal/cells/learn?method=GET&path_info=/group-org/project를 보내서 어떤 Cell이 처리할 수 있는지 확인함
  7. GitLab에 새로운 개념인 조직이 소개될 예정임
  8. 기존의 모든 엔드포인트는 URI로 라우팅될 필요가 있거나 특정 Cell에 고정되어 처리되어야 함. 이는 /dashboard와 같은 모호한 엔드포인트를 /organizations/my-organization/-/dashboard와 같이 범위를 지정해야 한다는 것을 의미함
  9. /admin과 같은 엔드포인트는 항상 cell_0과 같은 특정 Cell로 라우팅됨
  10. 각 Cell은 /api/v4/internal/cells/learn에 응답하여 각 엔드포인트를 분류할 수 있음
  11. gitlab_usersgitlab_routes에 대한 쓰기 작업은 US 지역의 기본 PostgreSQL 서버로 전송되지만 읽기 작업은 동일한 지역의 복제본에서 수행될 수 있음. 이로 인해 이러한 쓰기 작업에 대한 대기 시간이 추가되지만 이는 GitLab의 나머지 부분에 비해 빈번하지 않을 것으로 예상됨.

사전 비행 요청 학습

요청 처리 중에 URI가 디코딩되고 캐시되지 않은 각 엔드포인트에 대해 사전 비행 요청이 전송됨.

GitLab Rails에서 엔드포인트를 요청하면 라우터가 라우팅 가능한 경로에 대한 정보를 반환함. GitLab Rails는 path_info를 디코딩하고 기존 엔드포인트에 일치시키고 라우팅 가능한 엔티티(예: 프로젝트)를 찾음. 라우터는 이를 일시적인 캐시 정보로 취급함.

  1. 접두어 일치: /api/v4/internal/cells/learn?method=GET&path_info=/gitlab-org/gitlab-test/-/issues

    {
       "path": "/gitlab-org/gitlab-test",
       "cell": "cell_0",
       "source": "routable"
    }
    
  2. 일부 엔드포인트는 정확한 일치가 필요할 수 있음: /api/v4/internal/cells/learn?method=GET&path_info=/-/profile

    {
       "path": "/-/profile",
       "cell": "cell_0",
       "source": "fixed",
       "exact": true
    }
    

첫 번째 반복에서 기본 조직의 상세 설명

모든 사용자는 users.default_organization이라는 새 열을 받게 되며 이를 사용자 설정에서 제어할 수 있음. 우리는 GitLab.com Public 조직이라는 개념을 소개할 것임. 기존 사용자를 위해이 조직이 기본 조직으로 설정될 것임. 이 조직은 사용자가 Cell US0(즉, 원래의 GitLab.com 인스턴스)의 모든 네임스페이스에서 데이터를 볼 수 있게 할 것임. 이 동작은 기존 사용자에게는 보이지 않을 수 있어서, 그들은 심지어 /dashboard와 같은 글로벌 페이지를 볼 때에도 이것이 조직에 범위 지정되었다는 것을 알지 못할 수 있음.

GitLab.com Public 이외의 기본 조직을 가진 새로운 사용자는 별도의 사용자 경험을 받게 되며 로드하는 모든 페이지가 항상 단일 조직의 범위로 지정되어 있다는 사실을 완전히 알 수 있게 됨. 이러한 사용자들은 dashboard와 같은 글로벌 페이지를 로드할 수 없으며 항상 /organizations/<DEFAULT_ORGANIZATION>/-/dashboard로 리디렉션됨. 이는 기존의 API 및 이와 같은 사용자에 대해서도 마찬가지일 수 있음.

관리자 영역 설정의 자세한 설명

관리자 영역 설정을 유지 및 동기화하는 것이 괴로울 것으로 판단되어 모든 관리자 영역 설정을 gitlab_admin 스키마에서 분해하고 공유할 것임. 이는 매우 적은 쓰기 트래픽을 받기 때문에 안전할 것으로 기대됨.

다른 셀이 다른 설정을 필요로 할 때(예: Elasticsearch URL), 해당 설정의 관련 application_settings 행에 동적으로 설정할 수 있게 하는 템플릿 형식을 사용할 것임. 또는 그것이 어렵다고 판명된다면 각 셀에 맞게 설정을 하게 하기 위해 per_cell_application_settings라는 새로운 테이블을 도입할 것임. 각 셀당 1개의 행을 가지며 모든 셀의 설정을 동기화하고 관리하기 위해 gitlab_admin 스키마의 일부로 유지될 것임.

장점

  1. 라우터는 상태가 없으며 여러 지역에서 사용될 수 있음. 사용자에게 가장 가까운 지역으로 해결하기 위해 Anycast DNS를 사용함.
  2. 셀은 잘못된 셀에서의 네임스페이스에 대한 요청을 받을 수 있고 사용자는 여전히 올바른 응답을 받을 수 있으며 다음 요청이 올바른 셀로 전송되는 것을 보장하기 위해 라우터에서 캐싱됨
  3. 대부분의 코드는 여전히 gitlab 레일 코드베이스에 존재함. 라우터는 실제로 GitLab URL이 어떻게 구성되는지 이해할 필요가 없음.
  4. gitlab_users, gitlab_routesgitlab_admin을 읽고 쓰는 책임이 여전히 Rails에 있기 때문에 도메인 모델을 격리시키고 새로운 인터페이스를 구축해야 하는 서비스를 추출하는 것에 비해 Rails 애플리케이션에는 매우 적은 변경이 필요할 것으로 예상됨.
  5. 별도의 라우팅 서비스와 비교했을 때, 이는 더 복잡한 규칙을 인코딩할 수 있게 하며 기존 API 엔드포인트에 대해 작동할 수 있음.
  6. 모든 새로운 인프라(라우터만)는 선택 사항이며 단일 셀 자체 설치는 심지어 라우터를 실행할 필요가 없으며 다른 새로운 서비스도 존재하지 않음.

단점

  1. gitlab_users, gitlab_routesgitlab_admin 데이터베이스는 여러 지역에 복제되어야 할 수 있으며 쓰기는 여러 지역을 통해 이루어져야 합니다. 이와 관련된 테이블에 대한 쓰기 TPS를 분석하여 이것이 실행 가능한지 여부를 결정해야 합니다.
  2. 여러 다른 셀에서 데이터베이스에 대한 액세스 권한을 공유하면 모든 셀이 Postgres 스키마 수준에서 결합되며 이로 인해 데이터베이스 스키마의 변경은 모든 셀의 배포와 동기화에 주의하여 수행되어야 합니다. 이는 공유 서비스가 아닌 우리가 제어하는 API를 갖는 아키텍처와 비교하여 셀이 서로 비슷한 버전으로 유지되는 것을 제한합니다.
  3. 대부분의 데이터가 올바른 지역에 저장되어 있지만 다른 지역에서 프록시로 전달될 수 있으며 이는 특정 유형의 규정 준수에 문제가 될 수 있습니다.
  4. gitlab_usersgitlab_routes 데이터베이스의 데이터는 모든 지역에 복제되어야 하며 이는 특정 유형의 규정 준수에 문제가 될 수 있습니다.
  5. 라우터 캐시는 매우 크게 필요할 수 있습니다. (예: long tail의 다양한 URL). 이러한 경우 사용자 쿠키에서 캐싱의 2단계를 구현하여 자주 액세스하는 페이지가 항상 처음에 올바른 셀로 이동하도록 할 필요가 있습니다.
  6. 여러 셀에서 gitlab_usersgitlab_routes에 대한 공유 데이터베이스 액세스는 여러 셀에서 호출되는 추출 서비스에 비해 이례적인 아키텍처 결정입니다.
  7. 아마도 GraphQL URL의 캐시 가능한 요소를 찾지 못할 가능성이 매우 높으며 기존의 GraphQL 엔드포인트가 대부분 routes 테이블에 없는 id에 심각하게 의존하므로 셀은 데이터가 있는 셀을 알 수 없을 것입니다. 그러므로 우리는 아마도 경로에 조직 컨텍스트를 포함하여 GraphQL 호출을 업데이트해야 할 것입니다. 예: /api/organizations/<organization>/graphql.
  8. 이 아키텍처는 구현된 엔드포인트가 주어진 셀에서 쉽게 액세스 가능한 데이터에만 액세스할 수 있지만 여러 셀에서 정보를 집계하는 것은 불가능할 것으로 보입니다.
  9. 알려지지 않은 경로는 최신 배포로 보내지며 우리는 이것을 Cell US0로 가정합니다. 새로 추가된 엔드포인트는 최신 셀에서만 해독 가능하므로 이것이 문제가 될 가능성이 높지 않습니다. 아마도 /internal/cells/learn과 같은 경로는 가벼워서 처리하는 데 성능 영향을 미치지 않을 것입니다.

데이터베이스 구성 예시

공유 gitlab_users, gitlab_routesgitlab_admin 데이터베이스를 처리하면서 전용 gitlab_maingitlab_ci 데이터베이스를 사용하는 방법은 이미 config/database.yml 사용 방식에 의해 처리되어야 합니다. 또한, 단일 US 기본 위치와 전용 EU 복제본을 사용하는 것도 이미 처리되어야 합니다. 아래는 위에서 설명한 셀 아키텍처에 대한 데이터베이스 구성 일부분의 스니펫입니다.

Cell US0:

# config/database.yml
production:
  main:
    host: postgres-main.cell-us0.primary.consul
    load_balancing:
      discovery: postgres-main.cell-us0.replicas.consul
  ci:
    host: postgres-ci.cell-us0.primary.consul
    load_balancing:
      discovery: postgres-ci.cell-us0.replicas.consul
  users:
    host: postgres-users-primary.consul
    load_balancing:
      discovery: postgres-users-replicas.us.consul
  routes:
    host: postgres-routes-primary.consul
    load_balancing:
      discovery: postgres-routes-replicas.us.consul
  admin:
    host: postgres-admin-primary.consul
    load_balancing:
      discovery: postgres-admin-replicas.us.consul

Cell EU0:

# config/database.yml
production:
  main:
    host: postgres-main.cell-eu0.primary.consul
    load_balancing:
      discovery: postgres-main.cell-eu0.replicas.consul
  ci:
    host: postgres-ci.cell-eu0.primary.consul
    load_balancing:
      discovery: postgres-ci.cell-eu0.replicas.consul
  users:
    host: postgres-users-primary.consul
    load_balancing:
      discovery: postgres-users-replicas.eu.consul
  routes:
    host: postgres-routes-primary.consul
    load_balancing:
      discovery: postgres-routes-replicas.eu.consul
  admin:
    host: postgres-admin-primary.consul
    load_balancing:
      discovery: postgres-admin-replicas.eu.consul

요청 흐름

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

my-organization의 유료 사용자 경험

해당 사용자의 기본 조직은 /my-organization으로 설정되어 있으며 이 조직 외부의 전역 경로를로드할 수 없습니다. 다른 프로젝트/네임스페이스는로드할 수 있지만 페이지 상단의 MR/Todo/Issue 카운트는 첫 번째 이터레이션에서 올바르게 채워지지 않을 것입니다. 사용자는 이러한 제한을 인식하게 될 것입니다.

‘/my-company/my-project’로 이동한 경우에는 로그인 상태입니다

  1. 유저가 유럽에 있으므로 DNS가 유럽의 라우터로 해석됩니다.
  2. 라우터 캐시 없이 ‘/my-company/my-project’를 요청하면, 라우터는 무작위로 ‘Cell EU1’을 선택합니다.
  3. ‘/internal/cells/learn’이 ‘Cell EU1’로 전송되고, 해당 리소스가 ‘Cell EU0’에 있다고 응답합니다.
  4. ‘Cell EU0’에서 올바른 응답을 반환합니다.
  5. 이제 라우터는 ‘/my-company/*‘에 일치하는 모든 요청 경로가 ‘Cell EU0’로 가야 한다고 캐시하고 기억합니다.
sequenceDiagram participant user as User participant router_eu as Router EU participant cell_eu0 as Cell EU0 participant cell_eu1 as Cell EU1 user->>router_eu: GET /my-company/my-project router_eu->>cell_eu1: /api/v4/internal/cells/learn?method=GET&path_info=/my-company/my-project cell_eu1->>router_eu: {path: "/my-company", cell: "cell_eu0", source: "routable"} router_eu->>cell_eu0: GET /my-company/my-project cell_eu0->>user: <h1>My Project...

‘/my-company/my-project’로 이동한 경우에는 로그인 상태가 아닙니다

  1. 유저가 유럽에 있으므로 DNS가 유럽의 라우터로 해석됩니다.
  2. 라우터에는 아직 ‘/my-company/*‘에 대한 캐시가 없으므로 ‘Cell EU1’을 무작위로 선택합니다.
  3. ‘/internal/cells/learn’이 ‘Cell EU1’로 전송되고, 해당 리소스가 ‘Cell EU0’에 있다고 응답합니다.
  4. ‘Cell EU0’은 로그인 플로우를 통해 사용자를 리디렉션합니다.
  5. 사용자가 ‘/users/sign_in’을 요청하고, 무작위 셀을 사용하여 ‘/internal/cells/learn’을 실행합니다.
  6. ‘Cell EU1’은 고정 경로로 ‘cell_0’으로 응답합니다.
  7. 로그인 후 사용자가 ‘/my-company/my-project’를 요청하면 이는 ‘Cell EU0’에 캐시되어 저장됩니다.
  8. ‘Cell EU0’은 올바른 응답을 반환합니다.
sequenceDiagram participant user as User participant router_eu as Router EU participant cell_eu0 as Cell EU0 participant cell_eu1 as Cell EU1 user->>router_eu: GET /my-company/my-project router_eu->>cell_eu1: /api/v4/internal/cells/learn?method=GET&path_info=/my-company/my-project cell_eu1->>router_eu: {path: "/my-company", cell: "cell_eu0", source: "routable"} router_eu->>cell_eu0: GET /my-company/my-project cell_eu0->>user: 302 /users/sign_in?redirect=/my-company/my-project user->>router_eu: GET /users/sign_in?redirect=/my-company/my-project router_eu->>cell_eu1: /api/v4/internal/cells/learn?method=GET&path_info=/users/sign_in cell_eu1->>router_eu: {path: "/users", cell: "cell_eu0", source: "fixed"} router_eu->>cell_eu0: GET /users/sign_in?redirect=/my-company/my-project cell_eu0-->>user: <h1>Sign in... user->>router_eu: POST /users/sign_in?redirect=/my-company/my-project router_eu->>cell_eu0: POST /users/sign_in?redirect=/my-company/my-project cell_eu0->>user: 302 /my-company/my-project user->>router_eu: GET /my-company/my-project router_eu->>cell_eu0: GET /my-company/my-project router_eu->>cell_eu0: GET /my-company/my-project cell_eu0->>user: <h1>My Project...

마지막 단계 이후 ‘/my-company/my-other-project’로 이동한 경우

  1. 유저가 유럽에 있으므로 DNS가 유럽의 라우터로 해석됩니다.
  2. 라우터 캐시에 이제 ‘/my-company/* => Cell EU0’가 있으므로 라우터는 ‘Cell EU0’을 선택합니다.
  3. ‘Cell EU0’은 올바른 응답 및 캐시 헤더를 다시 반환합니다.
sequenceDiagram participant user as User participant router_eu as Router EU participant cell_eu0 as Cell EU0 participant cell_eu1 as Cell EU1 user->>router_eu: GET /my-company/my-project router_eu->>cell_eu0: GET /my-company/my-project cell_eu0->>user: <h1>My Project...

마지막 단계 이후 ‘/gitlab-org/gitlab’로 이동한 경우

  1. 유저가 유럽에 있으므로 DNS가 유럽의 라우터로 해석됩니다.
  2. 이 URL에 대해 라우터에는 캐시된 값이 없으므로 무작위로 ‘Cell EU0’을 선택합니다.
  3. ‘Cell EU0’은 라우터를 ‘Cell US0’로 리디렉션합니다.
  4. ‘Cell US0’은 올바른 응답과 함께 캐시 헤더를 다시 반환합니다.
sequenceDiagram participant user as User participant router_eu as Router EU participant cell_eu0 as Cell EU0 participant cell_us0 as Cell US0 user->>router_eu: GET /gitlab-org/gitlab router_eu->>cell_eu0: /api/v4/internal/cells/learn?method=GET&path_info=/gitlab-org/gitlab cell_eu0->>router_eu: {path: "/gitlab-org", cell: "cell_us0", source: "routable"} router_eu->>cell_us0: GET /gitlab-org/gitlab cell_us0->>user: <h1>GitLab.org...

이 경우 사용자는 “기본 조직”에 있지 않으므로 그들의 TODO 카운터에 일반적인 할 일이 포함되지 않습니다. 우리는 UI 어딘가에 이를 강조할 수 있습니다. 미래의 반복에서는 그들이 기본 조직에서 그것을 가져올 수 있을 것입니다.

/로 이동

  1. 사용자가 유럽에 있으므로 DNS는 유럽의 라우터로 해석됩니다.
  2. 라우터에는 / 경로에 대한 캐시가 없습니다(Rails는 명시적으로 이 경로를 캐시하라고 지시하지 않음).
  3. 라우터는 Cell EU0을 무작위로 선택합니다.
  4. Rails 애플리케이션은 사용자의 기본 조직이 /my-organization임을 알고 있으므로 사용자를 /organizations/my-organization/-/dashboard로 리디렉션합니다.
  5. 라우터는 /organizations/my-organization/*에 대한 캐시된 값을 가지고 있으므로 요청을 POD EU0로 보냅니다.
  6. Cell EU0은 오늘의 대시보드 뷰와 동일하지만 UI에서 명확하게 조직으로 범위가 지정된 /organizations/my-organization/-/dashboard 페이지를 제공합니다.
  7. 사용자는 (선택적으로) 이 페이지의 데이터가 기본 조직에서만 제공된다는 메시지를 볼 수 있으며, 기본 조직이 잘못되었을 경우 기본 조직을 변경할 수 있다는 메시지가 표시됩니다.
sequenceDiagram participant user as User participant router_eu as Router EU participant cell_eu0 as Cell EU0 user->>router_eu: GET / router_eu->>cell_eu0: GET / cell_eu0->>user: 302 /organizations/my-organization/-/dashboard user->>router: GET /organizations/my-organization/-/dashboard router->>cell_eu0: GET /organizations/my-organization/-/dashboard cell_eu0->>user: <h1>My Company 대시보드... X-Gitlab-Cell-Cache={path_prefix:/organizations/my-organization/}

/dashboard로 이동

위와 마찬가지로, rails 애플리케이션이 이미 /를 대시보드 페이지로 리디렉션하므로 /organizations/my-organization/-/dashboard에 도착합니다.

/not-my-company/not-my-project로 이동 (로그인했지만 액세스할 수 없음 - 해당 프로젝트/그룹이 비공개임)

  1. 사용자가 유럽에 있으므로 DNS는 유럽의 라우터로 해석됩니다.
  2. 라우터는 /not-my-companyCell US1에 있는 것을 알기 때문에 해당 셀로 요청을 보냅니다.
  3. 사용자는 액세스 권한이 없으므로 Cell US1은 404를 반환합니다.
sequenceDiagram participant user as User participant router_eu as Router EU participant cell_us1 as Cell US1 user->>router_eu: GET /not-my-company/not-my-project router_eu->>cell_us1: GET /not-my-company/not-my-project cell_us1->>user: 404

새로운 최상위 네임스페이스 생성

사용자는 네임스페이스가 어느 조직에 속하는지를 선택해야 합니다. 그들이 my-organization을 선택하면 이는 my-organization의 다른 모든 네임스페이스와 동일한 셀에 있게 됩니다. 아무것도 선택하지 않으면 기본적으로 GitLab.com Public로 설정되며 기존 조직과 분리되어 있음을 사용자에게 명확히 알립니다.

/gitlab-org의 일부인 GitLab 팀 멤버의 경험

해당 사용자는 레거시 사용자로 간주되며, 기본 조직이 GitLab.com Public으로 설정됩니다. 이는 실제로 존재하지 않는 “메타” 조직이지만, Rails 애플리케이션은 이 조직을 해석하여 /dashboard와 같은 레거시 글로벌 기능을 사용할 수 있다는 것을 알고 있습니다. 이러한 레거시 글로벌 기능은 Cell US0에 위치한 네임스페이스를 통해 데이터를 볼 수 있게 해주는 것입니다. 또한, Rails 백엔드는 /dashboard와 같은 애매한 경로의 기본적인 셀을 렌더링할 때 기본 셀이 Cell US0임을 알고 있습니다. 마지막으로 해당 사용자는 /my-organization과 같은 다른 셀의 조직으로 이동할 수 있지만, 이때 사용자는 일부 데이터가 누락될 수 있다는 메시지가 표시됩니다(MRs/이슈/할 일의 개수가 없는 경우 등).

/gitlab-org/gitlab으로 이동 (로그인되어 있지 않음)

  1. 사용자가 미국에 있으므로 DNS는 미국의 라우터로 해석됩니다.
  2. 라우터는 /gitlab-orgCell US0에 있는 것을 알기 때문에 해당 셀로 요청을 보냅니다.
  3. Cell US0은 응답을 제공합니다.
sequenceDiagram participant user as User participant router_us as Router US participant cell_us0 as Cell US0 user->>router_us: GET /gitlab-org/gitlab router_us->>cell_us0: GET /gitlab-org/gitlab cell_us0->>user: <h1>GitLab.org...

/로 이동

  1. 사용자가 미국에 있으므로 DNS는 미국의 라우터로 해석됩니다.
  2. 라우터에는 / 경로에 대한 캐시가 없습니다(Rails는 명시적으로 이 경로를 캐시하라고 지시하지 않음).
  3. 라우터는 Cell US1을 무작위로 선택합니다.
  4. Rails 애플리케이션은 사용자의 기본 조직이 GitLab.com Public임을 알고 있으므로 사용자를 /dashboards로 리디렉션합니다(레거시 사용자만이 /dashboard 글로벌 뷰를 볼 수 있습니다).
  5. 라우터에는 /dashboard 경로에 대한 캐시가 없습니다(Rails는 명시적으로 이 경로를 캐시하라고 지시하지 않음).
  6. 라우터는 Cell US1을 무작위로 선택합니다.
  7. Rails 애플리케이션은 사용자의 기본 조직이 GitLab.com Public임을 알고 있으므로 사용자가 /dashboards를 로드할 수 있으며(레거시 사용자만이 /dashboard 글로벌 뷰를 볼 수 있습니다) 동시에 레거시 셀인 Cell US0으로 리디렉션합니다.
  8. Cell US0은 오늘의 글로벌 뷰 대시보드 페이지 /dashboard를 제공합니다.
sequenceDiagram participant user as User participant router_us as Router US participant cell_us0 as Cell US0 participant cell_us1 as Cell US1 user->>router_us: GET / router_us->>cell_us1: GET / cell_us1->>user: 302 /dashboard user->>router_us: GET /dashboard router_us->>cell_us1: /api/v4/internal/cells/learn?method=GET&path_info=/dashboard cell_us1->>router_us: {path: "/dashboard", cell: "cell_us0", source: "routable"} router_us->>cell_us0: GET /dashboard cell_us0->>user: <h1>대시보드...

/my-company/my-other-project로 이동함(로그인되어 있지만이 프로젝트는 비공개이므로 액세스할 수 없음)

404 오류가 발생합니다.

비인증 사용자의 경험

흐름은 로그인한 사용자와 유사하지만 /dashboard와 같은 전역 경로는 기본 조직을 선택할 옵션이 없기 때문에 로그인 페이지로 리디렉션됩니다.

새 고객이 가입함

이미 조직의 구성원이거나 새로운 조직을 생성할지 묻습니다. 둘 중 어느 것도 선택하지 않으면 기본 GitLab.com Public 조직에 등록됩니다.

조직이 한 셀에서 다른 셀로 이동함

TODO

URL에 네임스페이스가 포함되지 않는 GraphQL/API 요청

TODO

최근 이슈/MR을 기억하는 검색 막대의 자동 완성 제안 기능

TODO

전역 검색

TODO

관리자

/admin 페이지 로드

  1. /adminCell US0에 잠겨 있습니다.
  2. Admin 내의 일부 엔드포인트, 예를 들어 Admin의 프로젝트는 셀에 대해 범위가 설정되며 사용자는 드롭다운에서 올바른 것을 선택해야 합니다. 이는 /admin/cells/cell_0/projects와 같은 엔드포인트로 이어집니다.

Postgres의 관리 영역 설정은 발산을 피하기 위해 모든 셀에서 공유되지만 URL 및 UI에 어떤 셀이 관리 영역 페이지를 제공하는지 동적 데이터가 생성되고 운영자가 특정 셀을 보고 싶어할 수 있기 때문에 명시적으로 표시합니다.

해결해야 할 더 많은 기술적 문제

모든 셀 간에 사용자 세션 복제

현재 사용자 세션은 Redis에 저장되지만 각 셀마다 고유의 Redis 인스턴스가 있습니다. 이미 세션용으로 전용 Redis 인스턴스를 사용하고 있으므로 gitlab_users PostgreSQL 데이터베이스와 마찬가지로 이를 모든 셀과 공유하는 것을 고려할 수 있습니다. 그러나 가장 중요한 고려 사항은 여전히 대부분의 세션을 동일한 지역에서 가져오고 싶어할 것이기 때문에 대기 시간일 것입니다.

대안으로 사용자 세션을 모든 세션 데이터를 인코딩하는 JWT 페이로드로 이동시키는 것이 있지만 이에는 단점이 있습니다. 예를 들어 사용자의 비밀번호가 변경되었을 때 또는 다른 이유로 세션이 만료되기 어렵습니다.

셀 간 이동하는 방법

셀 간의 데이터 이동은 모든 데이터 저장소를 고려해야 합니다:

  1. PostgreSQL
  2. 공유 상태의 Redis
  3. Gitaly
  4. Elasticsearch

타이밍 공격을 통해 비공개 그룹의 존재를 아직도 누설할 수 있을까요?

EU에 라우터가 있고 EU 라우터가 기본적으로 EU에 위치한 셀로 리디렉션한다는 것을 알고 있다면 그들의 대기 시간을 알 수 있습니다(우리는 라우터가 10밀리초라고 가정합니다). 이제 요청이 되돌아와 US로 리디렉션된 경우 다른 대기 시간이 있기 때문에(왕복이 약 60밀리초가 될 것입니다) 404가 미국 셀에서 반환되었다는 것을 추측할 수 있고 여러분의 404가 사실 403임을 알 수 있습니다.

실제로 다른 지역에 셀을 구현하기 전에 이를 미룰 수 있습니다. 이러한 타이밍 공격은 이미 우리가 오늘처럼 권한 확인을 하는 방법으로 이론적으로 가능하지만 시간 차이가 너무 작아서 감지할 수 없을 것입니다.

이러한 위험을 완화하는 한 가지 기술은 라우터가 셀로부터 404를 반환하는 모든 요청에 임의의 지연을 추가하는 것일 수 있습니다.

모든 셀 간에 러너를 공유해야 할까요?

두 가지 옵션이 있으며 더 쉬운 옵션을 결정해야 합니다:

  1. 러너 등록 및 대기열 테이블을 분해하여 모든 셀간에 공유합니다. 이는 확장 가능성에 대한 영향을 줄 수 있으며 그룹/프로젝트 러너를 포함해야 할지 여부를 고려해야 할 수 있습니다. 이는 확장 가능성과 관련된 우려사항일 수 있습니다.
  2. 러너는 셀별로 등록되며 아마도 많은 셀에 대해 별도의 러너 그룹이 있거나 많은 셀에 동일한 러너를 등록하거나 대기열에 대한 영향을 미칠 수 있습니다.

모든 셀에 걸친 충돌이 일어나서는 안 되는 것들을 위해 어떻게 고유 ID를 보장할까요?

이 프로젝트는 적어도 네임스페이스와 프로젝트가 모든 셀에서 고유한 ID를 가지고 있다고 가정합니다. 많은 요청이 그들의 ID를 기준으로 경로가 설정되어야 하기 때문입니다. 그러나 그러한 테이블들이 서로 다른 데이터베이스에 걸쳐 있기 때문에 고유한 ID를 보장하는 것은 새로운 솔루션이 필요할 것입니다. 아마도 고유 ID가 필요한 다른 테이블들이 있을 것이며 GraphQL 및 기타 API 및 다른 설계 목표에 따라 라우팅을 해결하기를 원하는 경우 기본 키를 모든 테이블에 대해 고유하게 만들고 싶어할 수 있습니다.