Sidekiq 문제 해결

Tier: Free, Premium, Ultimate Offering: Self-managed

Sidekiq는 GitLab이 비동기적으로 작업을 실행하는 데 사용하는 백그라운드 작업 프로세서입니다. 문제가 발생하면 문제 해결이 어려울 수 있습니다. 또한 이러한 상황은 프로덕션 시스템 작업 큐가 채워져 있을 수 있기 때문에 고압력일 수 있습니다. 사용자들은 이런 일이 일어날 때 새 브랜치가 표시되지 않거나 Merge Request이 업데이트되지 않는 것을 알 수 있습니다. 다음은 병목 현상을 진단하는 데 도움이 되는 몇 가지 문제 해결 단계입니다.

GitLab 관리자/사용자는 백트레이스가 당사 팀에 의해 분석될 수 있도록 GitLab 지원팀과 함께 이 디버깅 단계를 거칠 것을 고려해야 합니다. 이는 GitLab에서 버그가 발견되었거나 개선이 필요한 사항을 밝혀낼 수 있습니다.

백트레이스 중 어떠한 스레드가 데이터베이스, Redis에서 대기하거나 뮤텍스를 획들하려고 대기하고 있는 경우 의심해야 합니다. 예를 들어 이것은 데이터베이스에서 경합이 발생한 것일 수 있지만, 나머지와 다른 하나의 스레드를 찾아보세요. 이 다른 스레드는 모든 유휴 CPU를 사용하거나 Ruby Global Interpreter Lock을 가지고 있을 수 있으며 다른 스레드의 진행을 막을 수 있습니다.

Sidekiq 작업에 대한 인수 로깅

GitLab 13.6 이상에서는 일부 Sidekiq 작업에 전달된 인수가 기본적으로 로깅됩니다. 예를 들어, 비밀번호 재설정 토큰과 같이 민감한 정보를 로깅하지 않도록하려면 일부 특정한 작업을 제외하고 모든 워커에 대한 숫자형 인수를 로깅합니다.

예상 로깅 출력:

{"severity":"INFO","time":"2020-06-08T14:37:37.892Z","class":"AdminEmailsWorker","args":["[FILTERED]","[FILTERED]","[FILTERED]"],"retry":3,"queue":"admin_emails","backtrace":true,"jid":"9e35e2674ac7b12d123e13cc","created_at":"2020-06-08T14:37:37.373Z","meta.user":"root","meta.caller_id":"Admin::EmailsController#create","correlation_id":"37D3lArJmT1","uber-trace-id":"2d942cc98cc1b561:6dc94409cfdd4d77:9fbe19bdee865293:1","enqueued_at":"2020-06-08T14:37:37.410Z","pid":65011,"message":"AdminEmailsWorker JID-9e35e2674ac7b12d123e13cc: done: 0.48085 sec","job_status":"done","scheduling_latency_s":0.001012,"redis_calls":9,"redis_duration_s":0.004608,"redis_read_bytes":696,"redis_write_bytes":6141,"duration_s":0.48085,"cpu_s":0.308849,"completed_at":"2020-06-08T14:37:37.892Z","db_duration_s":0.010742}
{"severity":"INFO","time":"2020-06-08T14:37:37.894Z","class":"ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper","wrapped":"ActionMailer::MailDeliveryJob","queue":"mailers","args":["[FILTERED]"],"retry":3,"backtrace":true,"jid":"e47a4f6793d475378432e3c8","created_at":"2020-06-08T14:37:37.884Z","meta.user":"root","meta.caller_id":"AdminEmailsWorker","correlation_id":"37D3lArJmT1","uber-trace-id":"2d942cc98cc1b561:29344de0f966446d:5c3b0e0e1bef987b:1","enqueued_at":"2020-06-08T14:37:37.885Z","pid":65011,"message":"ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper JID-e47a4f6793d475378432e3c8: start","job_status":"start","scheduling_latency_s":0.009473}
{"severity":"INFO","time":"2020-06-08T14:39:50.648Z","class":"NewIssueWorker","args":["455","1"],"retry":3,"queue":"new_issue","backtrace":true,"jid":"a24af71f96fd129ec47f5d1e","created_at":"2020-06-08T14:39:50.643Z","meta.user":"root","meta.project":"h5bp/html5-boilerplate","meta.root_namespace":"h5bp","meta.caller_id":"Projects::IssuesController#create","correlation_id":"f9UCZHqhuP7","uber-trace-id":"28f65730f99f55a3:a5d2b62dec38dffc:48ddd092707fa1b7:1","enqueued_at":"2020-06-08T14:39:50.646Z","pid":65011,"message":"NewIssueWorker JID-a24af71f96fd129ec47f5d1e: start","job_status":"start","scheduling_latency_s":0.001144}

Sidekiq JSON 로깅을 사용할 때 인수 로그는 최대 10킬로바이트의 텍스트로 제한되며 이 한도 이상의 인수는 버려지고 문자열 "..."을 포함한 단일 인수로 대체됩니다.

SIDEKIQ_LOG_ARGUMENTS 환경 변수0(거짓)으로 설정하여 인수 로깅을 비활성화할 수 있습니다.

예시:

gitlab_rails['env'] = {"SIDEKIQ_LOG_ARGUMENTS" => "0"}

GitLab 13.5 이하에서는 Sidekiq에 전달된 인수를 로깅하기 위해 SIDEKIQ_LOG_ARGUMENTS1로 설정하세요.

Sidekiq 대기열 백로그 또는 성능 저하 조사

느린 Sidekiq 성능의 증상은 Merge Request 상태 업데이트 문제 및 CI 파이프라인이 실행되기 전의 지연 외에도, 다음과 같은 요소들을 포함합니다.

  • GitLab 인스턴스에는 더 많은 Sidekiq 워커가 필요할 수 있습니다. 기본적으로 단일 노드 Linux 패키지 설치는 하나의 워커를 실행하여 Sidekiq 작업을 최대 하나의 CPU 코어에서 실행하는 것을 제한합니다. 여러 Sidekiq 워커 실행에 대해 자세히 알아보기.

  • 인스턴스가 더 많은 Sidekiq 워커로 구성되어 있지만 추가 워커 중 대부분은 대기 중인 작업을 실행하도록 구성되어 있지 않습니다. 이렇게하면 인스턴스가 바쁠 때 작업 대기열이 발생할 수 있으며, 워커가 구성된 워크로드가 변경되었거나 월/년 단위로 변경되었거나 GitLab 제품 변경의 결과로 발생할 수 있습니다.

다음의 Ruby 스크립트를 사용하여 Sidekiq 워커의 상태에 대한 데이터를 수집하세요.

  1. 스크립트 생성:

    cat > /var/opt/gitlab/sidekiqcheck.rb <<EOF
    require 'sidekiq/monitor'
    Sidekiq::Monitor::Status.new.display('overview')
    Sidekiq::Monitor::Status.new.display('processes'); nil
    Sidekiq::Monitor::Status.new.display('queues'); nil
    puts "----------- workers ----------- "
    workers = Sidekiq::Workers.new
    workers.each do |_process_id, _thread_id, work|
      pp work
    end
    puts "----------- Queued Jobs ----------- "
    Sidekiq::Queue.all.each do |queue|
      queue.each do |job|
        pp job
      end
    end ;nil
    puts "----------- done! ----------- "
    EOF
    
  2. 실행 및 출력 캡처:

    sudo gitlab-rails runner /var/opt/gitlab/sidekiqcheck.rb > /tmp/sidekiqcheck_$(date '+%Y%m%d-%H:%M').out
    

    성능 문제가 간헐적인 경우:

    • 이것을 cron 작업을 통해 5분마다 실행하세요. 충분한 공간을 갖고 있는 위치에 파일을 작성하세요: 파일 당 최소 500KB로 설정하세요.

      cat > /etc/cron.d/sidekiqcheck <<EOF
      */5 * * * *  root  /opt/gitlab/bin/gitlab-rails runner /var/opt/gitlab/sidekiqcheck.rb > /tmp/sidekiqcheck_$(date '+\%Y\%m\%d-\%H:\%M').out 2>&1
      EOF
      
    • 잘못된 내용을 확인하려면 데이터를 다시 참조하세요.

  3. 출력을 분석하세요. 다음 명령어는 출력 파일 디렉터리를 사용한다고 가정합니다.

    1. grep 'Busy: ' *를 사용하여 실행된 작업의 수를 확인하세요. grep 'Enqueued: ' *를 사용하여 그때 대기열에 남아 있는 작업을 확인합니다.

    2. 부하가 걸린 Sidekiq의 작업자 전체 스레드 수를 확인하세요.

      ls | while read f ; do if grep -q 'Enqueued: 0' $f; then :
        else echo $f; egrep 'Busy:|Enqueued:|---- Processes' $f
        grep 'Threads:' $f ; fi
      done | more
      

      예시 출력:

      sidekiqcheck_20221024-14:00.out
             Busy: 47
         Enqueued: 363
      ---- Processes (13) ----
        Threads: 30 (0 busy)
        Threads: 30 (0 busy)
        Threads: 30 (0 busy)
        Threads: 30 (0 busy)
        Threads: 23 (0 busy)
        Threads: 30 (0 busy)
        Threads: 30 (0 busy)
        Threads: 30 (0 busy)
        Threads: 30 (0 busy)
        Threads: 30 (0 busy)
        Threads: 30 (0 busy)
        Threads: 30 (24 busy)
        Threads: 30 (23 busy)
      
      • 이 출력 파일에서 47개의 스레드가 바쁘게 실행되고 있으며, 363개의 작업이 대기열에 남아 있는 것을 보여줍니다.
      • 13개의 작업자 프로세스 중, 2개만이 바쁩니다.
      • 다른 작업자가 너무 구체적으로 구성된 것으로 보입니다.
      • GitLab gitlab.rb 파일의 sidekiq_queues 구성과 일치하는 작업자를 확인하세요.
      • 부하가 많은 단일 작업자 환경은 다음과 같을 수 있습니다:

        sidekiqcheck_20221024-14:00.out
               Busy: 25
           Enqueued: 363
        ---- Processes (1) ----
          Threads: 25 (25 busy)
        
    3. 출력 파일의 ---- Queues (xxx) ---- 섹션을 확인하여 그때 어떤 작업이 대기열에 추가되었는지 확인하세요.

    4. 이 파일에는 작업의 상태에 대한 상세 정보도 포함되어 있습니다. 이는 작업량이 어디서부터 발생하는지 식별하는 데 유용할 수 있습니다.

      • ----------- workers ----------- 섹션에는 요약에서 Busy 카운트를 이루는 작업에 대한 정보가 포함되어 있습니다.
      • ----------- Queued Jobs ----------- 섹션은 Enqueued된 작업에 대한 자세한 정보를 제공합니다.

스레드 덤프

Sidekiq 프로세스 ID에 TTIN 신호를 보내어 스레드 백트레이스를 로그 파일에 출력합니다.

kill -TTIN <sidekiq_pid>

/var/log/gitlab/sidekiq/current 또는 $GITLAB_HOME/log/sidekiq.log에서 백트레이스 출력을 확인합니다. 백트레이스는 길며 일반적으로 여러 WARN 레벨 메시지로 시작합니다. 다음은 단일 스레드의 백트레이스 예시입니다:

2016-04-13T06:21:20.022Z 31517 TID-orn4urby0 WARN: ActiveRecord::RecordNotFound: Couldn't find Note with 'id'=3375386
2016-04-13T06:21:20.022Z 31517 TID-orn4urby0 WARN: /opt/gitlab/embedded/service/gem/ruby/2.1.0/gems/activerecord-4.2.5.2/lib/active_record/core.rb:155:in `find'
/opt/gitlab/embedded/service/gitlab-rails/app/workers/new_note_worker.rb:7:in `perform'
/opt/gitlab/embedded/service/gem/ruby/2.1.0/gems/sidekiq-4.0.1/lib/sidekiq/processor.rb:150:in `execute_job'
/opt/gitlab/embedded/service/gem/ruby/2.1.0/gems/sidekiq-4.0.1/lib/sidekiq/processor.rb:132:in `block (2 levels) in process'
/opt/gitlab/embedded/service/gem/ruby/2.1.0/gems/sidekiq-4.0.1/lib/sidekiq/middleware/chain.rb:127:in `block in invoke'
/opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/sidekiq_middleware/memory_killer.rb:17:in `call'
/opt/gitlab/embedded/service/gem/ruby/2.1.0/gems/sidekiq-4.0.1/lib/sidekiq/middleware/chain.rb:129:in `block in invoke'
/opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/sidekiq_middleware/arguments_logger.rb:6:in `call'
...

일부 경우에는 Sidekiq가 멈추어 TTIN 신호에 응답하지 못할 수 있습니다. 그럴 경우 다른 문제 해결 방법으로 넘어갑니다.

rbspy를 사용한 루비 프로파일링

rbspy는 사용이 쉽고 오버헤드가 낮은 루비 프로파일러로, 루비 프로세스에 의한 CPU 사용량의 flamegraph 스타일 다이어그램을 생성하는 데 사용할 수 있습니다.

이를 사용하려면 GitLab에 변경을 가할 필요가 없으며 의존성도 없습니다. 다음과 같이 설치합니다:

  1. rbspy 릴리스 페이지에서 바이너리를 다운로드합니다.
  2. 바이너리를 실행할 수 있도록 설정합니다.

한 분 동안 Sidekiq 워커를 프로파일링하려면 다음 명령을 실행합니다:

sudo ./rbspy record --pid <sidekiq_pid> --duration 60 --file /tmp/sidekiq_profile.svg

예시 rbspy flamegraph

rbspy에 의해 생성된 flamegraph의 예시에서 거의 모든 Sidekiq 프로세스 시간이 Rugged의 네이티브 C 함수인 rev_parse에 소비되고 있습니다. 스택에서 rev_parseExpirePipelineCacheWorker에서 호출되고 있는 것을 볼 수 있습니다.

perf를 사용한 프로세스 프로파일링

Linux에는 특정 프로세스가 많은 CPU를 소비할 때 유용한 perf라는 프로세스 프로파일링 도구가 있습니다. 높은 CPU 사용량이 보이고 Sidekiq가 TTIN 신호에 응답하지 않는 경우, 다음 단계로 진행합니다.

시스템에 perf가 설치되어 있지 않은 경우 apt-get 또는 yum을 사용하여 설치합니다:

# Debian
sudo apt-get install linux-tools

# Ubuntu (추가 커널 패키지가 필요할 수 있음)
sudo apt-get install linux-tools-common linux-tools-generic linux-tools-`uname -r`

# Red Hat/CentOS
sudo yum install perf

Sidekiq PID에 대해 perf를 실행합니다:

sudo perf record -p <sidekiq_pid>

30-60초 동안 실행한 후 Ctrl-C를 누르고 perf 리포트를 확인합니다:

$ sudo perf report

# 샘플 출력
Samples: 348K of event 'cycles', Event count (approx.): 280908431073
 97.69%            ruby  nokogiri.so         [.] xmlXPathNodeSetMergeAndClear
  0.18%            ruby  libruby.so.2.1.0    [.] objspace_malloc_increase
  0.12%            ruby  libc-2.12.so        [.] _int_malloc
  0.10%            ruby  libc-2.12.so        [.] _int_free

위의 예시는 perf 리포트의 샘플 출력을 보여줍니다. 여기서 97%의 CPU가 Nokogiri과 xmlXPathNodeSetMergeAndClear 내부에서 소비되고 있음을 보여줍니다. 이와 같이 명백한 경우에는 어떤 작업이 Nokogiri와 XPath를 사용하는지 조사해야 합니다. 해당 부분이 일어나고 있는 루비 코드를 보여주기 위해 TTIN 또는 gdb 출력과 결합합니다.

GNU 프로젝트 디버거 (gdb)

gdb는 Sidekiq를 디버깅하는 데 또 다른 효과적인 도구일 수 있습니다. 각 스레드를 살펴볼 수 있고 문제의 원인을 볼 수 있는 약간 더 대화식 방법을 제공합니다.

gdb를 사용하여 프로세스에 연결하면 프로세스의 표준 작동이 중단되며(gdb가 연결된 동안 Sidekiq는 작업을 처리하지 않음) 다음과 같이 Sidekiq PID에 연결하여 시작합니다:

gdb -p <sidekiq_pid>

그런 다음 모든 스레드에 대한 정보를 수집합니다:

info threads

# 예제 출력
30 Thread 0x7fe5fbd63700 (LWP 26060) 0x0000003f7cadf113 in poll () from /lib64/libc.so.6
29 Thread 0x7fe5f2b3b700 (LWP 26533) 0x0000003f7ce0b68c in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
28 Thread 0x7fe5f2a3a700 (LWP 26534) 0x0000003f7ce0ba5e in pthread_cond_timedwait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
27 Thread 0x7fe5f2939700 (LWP 26535) 0x0000003f7ce0b68c in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
26 Thread 0x7fe5f2838700 (LWP 26537) 0x0000003f7ce0b68c in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
25 Thread 0x7fe5f2737700 (LWP 26538) 0x0000003f7ce0b68c in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
24 Thread 0x7fe5f2535700 (LWP 26540) 0x0000003f7ce0b68c in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
23 Thread 0x7fe5f2434700 (LWP 26541) 0x0000003f7ce0b68c in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
22 Thread 0x7fe5f2232700 (LWP 26543) 0x0000003f7ce0b68c in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
21 Thread 0x7fe5f2131700 (LWP 26544) 0x00007fe5f7b570f0 in xmlXPathNodeSetMergeAndClear ()
from /opt/gitlab/embedded/service/gem/ruby/2.1.0/gems/nokogiri-1.6.7.2/lib/nokogiri/nokogiri.so
...

위에서 보이는 Nokogiri와 관련된 스레드와 같은 의심스러운 스레드를 볼 때 더 많은 정보를 얻기 원할 수 있습니다:

thread 21
bt

# 예제 출력
#0  0x00007ff0d6afe111 in xmlXPathNodeSetMergeAndClear () from /opt/gitlab/embedded/service/gem/ruby/2.1.0/gems/nokogiri-1.6.7.2/lib/nokogiri/nokogiri.so
#1  0x00007ff0d6b0b836 in xmlXPathNodeCollectAndTest () from /opt/gitlab/embedded/service/gem/ruby/2.1.0/gems/nokogiri-1.6.7.2/lib/nokogiri/nokogiri.so
#2  0x00007ff0d6b09037 in xmlXPathCompOpEval () from /opt/gitlab/embedded/service/gem/ruby/2.1.0/gems/nokogiri-1.6.7.2/lib/nokogiri/nokogiri.so
#3  0x00007ff0d6b09017 in xmlXPathCompOpEval () from /opt/gitlab/embedded/service/gem/ruby/2.1.0/gems/nokogiri-1.6.7.2/lib/nokogiri/nokogiri.so
#4  0x00007ff0d6b092e0 in xmlXPathCompOpEval () from /opt/gitlab/embedded/service/gem/ruby/2.1.0/gems/nokogiri-1.6.7.2/lib/nokogiri/nokogiri.so
#5  0x00007ff0d6b0bc37 in xmlXPathRunEval () from /opt/gitlab/embedded/service/gem/ruby/2.1.0/gems/nokogiri-1.6.7.2/lib/nokogiri/nokogiri.so
#6  0x00007ff0d6b0be5f in xmlXPathEvalExpression () from /opt/gitlab/embedded/service/gem/ruby/2.1.0/gems/nokogiri-1.6.7.2/lib/nokogiri/nokogiri.so
#7  0x00007ff0d6a97dc3 in evaluate (argc=2, argv=0x1022d058, self=<value optimized out>) at xml_xpath_context.c:221
#8  0x00007ff0daeab0ea in vm_call_cfunc_with_frame (th=0x1022a4f0, reg_cfp=0x1032b810, ci=<value optimized out>) at vm_insnhelper.c:1510

모든 스레드의 백트레이스를 동시에 출력하려면:

set pagination off
thread apply all bt

gdb로 디버깅을 마치면 프로세스에서 분리하고 종료해야 합니다:

detach
exit

Sidekiq kill signals

위에서 TTIN은 로깅을 위해 백트레이스를 출력하는 신호로 설명되었지만, Sidekiq는 다른 신호에도 응답합니다. 예를 들어 TSTP 및 TERM은 Sidekiq를 정상적으로 종료하는 데 사용할 수 있습니다. 자세한 내용은 Sidekiq Signals 문서를 참조하세요.

블로킹 쿼리 확인

때로는 Sidekiq이 작업을 처리하는 속도가 너무 빠르면 데이터베이스 contention을 일으킬 수 있습니다. 위의 백트레이스에서 많은 스레드가 데이터베이스 어댑터에서 막혀 있는 것으로 나타나면 블로킹 쿼리를 확인하세요.

PostgreSQL 위키에는 블로킹 쿼리를 확인할 수 있는 쿼리에 대한 세부 정보가 있습니다. 쿼리는 PostgreSQL 버전에 따라 다릅니다. 쿼리 자세한 내용은 Lock Monitoring를 참조하세요.

Sidekiq 큐 관리

Sidekiq API를 사용하여 Sidekiq에서 여러 문제 해결 단계를 수행할 수 있습니다.

이러한 명령은 관리 명령이며 현재 설치의 규모로 인해 관리 인터페이스가 적합하지 않은 경우에만 사용해야 합니다.

모든 이러한 명령은 gitlab-rails console을 사용하여 실행해야 합니다.

큐 크기 보기

Sidekiq::Queue.new("pipeline_processing:build_queue").size

모든 대기 중인 작업 나열

queue = Sidekiq::Queue.new("chaos:chaos_sleep")
queue.each do |job|
  # job.klass # => 'MyWorker'
  # job.args # => [1, 2, 3]
  # job.jid # => jid
  # job.queue # => chaos:chaos_sleep
  # job["retry"] # => 3
  # job.item # => {
  #   "class"=>"Chaos::SleepWorker",
  #   "args"=>[1000],
  #   "retry"=>3,
  #   "queue"=>"chaos:chaos_sleep",
  #   "backtrace"=>true,
  #   "queue_namespace"=>"chaos",
  #   "jid"=>"39bc482b823cceaf07213523",
  #   "created_at"=>1566317076.266069,
  #   "correlation_id"=>"c323b832-a857-4858-b695-672de6f0e1af",
  #   "enqueued_at"=>1566317076.26761},
  # }
  
  # job.delete if job.jid == 'abcdef1234567890'
end

현재 실행 중인 작업 나열

workers = Sidekiq::Workers.new
workers.each do |process_id, thread_id, work|
  # process_id is a unique identifier per Sidekiq process
  # thread_id is a unique identifier per thread
  # work is a Hash which looks like:
  # {"queue"=>"chaos:chaos_sleep",
  #  "payload"=>
  #  { "class"=>"Chaos::SleepWorker",
  #    "args"=>[1000],
  #    "retry"=>3,
  #    "queue"=>"chaos:chaos_sleep",
  #    "backtrace"=>true,
  #    "queue_namespace"=>"chaos",
  #    "jid"=>"b2a31e3eac7b1a99ff235869",
  #    "created_at"=>1566316974.9215662,
  #    "correlation_id"=>"e484fb26-7576-45f9-bf21-b99389e1c53c",
  #    "enqueued_at"=>1566316974.9229589},
  #  "run_at"=>1566316974}],
end

지정된 매개변수에 대한 Sidekiq 작업 제거 (파괴적)

조건부로 작업을 제거하는 일반적인 방법은 다음 명령입니다. 이 명령은 시작되지 않은 대기 중인 작업을 제거합니다. 실행 중인 작업은 삭제할 수 없습니다.

queue = Sidekiq::Queue.new('<queue name>')
queue.each { |job| job.delete if <condition>}

실제 메서드에서 <queue-name>은 삭제하려는 작업을 포함하는 큐의 이름이고 <condition>은 삭제할 작업을 결정합니다.

일반적으로 <condition>은 작업의 종류에 따라 달라지는 작업 인수를 참조합니다. 특정 큐의 인수를 찾으려면 일반적으로 /app/workers/<queue-name>_worker.rb에서 찾을 수 있는 관련 작업 파일의 perform 함수를 참조할 수 있습니다.

예를 들어 repository_import에는 작업 인수로 project_id가 있으며, update_merge_requests에는 project_id, user_id, oldrev, newrev, ref가 있습니다.

인수는 job.args가 Sidekiq 작업에 제공된 모든 인수의 디렉터리이기 때문에 순번 ID로 참조해야 합니다.

다음은 몇 가지 예입니다:

queue = Sidekiq::Queue.new('update_merge_requests')
# 이 예에서 우리는 ID가 125이고 ref가 `ref/heads/my_branch`인 모든 update_merge_requests 작업을 제거하려고 합니다
queue.each { |job| job.delete if job.args[0] == 125 and job.args[4] == 'ref/heads/my_branch' }
# `RepositoryImportWorker.new.perform_async(100)`와 같은 작업을 취소하고 싶을 때
id_list = [100]

queue = Sidekiq::Queue.new('repository_import')
queue.each do |job|
  job.delete if id_list.include?(job.args[0])
end

특정 작업 ID 제거 (파괴적)

queue = Sidekiq::Queue.new('repository_import')
queue.each do |job|
  job.delete if job.jid == 'my-job-id'
end

실행 중인 작업 취소 (파괴적)

  • GitLab 12.3에 도입되었습니다.

이것은 매우 위험한 작업이므로 마지막 수단으로 사용하세요. 이렇게 하면 작업이 실행 중단되어 데이터 손실이 발생할 수 있으며, 트랜잭션의 롤백이 올바르게 구현되었는지 보장되지 않습니다.

Gitlab::SidekiqDaemon::Monitor.cancel_job('job-id')

이는 Sidekiq이 SIDEKIQ_MONITOR_WORKER=1 환경 변수로 실행되어야 합니다.

임의로 작업을 중단하려면 Thread.raise를 사용합니다. 이것에는 Why Ruby’s Timeout is dangerous (and Thread.raise is terrifying)에서 언급된 많은 단점이 있습니다.

여기서 의미가 흥미롭고 무서운 점이 나타납니다. 이는 예외가 발생할 수 있다는 것을 의미합니다.

  • 네트워크 요청 중 (주변 코드가 Timeout::Error를 catch할 준비가 돼 있으면 좋음)
  • 네트워크 요청 정리 중
  • rescue 블록 중
  • 나중에 데이터베이스에 저장할 객체를 만드는 중
  • 가능한 모든 코드에서, 이전에 예외가 발생했을 수 있든 상관없이

아무도 어떤 라인에서라도 예외를 방어하기 위한 코드를 작성하지 않습니다. 그것은 실제로 불가능합니다. 그래서 Thread.raise는 사실상 코드에 대한 비밀스런 공격 같습니다. 거의 모든 것으로 이어질 수 있는 공격이라고도 볼 수 있습니다. 상태를 수정하지 않는 순수 함수형 코드인 경우에는 괜찮을 수도 있습니다. 하지만 이는 루비이기 때문에 그럴 가능성이 적습니다 :)

크론 작업 수동으로 트리거하기

/admin/background_jobs을 방문하여 인스턴스에서 예약된/실행 중/보류 중인 작업을 확인할 수 있습니다.

UI에서 “지금 인큐(enqueue now)” 버튼을 선택하여 크론 작업을 트리거할 수 있습니다. 프로그래밍적으로 크론 작업을 트리거하려면 먼저 Rails 콘솔을 엽니다.

테스트하려는 크론 작업을 찾으려면:

job = Sidekiq::Cron::Job.find('job-name')

# 작업 상태 가져오기:
job.status

# 지금 바로 작업 인큐(enqueue)!
job.enque!

예를 들어, 리포지터리 미러를 업데이트하는 update_all_mirrors_worker 크론 작업을 트리거하려면:

irb(main):001:0> job = Sidekiq::Cron::Job.find('update_all_mirrors_worker')
=>
#<Sidekiq::Cron::Job:0x00007f147f84a1d0
...
irb(main):002:0> job.status
=> "enabled"
irb(main):003:0> job.enque!
=> 257

사용 가능한 작업 디렉터리은 workers 디렉터리에서 찾을 수 있습니다.

Sidekiq 작업에 대한 자세한 정보는 Sidekiq-cron 문서를 참조하십시오.

크론 작업 비활성화하기

관리자 지역(Admin Area)의 모니터링 섹션을 방문하여 모든 Sidekiq 크론 작업을 비활성화할 수 있습니다. 명령 줄과 Rails Runner를 사용하여 동일한 작업을 수행할 수도 있습니다.

모든 크론 작업을 비활성화하려면:

sudo gitlab-rails runner 'Sidekiq::Cron::Job.all.map(&:disable!)'

모든 크론 작업을 활성화하려면:

sudo gitlab-rails runner 'Sidekiq::Cron::Job.all.map(&:enable!)'

원하는 경우 한 번에 일부 작업만 활성화하려면 이름 일치를 사용할 수 있습니다. 예를 들어, 이름에 geo가 포함된 작업만 활성화하려면:

sudo gitlab-rails runner 'Sidekiq::Cron::Job.all.select{ |j| j.name.match("geo") }.map(&:disable!)'

Sidekiq 작업 중복성 아이덤포턴시 키 제거

가끔 기대했던 작업(예: 크론 작업)이 전혀 실행되지 않은 것으로 나타납니다. 로그를 확인하면 작업이 "job_status": "deduplicated"로 실행되지 않은 경우가 발견될 수 있습니다.

이는 작업이 실패하고 아이덤포턴시 키가 제대로 지워지지 않았을 때 발생할 수 있습니다. 예를 들어, Sidekiq 중지시 25초 후 남아 있는 모든 작업이 제거됩니다.

기본적으로 키는 6시간 후에 만료됩니다. 그러나 아이덤포턴시 키를 즉시 지우려면 다음 단계를 따르십시오(Geo::VerificationBatchWorker에 대한 예가 제공됨):

  1. Sidekiq 로그에서 작업의 worker 클래스와 args를 찾습니다:
  { ... "class":"Geo::VerificationBatchWorker","args":["container_repository"] ... }
  1. Rails 콘솔 세션을 시작합니다.
  2. 다음 스니펫을 실행합니다:
  worker_class = Geo::VerificationBatchWorker
  args = ["container_repository"]
  dj = Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob.new({ 'class' => worker_class.name, 'args' => args }, worker_class.queue)
  dj.send(:idempotency_key)
  dj.delete!

GitLab 14.0 이후: sidekiq-cluster 서비스 제거 (Linux 패키지 설치)

GitLab 14.0 이전에 sidekiq-cluster를 실행하도록 구성된 Linux 패키지 인스턴스는 나중의 릴리스에서 sidekiq와 병렬로 이 서비스가 여전히 실행될 수 있습니다.

sidekiq-cluster를 관리하는 코드는 GitLab 14.0에서 제거되었습니다. 구성 파일은 디스크에 남아 있어 sidekiq-cluster 프로세스가 GitLab systemd 서비스에 의해 계속해서 시작됩니다.

추가 서비스가 실행 중인 경우:

  • gitlab-ctl status가 두 개의 서비스를 보여줍니다:

    run: sidekiq: (pid 1386) 445s; run: log: (pid 1385) 445s
    run: sidekiq-cluster: (pid 1388) 445s; run: log: (pid 1381) 445s
    
  • ps -ef | grep 'runsv sidekiq'가 두 개의 프로세스를 보여줍니다:

    root     31047 31045  0 13:54 ?        00:00:00 runsv sidekiq-cluster
    root     31054 31045  0 13:54 ?        00:00:00 runsv sidekiq
    

GitLab 14.0 이후를 실행 중인 서버에서 sidekiq-cluster 서비스를 제거하려면:

  1. GitLab과 systemd 서비스를 중지합니다:

    sudo gitlab-ctl stop
    sudo systemctl stop gitlab-runsvdir.service
    
  2. runsv 서비스 정의를 제거합니다:

    sudo rm -rf /opt/gitlab/sv/sidekiq-cluster
    
  3. GitLab을 다시 시작합니다:

    sudo systemctl start gitlab-runsvdir.service
    
  4. 모든 서비스가 실행 중이고 sidekiq-cluster 서비스가 나열되지 않았는지 확인합니다:

    sudo gitlab-ctl status
    

이 변경으로 Sidekiq가 수행할 수 있는 작업 양이 줄어들 수 있습니다. 파이프라인 생성 지연과 같은 증상은 추가 Sidekiq 프로세스가 유익할 수 있음을 나타냅니다. sidekiq-cluster 서비스를 제거한 보상으로 추가 Sidekiq 프로세스 추가를 고려하십시오.

Sidekiq BRPOP 호출로 인한 Redis의 CPU 포화

Sidekiq의 BROP 호출은 Redis의 CPU 사용량을 증가시킬 수 있습니다. Redis의 CPU 사용량을 개선하려면 SIDEKIQ_SEMI_RELIABLE_FETCH_TIMEOUT 환경 변수를 늘리세요.

관련 주제