- 디스크 공간이 너무 많이 사용되는 작업 아티팩트
- 작업 아티팩트 업로드가 오류 500으로 실패하는 경우
500 Internal Server Error (Missing file)
로 작업 아티팩트 업로드가 실패하는 경우- Windows 마운트 사용 시
FATAL: invalid argument
로 작업 아티팩트 업로드가 실패하는 경우 - 사용량 할당량이 잘못된 아티팩트 저장소 사용량을 표시
- 아티팩트 다운로드 흐름 다이어그램
관리자를 위한 작업 아티팩트 문제 해결
작업 아티팩트를 관리할 때 다음과 같은 문제에 직면할 수 있습니다.
디스크 공간이 너무 많이 사용되는 작업 아티팩트
작업 아티팩트는 예상보다 빠르게 디스크 공간을 채울 수 있습니다. 몇 가지 가능한 이유는:
- 사용자가 작업 아티팩트의 유효 기간을 필요 이상으로 길게 구성한 경우
- 실행된 작업의 수 및 생성된 아티팩트가 예상보다 많은 경우
- 작업 로그가 예상보다 크고 시간이 지남에 따라 축적된 경우
- 아티팩트의 파일 시스템이 inode을 다 소진하는 경우 빈 디렉토리가 남아 있는 경우에 발생합니다. 고아 아티팩트 파일을 제거하는 Rake 작업으로 이를 제거합니다.
- 아티팩트 파일이 디스크에 남아있어 housekeeping에 의해 삭제되지 않은 경우. 고아 아티팩트 파일을 제거하는 Rake 작업을 실행하여 이를 제거합니다. 이 스크립트는 위에서 언급된 빈 디렉토리 또한 제거하므로 항상 작업을 찾을 수 있어야 합니다.
- 아티팩트 housekeeping이 크게 변경되었으며 업데이트된 시스템을 사용하려면 피쳐 플래그를 활성화해야 할 수 있습니다.
- 가장 최근 성공한 작업에서 최신 아티팩트를 유지하는 기능이 활성화되어 있는 경우.
이와 같은 경우에는 디스크 공간 사용량이 가장 많은 프로젝트를 확인하고, 어떤 유형의 아티팩트가 가장 많은 공간을 사용하는지 파악하며, 경우에 따라 수동으로 작업 아티팩트를 삭제하여 디스크 공간을 회수해야 합니다.
아티팩트 housekeeping
아티팩트 housekeeping은 유효 기간이 만료된 아티팩트를 식별하고 삭제할 프로세스입니다.
GitLab 15.0에서 15.2까지 사용 중지된 housekeeping
GitLab 15.0에서 아티팩트 housekeeping이 크게 개선되었으며 기본적으로 비활성화된 피쳐 플래그를 통해 도입되었습니다. 이 플래그는 기본적으로 GitLab 15.3에서 활성화되었습니다.
GitLab 15.0에서 15.2에서는 아티팩트 housekeeping이 정상적으로 작동하지 않는 것으로 보인다면 피쳐 플래그가 활성화되었는지 확인해야 합니다.
피쳐 플래그가 활성화되었는지 확인하려면 다음을 수행하세요:
-
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)
이러한 변경 내용에는 아티팩트를 잠긴
상태에서 보관
해야 한다는 것이 포함되어 있습니다.
(가장 최근 성공한 작업에서 최신 아티팩트 유지 참조)
unknown
상태의 아티팩트
housekeeping이 업데이트되기 전에 생성된 아티팩트는 unknown
상태를 가집니다. 만료되면
새 housekeeping에 의해 이러한 아티팩트가 처리되지 않습니다.
데이터베이스를 확인하여 인스턴스에 unknown
상태의 아티팩트가 있는지 확인할 수 있습니다:
-
데이터베이스 콘솔을 시작합니다:
리눅스 패키지 (Omnibus)sudo gitlab-psql
헬름 차트 (쿠버네티스)# 툴박스 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
직접 컴파일 (소스)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;
기록이 반환된다면 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 및 이후에 기본적으로 활성화되어 있습니다. 이는 위의 데이터베이스 쿼리에 의해 반환된 아티팩트를 분석하고
필요한 경우 이러한 아티팩트를 잠금
또는 잠금 해제
로 처리합니다. 그런 다음 필요한 경우 워커에 의해 이를 삭제합니다.
자체 관리되는 인스턴스에서 워커를 활성화할 수 있습니다:
-
Rails 콘솔을 시작합니다.
-
기능이 활성화되었는지 확인합니다.
Feature.enabled?(:ci_job_artifacts_backlog_work)
-
필요한 경우 기능을 활성화합니다:
Feature.enable(:ci_job_artifacts_backlog_work)
워커는 매 7분마다 1만 개의 unknown
아티팩트를 처리하며, 24시간 동안 대략 200만 개의 아티팩트를 처리합니다.
ci_job_artifacts_backlog_large_loop_limit
기능 플래그도 관련이 있으며 워커를 사용하여 unknown
아티팩트를 처리하고
5배 큰 일괄 처리량으로 처리합니다. 자체 관리되는 인스턴스에서 이 플래그를 사용하는 것은 권장되지 않습니다.
특정 만료 기간(또는 만료되지 않은)이 있는 프로젝트 및 빌드에 대한 목록
Rails console을 사용하여 다음과 같이 작업의 아티팩트가 있는 프로젝트를 찾을 수 있습니다.
- 만료일이 없는 경우
- 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
작업 아티팩트 저장의 총크기별 프로젝트 목록
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 "Job ID: #{art.job_id} - Size: #{art.size}b - Type: #{art.file_type} - Created: #{art.created_at} - File loc: #{art.file}"
end
limit(50)
의 숫자를 변경하여 나열할 작업 아티팩트 수를 변경할 수 있습니다.
이전 빌드 및 아티팩트 삭제
경고: 이러한 명령은 데이터를 데이터베이스와 저장소에서 영구적으로 제거합니다. 이러한 명령을 실행하기 전에 Support Engineer의 지침을 따르거나, 백업된 인스턴스를 복원할 준비가 된 테스트 환경에서 실행하는 것을 강력히 권장합니다.
프로젝트의 이전 아티팩트 삭제
이 단계는 사용자가 유지하기로 선택한 아티팩트도 지웁니다.
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
-
다음과 비슷한 오류 메시지에 대해 workhorse 로그를 확인하세요:
{"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
와 같이 폴더 경로가 포함된 버킷 이름이 있는 경우 500 Internal Server Error (Missing file)
와 유사한 오류가 발생할 수 있습니다.
다음과 같이 통합된 객체 저장소를 사용할 때 위의 오류로 작업 아티팩트 업로드가 실패하는 경우, 각 데이터 유형에 대해 별도의 버킷을 사용했는지 확인하세요.
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가 나열되어 있어야 합니다:
csv
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
로 설정됩니다. -
러너는 자동 생성된 CI/CD 작업 토큰을 비밀번호로 사용하여
gitlab-ci-token
사용자와 함께 HTTP 기본 인증을 사용하여 GET 요청을 보냅니다. 이 토큰은 GitLab에서 러너에게 제공됩니다. -
GET 요청은 GitLab API로 전달되어 토큰을 데이터베이스에서 찾아 작업을 시작한 사용자를 찾습니다.
-
5-8 단계에서:
-
사용자가 빌드에 액세스 할 수 있는 경우, GitLab은 사전 서명된 URL을 생성하고 해당 URL로
Location
을 설정한 302 리디렉트를 보냅니다. 러너는 302 리디렉트를 따라 아티팩트를 다운로드합니다. -
작업을 찾을 수 없거나 사용자가 작업에 액세스 권한이 없는 경우 API는 401 Unauthorized을 반환합니다.
러너는 다음과 같은 HTTP 상태 코드를 받으면 재시도하지 않습니다:
- 200 OK
- 401 Unauthorized
- 403 Forbidden
- 404 Not Found
그러나 러너가 500과 같은 기타 상태 코드를 받으면 1초씩 재우며 다시 2회 시도합니다. 이후의 재시도에서는
direct_download=true
를 생략합니다. ``` -
프록시 다운로드 활성화
만약 proxy_download
이 true
이면, GitLab은 항상 러너에 데이터를 전송하기 위해 artifact를 객체 저장소에서 가져옵니다. 심지어 러너가 direct_download=true
쿼리 매개변수를 보내더라도입니다. 프록시 다운로드는 러너가 제한된 네트워크 액세스를 갖고 있는 경우에 유용할 수 있습니다.
다음 다이어그램은 프록시 다운로드가 비활성화된 예제와 유사하지만, 단계 6-9에서 GitLab은 러너에게 302 Redirect를 보내지 않습니다. 대신에 GitLab은 Workhorse에게 데이터를 가져오도록 지시하고, 그것을 러너에게 스트리밍합니다. 러너의 관점에서, /api/v4/jobs/:id/artifacts
에 대한 원래의 GET 요청은 이진 데이터를 직접 반환합니다.