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

구성 가능한 GitLab 코드베이스

참고: GitLab.com의 전체 가용성 향상 및 기술 부채 감소에 중점을 두기 때문에 이 청사진에 대한 조치를 취할 여유가 없습니다. Q1-FY23에서 재평가할 예정입니다.

단일 코드베이스의 주요 위험 중 하나는 전체 응용 프로그램의 무한한 성장입니다. 계속해서 코드가 추가되면 실행하는 데 필요한 리소스 요구 사항이 증가할 뿐만 아니라 응용 프로그램 결합과 복잡성의 폭발도 증가합니다.

실행 요약

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

응용 프로그램 계층은 GitLab Rails 코드베이스를 수직 분할이 아니라 GitLab을 실제로 실행하는 방식의 패턴을 따라 수평적으로 나누려고 합니다. 이는 단일 기능이 다양한 방식으로 실행되어야 한다는 아이디어를 따릅니다(CI 예를 들어 Web 인터페이스, API를 사용하여 백그라운드 처리). 또한 특정 기능(예: CI)만 쉽게 나머지 응용 프로그램에서 분리하여 실행 할 수 없기 때문에 결합되어 있다는 것을 고려해야 합니다.

이 제안 자체는 일부 기능의 일부 요소를 분리해 떨어지게 합니다. 이러한 측면은 스택의 나머지에서 별도로 실행되는 구성요소로 다룰 수 있지만 여전히 핵심의 많은 부분을 공유합니다. 이 모델은 외부 도구에 대한 API 인터페이스를 제공하기 위해 구현될 수 있으며(Runners API, Packages API, Feature Flags Unleash API) 미래에 응용 프로그램을 훨씬 더 잘 견고하게 만들고 쉽게 확장할 수 있는 방법을 제공할 수 있습니다.

실제 분할은 Rails Engines의 사용을 통해 테스트되었으며 단일 리포지토리 내의 별도의 젬을 구현했습니다. Rails Engines를 사용하여 개별 컴포넌트를 의존성 및 관련 파일을 모두 포함하여 실행할 수 있었습니다.

이 청사진은 GitLab의 모든 주요 측면을 유지하는 데 목표를 두고 있습니다: 단일 및 단일 코드베이스(단일 데이터 저장소 포함), 그러나 응용 프로그램을 더 잘 모델링하고 코드베이스를 더 구성 가능하게 만들 수 있게 합니다.

단일체의 도전과제(현재 상태)

현재 단일체의 사용은 다양한 경우에 도전적임이 입증되었습니다. 명확한 경계가 없는 단일 대형 단일체 코드베이스는 몇 가지 문제와 비효율성을 초래합니다. 일부 문제는 다음과 같습니다.

  • 깊은 결합은 인터페이스 기반 아키텍처를 구축하는 대신 스파게티 구현으로 이어져서 장기적으로 응용 프로그램을 개발하기가 더 어려워집니다
  • 코드베이스의 부분을 테스트하는 것이 더 어려운 깊은 결합으로 인해 더 어려워집니다. 우리가 보통 어떤 부분이 영향을받았는지 자신있게 알기 위해서는 전체 테스트 스위트를 실행해야 합니다. 이 프로세스를 보조하기 위한 휴리스틱을 구축하되 이는 오류가 있을 가능성이 있고 항상 정확하게 유지하기가 어렵습니다
  • 전체 응용 프로그램의 모든 컴포넌트는 항상 로드되어야 하기 때문에 응용 프로그램의 부분만 실행하려면
  • 주어진 맥락에서 거의 사용되지 않는 응용 프로그램의 부분을로드하는 것이 증가된 리소스 사용
  • 높은 메모리 사용은 전체 응용 프로그램을 느리게하고 GC 주기의 지연 시간을 증가시켜 요청을 처리하는 데 훨씬 더 오랜 지연 시간이 걸리거나 CPU의 캐시 사용이 더 나빠지게 합니다
  • 더 많은 파일을로드하고 구문 분석해야 하기 때문에 응용 프로그램 부팅 시간 증가
  • 더 긴 부팅 시간은 응용 프로그램 또는 테스트 실행하는 것이 훨씬 더 오래 걸려 개발을 느리게 만들고 반복 횟수를 줄이는 동안 속도가 느려집니다

구성 가능한 코드베이스의 차원

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

  • 수직적으로: Bounded Contexts에 들어가는 각각의 경계 내용, 예: CI와 관련된 모든 기능은 특정 컨텍스트에 있음
  • 수평적으로: 응용 프로그램 계층 내에서: Sidekiq, GraphQL, REST API, Web Controller, 모든 DB와 직접적으로 상호 작용하는 도메인 모델 및 서비스

이 청사진은 명시적으로 수평적 분할 및 응용 프로그램 계층에 대해 언급합니다.

Bounded Contexts의 현재 상태(수직적 분할)

Bounded Contexts는 몇 년 동안 여러 번 광범위하게 토론 된 주제입니다. 다음과 같은 문제를 반영:

우리는 Bounded Contexts 아이디어를 부분적으로 실행중입니다:

  • 각 팀이 자체 네임스페이스를 소유하도록 만들며, 이는 코드베이스의 module로 정의된 네임스페이스임
  • 각 팀이 자신의 테스트를 소유하도록 만들고 네임스페이스는 명확한 경계를 정의함
  • 네임스페이스를 사용하기 때문에 개별 기여자 또는 리뷰어는 주어진 컨텍스트에서 도메인 전문가에게 도움을 요청할 수 있음

모듈 네임스페이스는 현재 팀의 경계를 중심으로 코드베이스 모델링에 사용되고 있습니다. 오늘날 가장 중요한 네임스페이스는 Ci::Packages::입니다. 이것은 그룹이 소유한 코드를 잘 정의된 구조로 포함하는 좋은 방법을 제공합니다.

그러나, Bounded Contexts가 개발에 도움이 되더라도 위에 언급한 목표에 도움이 되지 않습니다. 이것은 단순히 코드의 논리적인 분할뿐입니다. 이것은 깊은 결합을 방지하는 것이 아닙니다. CI 파이프라인의 백그라운드 처리와 러너 API 인터페이스 사이에 순환 종속성을 만드는 것은 여전히 가능합니다(그리고 종종 발생합니다). API는 Sidekiq Worker를 호출할 수 있으며, Sidekiq는 엔드포인트 경로를 만들기 위해 API를 사용할 수 있습니다.

Bounded Contexts는 응용 프로그램의 어떤 것이 무엇에 의존하는지를 더 잘 알지 못하도록 만들어줍니다. 전체 코드베이스는 로드되고 실행해야 하는 단일 패키지로 처리됩니다.

Bounded Context의 단점에 대한 추가적인 고려 사항:

  • 본성지식과 중복 코드로 이어질 수 있음
  • 깊은 결합으로 인해 반복을 어렵게 만들고 최소한의 변경을 가져오기가 어려울 수 있음
  • 수직 분할로 인한 격리가 어려워 변경에 연쇄 효과가 발생함

애플리케이션 계층 (수평 분할)

우리는 여전히 개발 및 리뷰 프로세스를 지원하는 네임스페이스 분리 형태의 Bounded Contexts를 활용하는 동안 애플리케이션 계층은 서로 다른 기능적 부분을 깔끔하게 분리하는 방법을 제공할 수 있습니다.

우리의 주요 코드베이스(GitLab Rails는 Ruby on Rails에서 실행되는 GitLab 이후)는 암시적인 애플리케이션 계층을 많이 포함하고 있습니다. 각 계층 간에 명확한 경계가 없어 강한 결합으로 이어지고 있습니다.

애플리케이션 계층의 개념은 개별 기능(예: CI 또는 패키지)의 관점에서가 아닌 애플리케이션을 실행하는 관점에서 애플리케이션을 살펴봅니다. 현재의 GitLab 애플리케이션은 다음과 같은 애플리케이션 계층으로 분해될 수 있습니다. 이 목록은 모두를 포괄하지는 않지만, 단일 단일 모놀리식 코드베이스의 다양한 부분을 보여줍니다:

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

실제 GitLab Rails 분할이 어떻게 보일지 가장 가능성이 높은 방법은 위성 모델입니다. 우리는 모놀리식 응용 프로그램에서 주로 응용 프로그램이 코드와 통신할 것으로 기대합니다. 위성 모델에서는 통신이 구성요소 외부에서 수행되어야 합니다. 이는 데이터베이스, Redis를 통해 또는 잘 정의된 노출된 API를 사용하여 수행될 수 있습니다.

flowchart TD subgraph Data Store D[데이터베이스] R[Redis] end subgraph Rails 엔진 subgraph 데이터 액세스 계층 C[코어] end subgraph 웹 처리 W[웹] end subgraph 백그라운드 처리 S[Sidekiq] end end C --> D & R W & S -- 응용프로그램 모델 사용 --> C R -- 백그라운드 작업 푸시 --> S W -- 비동기 일정을 통해 --> S S -- 웹 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.com에서 GitLab 레일스 사이의 종속성을 보여주고 있습니다.

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 레일스 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 레일스에서 주요 분할 선을 보여줍니다:

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

이러한 최상위 응용 프로그램 레이어 각각은 모든 관련된 종속성을 가진 코드베이스의 일부에만 의존합니다:

  • 모든 경우에 기본 데이터베이스 구조와 응용 프로그램 모델이 필요합니다.
  • 경우에 따라 종속 서비스가 필요합니다.
  • 우리는 응용 프로그램 공통 라이브러리의 일부만 필요로 합니다.
  • 요청된 기능을 지원하기위해 gems이 필요합니다.
  • 각 레이어는 다른 동료 레이어를 사용해서는 안됩니다 (빽빽한 결합), 대신 API, Redis 또는 DB를 통해 데이터를 공유하기 위해 연결해야 합니다 (느슨한 결합).

제안

메모리 팀은 “응용 프로그램 레이어” 도입의 영향을 이해하기 위해 개념 증명 단계를 진행했습니다. 이를 통해 이 제안을 실행하기 위한 복잡성, 영향 및 필요한 반복 작업을 이해하고자 했습니다.

여기서 제안된 내용은 이 블루프린트의 영향을 평가한 것으로, 실질적으로 시행할 최종 솔루션이 아니라고 취급해야 합니다. 정의된 PoC는 병합되어서는 안되는 것이지만, 향후 작업의 기반으로 제공됩니다.

Rails 엔진 사용한 PoC

웹 응용 프로그램 레이어를 모델링함으로써 Rails 엔진을 사용하기로 결정했습니다. 웹 엔진에는 컨트롤러, API, GraphQL이 포함되어 있었습니다. 이를 통해 웹 노드를 모든 종속성과 함께 실행할 수 있었지만, Sidekiq에 이러한 구성 요소가 로드되지 않았을 때의 영향을 측정할 수 있었습니다.

모든 작업은 다음 병합 요청에서 찾아볼 수 있습니다:

어떤 작업을 했나요?

  • Rails 엔진을 사용했습니다.
  • 위 MR에서 볼 수 있는 변경 사항의 99%는 파일을 그대로 이동한 것입니다.
  • 모든 GraphQL 코드와 스펙을 그대로engines/web_engine/로 이동했습니다.
  • 모든 API 및 컨트롤러 코드와 스펙을 engines/web_engine로 이동했습니다.
  • engines/web_engine/를 스택의 자체적인 구성요소로 테스트하기위해 CI를 적용했습니다.
  • Web 노드를 실행하는 동안 gem web_engine을 로드하도록 GitLab을 구성했습니다 (Puma 웹 서버)
  • 백그라운드 처리 노드 (Sidekiq)를 실행할 때는 web_engine를 로드하지 않도록 구성했습니다.

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

  1. 각 응용 프로그램 레이어를 위한 새로운 Rails Engine을 도입합니다.

    우리는 미래에 소개할 각 응용 프로그램 레이어를 위한 서로 다른 엔진을 포함할 수 있는 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로 이동했습니다.
    • Grape 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.rbgrape_validatorsweb_engine 초기화 프로그램 폴더로 이동했습니다.
  6. GitLab 애플리케이션을 WebEngine에 연결합니다.

    GitLab Gemfile.rb에서 web_engine을 engines 그룹에 추가하십시오.

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

    이 젬이 :engines 그룹 내에 있기 때문에 기본적으로 자동으로 요청되지 않습니다.

  7. 엔진을 로드할 타이밍을 구성합니다.

    GitLab config/engines.rb에서 Gitlab::Runtime을 활용하여 엔진을 언제 로드할지 구성할 수 있습니다.

     # config/engines.rb
     # web_server 또는 rails console을 실행 중인 경우에만 로드
     if Gitlab::Runtime.puma? || Gitlab::Runtime.console?
       require 'web_engine'
     end
    
  8. 엔진 구성

    우리의 엔진은 Rails::Engine 클래스를 상속합니다. 이 방법으로 Rails에게 해당 경로에 엔진이 있음을 알릴 수 있으므로 엔진이 올바르게 응용 프로그램 내에 마운트되어 모델, 메일러, 컨트롤러 및 뷰를 위한 로드 경로에 ‘engine’의 앱 디렉터리를 추가하는 작업을 수행할 수 있습니다. lib/web_engine/engine.rb에 있는 파일은 일반적인 Rails 애플리케이션의 config/application.rb 파일과 기능이 동일합니다. 이 방법으로 엔진은 모든 railtie와 응용 프로그램에서 공유하는 구성 객체에 접근할 수 있습니다. 또한 각 엔진은 해당 엔진에 대한 범위가 지정된 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, Controllers 없이 Sidekiq
  • Puma를 실행하는 Web 노드의 크기는 이전과 동일했습니다.

GraphQL, API, Controllers 없이 한 개의 Sidekiq 클러스터에 대한 start-up 이벤트에서의 절약

  • 우리는 264.13 MB의 RSS를 절약했습니다 (28.69%)
  • 우리는 264.09 MB의 USS를 절약했습니다 (29.36%)
  • 부팅 시간이 45.31초에서 21.80초로 줄었습니다. 23.51초 더 빨라졌습니다 (51.89%)
  • 우리는 805,772개의 적은 live 객체, 4,587,535개의 적은 할당된 객체, 2,866개의 적은 페이지로 할당된 객체 공간 외의 공간이 3.65 MB 덜 할당되었습니다
  • 우리는 2,326개의 덜 코드 파일을 로드했습니다 (15.64%)
  • 단일 full GC 주기의 기간을 0.80초에서 0.70초로 줄였습니다 (12.64%)

Puma single은 예상대로 매우 작은 차이를 보였습니다.

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

GitLab.com에 미치는 영향

GitLab.com을 실행하는 규모에 대한 예상 결과:

우리는 web_engine을 도입하여 가능한 최대 절약을 추정합니다:

  • GC 주기 시간을 20% 줄여서 200ms에서 160ms로 줄임
  • 초당 GC 주기 수는 동일하게 유지되지만 GC 주기 시간 감소로 인해 8 vCPU 대신 약 6 vCPU를 사용할 것으로 예상됨
  • 가장 좋은 경우 Sidekiq만 고려할 때, GitLab.com에서 최대 137 GB를 절약할 것으로 예상됨

이 모델은 유사한 혜택을 제공하는 sidekiq_engine을 도입하여 Web 노드에 미치는 영향이 사용자에게 미치는 영향을 고려할 수 있습니다.

결과

이러한 변경 사항을 도입함으로써 몇 가지 혜택을 얻었습니다.

장점:

  • 메모리 사용량이 현저히 감소함
  • Sidekiq의 애플리케이션 로드 시간이 현저히 단축됨
  • GC 주기가 훨씬 짧아져 Sidekiq 서비스의 응답성이 현저히 향상됨
  • 응용 프로그램 일부의 테스트가 훨씬 쉬워짐. 예: web_engines/ 변경은 해당 응용 프로그램 레이어에 대해서만 다시 테스트 실행이 필요함
  • 코드베이스의 단일 아키텍처를 유지했지만 데이터베이스 및 응용프로그램 모델을 공유함
  • 인프라면에서 상당한 비용 절감
  • 응용프로그램 풋프린트를 줄여 제약된 환경에서 편안하게 실행할 수 있는 능력

단점:

  • Sidekiq의 경우 GraphQL 구독을 구현하기가 더 어려움. 구독을 전달할 다른 방법이 필요함
  • 일부 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

현재, GraphQL을 로드하려면 일부 의존성이 필요합니다:

우리는 GitLab을 시작할 때 14480개의 파일을 로드/요구함을 발견했습니다. (memory-team-2gb-week#9) 이것은 GraphQL 중 1274개의 파일을 로드합니다. 즉, 이는 Sidekiq에서 필요하지 않을 때 1274개의 응용 프로그램 파일과 모든 관련된 GraphQL 젬을 로드하지 않으면 많은 메모리를 절약할 수 있음을 의미합니다.

GraphQL은 특정 컨텍스트에서만 실행될 필요가 있습니다. 로드되는 시점을 제한한다면, 응용 프로그램 로드 시간 및 필요 메모리를 효과적으로 줄일 수 있습니다. 예를 들어, 이것은 모든 크기의 설치에 적용될 수 있습니다.

GraphQL 및 웹소켓에 대한 잠재적인 도전 과제는 어느 시점에서는 Action Cable 구독을 구현하고 Sidekiq에서 클라이언트로 GraphQL/API 페이로드를 푸시하려고 할 수 있다는 것입니다. 이는 아마도 Redis를 통해 데이터를 전달하는데 사용될 것입니다. 여기서 Sidekiq는 Redis에 정보를 게시하고 ActionCable 노드가 해당 정보를 연결된 클라이언트로 전달할 것입니다. 이러한 방식은 위의 모델에서 가능하지만 무엇을 보낼지 계산하기 위해 GraphQL 또는 API (HTTP 엔드포인트를 통해)를 사용해야 합니다.

대체로, 웹소켓과 GraphQL과 관련하여 작동하는 대신, 항상 ActionCable 노드 (웹소켓을 처리하는 노드)가 전송 쿼리에 기반한 페이로드를 생성하도록 하는 알림 시스템을 사용하는 것이 다른 방법입니다. 이것은 같은 리소스를 많은 클라이언트가 지켜보라 할지라도 같은 페이로드를 다시 계산해야하는 단점을 가질 수 있습니다. 그러나 많은 경우에 시스템의 이러한 작동 방식은 보안 목적으로 여전히 원하는 경우가 있을 것입니다. 기본적으로 생성된 페이로드는 관찰하는 클라이언트의 권한에 의존할 수 있으므로 (익명인 경우와 프로젝트 멤버인 경우를 서로 다르게 표시할 것입니다).

예: API

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

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

Grape API와 관련된 잠재적 도전 과제:

  • 현재 모델에서 일부 API::API 종속성이 있습니다 (예: 프로젝트 모델에서 API::Helpers::Version에 대한 종속성 또는 geo_retrieve_url에 대한 GeoNode 모델에서의 API::API 종속성)
  • 도우미, 프리젠터 및 뷰에서 api_v4 경로가 사용됨 (예: PackagesHelper에서 api_v4_projects_path)

예시: 컨트롤러

임시: PoC - 컨트롤러 및 Grape API:API를 WebEngine으로 이동

  • 변경 사항의 99%, 위 MR에서 볼 수 있듯이, 파일을 그대로 이동시키는 것입니다.
  • 실제 작업은 교차 종속성, 스펙 수정, 초기화기 구성, 젬 및 라우트 설정에 있습니다.

컨트롤러, 직렬화기, 일부 프리젠터 및 일부 Grape:Entity는 웹 서버 컨텍스트에서만 실행해야 하는 좋은 예시입니다.

컨트롤러 이동에 대한 잠재적인 도전:

  • web_engine이 로드될 경우 engines/web_engine/config/routesengines/web_engine/ee/config/routes를 지원하기 위해 Gitlab::Patch::DrawRoute를 확장해야 했습니다. 잠재적인 해결책이 여기 있습니다.
  • Gitlab::Routing.url_helpers 경로가 모델 및 서비스에서 사용되며, 이는 Sidekiq에서 사용할 수 있습니다 (예: Gitlab::Routing.url_helpers.project_pipelines_pathExpirePipelineCacheService에서 ExpirePipelineCacheWorker에서 사용됩니다.)

Packwerk

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

미래 영향

응용 프로그램 계층 및 현재 제안은 현재 web_engine만을 정의합니다. 동일한 패턴을 따르면 추가 엔진을 손쉽게 소개할 수 있으며, 이를 통해 더 나은 분리, 낮은 메모리 사용량 및 앞으로의 GitLab Rails의 더 나은 유지 관리를 달성할 수 있습니다.

이것은 gitlab-core: 핵심 기능: DB 구조, 모델, 서비스, 공통 라이브러리를 현재 정의하는 프레임워크가 될 것으로 예상됩니다. 데이터 액세스 계층을 모델링하며, 초기에는 GitLab을 실행하는 데 필요한 모든 서비스가 포함됩니다. 이것은 잠재적으로 미래에 더 작은 측면으로 분리될 수 있습니다. - gitlab-web: 웹 서버 컨텍스트에서 실행해야 하는 컨트롤러/API/GraphQL/ActionCable 기능 (gitlab-core에 따라 달라짐) - gitlab-sidekiq: Sidekiq 워커를 실행하는 데 필요한 백그라운드 작업 기능 (gitlab-core에 따라 달라짐)

이 모델은 오늘날에는 공유 된 핵심 및 위성으로 가장 잘 설명됩니다. 공유 핵심은 데이터 액세스 계층을 정의하며, 위성은 데이터를 표시하고 처리하는 방법을 정의합니다. 위성은 Core와만 대화할 수 있습니다. 다른 위성을 직접로드하거나 대화하지 못합니다. API, GraphQL 또는 Redis의 명확히 정의된 인터페이스를 사용하지 않는 한 다른 위성과 대화할 수 없습니다 (예를 들어 Sidekiq 작업을 예약하는 데).

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

이슈 및 병합 요청