- API 요구 사항
- 저장 분석
- CI/CD 파이프라인 저장소 관리
- 컨테이너 레지스트리 저장소 관리
- 패키지 레지스트리 스토리지 관리
- 출력 가독성 향상
- 저장 관리 자동화를 위한 테스트
- 커뮤니티 리소스
스토리지 관리 자동화
이 페이지에서는 GitLab REST API를 사용하여 스토리지 사용량을 관리하기 위해 스토리지 분석 및 정리를 자동화하는 방법을 설명합니다.
파이프라인 효율성을 개선하여 스토리지 사용량을 관리할 수도 있습니다.
API 자동화에 대한 추가 도움을 받으려면 GitLab 커뮤니티 포럼 및 Discord를 사용할 수 있습니다.
API 요구 사항
스토리지 관리를 자동화하려면 GitLab.com SaaS 또는 자체 관리 인스턴스가 GitLab REST API에 접근할 수 있어야 합니다.
API 인증 범위
API와 인증하기 위해 다음 범위를 사용하세요:
- 스토리지 분석:
-
read_api
범위로 API 읽기 접근 권한. - 모든 프로젝트에서 최소 개발자 역할.
-
- 스토리지 정리:
-
api
범위로 전체 API 접근 권한. - 모든 프로젝트에서 최소 유지관리자 역할.
-
명령줄 도구나 프로그래밍 언어를 사용하여 REST API와 상호 작용할 수 있습니다.
명령줄 도구
API 요청을 보내려면 다음 중 하나를 설치하세요:
- 선호하는 패키지 관리자로 curl.
- GitLab CLI 및 ‘glab api’ 하위 명령 사용.
JSON 응답을 포맷하기 위해 jq
를 설치하세요. 자세한 정보는 생산적인 DevOps 워크플로를 위한 팁: jq 및 CI/CD 린팅 자동화를 사용한 JSON 포맷팅을 참조하세요.
이 도구를 REST API와 함께 사용하려면:
export GITLAB_TOKEN=xxx
curl --silent --header "Authorization: Bearer $GITLAB_TOKEN" "https://gitlab.com/api/v4/user" | jq
glab auth login
glab api groups/YOURGROUPNAME/projects
GitLab CLI 사용하기
일부 API 엔드포인트는 모든 결과를 검색하기 위해 페이지네이션과 후속 페이지 가져오기가 필요합니다. GitLab CLI는 --paginate
플래그를 제공합니다.
JSON 데이터 형식으로 POST 본체가 필요한 요청은 --raw-field
매개변수에 전달되는 key=value
쌍으로 작성할 수 있습니다.
자세한 정보는 GitLab CLI 엔드포인트 문서를 참조하세요.
API 클라이언트 라이브러리
이 페이지에서 설명하는 스토리지 관리 및 정리 자동화 방법은 다음을 사용합니다:
-
python-gitlab
라이브러리, 기능이 풍부한 프로그래밍 인터페이스를 제공합니다. -
GitLab API with Python 프로젝트의
get_all_projects_top_level_namespace_storage_analysis_cleanup_example.py
스크립트.
python-gitlab
라이브러리에 대한 사용 사례에 대한 자세한 내용은 효율적인 DevSecOps 워크플로: 실습 python-gitlab
API 자동화를 참조하세요.
기타 API 클라이언트 라이브러리에 대한 자세한 내용은 제 3자 클라이언트를 참고하세요.
저장 분석
저장 유형 식별
프로젝트 API 엔드포인트는 GitLab 인스턴스의 프로젝트에 대한 통계를 제공합니다.
프로젝트 API 엔드포인트를 사용하려면 statistics
키를 불리언 true
로 설정하세요.
이 데이터는 다음 저장 유형에 따른 프로젝트의 저장 소비에 대한 통찰력을 제공합니다:
-
storage_size
: 전체 저장소 -
lfs_objects_size
: LFS 객체 저장소 -
job_artifacts_size
: 작업 아티팩트 저장소 -
packages_size
: 패키지 저장소 -
repository_size
: Git 리포지토리 저장소 -
snippets_size
: 스니펫 저장소 -
uploads_size
: 업로드 저장소 -
wiki_size
: 위키 저장소
저장 유형을 식별하려면:
curl --silent --header "Authorization: Bearer $GITLAB_TOKEN" "https://gitlab.com/api/v4/projects/$GL_PROJECT_ID?statistics=true" | jq --compact-output '.id,.statistics' | jq
48349590
{
"commit_count": 2,
"storage_size": 90241770,
"repository_size": 3521,
"wiki_size": 0,
"lfs_objects_size": 0,
"job_artifacts_size": 90238249,
"pipeline_artifacts_size": 0,
"packages_size": 0,
"snippets_size": 0,
"uploads_size": 0
}
export GL_PROJECT_ID=48349590
glab api --method GET projects/$GL_PROJECT_ID --field 'statistics=true' | jq --compact-output '.id,.statistics' | jq
48349590
{
"commit_count": 2,
"storage_size": 90241770,
"repository_size": 3521,
"wiki_size": 0,
"lfs_objects_size": 0,
"job_artifacts_size": 90238249,
"pipeline_artifacts_size": 0,
"packages_size": 0,
"snippets_size": 0,
"uploads_size": 0
}
project_obj = gl.projects.get(project.id, statistics=True)
print("Project {n} statistics: {s}".format(n=project_obj.name_with_namespace, s=json.dump(project_obj.statistics, indent=4)))
프로젝트에 대한 통계를 터미널에 인쇄하려면 GL_GROUP_ID
환경 변수를 내보내고 스크립트를 실행하세요:
export GL_TOKEN=xxx
export GL_GROUP_ID=56595735
pip3 install python-gitlab
python3 get_all_projects_top_level_namespace_storage_analysis_cleanup_example.py
Project Developer Evangelism and Technical Marketing at GitLab / playground / Artifact generator group / Gen Job Artifacts 4 statistics: {
"commit_count": 2,
"storage_size": 90241770,
"repository_size": 3521,
"wiki_size": 0,
"lfs_objects_size": 0,
"job_artifacts_size": 90238249,
"pipeline_artifacts_size": 0,
"packages_size": 0,
"snippets_size": 0,
"uploads_size": 0
}
프로젝트 및 그룹에서 저장 분석
여러 프로젝트와 그룹의 분석을 자동화할 수 있습니다. 예를 들어, 최상위 네임스페이스 레벨에서 시작하여 모든 하위 그룹 및 프로젝트를 재귀적으로 분석할 수 있으며, 서로 다른 저장 유형도 분석할 수 있습니다.
여러 하위 그룹과 프로젝트를 분석하는 알고리즘의 예는 다음과 같습니다:
-
최상위 네임스페이스 ID를 가져옵니다. 네임스페이스/그룹 개요에서 ID 값을 복사할 수 있습니다.
-
최상위 그룹에서 모든 하위 그룹을 가져와 IDs를 목록에 저장합니다.
-
모든 그룹을 반복하여 각 그룹에서 모든 프로젝트를 가져와 IDs를 목록에 저장합니다.
-
분석할 저장 유형을 식별하고, 프로젝트 특성(예: 프로젝트 통계 및 작업 아티팩트)에서 정보를 수집합니다.
-
모든 프로젝트에 대한 개요를 그룹별로 인쇄하고, 그들의 저장 정보를 표시합니다.
glab
을 이용한 쉘 접근 방식은 더 작은 분석에 더 적합할 수 있습니다. 더 큰 분석의 경우 API 클라이언트 라이브러리를 사용하는 스크립트를 사용해야 합니다. 이러한 유형의 스크립트는 가독성, 데이터 저장, 흐름 제어, 테스트 및 재사용성을 향상시킬 수 있습니다.
스크립트가 API 속도 제한에 도달하지 않도록 하기 위해, 다음 예제 코드는 병렬 API 요청에 최적화되어 있지 않습니다.
이 알고리즘을 구현하려면:
export GROUP_NAME="gitlab-da"
# 하위 그룹 ID 반환
glab api groups/$GROUP_NAME/subgroups | jq --compact-output '.[]' | jq --compact-output '.id'
12034712
67218622
67162711
67640130
16058698
12034604
# 모든 하위 그룹을 반복하여 하위 그룹 가져오기, 결과 세트가 비어 있을 때까지. 예제 그룹: 12034712
glab api groups/12034712/subgroups | jq --compact-output '.[]' | jq --compact-output '.id'
56595735
70677315
67218606
70812167
# 최하위 그룹 수준
glab api groups/56595735/subgroups | jq --compact-output '.[]' | jq --compact-output '.id'
# 빈 결과, 반환하고 분석 계속
# 수집된 모든 그룹에서 프로젝트 가져오기. 예제 그룹: 56595735
glab api groups/56595735/projects | jq --compact-output '.[]' | jq --compact-output '.id'
48349590
48349263
38520467
38520405
# 프로젝트에서 저장 유형 가져오기 (ID 48349590): `artifacts` 키의 작업 아티팩트
glab api projects/48349590/jobs | jq --compact-output '.[]' | jq --compact-output '.id, .artifacts'
4828297946
[{"file_type":"archive","size":52444993,"filename":"artifacts.zip","file_format":"zip"},{"file_type":"metadata","size":156,"filename":"metadata.gz","file_format":"gzip"},{"file_type":"trace","size":3140,"filename":"job.log","file_format":null}]
4828297945
[{"file_type":"archive","size":20978113,"filename":"artifacts.zip","file_format":"zip"},{"file_type":"metadata","size":157,"filename":"metadata.gz","file_format":"gzip"},{"file_type":"trace","size":3147,"filename":"job.log","file_format":null}]
4828297944
[{"file_type":"archive","size":10489153,"filename":"artifacts.zip","file_format":"zip"},{"file_type":"metadata","size":158,"filename":"metadata.gz","file_format":"gzip"},{"file_type":"trace","size":3146,"filename":"job.log","file_format":null}]
4828297943
[{"file_type":"archive","size":5244673,"filename":"artifacts.zip","file_format":"zip"},{"file_type":"metadata","size":157,"filename":"metadata.gz","file_format":"gzip"},{"file_type":"trace","size":3145,"filename":"job.log","file_format":null}]
4828297940
[{"file_type":"archive","size":1049089,"filename":"artifacts.zip","file_format":"zip"},{"file_type":"metadata","size":157,"filename":"metadata.gz","file_format":"gzip"},{"file_type":"trace","size":3140,"filename":"job.log","file_format":null}]
#!/usr/bin/env python
import datetime
import gitlab
import os
import sys
GITLAB_SERVER = os.environ.get('GL_SERVER', 'https://gitlab.com')
GITLAB_TOKEN = os.environ.get('GL_TOKEN') # 토큰은 개발자 권한이 필요합니다.
PROJECT_ID = os.environ.get('GL_PROJECT_ID') # 선택 사항
GROUP_ID = os.environ.get('GL_GROUP_ID') # 선택 사항
if __name__ == "__main__":
if not GITLAB_TOKEN:
print("🤔 GL_TOKEN 환경 변수를 설정해주세요.")
sys.exit(1)
gl = gitlab.Gitlab(GITLAB_SERVER, private_token=GITLAB_TOKEN, pagination="keyset", order_by="id", per_page=100)
# 모든 프로젝트 수집, 또는 그룹 ID 또는 프로젝트 ID에서 선호
projects = []
# 직접 프로젝트 ID
if PROJECT_ID:
projects.append(gl.projects.get(PROJECT_ID))
# 그룹 및 내부 프로젝트
elif GROUP_ID:
group = gl.groups.get(GROUP_ID)
for project in group.projects.list(include_subgroups=True, get_all=True):
manageable_project = gl.projects.get(project.id , lazy=True)
projects.append(manageable_project)
for project in projects:
jobs = project.jobs.list(pagination="keyset", order_by="id", per_page=100, iterator=True)
for job in jobs:
print("DEBUG: ID {i}: {a}".format(i=job.id, a=job.attributes['artifacts']))
스크립트는 프로젝트 작업 아티팩트를 JSON 형식의 목록으로 출력합니다:
[
{
"file_type": "archive",
"size": 1049089,
"filename": "artifacts.zip",
"file_format": "zip"
},
{
"file_type": "metadata",
"size": 157,
"filename": "metadata.gz",
"file_format": "gzip"
},
{
"file_type": "trace",
"size": 3146,
"filename": "job.log",
"file_format": null
}
]
CI/CD 파이프라인 저장소 관리
작업 아티팩트는 파이프라인 저장소의 대부분을 소비하며, 작업 로그도 수백 킬로바이트를 생성할 수 있습니다.
불필요한 작업 아티팩트를 먼저 삭제한 후 분석 후 작업 로그를 정리해야 합니다.
경고:
작업 로그와 아티팩트를 삭제하는 것은 되돌릴 수 없는 파괴적인 행위입니다. 주의해서 사용하세요. 보고서 아티팩트, 작업 로그 및 메타데이터 파일을 포함한 특정 파일을 삭제하면 이러한 파일을 데이터 소스로 사용하는 GitLab 기능에 영향을 미칩니다.
작업 아티팩트 목록
파이프라인 저장소를 분석하려면 작업 API 엔드포인트를 사용하여
작업 아티팩트 목록을 가져올 수 있습니다. 엔드포인트는 artifacts
속성에서 작업 아티팩트의 file_type
키를 반환합니다.
file_type
키는 아티팩트 유형을 나타냅니다:
-
archive
는 생성된 작업 아티팩트를 zip 파일로 사용합니다. -
metadata
는 Gzip 파일에서 추가 메타데이터로 사용됩니다. -
trace
는 원시 파일로서job.log
에 사용됩니다.
작업 아티팩트는 디스크에 캐시 파일로 쓸 수 있는 데이터 구조를 제공합니다.
이를 통해 구현을 테스트할 수 있습니다.
모든 프로젝트를 가져오는 예제 코드를 기반으로 Python 스크립트를 확장하여 더 많은 분석을 수행할 수 있습니다.
다음 예제는 프로젝트에서 작업 아티팩트를 쿼리한 결과를 보여줍니다:
[
{
"file_type": "archive",
"size": 1049089,
"filename": "artifacts.zip",
"file_format": "zip"
},
{
"file_type": "metadata",
"size": 157,
"filename": "metadata.gz",
"file_format": "gzip"
},
{
"file_type": "trace",
"size": 3146,
"filename": "job.log",
"file_format": null
}
]
스크립트를 구현하는 방식에 따라 다음과 같은 작업을 수행할 수 있습니다:
-
모든 작업 아티팩트를 수집하고 스크립트의 마지막에 요약 테이블을 인쇄합니다.
-
정보를 즉시 인쇄합니다.
다음 예제에서는 작업 아티팩트가 ci_job_artifacts
목록에 수집됩니다. 스크립트는 모든 프로젝트를 반복하고 다음을 가져옵니다:
-
모든 속성을 포함하는
project_obj
객체 변수. -
job
객체의artifacts
속성.
대량의 파이프라인 및 작업 목록을 반복하기 위해 키셋 페이지네이션을 사용할 수 있습니다.
ci_job_artifacts = []
for project in projects:
project_obj = gl.projects.get(project.id)
jobs = project.jobs.list(pagination="keyset", order_by="id", per_page=100, iterator=True)
for job in jobs:
artifacts = job.attributes['artifacts']
#print("DEBUG: ID {i}: {a}".format(i=job.id, a=json.dumps(artifacts, indent=4)))
if not artifacts:
continue
for a in artifacts:
data = {
"project_id": project_obj.id,
"project_web_url": project_obj.name,
"project_path_with_namespace": project_obj.path_with_namespace,
"job_id": job.id,
"artifact_filename": a['filename'],
"artifact_file_type": a['file_type'],
"artifact_size": a['size']
}
ci_job_artifacts.append(data)
print("\n데이터 수집이 완료되었습니다.")
if len(ci_job_artifacts) > 0:
print("|프로젝트|작업|아티팩트 이름|아티팩트 유형|아티팩트 크기|\n|-|-|-|-|-|") # 마크다운 친화적인 테이블 시작
for artifact in ci_job_artifacts:
print('| [{project_name}]({project_web_url}) | {job_name} | {artifact_name} | {artifact_type} | {artifact_size} |'.format(project_name=artifact['project_path_with_namespace'], project_web_url=artifact['project_web_url'], job_name=artifact['job_id'], artifact_name=artifact['artifact_filename'], artifact_type=artifact['artifact_file_type'], artifact_size=render_size_mb(artifact['artifact_size'])))
else:
print("아티팩트가 발견되지 않았습니다.")
스크립트의 마지막에서 작업 아티팩트는 마크다운 형식의 테이블로 인쇄됩니다. 테이블 내용을 이슈 코멘트나 설명에 붙여넣거나, GitLab 리포지토리의 마크다운 파일을 작성할 수 있습니다.
$ python3 get_all_projects_top_level_namespace_storage_analysis_cleanup_example.py
|프로젝트|작업|아티팩트 이름|아티팩트 유형|아티팩트 크기|
|-|-|-|-|-|
| [gitlab-da/playground/artifact-gen-group/gen-job-artifacts-4](Gen Job Artifacts 4) | 4828297946 | artifacts.zip | archive | 50.0154 |
| [gitlab-da/playground/artifact-gen-group/gen-job-artifacts-4](Gen Job Artifacts 4) | 4828297946 | metadata.gz | metadata | 0.0001 |
| [gitlab-da/playground/artifact-gen-group/gen-job-artifacts-4](Gen Job Artifacts 4) | 4828297946 | job.log | trace | 0.0030 |
| [gitlab-da/playground/artifact-gen-group/gen-job-artifacts-4](Gen Job Artifacts 4) | 4828297945 | artifacts.zip | archive | 20.0063 |
| [gitlab-da/playground/artifact-gen-group/gen-job-artifacts-4](Gen Job Artifacts 4) | 4828297945 | metadata.gz | metadata | 0.0001 |
| [gitlab-da/playground/artifact-gen-group/gen-job-artifacts-4](Gen Job Artifacts 4) | 4828297945 | job.log | trace | 0.0030 |
작업 아티팩트를 대량으로 삭제하기
대량으로 삭제할 작업 아티팩트의 유형을 필터링하기 위해 Python 스크립트를 사용할 수 있습니다.
API 쿼리 결과를 필터링하여 비교합니다:
-
created_at
값을 사용하여 아티팩트의 나이를 계산합니다. -
size
속성을 사용하여 아티팩트가 크기 임계값을 충족하는지 확인합니다.
전형적인 요청:
- 지정된 일수보다 오래된 작업 아티팩트를 삭제합니다.
- 지정된 저장소를 초과하는 작업 아티팩트를 삭제합니다. 예: 100MB.
다음 예에서, 스크립트는 작업 속성을 반복하고 삭제를 위해 표시합니다.
컬렉션 루프에서 객체 잠금을 제거하면, 스크립트는 삭제를 위해 표시된 작업 아티팩트를 삭제합니다.
for project in projects:
project_obj = gl.projects.get(project.id)
jobs = project.jobs.list(pagination="keyset", order_by="id", per_page=100, iterator=True)
for job in jobs:
artifacts = job.attributes['artifacts']
if not artifacts:
continue
# 고급 필터링: 나이와 크기
# 예: 90일, 10MB 임계값 (TODO: 구성 가능하게 만들기)
threshold_age = 90 * 24 * 60 * 60
threshold_size = 10 * 1024 * 1024
# 작업 나이, API 형식 파싱 필요: 2023-08-08T22:41:08.270Z
created_at = datetime.datetime.strptime(job.created_at, '%Y-%m-%dT%H:%M:%S.%fZ')
now = datetime.datetime.now()
age = (now - created_at).total_seconds()
# 짧게: 함수 사용
# age = calculate_age(job.created_at)
for a in artifacts:
# ... 가독성을 위해 분석 수집 코드 생략
# 고급 필터링: 작업 아티팩트의 나이와 크기를 임계값과 비교
if (float(age) > float(threshold_age)) or (float(a['size']) > float(threshold_size)):
# 삭제를 위해 작업 표시 (루프 내에서 삭제 불가)
jobs_marked_delete_artifacts.append(job)
print("\n데이터 수집 완료.")
# 고급 필터링: 삭제를 위해 표시된 모든 작업 아티팩트 삭제
for job in jobs_marked_delete_artifacts:
# 아티팩트 삭제
print("DEBUG", job)
job.delete_artifacts()
# 수집 요약 인쇄 (가독성을 위해 제거)
프로젝트에 대한 모든 작업 아티팩트 삭제
프로젝트의 작업 아티팩트가 필요하지 않은 경우, 다음 명령을 사용하여 모든 작업 아티팩트를 삭제할 수 있습니다. 이 작업은 되돌릴 수 없습니다.
아티팩트 삭제는 삭제할 아티팩트의 수에 따라 몇 분 또는 몇 시간이 걸릴 수 있습니다. 이후의 API에 대한 분석 쿼리는 아티팩트를 잘못된 긍정 결과로 반환할 수 있습니다.
결과와 혼동되지 않도록 즉시 추가 API 요청을 실행하지 마세요.
기본적으로 가장 최근에 성공한 작업의 아티팩트는 유지됩니다.
프로젝트에 대한 모든 작업 아티팩트를 삭제하려면:
export GL_PROJECT_ID=48349590
curl --silent --header "Authorization: Bearer $GITLAB_TOKEN" --request DELETE "https://gitlab.com/api/v4/projects/$GL_PROJECT_ID/artifacts"
glab api --method GET projects/$GL_PROJECT_ID/jobs | jq --compact-output '.[]' | jq --compact-output '.id, .artifacts'
glab api --method DELETE projects/$GL_PROJECT_ID/artifacts
project.artifacts.delete()
작업 로그 삭제
작업 로그를 삭제하면 전체 작업도 삭제됩니다.
GitLab CLI 사용 예시:
glab api --method GET projects/$GL_PROJECT_ID/jobs | jq --compact-output '.[]' | jq --compact-output '.id'
4836226184
4836226183
4836226181
4836226180
glab api --method POST projects/$GL_PROJECT_ID/jobs/4836226180/erase | jq --compact-output '.name,.status'
"generate-package: [1]"
"성공"
python-gitlab
API 라이브러리에서는 job.delete_artifacts()
대신에 job.erase()
를 사용하세요.
이 API 호출이 차단되지 않도록 하려면 작업 아티팩트를 삭제하는 호출 사이에 짧은 시간 동안 스크립트를 대기하도록 설정하세요.
for job in jobs_marked_delete_artifacts:
# 아티팩트 및 작업 로그 삭제
print("DEBUG", job)
#job.delete_artifacts()
job.erase()
# 1초 대기
time.sleep(1)
작업 로그에 대한 보존 정책 생성을 지원하는 내용은 이슈 374717에서 제안되었습니다.
오래된 파이프라인 삭제
파이프라인은 전체 스토리지 사용량에 추가되지 않지만, 필요시 자동으로 삭제할 수 있습니다.
특정 날짜를 기준으로 파이프라인을 삭제하려면 created_at
키를 지정하세요.
날짜를 사용하여 현재 날짜와 파이프라인이 생성된 날짜의 차이를 계산할 수 있습니다.
나이가 임계값보다 크면 파이프라인이 삭제됩니다.
참고:
created_at
키는 타임스탬프에서 Unix 에포크 시간으로 변환되어야 합니다.
예를 들어, date -d '2023-08-08T18:59:47.581Z' +%s
와 같이 사용할 수 있습니다.
GitLab CLI 사용 예시:
export GL_PROJECT_ID=48349590
glab api --method GET projects/$GL_PROJECT_ID/pipelines | jq --compact-output '.[]' | jq --compact-output '.id,.created_at'
960031926
"2023-08-08T22:09:52.745Z"
959884072
"2023-08-08T18:59:47.581Z"
glab api --method DELETE projects/$GL_PROJECT_ID/pipelines/960031926
glab api --method GET projects/$GL_PROJECT_ID/pipelines | jq --compact-output '.[]' | jq --compact-output '.id,.created_at'
959884072
"2023-08-08T18:59:47.581Z"
다음은 Bash 스크립트를 사용하는 예시입니다:
-
jq
와 GitLab CLI가 설치되어 있고 인증되었습니다. - 내보낸 환경 변수
GL_PROJECT_ID
.
전체 스크립트 get_cicd_pipelines_compare_age_threshold_example.sh
는 GitLab API with Linux Shell 프로젝트에 위치해 있습니다.
#/bin/bash
CREATED_AT_ARR=$(glab api --method GET projects/$GL_PROJECT_ID/pipelines | jq --compact-output '.[]' | jq --compact-output '.created_at' | jq --raw-output @sh)
for row in ${CREATED_AT_ARR[@]}
do
stripped=$(echo $row | xargs echo)
#echo $stripped #DEBUG
CREATED_AT_TS=$(date -d "$stripped" +%s)
NOW=$(date +%s)
AGE=$(($NOW-$CREATED_AT_TS))
AGE_THRESHOLD=$((90*24*60*60)) # 90일
if [ $AGE -gt $AGE_THRESHOLD ];
then
echo "파이프라인 나이 $AGE 임계값 $AGE_THRESHOLD보다 큽니다. 삭제되어야 합니다."
# TODO glab 호출로 파이프라인을 삭제합니다. 위 glab 호출에서 수집된 ID가 필요합니다.
else
echo "파이프라인 나이 $AGE 임계값 $AGE_THRESHOLD보다 크지 않습니다. 무시합니다."
fi
done
유사한 알고리즘을 구현하고 created_at
속성을 비교하여 작업 아티팩트 나이를 비교하려면 python-gitlab
API 라이브러리를 사용할 수도 있습니다:
# ...
for pipeline in project.pipelines.list(iterator=True):
pipeline_obj = project.pipelines.get(pipeline.id)
print("DEBUG: {p}".format(p=json.dumps(pipeline_obj.attributes, indent=4)))
created_at = datetime.datetime.strptime(pipeline.created_at, '%Y-%m-%dT%H:%M:%S.%fZ')
now = datetime.datetime.now()
age = (now - created_at).total_seconds()
threshold_age = 90 * 24 * 60 * 60
if (float(age) > float(threshold_age)):
print("파이프라인 삭제", pipeline.id)
pipeline_obj.delete()
오래된 파이프라인의 자동 삭제는 이슈 338480에서 제안되었습니다.
작업 아티팩트 만료 설정 목록
아티팩트 저장소를 관리하기 위해 아티팩트가 만료되는 시간을 업데이트하거나 구성할 수 있습니다.
아티팩트의 만료 설정은 .gitlab-ci.yml
의 각 작업 구성에서 구성됩니다.
여러 프로젝트가 있는 경우 CI/CD 구성에서 작업 정의가 어떻게 구성되는지에 따라 만료 설정을 찾기가 어려울 수 있습니다. 전체 CI/CD 구성을 검색하는 스크립트를 사용할 수 있습니다. 이는 extends
또는 !reference
와 같이 값을 상속받은 후에 해결되는 객체에 대한 접근을 포함합니다.
스크립트는 병합된 CI/CD 구성 파일을 검색하고 아티팩트 키를 검색하여 다음을 수행합니다:
- 만료 설정이 없는 작업 식별.
- 아티팩트 만료가 구성된 작업의 만료 설정 반환.
다음 프로세스는 스크립트가 아티팩트 만료 설정을 검색하는 방법을 설명합니다:
-
병합된 CI/CD 구성을 생성하기 위해, 스크립트는 모든 프로젝트를 반복하고
ci_lint()
메서드를 호출합니다. -
yaml_load
함수는 병합된 구성을 Python 데이터 구조로 로드하여 추가 분석을 수행합니다. -
script
키도 포함된 사전은 아티팩트 키가 존재할 수 있는 작업 정의로 식별됩니다. -
그렇다면, 스크립트는 하위 키
expire_in
을 분석하고 나중에 Markdown 테이블 요약에 인쇄할 세부 정보를 저장합니다.
ci_job_artifacts_expiry = {}
# 프로젝트를 반복하고 .gitlab-ci.yml을 가져오고, 전체 변환 구성을 얻기 위해 린터를 실행하고, `artifacts:` 설정을 추출합니다.
# https://python-gitlab.readthedocs.io/en/stable/gl_objects/ci_lint.html
for project in projects:
project_obj = gl.projects.get(project.id)
project_name = project_obj.name
project_web_url = project_obj.web_url
try:
lint_result = project.ci_lint.get()
if lint_result.merged_yaml is None:
continue
ci_pipeline = yaml.safe_load(lint_result.merged_yaml)
#print("Project {p} Config\n{c}\n\n".format(p=project_name, c=json.dumps(ci_pipeline, indent=4)))
for k in ci_pipeline:
v = ci_pipeline[k]
# `script` 속성을 가진 작업 객체입니다
if isinstance(v, dict) and 'script' in v:
print(".", end="", flush=True) # 여전히 반복되고 있다는 피드백을 얻습니다
artifacts = v['artifacts'] if 'artifacts' in v else {}
print("Project {p} job {j} artifacts {a}".format(p=project_name, j=k, a=json.dumps(artifacts, indent=4)))
expire_in = None
if 'expire_in' in artifacts:
expire_in = artifacts['expire_in']
store_key = project_web_url + '_' + k
ci_job_artifacts_expiry[store_key] = { 'project_web_url': project_web_url,
'project_name': project_name,
'job_name': k,
'artifacts_expiry': expire_in}
except Exception as e:
print(f"ci_pipelines에서 아티팩트를 검색하는 동안 예외가 발생했습니다: {e}".format(e=e))
if len(ci_job_artifacts_expiry) > 0:
print("|프로젝트|작업|아티팩트 만료|\n|-|-|-|") #Markdown 친화적인 테이블 시작
for k, details in ci_job_artifacts_expiry.items():
if details['job_name'][0] == '.':
continue # '.' 문자로 시작하는 작업 템플릿 무시
print(f'| [{ details["project_name"] }]({details["project_web_url"]}) | { details["job_name"] } | { details["artifacts_expiry"] if details["artifacts_expiry"] is not None else "❌ N/A" } |')
이 스크립트는 다음과 같은 Markdown 요약 테이블을 생성합니다:
- 프로젝트 이름 및 URL.
- 작업 이름.
-
artifacts:expire_in
설정, 또는 설정이 없는 경우N/A
.
스크립트는 다음과 같은 작업 템플릿을 인쇄하지 않습니다:
-
.
문자로 시작하는 작업 템플릿. - 아티팩트를 생성하는 런타임 작업 객체로 인스턴스화되지 않은 작업 템플릿들.
export GL_GROUP_ID=56595735
# 스크립트는 pyyaml도 필요합니다.
pip3 install python-gitlab pyyaml
python3 get_all_cicd_config_artifacts_expiry.py
|프로젝트|작업|아티팩트 만료|
|-|-|-|
| [Gen Job Artifacts 4](https://gitlab.com/gitlab-da/playground/artifact-gen-group/gen-job-artifacts-4) | generator | 30 days |
| [Gen Job Artifacts with expiry and included jobs](https://gitlab.com/gitlab-da/playground/artifact-gen-group/gen-job-artifacts-expiry-included-jobs) | included-job10 | 10 days |
| [Gen Job Artifacts with expiry and included jobs](https://gitlab.com/gitlab-da/playground/artifact-gen-group/gen-job-artifacts-expiry-included-jobs) | included-job1 | 1 days |
| [Gen Job Artifacts with expiry and included jobs](https://gitlab.com/gitlab-da/playground/artifact-gen-group/gen-job-artifacts-expiry-included-jobs) | included-job30 | 30 days |
| [Gen Job Artifacts with expiry and included jobs](https://gitlab.com/gitlab-da/playground/artifact-gen-group/gen-job-artifacts-expiry-included-jobs) | generator | 30 days |
| [Gen Job Artifacts 2](https://gitlab.com/gitlab-da/playground/artifact-gen-group/gen-job-artifacts-2) | generator | ❌ N/A |
| [Gen Job Artifacts 1](https://gitlab.com/gitlab-da/playground/artifact-gen-group/gen-job-artifacts-1) | generator | ❌ N/A |
get_all_cicd_config_artifacts_expiry.py
스크립트는 GitLab API with Python project에 위치합니다.
또한, 고급 검색을 API 요청과 함께 사용할 수 있습니다. 다음 예제는 scope: blobs를 사용하여 모든 *.yml
파일에서 artifacts
문자열을 검색합니다:
# https://gitlab.com/gitlab-da/playground/artifact-gen-group/gen-job-artifacts-expiry-included-jobs
export GL_PROJECT_ID=48349263
glab api --method GET projects/$GL_PROJECT_ID/search --field "scope=blobs" --field "search=expire_in filename:*.yml"
재고 접근 방식에 대한 자세한 내용은 GitLab이 Docker Hub에서 오픈 소스 컨테이너 이미지 삭제 완화에 어떻게 도움이 되는지를 참조하세요.
작업 아티팩트의 기본 만료 설정
프로젝트에서 작업 아티팩트의 기본 만료를 설정하려면 .gitlab-ci.yml
파일에 expire_in
값을 지정합니다:
default:
artifacts:
expire_in: 1 week
컨테이너 레지스트리 저장소 관리
컨테이너 레지스트리는 프로젝트 또는 그룹에서 사용할 수 있습니다. 두 위치를 분석하여 정리 전략을 구현할 수 있습니다.
컨테이너 레지스트리 목록
프로젝트에서 컨테이너 레지스트리를 나열하려면:
export GL_PROJECT_ID=48057080
curl --silent --header "Authorization: Bearer $GITLAB_TOKEN" "https://gitlab.com/api/v4/projects/$GL_PROJECT_ID/registry/repositories" | jq --compact-output '.[]' | jq --compact-output '.id,.location' | jq
4435617
"registry.gitlab.com/gitlab-da/playground/container-package-gen-group/docker-alpine-generator"
curl --silent --header "Authorization: Bearer $GITLAB_TOKEN" "https://gitlab.com/api/v4/registry/repositories/4435617?size=true" | jq --compact-output '.id,.location,.size'
4435617
"registry.gitlab.com/gitlab-da/playground/container-package-gen-group/docker-alpine-generator"
3401613
export GL_PROJECT_ID=48057080
glab api --method GET projects/$GL_PROJECT_ID/registry/repositories | jq --compact-output '.[]' | jq --compact-output '.id,.location'
4435617
"registry.gitlab.com/gitlab-da/playground/container-package-gen-group/docker-alpine-generator"
glab api --method GET registry/repositories/4435617 --field='size=true' | jq --compact-output '.id,.location,.size'
4435617
"registry.gitlab.com/gitlab-da/playground/container-package-gen-group/docker-alpine-generator"
3401613
glab api --method GET projects/$GL_PROJECT_ID/registry/repositories/4435617/tags | jq --compact-output '.[]' | jq --compact-output '.name'
"latest"
glab api --method GET projects/$GL_PROJECT_ID/registry/repositories/4435617/tags/latest | jq --compact-output '.name,.created_at,.total_size'
"latest"
"2023-08-07T19:20:20.894+00:00"
3401613
컨테이너 이미지를 대량으로 삭제
컨테이너 이미지 태그를 대량으로 삭제할 때, 다음을 구성할 수 있습니다:
- 유지할 태그 이름 및 이미지를 위한 일치하는 정규 표현식(
name_regex_keep
) 또는 삭제할 것(name_regex_delete
) - 태그 이름과 일치하는 이미지 태그 수(
keep_n
) - 이미지 태그를 삭제할 수 있는 일 수(
older_than
)
경고:
GitLab.com에서 컨테이너 레지스트리의 규모로 인해 이 API로 삭제할 수 있는 태그 수가 제한됩니다.
컨테이너 레지스트리에 삭제할 태그가 많은 경우 일부만 삭제됩니다. API를 여러 번 호출해야 할 수 있습니다. 태그를 자동 삭제하도록 예약하려면 정리 정책을 사용하세요.
다음 예는 python-gitlab
API 라이브러리를 사용하여 태그 목록을 가져오고, 필터 매개변수를 사용하여 delete_in_bulk()
메서드를 호출합니다.
repositories = project.repositories.list(iterator=True, size=True)
if len(repositories) > 0:
repository = repositories.pop()
tags = repository.tags.list()
# 정리: 최신 태그만 유지
repository.tags.delete_in_bulk(keep_n=1)
# 정리: 1개월 이상 된 모든 태그 삭제
repository.tags.delete_in_bulk(older_than="1m")
# 정리: 정규표현식 `v.*`과 일치하는 모든 태그 삭제, 최신 2개 태그 유지
repository.tags.delete_in_bulk(name_regex_delete="v.+", keep_n=2)
컨테이너 정리 정책 생성
프로젝트 REST API 엔드포인트를 사용하여 컨테이너 정리 정책을 생성하세요. 정리 정책을 설정한 후, 귀하의 사양에 일치하는 모든 컨테이너 이미지가 자동으로 삭제됩니다. 추가 API 자동화 스크립트가 필요하지 않습니다.
속성을 바디 매개변수로 전송하려면:
- 표준 입력에서 읽기 위해
--input -
매개변수를 사용하세요. -
Content-Type
헤더를 설정하세요.
다음 예제는 GitLab CLI를 사용하여 정리 정책을 생성합니다:
export GL_PROJECT_ID=48057080
echo '{"container_expiration_policy_attributes":{"cadence":"1month","enabled":true,"keep_n":1,"older_than":"14d","name_regex":".*","name_regex_keep":".*-main"}}' | glab api --method PUT --header 'Content-Type: application/json;charset=UTF-8' projects/$GL_PROJECT_ID --input -
...
"container_expiration_policy": {
"cadence": "1month",
"enabled": true,
"keep_n": 1,
"older_than": "14d",
"name_regex": ".*",
"name_regex_keep": ".*-main",
"next_run_at": "2023-09-08T21:16:25.354Z"
},
컨테이너 이미지 최적화
컨테이너 이미지를 최적화하여 이미지 크기와 전체 스토리지 소비를 줄일 수 있습니다. 파이프라인 효율성 문서에서 자세히 알아보세요.
패키지 레지스트리 스토리지 관리
패키지 레지스트리는 프로젝트 또는 그룹에 대해 사용할 수 있습니다.
패키지 및 파일 목록
다음 예제는 GitLab CLI를 사용하여 정의된 프로젝트 ID에서 패키지를 가져오는 방법을 보여줍니다. 결과 집합은 jq
명령 체인을 사용하여 필터링할 수 있는 사전 항목 배열입니다.
# https://gitlab.com/gitlab-da/playground/container-package-gen-group/generic-package-generator
export GL_PROJECT_ID=48377643
glab api --method GET projects/$GL_PROJECT_ID/packages | jq --compact-output '.[]' | jq --compact-output '.id,.name,.package_type'
16669383
"generator"
"generic"
16671352
"generator"
"generic"
16672235
"generator"
"generic"
16672237
"generator"
"generic"
패키지 ID를 사용하여 패키지 내의 파일과 그 크기를 검사하세요.
glab api --method GET projects/$GL_PROJECT_ID/packages/16669383/package_files | jq --compact-output '.[]' |
jq --compact-output '.package_id,.file_name,.size'
16669383
"nighly.tar.gz"
10487563
유사한 자동화 셸 스크립트는 오래된 파이프라인 삭제 섹션에 작성되어 있습니다.
다음 스크립트 예제는 python-gitlab
라이브러리를 사용하여 모든 패키지를 루프를 통해 가져오고,
그 패키지 파일을 반복하여 file_name
및 size
속성을 출력합니다.
packages = project.packages.list(order_by="created_at")
for package in packages:
package_files = package.package_files.list()
for package_file in package_files:
print("패키지 이름: {p} 파일 이름: {f} 크기 {s}".format(
p=package.name, f=package_file.file_name, s=render_size_mb(package_file.size)))
패키지 삭제
패키지에서 파일 삭제는 패키지를 손상시킬 수 있습니다. 자동화된 정리 유지 관리를 수행할 때 패키지를 삭제해야 합니다.
패키지를 삭제하려면 GitLab CLI를 사용하여 --method
매개변수를 DELETE
로 변경하세요:
glab api --method DELETE projects/$GL_PROJECT_ID/packages/16669383
패키지 크기를 계산하고 크기 임계값과 비교하려면 python-gitlab
라이브러리를 사용하여 패키지 및 파일 목록 섹션에서 설명된 코드를 확장할 수 있습니다.
다음 코드 예제는 패키지의 연령을 계산하고 조건이 일치할 때 패키지를 삭제합니다:
packages = project.packages.list(order_by="created_at")
for package in packages:
package_size = 0.0
package_files = package.package_files.list()
for package_file in package_files:
print("패키지 이름: {p} 파일 이름: {f} 크기 {s}".format(
p=package.name, f=package_file.file_name, s=render_size_mb(package_file.size)))
package_size =+ package_file.size
print("패키지 크기: {s}\n\n".format(s=render_size_mb(package_size)))
threshold_size = 10 * 1024 * 1024
if (package_size > float(threshold_size)):
print("패키지 크기 {s} > 임계값 {t}, 패키지를 삭제합니다.".format(
s=render_size_mb(package_size), t=render_size_mb(threshold_size)))
package.delete()
threshold_age = 90 * 24 * 60 * 60
package_age = created_at = calculate_age(package.created_at)
if (float(package_age > float(threshold_age))):
print("패키지 연령 {a} > 임계값 {t}, 패키지를 삭제합니다.".format(
a=render_age_time(package_age), t=render_age_time(threshold_age)))
package.delete()
코드는 다음과 같은 출력을 생성하며, 이를 통해 추가 분석에 사용할 수 있습니다:
패키지 이름: generator 파일 이름: nighly.tar.gz 크기 10.0017
패키지 크기: 10.0017
패키지 크기 10.0017 > 임계값 10.0000, 패키지를 삭제합니다.
패키지 이름: generator 파일 이름: 1-nightly.tar.gz 크기 1.0004
패키지 크기: 1.0004
패키지 이름: generator 파일 이름: 10-nightly.tar.gz 크기 10.0018
패키지 이름: generator 파일 이름: 20-nightly.tar.gz 크기 20.0033
패키지 크기: 20.0033
패키지 크기 20.0033 > 임계값 10.0000, 패키지를 삭제합니다.
의존성 프록시
정리 정책와 API를 사용하여 캐시 삭제 방법을 검토하세요.
출력 가독성 향상
타임스탬프 초를 지속 시간 형식으로 변환하거나 원시 바이트를 보다 표현적인 형식으로 인쇄해야 할 수 있습니다. 다음 도우미 함수를 사용하여 값을 변환하여 가독성을 향상할 수 있습니다:
# 현재 유닉스 타임스탬프
date +%s
# 시간대가 있는 `created_at` 날짜 시간을 유닉스 타임스탬프으로 변환
date -d '2023-08-08T18:59:47.581Z' +%s
python-gitlab
API 라이브러리를 사용하는 Python 예제:
def render_size_mb(v):
return "%.4f" % (v / 1024 / 1024)
def render_age_time(v):
return str(datetime.timedelta(seconds = v))
# 시간대가 있는 `created_at` 날짜 시간을 유닉스 타임스탬프으로 변환
def calculate_age(created_at_datetime):
created_at_ts = datetime.datetime.strptime(created_at_datetime, '%Y-%m-%dT%H:%M:%S.%fZ')
now = datetime.datetime.now()
return (now - created_at_ts).total_seconds()
저장 관리 자동화를 위한 테스트
저장 관리 자동화를 테스트하기 위해, 테스트 데이터를 생성하거나 저장소를 채워서 분석 및 삭제가 예상대로 작동하는지 확인해야 할 수 있습니다. 다음 섹션에서는 짧은 시간 안에 저장 블롭을 테스트하고 생성하는 도구와 팁을 제공합니다.
작업 아티팩트 생성
CI/CD 작업 매트릭스 빌드를 사용하여 가짜 아티팩트 블롭을 생성할 테스트 프로젝트를 만듭니다. 아티팩트를 매일 생성하기 위해 CI/CD 파이프라인을 추가하세요.
- 새 프로젝트를 만듭니다.
-
다음 스니펫을
.gitlab-ci.yml
에 추가하여 작업 아티팩트 생성기 구성을 포함시킵니다.include: - remote: https://gitlab.com/gitlab-da/use-cases/efficiency/job-artifact-generator/-/raw/main/.gitlab-ci.yml
- 파이프라인 일정을 구성합니다.
- 파이프라인을 수동으로 트리거합니다.
대안으로, MB_COUNT
변수의 다양한 값으로 86 MB의 하루 생성 MB를 줄이세요.
include:
- remote: https://gitlab.com/gitlab-da/use-cases/efficiency/job-artifact-generator/-/raw/main/.gitlab-ci.yml
generator:
parallel:
matrix:
- MB_COUNT: [1, 5, 10, 20, 50]
자세한 내용은 작업 아티팩트 생성기 README와 예제 그룹을 참조하세요.
만료 있는 작업 아티팩트 생성
프로젝트 CI/CD 구성은 다음에 작업 정의를 지정합니다:
- 주요
.gitlab-ci.yml
구성 파일. -
artifacts:expire_in
설정. - 프로젝트 파일 및 템플릿.
분석 스크립트를 테스트하기 위해, gen-job-artifacts-expiry-included-jobs
프로젝트는 예제 구성을 제공합니다.
# .gitlab-ci.yml
include:
- include_jobs.yml
default:
artifacts:
paths:
- '*.txt'
.gen-tmpl:
script:
- dd if=/dev/urandom of=${$MB_COUNT}.txt bs=1048576 count=${$MB_COUNT}
generator:
extends: [.gen-tmpl]
parallel:
matrix:
- MB_COUNT: [1, 5, 10, 20, 50]
artifacts:
untracked: false
when: on_success
expire_in: 30 days
# include_jobs.yml
.includeme:
script:
- dd if=/dev/urandom of=1.txt bs=1048576 count=1
included-job10:
script:
- echo "Servus"
- !reference [.includeme, script]
artifacts:
untracked: false
when: on_success
expire_in: 10 days
included-job1:
script:
- echo "Gruezi"
- !reference [.includeme, script]
artifacts:
untracked: false
when: on_success
expire_in: 1 days
included-job30:
script:
- echo "Grias di"
- !reference [.includeme, script]
artifacts:
untracked: false
when: on_success
expire_in: 30 days
컨테이너 이미지 생성
예제 그룹 container-package-gen-group
은 다음과 같은 프로젝트를 제공합니다:
- Dockerfile에서 기본 이미지를 사용하여 새로운 이미지를 구축합니다.
- GitLab.com SaaS에서 이미지를 빌드하기 위해
Docker.gitlab-ci.yml
템플릿을 포함합니다. - 새로운 이미지를 매일 생성하기 위해 파이프라인 일정을 구성합니다.
포크할 수 있는 예제 프로젝트:
일반 패키지 생성
예제 프로젝트 generic-package-generator
은 다음과 같은 프로젝트를 제공합니다:
- 무작위 텍스트 블롭을 생성하고 현재 유닉스 타임스탬프를 릴리즈 버전으로 사용하는 tarball을 만듭니다.
- 유닉스 타임스탬프를 릴리즈 버전으로 하여 tarball을 일반 패키지 레지스트리에 업로드합니다.
일반 패키지를 생성하기 위해, 다음 독립형 .gitlab-ci.yml
구성 파일을 사용할 수 있습니다:
generate-package:
parallel:
matrix:
- MB_COUNT: [1, 5, 10, 20]
before_script:
- apt update && apt -y install curl
script:
- dd if=/dev/urandom of="${MB_COUNT}.txt" bs=1048576 count=${MB_COUNT}
- tar czf "generated-$MB_COUNT-nighly-`date +%s`.tar.gz" "${MB_COUNT}.txt"
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file "generated-$MB_COUNT-nighly-`date +%s`.tar.gz" "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/generator/`date +%s`/${MB_COUNT}-nightly.tar.gz"'
artifacts:
paths:
- '*.tar.gz'
포크를 사용한 스토리지 사용량 생성
다음 프로젝트를 사용하여 포크의 비용 요소에 대한 저장소 사용량 테스트를 실행하세요:
-
gitlab-org/gitlab
를 새로운 네임스페이스 또는 그룹으로 포크합니다 (LFS, Git 리포지토리 포함). -
gitlab-com/www-gitlab-com
를 새로운 네임스페이스 또는 그룹으로 포크합니다.
커뮤니티 리소스
다음 리소스는 공식 지원을 받지 않습니다. 파괴적인 정리 명령을 실행하기 전에 스크립트와 튜토리얼을 테스트하여 되돌릴 수 없는지 확인하십시오.
- 포럼 주제: 스토리지 관리 자동화 리소스
- 스크립트: GitLab Storage Analyzer, GitLab Developer Evangelism team의 비공식 프로젝트입니다. 유사한 코드 예제를 이 문서의 방법 섹션에서 찾을 수 있습니다.