- 디스크 공간을 너무 많이 차지하는 작업 아티팩트
- 작업 아티팩트 업로드가 500 오류로 실패
- 작업 아티팩트 업로드가
500 Internal Server Error (Missing file)
로 실패 - 작업 산출물이 Windows 마운트를 사용할 때
FATAL: invalid argument
로 업로드 실패 - 사용 쿼타가 잘못된 산출물 저장 사용량 표시
- 산출물 다운로드 흐름 다이어그램
작업 아티팩트 문제 해결을 위한 관리자 가이드
작업 아티팩트를 관리하는 과정에서 다음과 같은 문제를 만날 수 있습니다.
디스크 공간을 너무 많이 차지하는 작업 아티팩트
작업 아티팩트는 예상보다 빨리 디스크 공간을 채울 수 있습니다. 가능한 이유는 다음과 같습니다:
- 사용자가 작업 아티팩트 만료 시간을 필요 이상으로 길게 설정했습니다.
- 실행된 작업의 수와 따라서 생성된 아티팩트의 수가 예상보다 많습니다.
- 작업 로그가 예상보다 크고 시간이 지남에 따라 축적되었습니다.
- 파일 시스템이 인오드 부족 문제에 처할 수 있으며, 아티팩트 정리에 의해 남겨진 빈 디렉토리가 원인일 수 있습니다. 고아 아티팩트 파일을 위한 Rake 작업 이들을 제거합니다.
- 아티팩트 파일이 디스크에 남아 있으며 정리 프로세스에 의해 삭제되지 않을 수 있습니다. 고아 아티팩트 파일을 위한 Rake 작업 를 실행하여 이들을 제거합니다. 이 스크립트는 또한 위에서 언급한 빈 디렉토리를 제거하므로 항상 작업할 내용이 있어야 합니다.
- 가장 최근 성공한 작업의 최신 아티팩트 유지 기능이 활성화 되어 있습니다.
이러한 경우 및 기타 경우에 대해 디스크 공간 사용에 가장 책임이 있는 프로젝트를 식별하고, 가장 많은 공간을 사용하는 아티팩트 유형을 파악한 후, 경우에 따라 수동으로 작업 아티팩트를 삭제하여 디스크 공간을 회수합니다.
아티팩트 정리
아티팩트 정리는 어떤 아티팩트가 만료되었는지를 식별하고 삭제할 수 있는 프로세스입니다.
GitLab 15.0에서 15.2까지 정리 비활성화
아티팩트 정리는 GitLab 15.0에서 크게 개선되었으며, 기본적으로 비활성화된 기능 플래그 뒤에 도입되었습니다. 이 플래그는 GitLab 15.3에서 기본적으로 활성화되었습니다.
GitLab 15.0에서 GitLab 15.2에 아티팩트 정리가 제대로 작동하지 않는 것 같다면 기능 플래그가 활성화되어 있는지 확인해야 합니다.
기능 플래그가 활성화되어 있는지 확인하려면:
-
Rails 콘솔을 시작합니다.
-
기능 플래그가 활성화되어 있는지 확인합니다.
Feature.enabled?(:ci_detect_wrongly_expired_artifacts) Feature.enabled?(:ci_update_unlocked_job_artifacts) Feature.enabled?(:ci_job_artifacts_backlog_work)
-
기능 플래그가 비활성화된 경우, 활성화합니다:
Feature.enable(:ci_detect_wrongly_expired_artifacts) Feature.enable(:ci_update_unlocked_job_artifacts) Feature.enable(:ci_job_artifacts_backlog_work)
이러한 변경 사항에는 아티팩트를 unlocked
에서 locked
로 전환하는 것이 포함됩니다.
아티팩트를 유지해야 하는 경우.
unknown
상태의 아티팩트
정리가 업데이트되기 전에 생성된 아티팩트는 unknown
상태입니다. 만료된 후, 이러한 아티팩트는 새로운 정리에 의해 처리되지 않습니다.
인스턴스에 unknown
상태의 아티팩트가 있는지 확인하기 위해 데이터베이스를 확인할 수 있습니다:
-
데이터베이스 콘솔을 시작합니다:
리눅스 패키지 (Omnibus)sudo gitlab-psql
헬름 차트 (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
도커sudo docker exec -it <container_name> /bin/bash gitlab-psql
소스에서 직접 컴파일 (Self-compiled)sudo -u git -H psql -d gitlabhq_production
-
다음 쿼리를 실행합니다:
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;
레코드가 반환되면, 정리 작업이 처리할 수 없는 아티팩트가 있다는 의미입니다. 예를 들어:
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
입니다.
자세한 내용은
이슈 #346261를 확인하세요.
unknown
아티팩트 정리
unknown
아티팩트를 처리하는 Sidekiq 워커는 GitLab 15.3 이상에서 기본적으로 활성화되어 있습니다. 이 워커는 위의 데이터베이스 쿼리에서 반환된 아티팩트를 분석하고, 어떤 것을 locked
또는 unlocked
해야 하는지 결정합니다. 필요한 경우 해당 워커가 아티팩트를 삭제합니다.
워커는 셀프 관리 인스턴스에서 활성화할 수 있습니다:
-
Rails 콘솔을 시작합니다.
-
기능이 활성화되어 있는지 확인합니다.
Feature.enabled?(:ci_job_artifacts_backlog_work)
-
필요 시 기능을 활성화합니다:
Feature.enable(:ci_job_artifacts_backlog_work)
워커는 매 7분마다 10,000개의 unknown
아티팩트를 처리하며, 24시간에 대략 200만 개를 처리합니다.
ci_job_artifacts_backlog_large_loop_limit
와 관련된 기능 플래그가 있으며, 이는 워커가 unknown
아티팩트를 처리하도록 합니다 다섯 배 큰 배치로. 이 플래그는 셀프 관리 인스턴스에서 사용을 권장하지 않습니다.
특정 만료( 또는 만료 없음)가 있는 아티팩트를 가진 프로젝트 및 빌드 목록
Rails 콘솔을 사용하여 만료일이 없는 또는 미래의 7일보다 더 먼 만료일이 있는 작업 아티팩트를 가진 프로젝트를 찾을 수 있습니다:
- 만료일 없음.
- 만료일이 7일 이상 미래인 경우.
아티팩트 삭제하기와 유사하게, 다음 예제 시간 프레임을 사용하고 필요에 따라 수정하십시오:
7.days.from_now
10.days.from_now
2.weeks.from_now
3.months.from_now
1.year.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 "Build with id #{build.id} has artifacts that don't expire and belongs to project #{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 "Build with id #{build.id} has artifacts that expire at #{build.artifacts_expire_at} and belongs to project #{build.project.full_path}"
end
저장된 작업 아티팩트의 총 크기로 프로젝트 목록
저장된 작업 아티팩트의 총 크기로 정렬하여 상위 20개 프로젝트를 나열하려면, Rails 콘솔에서 다음 코드를 실행합니다:
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개의 작업 아티팩트를 나열하려면 다음 코드를 Rails 콘솔에서 실행하세요:
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 "Job ID: #{art.job_id} - Size: #{art.size}b - Type: #{art.file_type} - Created: #{art.created_at} - File loc: #{art.file}"
end
목록에 나열되는 작업 아티팩트의 수를 변경하려면 limit(50)
의 숫자를 변경하세요.
오래된 빌드 및 아티팩트 삭제
경고:
이 명령은 데이터베이스와 스토리지에서 데이터를 영구적으로 제거합니다. 이를 실행하기 전에 지원 엔지니어에게 안내를 받거나, 인스턴스를 백업하여 복원할 준비가 된 테스트 환경에서 실행하는 것을 권장합니다.
프로젝트의 오래된 아티팩트 삭제
이 단계는 사용자가 유지하도록 선택한 아티팩트도 지웁니다:
project = Project.find_by_full_path('path/to/project')
builds_with_artifacts = project.builds.with_downloadable_artifacts
builds_with_artifacts.where("finished_at < ?", 1.year.ago).each_batch do |batch|
batch.each do |build|
Ci::JobArtifacts::DeleteService.new(build).execute
end
batch.update_all(artifacts_expire_at: Time.current)
end
GitLab 15.3 이전 버전에서는 다음과 같이 대신 사용하세요:
project = Project.find_by_full_path('path/to/project')
builds_with_artifacts = project.builds.with_downloadable_artifacts
builds_with_artifacts.where("finished_at < ?", 1.year.ago).each_batch do |batch|
batch.each do |build|
build.artifacts_expire_at = Time.current
build.erase_erasable_artifacts!
end
end
인스턴스 전역의 오래된 아티팩트 삭제
이 단계는 사용자가 유지하도록 선택한 아티팩트도 지웁니다:
builds_with_artifacts = Ci::Build.with_downloadable_artifacts
builds_with_artifacts.where("finished_at < ?", 1.year.ago).each_batch do |batch|
batch.each do |build|
Ci::JobArtifacts::DeleteService.new(build).execute
end
batch.update_all(artifacts_expire_at: Time.current)
end
GitLab 15.3 이전 버전에서는 다음과 같이 대신 사용하세요:
builds_with_artifacts = Ci::Build.with_downloadable_artifacts
builds_with_artifacts.where("finished_at < ?", 1.year.ago).each_batch do |batch|
batch.each do |build|
build.artifacts_expire_at = Time.current
build.erase_erasable_artifacts!
end
end
프로젝트의 오래된 작업 로그 및 아티팩트 삭제
project = Project.find_by_full_path('path/to/project')
builds = project.builds
admin_user = User.find_by(username: 'username')
builds.where("finished_at < ?", 1.year.ago).each_batch do |batch|
batch.each do |build|
print "Ci::Build ID #{build.id}... "
if build.erasable?
Ci::BuildEraseService.new(build, admin_user).execute
puts "Erased"
else
puts "Skipped (Nothing to erase or not erasable)"
end
end
end
인스턴스 전체에서 오래된 작업 로그 및 아티팩트 삭제
builds = Ci::Build.all
admin_user = User.find_by(username: 'username')
builds.where("finished_at < ?", 1.year.ago).each_batch do |batch|
batch.each do |build|
print "Ci::Build ID #{build.id}... "
if build.erasable?
Ci::BuildEraseService.new(build, admin_user).execute
puts "Erased"
else
puts "Skipped (Nothing to erase or not erasable)"
end
end
end
GitLab 15.3 및 이전 버전에서는
Ci::BuildEraseService.new(build, admin_user).execute
를 build.erase(erased_by: admin_user)
로 교체하세요.
1.year.ago
는 Rails ActiveSupport::Duration
메서드입니다.
아직 사용 중인 아티팩트를 실수로 삭제할 위험을 줄이기 위해 긴 지속 시간으로 시작하세요.
필요에 따라 짧은 지속 시간으로 삭제를 다시 실행할 수 있습니다. 예를 들어 3.months.ago
, 2.weeks.ago
또는 7.days.ago
등으로.
erase_erasable_artifacts!
메서드는 동기적이며 실행 시 아티팩트가 즉시 제거됩니다;
백그라운드 큐에 의해 예약되지 않습니다.
작업 아티팩트 업로드가 500 오류로 실패
아티팩트에 대해 객체 저장소를 사용하고 작업 아티팩트 업로드가 실패하는 경우,
다음 사항을 검토하세요:
-
다음과 유사한 오류 메시지에 대한 작업 로그:
WARNING:Uploading artifacts as "archive" to coordinator... failed id=12345 responseStatus=500 Internal Server Error status=500 token=abcd1234
-
다음과 유사한 오류 메시지에 대한 워크호스 로그:
{"error":"MissingRegion: could not find region configuration","level":"error","msg":"error uploading S3 session","time":"2021-03-16T22:10:55-04:00"}
두 경우 모두, 작업 아티팩트 객체 저장소 구성에 region
을 추가해야 할 수 있습니다.
작업 아티팩트 업로드가 500 Internal Server Error (Missing file)
로 실패
폴더 경로를 포함하는 버킷 이름은 통합 객체 저장소에서 지원되지 않습니다.
예를 들어, bucket/path
. 버킷 이름에 경로가 포함된 경우 다음과 유사한 오류가 발생할 수 있습니다:
WARNING:Uploading 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
로 업로드 실패
CIFS와 함께 Windows 마운트를 사용하고 있는 경우, 러너가 산출물을 업로드하려고 할 때 invalid argument
오류가 발생할 수 있습니다:
WARNING:Uploading 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
으로 변동할 수 있습니다. 재계산 후, 사용량은 다시 예상대로 표시되어야 합니다.
산출물 다운로드 흐름 다이어그램
다음 흐름 다이어그램은 작업 산출물이 작동하는 방식을 설명합니다. 이 다이어그램은 객체 저장소가 작업 산출물에 대해 구성되었다고 가정합니다.
프록시 다운로드 비활성화
proxy_download
가 false
로 설정된 경우, GitLab은 러너를 객체 저장소에서 사전 서명된 URL로 산출물을 다운로드하도록 리디렉션합니다. 일반적으로 러너는 소스에서 직접 가져오는 것이 더 빠르기 때문에 이 구성이 일반적으로 권장됩니다. 이는 GitLab이 데이터를 가져와서 러너에게 전송할 필요가 없기 때문에 대역폭 사용도 줄일 수 있습니다. 그러나 이는 러너가 객체 저장소에 직접 접근할 수 있도록 요구합니다.
요청 흐름은 다음과 같습니다:
이 다이어그램에서:
-
먼저, 러너는
GET /api/v4/jobs/:id/artifacts
엔드포인트를 사용하여 작업 산출물을 가져오려고 시도합니다. 러너는 객체 저장소에서 직접 다운로드할 수 있음을 나타내기 위해 첫 번째 시도에서direct_download=true
쿼리 매개변수를 첨부합니다. 직접 다운로드는FF_USE_DIRECT_DOWNLOAD
기능 플래그를 통해 러너 구성에서 비활성화할 수 있습니다. 이 플래그는 기본적으로true
로 설정되어 있습니다. -
러너는
gitlab-ci-token
사용자 이름과 자동 생성된 CI/CD 작업 토큰을 비밀번호로 사용하여 HTTP 기본 인증을 통해 GET 요청을 보냅니다. 이 토큰은 GitLab에 의해 생성되며 작업이 시작될 때 러너에게 제공됩니다. -
GET 요청은 GitLab API로 전달되어 데이터베이스에서 토큰을 조회하고 작업을 트리거한 사용자를 찾습니다.
-
5-8단계에서는:
-
사용자가 빌드에 대한 접근 권한이 있는 경우, GitLab은 사전 서명된 URL을 생성하고 그 URL이 설정된 302 Redirect를 전송합니다. 러너는 302 리디렉션을 따르고 산출물을 다운로드합니다.
-
작업을 찾을 수 없는 경우 또는 사용자가 작업에 접근할 수 없는 경우, API는 401 Unauthorized를 반환합니다.
러너는 다음 HTTP 상태 코드를 수신하면 재시도하지 않습니다:
- 200 OK
- 401 Unauthorized
- 403 Forbidden
- 404 Not Found
그러나, 러너가 500 오류와 같은 다른 상태 코드를 수신하면 두 번 더 산출물 다운로드를 재시도하며 각 시도 사이에 1초 간격으로 대기합니다. 후속 시도에서는
direct_download=true
를 생략합니다. -
프록시 다운로드 활성화
proxy_download
가 true
인 경우, GitLab은 항상 객체 스토리지에서 아티팩트를 가져와서 러너에게 데이터를 전송합니다. 러너가 direct_download=true
쿼리 매개변수를 전송하더라도 마찬가지입니다. 러너의 네트워크 접근이 제한된 경우 프록시 다운로드가 바람직할 수 있습니다.
다음 다이어그램은 비활성화된 프록시 다운로드 예제와 유사하지만, 6-9단계에서 GitLab은 러너에게 302 Redirect를 전송하지 않습니다. 대신, GitLab은 Workhorse에 데이터를 가져와서 러너에게 스트리밍하도록 지시합니다. 러너 관점에서는, /api/v4/jobs/:id/artifacts
에 대한 원래의 GET 요청이 이진 데이터를 직접 반환합니다.