시스템 관리자를 위한 작업 아티팩트 문제 해결

작업 아티팩트를 관리하는 동안 다음과 같은 문제가 발생할 수 있습니다.

디스크 공간을 너무 많이 사용하는 작업 아티팩트

작업 아티팩트는 예상보다 더 빨리 디스크 공간을 채울 수 있습니다. 몇 가지 가능한 이유는 다음과 같습니다.

이와 같은 경우를 포함하여 디스크 공간 사용 책임이 가장 큰 프로젝트를 확인하고, 가장 많은 공간을 사용하는 아티팩트 유형을 파악하고, 일부 경우에는 매뉴얼으로 작업 아티팩트를 삭제하여 디스크 공간을 회수하세요.

아티팩트 housekeeping

아티팩트 housekeeping은 만료된 아티팩트를 식별하고 삭제할 수 있는 프로세스입니다.

GitLab 14.6에서 15.2에서 housekeeping 비활성화

GitLab 14.6에서 아티팩트 housekeeping이 비활성화되었습니다. GitLab 14.10에서 상당히 개선되었으며, 변경 사항은 GitLab 14.6 및 이후 버전의 패치 버전에 백포트되어, 기본적으로 비활성화된 feature flags를 도입했습니다. 이 플래그는 기본적으로 GitLab 15.3에서 사용되도록 설정되었습니다.

GitLab 14.6에서 15.2에서 아티팩트 housekeeping이 작동하지 않는 것으로 보인다면 feature flag가 활성화되었는지 확인해야 합니다.

feature flags가 활성화되어 있는지 확인하려면:

  1. Rails console을 시작하십시오.

  2. feature flags가 활성화되어 있는지 확인하십시오.

    • GitLab 14.10 이전:

      Feature.enabled?(:ci_detect_wrongly_expired_artifacts, default_enabled: :yaml)
      Feature.enabled?(:ci_update_unlocked_job_artifacts, default_enabled: :yaml)
      Feature.enabled?(:ci_job_artifacts_backlog_work, default_enabled: :yaml)
      
    • GitLab 15.0 및 이후:

      Feature.enabled?(:ci_detect_wrongly_expired_artifacts)
      Feature.enabled?(:ci_update_unlocked_job_artifacts)
      Feature.enabled?(:ci_job_artifacts_backlog_work)
      
  3. feature flags 중 하나라도 비활성화된 경우 활성화하십시오.

    Feature.enable(:ci_detect_wrongly_expired_artifacts)
    Feature.enable(:ci_update_unlocked_job_artifacts)
    Feature.enable(:ci_destroy_unlocked_job_artifacts)
    

이 변경 사항에는 가장 최근에 성공한 작업의 아티팩트를 unlocked에서 locked로 전환하는 것이 포함되어 있습니다.

unknown 상태의 아티팩트

아티파트 housekeeping이 업데이트되기 전에 만들어진 아티팩트는 unknown 상태를 가집니다. 만료되면 이러한 아티팩트는 새로운 housekeeping에 의해 처리되지 않습니다.

데이터베이스를 확인하여 인스턴스에 unknown 상태의 아티팩트가 있는지 확인할 수 있습니다:

  1. 데이터베이스 콘솔을 시작하십시오:

    Linux 패키지 (Omnibus)
    sudo gitlab-psql
    
    Helm 차트 (Kubernetes)
    # 툴박스 pod를 찾으십시오
    kubectl --namespace <namespace> get pods -lapp=toolbox
    # PostgreSQL 콘솔에 연결하십시오
    kubectl exec -it <toolbox-pod-name> -- /srv/gitlab/bin/rails dbconsole --include-password --database main
    
    Docker
    sudo docker exec -it <container_name> /bin/bash
    gitlab-psql
    
    직접 컴파일한 경우 (소스)
    sudo -u git -H psql -d gitlabhq_production
    
  2. 다음 쿼리를 실행하십시오:

    select expire_at, file_type, locked, count(*) from ci_job_artifacts
    where expire_at is not null and
    file_type != 3
    group by expire_at, file_type, locked having count(*) > 1;
    

레코드가 반환되면 housekeeping 작업이 처리하지 못하는 아티팩트가 있는 것입니다. 예를 들어:

           expire_at           | file_type | locked | count
-------------------------------+-----------+--------+--------
 2021-06-21 22:00:00+00        |         1 |      2 |  73614
 2021-06-21 22:00:00+00        |         2 |      2 |  73614
 2021-06-21 22:00:00+00        |         4 |      2 |   3522
 2021-06-21 22:00:00+00        |         9 |      2 |     32
 2021-06-21 22:00:00+00        |        12 |      2 |    163

상태가 2인 아티팩트는 unknown 상태입니다. 자세한 내용은 issue #346261를 확인하세요.

unknown 아티팩트 정리

unknown 아티팩트를 처리하는 Sidekiq 워커는 GitLab 15.3 이상에서 기본적으로 활성화됩니다. 이는 위의 데이터베이스 쿼리로 반환된 아티팩트를 분석하고 필요에 따라 이를 locked 또는 unlocked로 결정합니다. 그런 다음 필요한 경우 해당 워커에 의해 아티팩트가 삭제됩니다.

GitLab 14.10 이상을 실행하는 Self-managed 인스턴스에서 이 워커를 활성화할 수 있습니다.:

  1. Rails console을 시작하십시오.

  2. 해당 기능이 활성화되어 있는지 확인하십시오.

    • GitLab 14.10:

      Feature.enabled?(:ci_job_artifacts_backlog_work, default_enabled: :yaml)
      
    • GitLab 15.0 및 이후:

      Feature.enabled?(:ci_job_artifacts_backlog_work)
      
  3. 필요한 경우 해당 기능을 활성화하십시오.

    Feature.enable(:ci_job_artifacts_backlog_work)
    

워커는 7분마다 10,000개의 unknown 아티팩트를 처리하거나, 24시간에 약 200만개 정도를 처리합니다.

Self-managed 인스턴스에서 사용되지 않는 것을 권장하지 않는 관련 ci_job_artifacts_backlog_large_loop_limit feature flag가 있으며 이는 다섯 배 더 큰 배치로 unknown 아티팩트를 처리하도록 워커를 유도합니다.

특정 만료일 또는 만료되지 않은 상태의 프로젝트 및 빌드 아티팩트 디렉터리

Rails console을 사용하여 다음과 같이 작업 아티팩트가 있는 프로젝트를 찾을 수 있습니다:

  • 만료일이 지정되지 않음.
  • 만료일이 현재로부터 7일 이상 남음.

아티팩트 삭제와 유사한 방식으로 다음 예시 시간 범위를 사용하고 필요에 따라 수정할 수 있습니다.

  • 7.days.from_now
  • 10.days.from_now
  • 2.weeks.from_now
  • 3.months.from_now

다음 스크립트는 검색 결과를 50개로 제한하지만, 필요에 따라 숫자를 변경할 수 있습니다:

# 만료되지 않는 빌드 및 프로젝트 아티팩트 찾기
builds_with_artifacts_that_never_expire = Ci::Build.with_downloadable_artifacts.where(artifacts_expire_at: nil).limit(50)
builds_with_artifacts_that_never_expire.find_each do |build|
  puts "빌드 ID #{build.id}는 만료되지 않는 아티팩트를 가지고 있으며, 프로젝트 #{build.project.full_path}에 속합니다."
end

# 현재로부터 7일 후에 만료되는 빌드 및 프로젝트 아티팩트 찾기
builds_with_artifacts_that_expire_in_a_week = Ci::Build.with_downloadable_artifacts.where('artifacts_expire_at > ?', 7.days.from_now).limit(50)
builds_with_artifacts_that_expire_in_a_week.find_each do |build|
  puts "빌드 ID #{build.id}는 만료일이 #{build.artifacts_expire_at}이며, 프로젝트 #{build.project.full_path}에 속합니다."
end

작업 아티팩트 저장된 프로젝트의 총 크기별 프로젝트 디렉터리

다음 코드를 Rails console에서 실행하여 저장된 작업 아티팩트의 총 크기를 기준으로 상위 20개 프로젝트를 나열할 수 있습니다:

include ActionView::Helpers::NumberHelper
ProjectStatistics.order(build_artifacts_size: :desc).limit(20).each do |s|
  puts "#{number_to_human_size(s.build_artifacts_size)} \t #{s.project.full_path}"
end

나열할 프로젝트 수를 변경하려면 .limit(20)을 원하는 숫자로 수정할 수 있습니다.

단일 프로젝트 내에서 가장 큰 아티팩트 나열

다음 코드를 실행하여 단일 프로젝트 내에서 가장 큰 50개의 작업 아티팩트를 나열할 수 있습니다:

include ActionView::Helpers::NumberHelper
project = Project.find_by_full_path('path/to/project')
Ci::JobArtifact.where(project: project).order(size: :desc).limit(50).map { |a| puts "ID: #{a.id} - #{a.file_type}: #{number_to_human_size(a.size)}" }

나열할 작업 아티팩트 수를 변경하려면 .limit(50)을 원하는 숫자로 수정할 수 있습니다.

단일 프로젝트 내의 아티팩트 나열

아티팩트 크기별로 정렬된 단일 프로젝트의 아티팩트를 나열합니다. 결과에는 다음이 포함됩니다:

  • 아티팩트를 생성한 작업의 ID
  • 아티팩트 크기
  • 아티팩트 파일 유형
  • 아티팩트 생성 날짜
  • 아티팩트의 디스크 상 위치
p = Project.find_by_id(<project_id>)
arts = Ci::JobArtifact.where(project: p)

list = arts.order(size: :desc).limit(50).each do |art|
    puts "작업 ID: #{art.job_id} - 크기: #{art.size}b - 유형: #{art.file_type} - 생성일: #{art.created_at} - 파일 위치: #{art.file}"
end

나열할 작업 아티팩트 수를 변경하려면 limit(50)의 숫자를 수정합니다.

특정 날짜 이전에 완료된 작업에서 작업 아티팩트 삭제

caution
이러한 명령은 데이터를 데이터베이스와 리포지터리에서 영구적으로 삭제합니다. 실행하기 전에 지원 엔지니어의 지침을 따르거나, 예비 복원용으로 인스턴스를 백업한 테스트 환경에서 실행하는 것을 강력히 권장합니다.

Rails console에서 다음 명령을 실행하여 작업 로그를 유지한 채 완료된 작업에 연결된 작업 아티팩트를 매뉴얼으로 삭제할 수 있습니다. 완료된 작업이란 성공, 실패, 취소 또는 건너뜀 상태의 작업을 의미합니다.

특정 날짜 이전에 완료된 작업의 작업 아티팩트를 삭제하려면:

  1. 삭제할 아티팩트가 있는 작업 선택:

    단일 프로젝트의 모든 아티팩트를 선택하려면:

    project = Project.find_by_full_path('path/to/project')
    builds_with_artifacts =  project.builds.with_downloadable_artifacts
    

    전체 GitLab 인스턴스에서 모든 아티팩트를 선택하려면:

    builds_with_artifacts = Ci::Build.with_downloadable_artifacts
    
  2. 특정 날짜 이전에 완료된 아티팩트 삭제:

    note
    이 단계에서 사용자가 선택한 “보관”해야 하는 아티팩트들도 지웁니다.
    builds_to_clear = builds_with_artifacts.where("finished_at < ?", 1.year.ago)
    builds_to_clear.find_each do |build|
      Ci::JobArtifacts::DeleteService.new(build).execute
      build.update!(artifacts_expire_at: Time.now)
    end
    

    GitLab 15.3 이하에서는 대신 다음을 사용합니다:

    builds_to_clear = builds_with_artifacts.where("finished_at < ?", 1.year.ago)
    builds_to_clear.find_each do |build|
      build.artifacts_expire_at = Time.now
      build.erase_erasable_artifacts!
    end
    

    1.year.ago는 Rails의 ActiveSupport::Duration 메서드입니다. 실수로 아직 사용 중인 아티팩트를 실수로 삭제하는 위험을 줄이기 위해 긴 기간부터 시작합니다. 필요한 경우 더 짧은 기간으로 재 실행하려면, 예를 들어 3.months.ago, 2.weeks.ago, 또는 7.days.ago 등으로 설정합니다.

    erase_erasable_artifacts!는 동기 메서드이며 실행하면 즉시 아티팩트가 제거됩니다. 백그라운드 큐에 의해 예약되지 않습니다.

특정 날짜 이전에 완료된 작업에서 작업 아티팩트 및 로그 삭제

caution
이 명령은 데이터베이스와 디스크에서 데이터를 영구적으로 삭제합니다. 실행하기 전에 지원 엔지니어의 지침을 따르거나, 예비 복원용으로 인스턴스를 백업한 테스트 환경에서 실행하는 것을 강력히 권장합니다.

Rails console에서 다음 명령을 실행하여 완료된 작업에 연결된 작업 아티팩트를 작업 로그를 유지한 채 매뉴얼으로 삭제할 수 있습니다. 완료된 작업이란 성공, 실패, 취소 또는 건너뜀 상태의 작업을 의미합니다.

특정 날짜 이전에 완료된 작업에서 작업 아티팩트 및 로그를 삭제하려면:

  1. 삭제할 아티팩트 및 로그가 있는 작업 선택:

    단일 프로젝트의 아티팩트를 선택하려면:

    project = Project.find_by_full_path('path/to/project')
    builds_with_artifacts =  project.builds.with_downloadable_artifacts
    

    전체 GitLab 인스턴스에서 아티팩트를 선택하려면:

    builds_with_artifacts = Ci::Build.with_downloadable_artifacts
    

    때때로 아티팩트를 선택할 때 프로세스가 종료되는 위험이 있어 한 번에 많은 행을 선택할 경우 고메모리 사용량과 결국 메모리 부족 (OOM) 에러로 인해 프로세스가 종료될 수 있습니다. 이 문제를 해결하기 위해 작은 일괄 작업을 실행할 수 있습니다. 아래 예시에서 각 일괄 작업을 1000으로 제한합니다.

    단일 프로젝트의 아티팩트를 선택하려면:

    project = Project.find_by_full_path('path/to/project')
    builds_with_artifacts =  project.builds.with_downloadable_artifacts.find_each(batch_size: 1000)
    

    전체 GitLab 인스턴스에서 아티팩트를 선택할 경우:

    builds_with_artifacts = Ci::Build.with_downloadable_artifacts.find_each(batch_size: 1000)
    
  2. 웹 UI에서 지정된 사용자 선택:

    admin_user = User.find_by(username: 'username')
    
  3. 특정 날짜 이전에 완료된 아티팩트 및 로그 삭제:

    builds_to_clear = builds_with_artifacts.where("finished_at < ?", 1.year.ago)
    builds_to_clear.find_each do |build|
      print "Ci::Build ID #{build.id}... "
         
      if build.erasable?
        Ci::BuildEraseService.new(build, admin_user).execute
        puts "삭제됨"
      else
        puts "건너뜁니다 (삭제할 것이나 삭제할 수 없음)"
      end
    end
    

    GitLab 15.3 이하에서는 Ci::BuildEraseService.new(build, admin_user).executebuild.erase(erased_by: admin_user)로 대체합니다.

    1.year.ago는 Rails의 ActiveSupport::Duration 메서드입니다. 실수로 아직 사용 중인 아티팩트를 실수로 삭제하는 위험을 줄이기 위해 긴 기간부터 시작합니다. 필요한 경우 더 짧은 기간으로 재 실행하려면, 예를 들어 3.months.ago, 2.weeks.ago, 또는 7.days.ago 등으로 설정합니다.

작업 아티팩트 업로드가 오류 500으로 실패합니다

만약 아티팩트에 대한 객체 리포지터리를 사용하고 작업 아티팩트가 업로드에 실패한다면, 다음을 확인하세요:

  • 오류 메시지와 비슷한 작업 로그를 확인하세요:

    WARNING: Upload artifacts as "archive" to coordinator... failed id=12345 responseStatus=500 Internal Server Error status=500 token=abcd1234
    
  • 오류 메시지와 비슷한 Workhorse 로그를 확인하세요: workhorse log

    {"error":"MissingRegion: could not find region configuration","level":"error","msg":"error uploading S3 session","time":"2021-03-16T22:10:55-04:00"}
    

두 경우 모두에서 작업 아티팩트가 객체 리포지터리 구성(object storage configuration)에 region을 추가해야 할 수 있습니다.

작업 아티팩트 업로드가 500 내부 서버 오류 (파일 누락)으로 실패합니다

폴더 경로를 포함하는 버킷 이름은 통합된 객체 리포지터리와 호환되지 않습니다. 예를 들어, bucket/path. 버킷 이름에 경로가 포함되어 있다면 다음과 유사한 오류가 발생할 수 있습니다:

WARNING: Upload artifacts as "archive" to coordinator... POST https://gitlab.example.com/api/v4/jobs/job_id/artifacts?artifact_format=zip&artifact_type=archive&expire_in=1+day: 500 Internal Server Error (Missing file)
FATAL: invalid argument

통합된 객체 리포지터리를 사용할 때 위와 같은 오류로 작업 아티팩트가 업로드에 실패한다면, 각 데이터 유형에 대해 별도의 버킷을 사용하는지 확인하세요.

Windows 마운트를 사용할 때 작업 아티팩트 업로드가 FATAL: invalid argument으로 실패합니다.

작업 아티팩트에 Windows 마운트 및 CIFS를 사용하는 경우, 실행자가 아티팩트를 업로드하려고 할 때 invalid argument 오류가 발생할 수 있습니다:

WARNING: Upload artifacts as "dotenv" to coordinator... POST https://<your-gitlab-instance>/api/v4/jobs/<JOB_ID>/artifacts: 500 Internal Server Error  id=1296 responseStatus=500 Internal Server Error status=500 token=*****
FATAL: invalid argument

이 문제를 해결하기 위해 다음을 시도해 볼 수 있습니다:

  • CIFS 대신 ext4 마운트로 전환하기
  • CIFS 파일 임대에 관련된 중요한 버그 수정이 포함된 적어도 Linux 커널 5.15로 업그레이드하기
  • 더 구버전 커널의 경우, 파일 임대를 비활성화하는 nolease 마운트 옵션 사용

더 많은 정보는 조사 세부 정보를 확인하세요.

사용량 할당량이 아티팩트 저장 공간 사용량을 잘못 표시합니다

가끔 아티팩트 저장 공간 사용량이 잘못된 값을 표시할 때가 있습니다. 모든 프로젝트에 대한 아티팩트 사용 통계를 다시 계산하려면 이 백그라운드 스크립트를 실행할 수 있습니다:

gitlab-rake gitlab:refresh_project_statistics_build_artifacts_size[https://example.com/path/file.csv]

https://example.com/path/file.csv 파일은 다시 계산하려는 모든 프로젝트의 프로젝트 ID를 나열해야 합니다. 파일 형식은 다음과 같이 사용하세요:

PROJECT_ID
1
2

스크립트가 실행되는 동안 아티팩트 사용량 값은 0으로 변동할 수 있습니다. 다시 계산한 후에는 사용량이 올바르게 표시되어야 합니다.