메모리 사용량 줄이기

GitLab Rails 애플리케이션 코드는 메모리 누수 문제를 겪고 있습니다. 웹 요청의 경우, 관리 스레드를 사용하여 문제를 관리하며, 주어진 resident set size (RSS) 임계값을 초과한 워커들을 자동으로 재시작합니다. 우리는 GitLab에서 사용하는 Sidekiq 프로세스에도 동일한 방식을 사용하여 백그라운드 작업을 처리합니다.

기본 설정에서 GitLab은 Linux 패키지 또는 Docker 설치에 대해서만 RSS 제한을 모니터링합니다. 이는 GitLab이 runit을 사용하여 Sidekiq을 메모리 중단 후 재시작하며, self-compiled 및 Helm 차트 설치에서는 runit이나 유사한 도구를 사용하지 않기 때문입니다.

기본 설정으로는 Sidekiq은 15분에 한 번 초과하지 않으며, 재시작으로 인해 새로운 백그라운드 작업에 약 1분의 지연이 발생합니다.

일부 백그라운드 작업은 장기 실행되는 외부 프로세스에 의존합니다. Sidekiq 재시작 시 이러한 프로세스들이 깨끗하게 종료되도록 하기 위해 각 Sidekiq 프로세스를 프로세스 그룹 리더로 실행해야 합니다 (예: chpst -P를 사용하여). 만약 Linux 패키지 설치 또는 runit이 설치된 bin/background_jobs 스크립트를 사용한다면, 이 작업은 자동으로 처리됩니다.

제한 구성

Sidekiq 메모리 제한은 환경 변수를 사용하여 제어됩니다.

  • SIDEKIQ_MEMORY_KILLER_MAX_RSS (KB): 허용된 RSS의 Sidekiq 프로세스 소프트 제한을 정의합니다. Sidekiq 프로세스의 RSS (킬로바이트 단위)가 SIDEKIQ_MEMORY_KILLER_MAX_RSS를 초과하고 SIDEKIQ_MEMORY_KILLER_GRACE_TIME보다 긴 시간 동안 초과한다면 우아한 재시작이 트리거됩니다. SIDEKIQ_MEMORY_KILLER_MAX_RSS가 설정되지 않았거나 값이 0으로 설정된 경우 소프트 제한은 모니터링되지 않습니다. SIDEKIQ_MEMORY_KILLER_MAX_RSS는 기본적으로 2000000로 설정됩니다.

  • SIDEKIQ_MEMORY_KILLER_GRACE_TIME: 허용된 RSS 소프트 제한을 초과하여 Sidekiq 프로세스가 실행될 수 있는 우아한 시간 기간(초)을 정의합니다. Sidekiq 프로세스가 허용된 RSS (소프트 제한) 아래로 SIDEKIQ_MEMORY_KILLER_GRACE_TIME 내에 내려가면 재시작은 중단됩니다. 기본값은 900초 (15분)입니다.

  • SIDEKIQ_MEMORY_KILLER_HARD_LIMIT_RSS (KB): 허용된 RSS의 Sidekiq 프로세스 하드 제한을 정의합니다. Sidekiq 프로세스의 RSS (킬로바이트 단위)가 SIDEKIQ_MEMORY_KILLER_HARD_LIMIT_RSS를 초과하면 Sidekiq의 즉시 우아한 재시작이 트리거됩니다. 이 값이 설정되지 않거나 0으로 설정된 경우 하드 제한은 모니터링되지 않습니다.

  • SIDEKIQ_MEMORY_KILLER_CHECK_INTERVAL: 프로세스 RSS를 확인하는 간격을 정의합니다. 기본값은 3초입니다.

  • SIDEKIQ_MEMORY_KILLER_SHUTDOWN_WAIT: 모든 Sidekiq 작업이 완료되는데 허용된 최대 시간을 정의합니다. 해당 시간 동안 새로운 작업은 받아들여지지 않습니다. 기본값은 30초입니다.

    만약 Sidekiq에 의해 프로세스 재시작이 수행되지 않는 경우, Sidekiq 프로세스는 Sidekiq 셧다운 타임아웃 (기본값은 25초) +2초 이후에 강제로 종료됩니다. 이 시간 동안 작업이 완료되지 않으면 현재 실행 중인 모든 작업에 SIGTERM 신호가 보내져 Sidekiq 프로세스가 중단됩니다.

  • GITLAB_MEMORY_WATCHDOG_ENABLED: 기본적으로 활성화됩니다. Watchdog를 실행하지 않으려면 GITLAB_MEMORY_WATCHDOG_ENABLED를 false로 설정하세요.

워커 재시작 모니터링

높은 메모리 사용으로 인해 워커가 재시작되면 GitLab은 로그 이벤트를 발생시킵니다.

다음은 /var/log/gitlab/gitlab-rails/sidekiq_client.log 파일에서 이러한 로그 이벤트 중 하나의 예시입니다:

{
  "severity": "WARN",
  "time": "2023-02-04T09:45:16.173Z",
  "correlation_id": null,
  "pid": 2725,
  "worker_id": "sidekiq_1",
  "memwd_handler_class": "Gitlab::Memory::Watchdog::SidekiqHandler",
  "memwd_sleep_time_s": 3,
  "memwd_rss_bytes": 1079683247,
  "memwd_max_rss_bytes": 629145600,
  "memwd_max_strikes": 5,
  "memwd_cur_strikes": 6,
  "message": "rss memory limit exceeded",
  "running_jobs": [
    {
      jid: "83efb701c59547ee42ff7068",
      worker_class: "Ci::DeleteObjectsWorker"
    },
    {
      jid: "c3a74503dc2637f8f9445dd3",
      worker_class: "Ci::ArchiveTraceWorker"
    }
  ]
}

여기서:

  • memwd_rss_bytes는 실제 메모리 소비량입니다.
  • memwd_max_rss_bytesper_worker_max_memory_mb를 통해 설정된 RSS 한계입니다.
  • running jobs는 프로세스가 RSS 한계를 초과하여 우아한 재시작을 시작할 때 실행되고 있던 작업들의 디렉터리입니다.