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
proposed @ayufan @mkaeppler @glopezfernandez devops non_devops 2021-05-19

구성 가능한 GitLab 코드베이스

caution
GitLab.com의 전반적인 가용성 개선과 기술 부채 감소에 초점을 맞추기 때문에, 이 청사진에 대해 조치할 여력이 없습니다. 우리는 Q1-FY23에 재평가할 것입니다.

단일 코드베이스의 주요 위험 중 하나는 전체 애플리케이션의 무한한 성장입니다. 추가되는 코드는 애플리케이션을 실행하기 위한 리소스 요구 사항뿐만 아니라 응용프로그램 결합 및 복잡성 폭발을 계속해서 증가시킵니다.

실행 요약

이 청사진은 응용 프로그램 레이어를 도입하여 애플리케이션 코드베이스를 줄이고 개선하는 영향에 대해 논의합니다. 이것은 제안된 솔루션의 긍정적인 측면과 부정적인 결과에 대해 논의하고, GitLab.com 및 더 작은 설치에 미치는 영향을 추정하려고 노력합니다.

응용 프로그램 레이어는 GitLab Rails 코드베이스를 수평으로 나누어 여러 방식으로 실행되는 기능이 단일 기능과 결합도가 높으므로 특정 기능을 쉽게 분리하여 실행할 수 없는 아이디어를 따릅니다.

제안 자체는 기능의 일부를 분리할 수 있도록 허용합니다. 이러한 측면은 스택의 나머지 부분과 별도로 실행되지만 여전히 핵심 부분을 많이 공유합니다. 이 모델은 외부 도구용 API 인터페이스를 제공하고(러너(runner) API, 패키지 API, 피처 플래그 Unleash API), 훨씬 더 나은 신뢰성과 향후 응용프로그램을 쉽게 확장할 수 있는 방법을 제공할 수 있습니다.

실제 분할은 Rails Engines의 사용을 통해 테스트되었습니다. Rails Engines를 사용하여 단일 리포지터리에서 별도의 gem을 구현했습니다. Rails Engines를 사용하면 각각의 컴포넌트를 컴포넌트와 의존성과 함께 잘 설명하고 여러 Rails 엔진으로 구성된 응용프로그램을 실행할 수 있었습니다.

이 청사진은 GitLab의 주요 성공 요소인 단일 및 단일 코드베이스(단일 데이터 리포지터리 포함)를 유지하려는 목표를 가지고 있지만, 응용프로그램을 더 잘 설계하고 코드베이스를 더 구성 가능하게 만드는 것을 허용합니다.

모놀리식의 도전과제(현재 상태)

현재 다중체의 사용은 여러 상황에서 어려움을 겪고 있습니다. 명확한 경계가 없는 내부 단일 큰 다중체 코드베이스는 일부 문제와 비효율성을 야기할 수 있습니다.

  • 심도 결합은 인터페이스 기반 아키텍처를 고려하는 대신, 스파게티 구현을 만들어 더 긴 기간 동안 개발하기 어렵게 합니다
  • 코드베이스의 일부분을 테스트하기 어렵게 만드는 심도 있는 결합. 특정 부분만 테스트하려면 보통 전체 테스트 스위트를 실행해야만 영향을 받는 부분을 확신할 수 있습니다. 이것은 이 과정을 돕기 위한 휴리스틱을 만들어 어느 정도 향상할 수는 있지만, 항상 정확하게 유지하기가 어렵고 오류를 범하거나 어렵습니다.
  • 응용프로그램의 모든 구성요소는 언제나 로딩되어야 한다.
  • 주어진 상황에서 드물게 사용되는 응용프로그램의 일부분을 로딩하려고 할 때 부하가 증가합니다.
  • 높은 메모리 사용으로 전체 응용프로그램의 속도가 느려지게 됩니다. 이로 인해 GC 주기가 길어져 요청 처리의 지연 시간이 급격하게 증가하거나 더 나쁜 경우 CPU 캐시 사용이 느려질 수 있습니다.
  • 파일을 로딩하고 파싱해야하므로 응용프로그램 부팅 시간이 늘어납니다.
  • 더 많은 파일을 로딩하고 파싱해야 하므로 개발 속도가 느려집니다. 응용프로그램이나 테스트 실행이 크게 더 길어지므로 개발 속도와 이터레이션 수가 줄어듭니다.

구성 가능한 코드베이스 치수

일반적으로 코드베이스를 모델링할 수 있는 두 가지 방법에 대해 생각할 수 있습니다:

  • 수직적으로 경계된 컨텍스트에서, 각각이 응용프로그램의 도메인을 나타내는 경계된 컨텍스트로, 예: CI와 관련된 모든 기능은 특정 컨텍스트에 있습니다.
  • 수평적으로 응용프로그램 레이어에서: Sidekiq, GraphQL, REST API, Web Controllers, 모든 도메인 모델 및 DB와 직접적으로 인터페이스하는 서비스

이 청사진은 명시적으로 수평적으로 분할된 응용프로그램 레이어에 대해 이야기합니다.

현재의 경계된 컨텍스트의 상태 (수직 분할)

경계된 컨텍스트는 몇 년 동안 여러 번 상세히 논의된 주제입니다. 다음 이슈에서 반영됩니다:

우리는 경계된 컨텍스트 아이디어를 부분적으로 실행하고 있습니다:

  • 각 팀이 자신의 이름 공간을 소유하도록 만들고, 이 네임스페이스는 코드베이스의 module으로 정의됨
  • 각 팀이 자신의 테스트를 소유하도록 만들고, 네임스페이스는 명확한 경계를 정의함
  • 우리는 네임스페이스를 사용하므로 도메인 전문가와의 도움을 얻을 수 있도록 개별 기여자 또는 리뷰어가 누구에게 연락해야 하는지 알 수 있습니다

현재 모듈 네임스페이스는 팀 경계 주변에 코드베이스를 모델링하는 데 사용되고 있습니다. 오늘날 가장 중요한 네임스페이스는 Ci::Packages::입니다. 이것들은 그룹이 소유한 코드를 명확한 구조로 담는 좋은 방법을 제공합니다.

그러나 경계된 컨텍스트는 개발을 도와주지만 위에서 언급한 목표를 달성하기에 도움이 되지 않습니다. 이것은 단순히 코드의 논리적 분할뿐이며 심도 있는 결합을 막지 못합니다. (대개 발생하는) CI 파이프라인의 백그라운드 처리와 러너 API 인터페이스 사이에 순환 의존성을 만드는 것이 가능합니다. API가 Sidekiq Worker를 호출할 수 있고, Sidekiq는 API를 사용하여 엔드포인트 경로를 만들 수 있습니다.

Bounded Contexts는 응용프로그램이 무엇이 의존하는지를 알기 위해 우리의 코드베이스를 더 똑똑하게 만들지 않습니다. 전체 코드베이스를 로드하여 실행해야 하는 단일 패키지로 처리됩니다.

경계된 컨텍스트의 단점을 고려하여 추가적인 고려사항:

  • 부족한 지식과 중복 코드로 이어질 수 있음
  • 심도 있는 결합으로 반복 과정과 최소한의 변경이 어려울 수 있음
  • 세로 분할로 인해 격파 효과가 생길 수 있으며 격리하기 어려울 수 있음

응용프로그램 레이어(수평적으로 분할)

우리가 개발 및 리뷰 프로세스를 돕는 네임스페이스 분리 형태로 경계된 컨텍스트를 계속 활용하는 동안, 응용프로그램 레이어는 다른 기능적 부분들 사이에 깔끔한 분리를 만들 수 있는 방법을 제공할 수 있습니다.

우리의 주요 코드베이스(‘GitLab Rails’: Ruby on Rails에서 실행되는 GitLab)는 많은 암시적 응용프로그램 레이어로 구성됩니다. 각 레이어 간에 명확한 경계가 없어서 심도 결합이 발생합니다.

응용프로그램 레이어 개념은 우리가 웹 인터페이스를 통해 발생하는 웹 요청을 처리하는 웹 컨트롤러, 자동화된 도구에서 오는 API 호출, API 호출 러너로부터 오는 API 호출 등 다양한 애플리케이션 레이어를 살펴봅니다. GitLab 응용프로그램은 오늘날 다음 응용프로그램 레이어들로 분해될 수 있습니다. 이 디렉터리은 모두를 열거한 것은 아니지만, 단일 단일 다중체 코드베이스의 다양한 부분들의 일반적인 디렉터리을 보여줍니다:

  • 웹 컨트롤러: 사용자가 웹 인터페이스를 방문하는 웹 요청을 처리
  • 웹 API: 자동화된 도구로부터 오는 API 호출, 경우에 따라 사용자가 웹 인터페이스를 방문하기도
  • Web 러너 API: 러너에서 오는 API 호출로, 러너가 새 작업을 가져오거나 추적 로그를 업데이트할 수 있게 함
  • 웹 GraphQL: 유연한 API 인터페이스를 제공하여 필요한 데이터만 가져와서 컴퓨팅 및 데이터 전송량을 줄입니다.
  • 웹 액션케이블: 사용자가 웹 인터페이스 방문시 실시간 기능을 가능하게 하는 양방향 연결을 제공
  • 웹 피처 플래그 Unleash 백엔드: GitLab API를 사용하는 Unleash 호환 서버를 제공
  • 웹 패키지 API: 패키지 도구와 호환되는 REST API를 제공: Debian, Maven, 컨테이너 레지스트리 프록시 등.
  • git 노드: SSH 또는 HTTPS를 통해 git pull/push를 인가하는 데 필요한 모든 코드
  • Sidekiq: 백그라운드 작업 실행
  • 서비스/모델/DB: 데이터베이스 구조, 데이터 유효성 검사, 비즈니스 로직 및 정책 모델을 유지하는 데 필요한 모든 코드, DB와 직접적으로 인터페이스하는 서비스

실제 GitLab Rails 분할이 어떻게 보일지를 설명하는 가장 좋은 방법은 위성 모델입니다. 여기서는 하나의 코어가 모든 위성 컴포넌트에 걸쳐 공유되는 모습입니다. 이 설계의 의미는 위성 컴포넌트가 서로 제한된 방법으로 통신해야 한다는 것입니다. 대부분의 경우 단일 다중체 응용프로그램에서 응용프로그램이 코드로 통신을 합니다. 위성 모델에서는 통신은 컴포넌트 외부로 수행되어야 합니다. 이는 데이터베이스, Redis 또는 잘 정의된 노출된 API를 통해 이루어질 수 있습니다.

flowchart TD subgraph Data Store D[Database] R[Redis] end subgraph Rails Engines subgraph Data Access Layer C[Core] end subgraph Web Processing W[Web] end subgraph Background Processing S[Sidekiq] end end C --> D & R W & S -- using application models --> C R -- push background job --> S W -- via async schedule --> S S -- via Web API --> W

온프레미스 설치용 애플리케이션 레이어

온프레미스 설치는 규모가 훨씬 작으며 일반적으로 GitLab Rails를 두 가지 주요 형식으로 실행합니다.

graph LR gitlab_node[로드 밸런서를 사용하는 GitLab 노드] gitlab_node_web[Puma를 실행하는 웹] gitlab_node_sidekiq[Sidekiq를 실행하는 백그라운드 작업] gitlab_node_git[Puma 및 SSH를 실행하는 Git] subgraph GitLab Rails gitlab_rails_web_controllers[컨트롤러] gitlab_rails_api[API] gitlab_rails_api_runners[API 러너] gitlab_rails_graphql[GraphQL] gitlab_rails_actioncable[ActionCable] gitlab_rails_services[서비스] gitlab_rails_models[모델] gitlab_rails_sidekiq[Sidekiq 작업자] end postgresql_db[(PostgreSQL 데이터베이스)] redis_db[(Redis 데이터베이스)] gitlab_node --> gitlab_node_web gitlab_node --> gitlab_node_sidekiq gitlab_node --> gitlab_node_git gitlab_node_web --> gitlab_rails_web_controllers gitlab_node_web --> gitlab_rails_api gitlab_node_web --> gitlab_rails_api_runners gitlab_node_web --> gitlab_rails_graphql gitlab_node_web --> gitlab_rails_actioncable gitlab_node_git --> gitlab_rails_api gitlab_node_sidekiq --> gitlab_rails_sidekiq gitlab_rails_web_controllers --> gitlab_rails_services gitlab_rails_api --> gitlab_rails_services gitlab_rails_api_runners --> gitlab_rails_services gitlab_rails_graphql --> gitlab_rails_services gitlab_rails_actioncable --> gitlab_rails_services gitlab_rails_sidekiq --> gitlab_rails_services gitlab_rails_services --> gitlab_rails_models gitlab_rails_models --> postgresql_db gitlab_rails_models --> redis_db

GitLab.com의 애플리케이션 레이어

규모 때문에 GitLab.com은 실행에 훨씬 더 많은 주의가 필요합니다. 이는 리소스를 더 잘 관리하고 다양한 기능 부분에 대한 SLA를 제공하기위한 것입니다. 아래 차트에서는 GitLab.com 애플리케이션 레이어의 간단한 개요를 제공합니다. 개체 리포지터리나 Gitaly 노드와 같은 모든 컴포넌트를 포함하지는 않지만, 서로 다른 컴포넌트 간의 GitLab Rails 의존성 및 GitLab.com에서 구성된 방법을 보여줍니다.

graph LR gitlab_com_lb[GitLab.com 로드 밸런서] gitlab_com_web[Puma를 실행하는 웹 노드] gitlab_com_api[Puma에서 실행하는 API 노드] gitlab_com_websockets[Puma에서 실행하는 웹소켓 노드] gitlab_com_sidekiq[Sidekiq에서 실행하는 백그라운드 작업] gitlab_com_git[Puma 및 SSH로 실행하는 Git 노드] subgraph GitLab Rails gitlab_rails_web_controllers[컨트롤러] gitlab_rails_api[API] gitlab_rails_api_runners[API 러너] gitlab_rails_graphql[GraphQL] gitlab_rails_actioncable[ActionCable] gitlab_rails_services[서비스] gitlab_rails_models[모델] gitlab_rails_sidekiq[Sidekiq 작업자] end postgresql_db[(PostgreSQL 데이터베이스)] redis_db[(Redis 데이터베이스)] gitlab_com_lb --> gitlab_com_web gitlab_com_lb --> gitlab_com_api gitlab_com_lb --> gitlab_com_websockets gitlab_com_lb --> gitlab_com_git gitlab_com_web --> gitlab_rails_web_controllers gitlab_com_api --> gitlab_rails_api gitlab_com_api --> gitlab_rails_api_runners gitlab_com_api --> gitlab_rails_graphql gitlab_com_websockets --> gitlab_rails_actioncable gitlab_com_git --> gitlab_rails_api gitlab_com_sidekiq --> gitlab_rails_sidekiq gitlab_rails_web_controllers --> gitlab_rails_services gitlab_rails_api --> gitlab_rails_services gitlab_rails_api_runners --> gitlab_rails_services gitlab_rails_graphql --> gitlab_rails_services gitlab_rails_actioncable --> gitlab_rails_services gitlab_rails_sidekiq --> gitlab_rails_services gitlab_rails_services --> gitlab_rails_models gitlab_rails_models --> postgresql_db gitlab_rails_models --> redis_db

레이어 의존성

온프레미스에서 GitLab를 실행하는 방식과 GitLab.com에서 실행하는 방식 사이에는 GitLab Rails의 주요 분할 선이 나타납니다:

  • Web: 모든 API, 모든 컨트롤러, 모든 GraphQL 및 ActionCable 기능을 포함
  • Sidekiq: 모든 백그라운드 처리 작업을 포함
  • Core: 웹 및 Sidekiq 간에 공유되어야 하는 모든 데이터베이스, 모델 및 서비스를 포함

이러한 최상위 애플리케이션 레이어 각각은 모든 관련 의존성을 포함합니다:

  • 모든 경우에 기본 데이터베이스 구조 및 애플리케이션 모델이 필요합니다
  • 일부 경우에는 종속 서비스가 필요합니다
  • 응용 프로그램의 일부 공통 라이브러리만 필요합니다
  • 요청된 기능을 지원하는 ​​젬이 필요합니다
  • 개별 레이어는 다른 동료 레이어를 사용해서는 안 되며(타이트한 결합), 대신 데이터를 공유하기 위해 API, Redis 또는 DB를 통해 연결해야 합니다(느슨한 결합)

제안

메모리 팀은 애플리케이션 레이어 도입의 영향을 이해하기 위해 컨셉 증명 단계를 진행했습니다. 우리는 이 제안을 실행하는 데 필요한 복잡성, 영향 및 필요한 반복을 이해하기 위해 이를 수행했습니다.

여기서 제안된 것은 이 청사진의 영향을 평가하는 것이지만, 실행할 최종 솔루션이 아닙니다. 정의된 PoC는 Merge되어서는 안 되는 것이지만, 향후 작업의 기반으로 기능합니다.

Rails 엔진을 사용한 PoC

웹 애플리케이션 레이어를 모델링하기 위해 Rails 엔진을 사용하기로 결정했습니다. 웹 엔진에는 컨트롤러, API, GraphQL이 포함되어 있습니다. 이는 웹 노드를 모든 종속 항목과 함께 실행할 수 있도록 했지만, Sidekiq에서 이러한 컴포넌트를로드하지 않을 때의 영향을 메트릭할 수 있었습니다.

이 모든 작업은 다음 MRs에 구현되어 있습니다:

수행된 작업:

  • Rails 엔진을 사용했습니다.
  • 위의 MR에서 볼 수있는 변경의 99%는 파일을 그대로 이동한 것입니다.
  • 모든 GraphQL 코드 및 사양을 그대로 engines/web_engine/로 이동했습니다.
  • 모든 API 및 컨트롤러 코드 및 사양을 engines/web_engine로 이동했습니다.
  • 스택의 자체적인 컴포넌트로 engines/web_engine/을 테스트하기 위해 CI를 수정했습니다.

제안된 솔루션의 구현 세부 정보

  1. 각 애플리케이션 레이어에 대한 새로운 Rails 엔진 소개

    미래에 도입할 각 애플리케이션 레이어를 위해 서로 다른 엔진을 포함할 수 있는 engines 폴더를 만들었습니다.

    위의 PoC에서 새로운 웹 애플리케이션 레이어를 소개했으며, 해당 레이어는 engines/web_engine 폴더에 위치해 있습니다.

  2. 모든 코드 및 사양을 engines/web_engine/로 이동

    • 모든 GraphQL 코드 및 사양을 그대로 engines/web_engine/로 이동했습니다.
    • 모든 Grape API 및 컨트롤러 코드를 그대로 engines/web_engine/로 이동했습니다.
  3. 젬을 engines/web_engine/로 이동

    • 모든 GraphQL 젬을 실제 web_engine Gemfile로 이동했습니다.
    • API 젬을 실제 web_engine Gemfile로 이동했습니다.
     Gem::Specification.new do |spec|
       spec.add_dependency 'apollo_upload_server'
       spec.add_dependency 'graphql'
       spec.add_dependency 'graphiql-rails'
          
       spec.add_dependency 'graphql-docs'
       spec.add_dependency 'grape'
     end
    
  4. 라우트를 engines/web_engine/config/routes.rb 파일로 이동

    • GraphQL 라우트를 web_engine 라우트로 이동했습니다.
    • API 라우트를 web_engine 라우트로 이동했습니다.
    • 대부분의 컨트롤러 라우트를 web_engine 라우트로 이동했습니다.
     Rails.application.routes.draw do
       post '/api/graphql', to: 'graphql#execute'
       mount GraphiQL::Rails::Engine, at: '/-/graphql-explorer', graphql_path:
       Gitlab::Utils.append_path(Gitlab.config.gitlab.relative_url_root, '/api/graphql')
          
       draw :api
          
       #...
     end
    
  5. 초기화기를 engines/web_engine/config/initializers 폴더로 이동

    • graphql.rb 초기화기를 web_engine 초기화기 폴더로 이동했습니다.
    • grape_patch.rbgraphe_validatorsweb_engine 초기화기 폴더로 이동했습니다.
  6. GitLab 애플리케이션을 WebEngine과 연결

    GitLab Gemfile.rb에서 엔진 그룹에 web_engine을 추가합니다.

     # Gemfile
     group :engines, :test do
       gem 'web_engine', path: 'engines/web_engine'
     end
    

    해당 gem은 기본적으로 요청되지 않기 때문에 :engines 그룹 내에 있습니다.

  7. 엔진을로드하는 시기를 구성합니다.

    GitLab config/engines.rb에서 Gitlab::Runtime을 기반으로 엔진을 로드할 때의 구성을 지정할 수 있습니다.

    # config/engines.rb
    # 웹 서버 또는 레일스 콘솔을 실행하는 경우에만 로드
    if Gitlab::Runtime.puma? || Gitlab::Runtime.console?
      require 'web_engine'
    end
    
  8. 엔진 구성

    해당 엔진은 Rails::Engine 클래스를 상속합니다. 따라서 엔진이 지정된 경로에 있는 엔진이라는 것을 Rails에 알릴 수 있어 앱 디렉터리를 모델, 메일러, 컨트롤러 및 뷰의 로드 경로에 추가하는 작업을 수행합니다. lib/web_engine/engine.rb 파일은 표준 Rails 애플리케이션의 config/application.rb 파일과 기능이 같습니다. 따라서 엔진은 모든 레일티와 애플리케이션에서 공유되는 구성 객체에 액세스할 수 있습니다. 또한 각 엔진은 해당 엔진에 대한 autoload_paths, eager_load_paths, autoload_once_paths 설정에 액세스할 수 있으며 해당 설정은 해당 엔진에 대해 범위가 지정됩니다.

    module WebEngine
      class Engine < ::Rails::Engine
        config.eager_load_paths.push(*%W[#{config.root}/lib
                                         #{config.root}/app/graphql/resolvers/concerns
                                         #{config.root}/app/graphql/mutations/concerns
                                         #{config.root}/app/graphql/types/concerns])
           
        if Gitlab.ee?
          ee_paths = config.eager_load_paths.each_with_object([]) do |path, memo|
            ee_path = config.root
                        .join('ee', Pathname.new(path).relative_path_from(config.root))
            memo << ee_path.to_s
          end
          # Eager load should load CE first
          config.eager_load_paths.push(*ee_paths)
        end
      end
    end
    
  9. 테스팅

    CI를 수정하여 스택의 자체 컴포넌트인 engines/web_engine/을 테스트했습니다.

    • spec 파일을 engines/web_engine/spec 폴더로 그대로 이동했습니다.
    • ee/spec 파일을 engines/web_engine/ee/spec 폴더로 그대로 이동했습니다.
    • 주요 응용 프로그램에서 환경 변수 TEST_WEB_ENGINE을 사용하여 사양을 제어했습니다.
    • TEST_WEB_ENGINE 환경 변수를 사용하여 engines/web_engine/spec 테스트를 별도로 실행하는 새로운 CI 작업을 추가했습니다.
    • TEST_WEB_ENGINE 환경 변수를 사용하여 engines/web_engine/ee/spec 테스트를 별도로 실행하는 새로운 CI 작업을 추가했습니다.
    • 모든 화이트박스 프론트엔드 테스트를 TEST_WEB_ENGINE=true로 실행했습니다.

결과

이러한 변경 사항 도입의 효과:

  • RSS 저장
  • 61.06 MB (7.76%) - GraphQL이 없는 Sidekiq
  • 100.11 MB (12.73%) - GraphQL 및 API가 없는 Sidekiq
  • 208.83 MB (26.56%) - GraphQL, API, 컨트롤러가 없는 Sidekiq
  • Puma를 실행하는 Web 노드의 크기는 이전과 동일했습니다.

GraphQL, API, 컨트롤러가 없는 단일 Sidekiq 클러스터의 start-up 이벤트에 대한 저장

  • 단일 Sidekiq 클러스터의 경우 264.13 MB의 RSS를 저장했습니다 (28.69%)
  • 264.09 MB USS를 저장했습니다 (29.36%)
  • 부팅 시간이 45.31초에서 21.80초로 줄었습니다. 23.51초 빨라졌습니다 (51.89%)
  • 라이브 객체가 805,772개 줄었고, 할당된 객체가 4,587,535개 줄었으며, 할당된 페이지는 2,866개 줄고 힙 외부의 객체 공간이 3.65 MB 줄었습니다
  • 2,326개의 코드 파일을 덜로딩했습니다 (15.64%)
  • 단일 전체 GC 주기의 기간을 0.80초에서 0.70초로 줄였습니다 (12.64%)

Puma 단일은 예상대로 매우 작은 차이를 보여주었습니다.

자세한 내용은 이슈에서 확인할 수 있습니다.

GitLab.com에 미치는 영향

GitLab.com의 규모에 대한 결과를 추정하면 오늘날 우리는 다음을 사용합니다:

web_engine를 도입하여 가능한 최대 저장을 추정합니다:

  • GC 사이클 타임을 20% 줄여 200ms에서 160ms로 줄입니다
  • 초당 GC 주기 수는 동일한 상태로 유지되지만, GC 사이클 타임이 감소하여 8개의 vCPU 대신 약 6개의 vCPU를 사용할 것으로 예상됩니다
  • 최선의 경우에는 Sidekiq에 대해 GitLab.com에서 최대 137GB를 저장할 것으로 예상됩니다

이 모델은 sidekiq_engine를 도입하여 유사한 이점을 제공하는 방식으로 확장될 수 있습니다 (사용자에게 뚜렷한 영향을 미치므로 더 중요함)을 Web 노드.

결과

이러한 변경 사항을 도입하여 다수의 이점을 얻었습니다.

장점:

  • 크게 줄어든 메모리 사용량
  • Sidekiq의 애플리케이션 로드 시간이 크게 단축
  • 매우 짧아진 GC 주기로 인한 Sidekiq 서비스의 반응성 크게 향상
  • 애플리케이션의 일부를 테스트하기가 훨씬 용이해짐, 예를 들어 web_engines/를 변경할 경우 해당 애플리케이션 계층에 대해서만 다시 테스트하면 됨
  • 코드베이스의 단일 아키텍처를 유지했지만 데이터베이스 및 애플리케이션 모델을 공유함
  • 인프라 측면에서 상당한 절약
  • 응용 프로그램 풋프린트를 줄여서 환경이 제한된 상황에서 편안한 실행 능력

단점:

  • GraphQL 구독을 구현하기가 더 어려워짐, Sidekiq의 경우 구독을 전달할 다른 방법이 필요함
  • 일부 Sidekiq에서 api_v4 경로를 사용할 수 있음 (예: api_v4_projects_path가 사용되는 몇 가지 서비스)
  • 모델 및 서비스에서 사용되는 url_helpers 경로는 Sidekiq에서 사용될 수 있음 (예: Gitlab::Routing.url_helpers.project_pipelines_pathExpirePipelineCacheServiceExpirePipelineCacheWorker에서 사용됨)

예: GraphQL

Draft: PoC - Move GraphQL to the WebEngine

  • 99%의 변경(위 MR에서 보이는 대로)은 파일을 그대로 이동하는 것입니다.
  • web_engine의 교차 의존성, 스펙 수정 및 구성에 대한 실제 작업
  • CI를 적응시켜 engines/web_engine/를 스택의 자기충족 컴포넌트로 테스트하게 되었습니다

현재, GraphQL을 로드하려면 14480개의 파일을 로드/요구하게 된다는 것을 발견했습니다:

우리는 GitLab을 시작할 때 14480개의 파일을 로드/요구한다는 것을 발견했습니다. 1274개의 파일은 GraphQL에 속합니다. 이는 우리가 1274개의 애플리케이션 파일을 로드하지 않고 필요할 때만 로딩하면 (Sidekiq에 대해) 많은 메모리를 절약할 수 있음을 의미합니다.

GraphQL은 특정 컨텍스트에서만 실행되어야 합니다. 로드되는 시기를 제한할 수 있다면 애플리케이션의 효율성을 향상시킬 수 있습니다. 예를 들어, 모든 규모의 설치에 적용 가능합니다.

GraphQL 및 웹소켓의 잠재적인 도전 과제는 어느 시점에서는 Action Cable 구독을 사용하고 Sidekiq에서 클라이언트로 GraphQL/API 페이로드를 푸시하고 싶어질 수 있다는 것입니다. 이렇게 하면 Sidekiq가 Redis를 통해 정보를 게시하고 ActionCable 노드가 해당 정보를 연결된 클라이언트로 전달할 수 있게 됩니다. 이와 같은 작업 방식은 위 모델에서 가능하지만 어떤 경우에는 무엇을 보내야 하는지 계산하기 위해 GraphQL 또는 API (HTTP 엔드포인트를 통해)를 사용해야 합니다.

대안적인 방법은 항상 ActionCable 노드(웹소켓을 처리하는 노드)가 보내기 쿼리에 기반한 페이로드를 생성하도록 하는 알림 시스템을 사용하는 것입니다. 이는 연결된 클라이언트에 대한 주어진 연결을 처리하는 ActionCable이기 때문에 적용 가능할 수 있습니다. 하지만 많은 클라이언트가 동일한 리소스를 관찰할 경우 동일한 페이로드를 재계산해야 하는 단점이 있을 수 있습니다. 그러나 이러한 시스템의 행동은 여전히 보안 목적을 위해 사용할 수 있고, 생성된 페이로드는 관찰 클라이언트의 권한에 의존할 수 있으므로(익명 사용자와 프로젝트 구성원에 대해 다른 내용을 표시할 수 있음) 여전히 원하는 시스템의 동작일 수 있습니다.

예시: API

Draft: PoC - Move only Grape API:API to the WebEngine

  • 위 MR에서 볼 수 있는 것처럼, 변경 사항의 99%는 파일을 그대로 이동하는 것입니다.
  • 교차 의존성, 명세, 초기화 프로그램 구성, 젬 및 라우트를 수정하는 실제 작업입니다.

Grape::API는 웹 서버 컨텍스트에서만 실행되어야 하는 또 다른 예시입니다.

Grape API의 잠재적인 문제:

  • 현재 모델에서 일부 API::API 의존성이 있습니다 (예: 프로젝트 모델API::Helpers::Version 의존성 또는 geo_retrieve_url을 위해 GeoNode 모델에서 API::API 의존성이 있음).
  • 헬퍼, 프레젠테이션 및 뷰에서 api_v4 경로를 사용합니다 (예: PackagesHelper에서 api_v4_projects_path 사용).

예시: 컨트롤러

Draft: PoC - Move Controllers and Grape API:API to the WebEngine

  • 위 MR에서 볼 수 있는 것처럼, 변경 사항의 99%는 파일을 그대로 이동하는 것입니다.
  • 교차 의존성, 명세, 초기화 프로그램 구성, 젬 및 라우트를 수정하는 실제 작업입니다.

컨트롤러, 직렬화기, 일부 프레젠테이션 및 Grape:Entities 중 일부는 웹 서버 컨텍스트에서만 실행되어야 하는 좋은 예시입니다.

컨트롤러 이동과 관련된 잠재적인 문제:

  • web_engine이 로드될 때 Gitlab::Patch::DrawRouteengines/web_engine/config/routesengines/web_engine/ee/config/routes를 지원할 수 있도록 확장해야 했습니다. 여기에 잠재적인 해결책이 있습니다.
  • 모델 및 서비스에서 Gitlab::Routing.url_helpers 경로가 사용되며, Sidekiq에서 사용될 수 있습니다 (예: ExpirePipelineCacheServiceGitlab::Routing.url_helpers.project_pipelines_pathExpirePipelineCacheWorker에서 사용됨).

Packwerk

note
Packwerk는 현재 버그 수정만 허용하고 있으며 활발하게 개발되고 있지 않습니다. 자세한 내용을 확인하세요.

미래의 영향

애플리케이션 레이어 및 이 제안은 현재 web_engine만 정의합니다. 동일한 패턴을 따르면 우리는 더 나은 분리, 낮은 메모리 사용량 및 훨씬 더 나은 유지 관리성을 유지할 수 있는 추가 엔진을 손쉽게 도입할 수 있습니다.

이것은 새로운 인터페이스를 소개하는 데 사용할 수 있는 모든 기능을 핵심 코드베이스의 일부가 되지 않아도 되는 특징에 대한 프레임워크가 될 것입니다. 우리를 더 나은 확장성을 가진 애플리케이션을 가능하게 하지만 단일 코드베이스 및 GitLab의 모놀리식 아키텍처를 유지하는 데 기여합니다.

오늘날, 세 가지 애플리케이션 레이어를 정의하는 것이 합리적으로 보입니다:

  • gitlab-core: 핵심 기능: DB 구조, 모델, 서비스, 공통 라이브러리. 데이터 액세스 레이어를 나타냅니다. 초기에는 GitLab을 실행하는 데 필요한 모든 서비스가 포함될 수 있습니다. 이는 잠재적으로 미래에 더 작은 측면으로 분할될 수 있습니다.
  • gitlab-web: 웹 서버 컨텍스트에서 실행해야 하는 컨트롤러/API/GraphQL/ActionCable 기능 (gitlab-core에 의존).
  • gitlab-sidekiq: Sidekiq 워커를 실행하는 데 필요한 백그라운드 작업 기능 (gitlab-core에 의존).

이 모델은 현재 공유 핵심과 위성으로 가장 잘 설명됩니다. 공유 핵심은 데이터 액세스 레이어를 정의하며, 위성은이 데이터를 표시하고 처리하는 방법을 정의합니다. 위성은 Core와만 통신할 수 있습니다. 다른 위성을 직접로드하거나 대화하지 않을 수 있습니다. API, GraphQL 또는 Redis 형식의 명확히 정의된 인터페이스를 사용하지 않는 이상.

우리는 허용하는 엔진의 수를 제한하는 것이 합리적이라고 가정할 수 있습니다. 초기 제안은 5개의 엔진을 생성 할 수 있도록 허용하는 것입니다. 엔진이 폭발적으로 증가하지 않도록 하기 위함입니다.

이슈 및 Merge Request