프로파일링

성능 문제를 추적하기 쉽게 하기 위해 GitLab은 일련의 프로파일링 도구를 제공합니다. 이 중 일부는 기본적으로 제공되며, 다른 일부는 명시적으로 활성화해야 합니다.

URL 프로파일링

특정 URL에 대한 GET 또는 POST 요청을 프로파일링할 수 있는 Gitlab::Profiler.profile 메서드와 해당하는 bin/profile-url 스크립트가 있습니다. 익명 사용자(기본값) 또는 특정 사용자로 프로파일링할 수 있습니다.

프로파일러의 첫 번째 인수는 전체 URL(인스턴스 호스트 이름 포함) 또는 슬래시가 포함된 절대 경로입니다.

기본적으로 보고서 덤프는 임시 파일에 저장되며, 이 파일은 Stackprof API를 사용하여 상호작용할 수 있습니다.

스크립트를 사용할 때는 인수 없이 명령줄 문서를 볼 수 있습니다.

대화형 콘솔 세션에서 메서드를 사용할 경우, 해당 콘솔 세션 내에서 애플리케이션 코드의 모든 변경 사항이 프로파일러 출력에 반영됩니다.

예를 들면:

Gitlab::Profiler.profile('/my-user')
# 보고서 덤프가 저장된 임시 파일의 위치를 반환합니다
class UsersController; def show; sleep 100; end; end
Gitlab::Profiler.profile('/my-user')
# 보고서 덤프가 저장된 임시 파일의 위치를 반환합니다
# UsersController#show에서 100초가 소모됩니다

인증이 필요한 경로의 경우 Gitlab::Profiler에 사용자를 제공해야 합니다. 다음과 같이 할 수 있습니다:

Gitlab::Profiler.profile('/gitlab-org/gitlab-test', user: User.first)

Gitlab::Profiler.profilelogger: 키워드 인수를 전달하면 ActiveRecord 및 ActionController 로그 출력이 해당 로거로 전송됩니다. 추가 옵션은 메서드 소스에서 문서화되어 있습니다.

Gitlab::Profiler.profile('/gitlab-org/gitlab-test', user: User.first, logger: Logger.new($stdout))

샘플링 데이터의 출력 파일(out)을 구성하기 위해 profiler_options 해시를 전달합니다. 예를 들어:

Gitlab::Profiler.profile('/gitlab-org/gitlab-test', user: User.first, profiler_options: { out: 'tmp/profile.dump' })

GitLab::Profiler 보고서 읽기

샘플링 데이터에 대해 Stackprof를 실행하여 시간 소비 요약을 얻을 수 있습니다. 예를 들어:

stackprof tmp/profile.dump

예시 샘플링 데이터:

==================================
  모드: wall(1000)
  샘플: 8745 (6.92% 누락률)
  GC: 1399 (16.00%)
==================================
     총계    (pct)     샘플    (pct)     프레임
      1022  (11.7%)        1022  (11.7%)     Sprockets::PathUtils#stat
       957  (10.9%)         957  (10.9%)     (marking)
       493   (5.6%)         493   (5.6%)     Sprockets::PathUtils#entries
       576   (6.6%)         471   (5.4%)     Mustermann::AST::Translator#decorator_for
       439   (5.0%)         439   (5.0%)     (sweeping)
       630   (7.2%)         241   (2.8%)     Sprockets::Cache::FileStore#get
       208   (2.4%)         208   (2.4%)     ActiveSupport::FileUpdateChecker#watched
       206   (2.4%)         206   (2.4%)     Digest::Instance#file
       544   (6.2%)         176   (2.0%)     Sprockets::Cache::FileStore#safe_open
       176   (2.0%)         176   (2.0%)     ActiveSupport::FileUpdateChecker#max_mtime
       268   (3.1%)         147   (1.7%)     ActiveRecord::ConnectionAdapters::PostgreSQLAdapter#exec_no_cache
       140   (1.6%)         140   (1.6%)     ActiveSupport::BacktraceCleaner#add_gem_filter
       116   (1.3%)         116   (1.3%)     Bootsnap::CompileCache::ISeq.storage_to_output
       160   (1.8%)         113   (1.3%)     Gem::Version#<=>
       109   (1.2%)         109   (1.2%)     block in <main>
       108   (1.2%)         108   (1.2%)     Gem::Version.new
       131   (1.5%)         105   (1.2%)     Sprockets::EncodingUtils#unmarshaled_deflated
      1166  (13.3%)          82   (0.9%)     Mustermann::RegexpBased#initialize
        82   (0.9%)          78   (0.9%)     FileUtils.touch
        72   (0.8%)          72   (0.8%)     Sprockets::Manifest.compile_match_filter
        71   (0.8%)          70   (0.8%)     Grape::Router#compile!
        91   (1.0%)          65   (0.7%)     ActiveRecord::ConnectionAdapters::PostgreSQL::DatabaseStatements#query
        93   (1.1%)          64   (0.7%)     ActionDispatch::Journey::Path::Pattern::AnchoredRegexp#accept
        59   (0.7%)          59   (0.7%)     Mustermann::AST::Translator.dispatch_table
        62   (0.7%)          59   (0.7%)     Rails::BacktraceCleaner#initialize
      2492  (28.5%)          49   (0.6%)     Sprockets::PathUtils#stat_directory
       242   (2.8%)          49   (0.6%)     Gitlab::Instrumentation::RedisBase.add_call_details
        47   (0.5%)          47   (0.5%)     URI::RFC2396_Parser#escape
        46   (0.5%)          46   (0.5%)     #<Class:0x00000001090c2e70>#__setobj__
        44   (0.5%)          44   (0.5%)     Sprockets::Base#normalize_logical_path

플레임그래프를 생성할 수도 있습니다:

stackprof --d3-flamegraph tmp/profile.dump > flamegraph.html

자세한 내용은 Stackprof 문서를 참조하세요.

Speedscope 플레임 그래프

특정 URL에 대한 플레임 그래프를 생성하려면 성능 바에서 플레임 그래프 샘플링 모드 버튼을 선택하거나 요청에 performance_bar=flamegraph 매개변수를 추가하십시오.

Speedscope

Speedscope 문서에서 뷰에 대한 자세한 정보를 확인하세요.

Stackprof 문서에서 다양한 샘플링 모드에 대한 자세한 정보를 확인하세요.

성능 바에 접근할 수 있는 모든 사용자에게 사용 가능하도록 설정되어 있습니다.

Bullet

Bullet은 N+1 쿼리 문제를 추적하는 데 사용할 수 있는 Gem입니다. Rails 로그와 브라우저 콘솔에 쿼리 문제를 기록합니다. Bullet 섹션은 성능 바에 표시됩니다.

Bullet

Bullet은 기본적으로 개발 모드에서만 활성화됩니다. 그러나 Bullet 로그는 소음이 많기 때문에 로깅은 비활성화되어 있습니다. Bullet과 그 로깅을 구성하려면:

  • 환경에서 Bullet을 수동으로 활성화하거나 비활성화하려면 config/gitlab.yml에 다음 줄을 추가하고 필요에 따라 enabled 값을 변경하십시오:

    bullet:
      enabled: false
    
  • Bullet 로깅을 활성화하려면 GitLab을 시작하기 전에 ENABLE_BULLET 환경 변수를 비어 있지 않은 값으로 설정하십시오:

    ENABLE_BULLET=true bundle exec rails s
    

Bullet로 N+1 쿼리를 찾은 후에는 회귀를 방지하기 위해 QueryRecoder 테스트를 작성하는 것을 고려하십시오.

시스템 통계

프로파일링 중 또는 이후에 Ruby 가상 머신 프로세스에 대한 자세한 정보, 예를 들어 메모리 소비, CPU에서 소요된 시간 또는 가비지 컬렉터 통계를 얻고 싶을 수 있습니다. 이러한 정보는 다양한 도구를 통해 개별적으로 쉽게 생성할 수 있지만, 편의를 위해 이 데이터를 JSON 페이로드로 내보내는 요약 엔드포인트가 추가되었습니다:

curl localhost:3000/-/metrics/system | jq

예시 출력:

{
  "version": "ruby 2.7.2p137 (2020-10-01 revision a8323b79eb) [x86_64-linux-gnu]",
  "gc_stat": {
    "count": 118,
    "heap_allocated_pages": 11503,
    "heap_sorted_length": 11503,
    "heap_allocatable_pages": 0,
    "heap_available_slots": 4688580,
    "heap_live_slots": 3451712,
    "heap_free_slots": 1236868,
    "heap_final_slots": 0,
    "heap_marked_slots": 3451450,
    "heap_eden_pages": 11503,
    "heap_tomb_pages": 0,
    "total_allocated_pages": 11503,
    "total_freed_pages": 0,
    "total_allocated_objects": 32679478,
    "total_freed_objects": 29227766,
    "malloc_increase_bytes": 84760,
    "malloc_increase_bytes_limit": 32883343,
    "minor_gc_count": 88,
    "major_gc_count": 30,
    "compact_count": 0,
    "remembered_wb_unprotected_objects": 114228,
    "remembered_wb_unprotected_objects_limit": 228456,
    "old_objects": 3185330,
    "old_objects_limit": 6370660,
    "oldmalloc_increase_bytes": 21838024,
    "oldmalloc_increase_bytes_limit": 119181499
  },
  "memory_rss": 1326501888,
  "memory_uss": 1048563712,
  "memory_pss": 1139554304,
  "time_cputime": 82.885264633,
  "time_realtime": 1610459445.5579069,
  "time_monotonic": 24001.23145713,
  "worker_id": "puma_0"
}

참고:
이 엔드포인트는 Rails 웹 워커에 대해서만 사용할 수 있습니다. Sidekiq 워커는 이 방법으로 검사할 수 없습니다.

성능에 영향을 미치는 설정

애플리케이션 설정

  1. 기본적으로 development 환경은 핫 리로딩이 활성화되어 작동하며, 이로 인해 Rails가 매 요청마다 파일 변경 사항을 확인하고, 핫 리로드가 단일 스레드로 작동하므로 잠재적인 경합 잠금을 생성합니다.
  2. development 환경은 요청이 발생하면 코드를 지연 로딩할 수 있으며, 이로 인해 첫 번째 요청이 항상 느려집니다.

프로파일링/벤치마킹을 위한 이러한 기능을 비활성화하려면 GitLab을 시작하기 전에 RAILS_PROFILE 환경 변수를 true로 설정하세요. 예를 들어 GDK를 사용할 때:

  • 루트 GDK 디렉토리에 env.runit 파일을 생성합니다.
  • env.runit 파일에 export RAILS_PROFILE=true를 추가합니다.
  • gdk restart로 GDK를 재시작합니다.

이 환경 변수는 개발 모드에만 적용됩니다.

GC 설정

루비의 가비지 컬렉터(GC)는 다양한 환경 변수를 통해 조정할 수 있으며, 이는 애플리케이션 성능에 직접적인 영향을 미칩니다.

다음 표는 이러한 변수와 기본 값을 나열하고 있습니다.

환경 변수 기본 값
RUBY_GC_HEAP_INIT_SLOTS 10000
RUBY_GC_HEAP_FREE_SLOTS 4096
RUBY_GC_HEAP_FREE_SLOTS_MIN_RATIO 0.20
RUBY_GC_HEAP_FREE_SLOTS_GOAL_RATIO 0.40
RUBY_GC_HEAP_FREE_SLOTS_MAX_RATIO 0.65
RUBY_GC_HEAP_GROWTH_FACTOR 1.8
RUBY_GC_HEAP_GROWTH_MAX_SLOTS 0 (비활성화)
RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR 2.0
RUBY_GC_MALLOC_LIMIT(_MIN) (16 * 1024 * 1024 /* 16MB */)
RUBY_GC_MALLOC_LIMIT_MAX (32 * 1024 * 1024 /* 32MB */)
RUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR 1.4
RUBY_GC_OLDMALLOC_LIMIT(_MIN) (16 * 1024 * 1024 /* 16MB */)
RUBY_GC_OLDMALLOC_LIMIT_MAX (128 * 1024 * 1024 /* 128MB */)
RUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTOR 1.2

(출처)

GitLab은 애플리케이션 성능을 높이거나 메모리 요구 사항을 줄이기 위해 이러한 설정을 변경할 수 있습니다.

각 설정이 GC 성능, 메모리 사용 및 GitLab의 유휴 인스턴스 시작 시간에 어떻게 영향을 미치는지 보려면 scripts/perf/gc/collect_gc_stats.rb 스크립트를 실행하세요. 이 스크립트는 GC 통계 및 일반 타이밍 데이터를 CSV 형식으로 표준 출력에 출력합니다.