- API 요구사항
- 스토리지 분석
- CI/CD 파이프라인 리포지터리 관리
- 컨테이너 레지스트리 리포지터리 관리
- 패키지 레지스트리 리포지터리 관리
- 출력 가독성 향상
- 리포지터리 관리 자동화를 위한 테스트
- 커뮤니티 리소스
스토리지 관리 자동화
이 페이지는 GitLab REST API를 사용하여 스토리지 분석 및 정리를 자동화하는 방법에 대해 설명합니다.
파이프라인 효율성을 향상시킴으로써 스토리지 사용량을 관리할 수도 있습니다.
API 자동화에 대한 자세한 도움은 GitLab 커뮤니티 포럼 및 디스코드를 참고하세요.
API 요구사항
스토리지 관리를 자동화하려면 GitLab.com SaaS 또는 Self-Managed형 인스턴스에서 GitLab REST API에 액세스해야 합니다.
API 인증 범위
API와 인증을 하려면 다음 범위를 사용하세요:
- 스토리지 분석:
-
read_api
범위를 갖고 읽기 API 액세스 - 모든 프로젝트에 대해 적어도 Developer 역할
-
- 스토리지 정리:
-
api
범위를 갖고 전체 API 액세스 - 모든 프로젝트에 대해 적어도 Maintainer 역할
-
REST API와 상호 작용하기 위해 명령줄 도구나 프로그래밍 언어를 사용할 수 있습니다.
명령줄 도구
API 요청을 보내려면 다음 중 하나를 설치하세요:
- 선호하는 패키지 관리자로 curl
-
GitLab CLI 설치하고
glab api
하위명령을 사용하세요.
JSON 응답을 포맷팅하려면 jq
를 설치하세요. 자세한 정보는 유용한 DevOps 워크플로우 팁: jq를 사용한 JSON 포맷팅과 CI/CD 린트 자동화를 참고하세요.
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 클라이언트 라이브러리에 대한 자세한 정보는 Third-party clients를 참고하세요.
참고: 코드를 더 효율적으로 작성하려면 GitLab Duo Code Suggestions를 사용하세요.
스토리지 분석
스토리지 유형 식별
프로젝트 API 엔드포인트는 GitLab 인스턴스의 프로젝트에 대한 통계를 제공합니다. 프로젝트 API 엔드포인트를 사용하려면 statistics
키를 boolean 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 값을 복사할 수 있습니다.
- 최상위 그룹에서 모든 하위 그룹을 가져와 ID를 디렉터리에 저장합니다.
- 모든 그룹을 순회하고 각 그룹에서 각 프로젝트를 가져와 ID를 디렉터리에 저장합니다.
- 분석할 스토리지 유형을 식별하고 프로젝트 통계 및 작업 아티팩트와 같은 프로젝트 속성에서 정보를 수집합니다.
- 모든 프로젝트의 개요와 그룹별로 정렬된 스토리지 정보를 출력합니다.
glab
을 사용하는 쉘 접근 방식은 작은 분석에 더 적합할 수 있습니다. 큰 분석에는 API 클라이언트 라이브러리를 사용하는 스크립트를 사용해야 합니다. 이 유형의 스크립트는 가독성, 데이터 저장, 흐름 제어, 테스트 및 재사용성을 향상시킬 수 있습니다.
이 알고리즘이 API 요청 속도 제한에 도달하지 않도록 하려면 다음과 같은 예시 코드는 병렬 API 요청에 최적화되지 않았습니다.
이 알고리즘을 실행하려면:
export GROUP_NAME="gitlab-de"
# 하위 그룹 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') # token requires developer permissions
PROJECT_ID = os.environ.get('GL_PROJECT_ID') #optional
GROUP_ID = os.environ.get('GL_GROUP_ID') #optional
if __name__ == "__main__":
if not GITLAB_TOKEN:
print("🤔 Please set the GL_TOKEN env variable.")
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 파이프라인 리포지터리 관리
작업 아티팩트는 대부분의 파이프라인 리포지터리를 차지하며, 작업 로그도 몇 백 킬로바이트를 생성할 수 있습니다. 불필요한 작업 아티팩트를 먼저 삭제한 후 분석을 통해 작업 로그를 정리해야 합니다.
작업 아티팩트 디렉터리
파이프라인 리포지터리를 분석하기 위해 Job API 엔드포인트를 사용하여 작업 아티팩트 디렉터리을 검색할 수 있습니다.
엔드포인트는 작업 아티팩트의 file_type
키를 artifacts
속성 내에서 반환합니다.
file_type
키는 아티팩트 유형을 나타냅니다.
-
archive
는 생성된 작업 아티팩트용 zip 파일에 사용됩니다. -
metadata
는 Gzip 파일의 추가 메타데이터에 사용됩니다. -
trace
는job.log
을 원시 파일로 사용합니다.
작업 아티팩트는 디스크에 캐시 파일로 쓰일 수 있는 데이터 구조를 제공하며, 이를 테스트하는 데 사용할 수 있습니다.
모든 프로젝트를 가져오는 예제 코드를 기반으로 하여 Python 스크립트를 확장할 수 있습니다.
다음 예제는 프로젝트에서 작업 아티팩트를 캐시하는 방법을 보여줍니다. 스크립트는 프로젝트를 모두 순회하고 다음을 가져옵니다:
- 모든 속성을 포함하는
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("|Project|Job|Artifact name|Artifact type|Artifact size|\n|-|-|-|-|-|") #Start markdown friendly table
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("아티패크트를 찾을 수 없습니다.")
스크립트 끝에 작업 아티팩트가 Markdown 형식으로 출력됩니다. 테이블 내용을 이슈 코멘트나 설명에 복사하거나 GitLab 리포지터리의 Markdown 파일을 작성할 수 있습니다.
$ python3 get_all_projects_top_level_namespace_storage_analysis_cleanup_example.py
|Project|Job|Artifact name|Artifact type|Artifact size|
|-|-|-|-|-|
| [gitlab-de/playground/artifact-gen-group/gen-job-artifacts-4](Gen Job Artifacts 4) | 4828297946 | artifacts.zip | archive | 50.0154 |
| [gitlab-de/playground/artifact-gen-group/gen-job-artifacts-4](Gen Job Artifacts 4) | 4828297946 | metadata.gz | metadata | 0.0001 |
| [gitlab-de/playground/artifact-gen-group/gen-job-artifacts-4](Gen Job Artifacts 4) | 4828297946 | job.log | trace | 0.0030 |
| [gitlab-de/playground/artifact-gen-group/gen-job-artifacts-4](Gen Job Artifacts 4) | 4828297945 | artifacts.zip | archive | 20.0063 |
| [gitlab-de/playground/artifact-gen-group/gen-job-artifacts-4](Gen Job Artifacts 4) | 4828297945 | metadata.gz | metadata | 0.0001 |
| [gitlab-de/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 임계값 (할당 가능하도록 만들기)
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를 통한 후속 분석 쿼리는 아티팩트를 false-positive 결과로 반환할 수 있습니다. 결과와의 혼란을 피하기 위해 즉시 추가 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]"
"success"
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 epoch 시간으로 변환되어야 합니다. 예를 들어 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 days
if [ $AGE -gt $AGE_THRESHOLD ];
then
echo "Pipeline age $AGE older than threshold $AGE_THRESHOLD, should be deleted."
# TODO call glab to delete the pipeline. Needs an ID collected from the glab call above.
else
echo "Pipeline age $AGE not older than threshold $AGE_THRESHOLD. Ignore."
fi
done
또한 유사한 알고리즘을 구현하기 위해 python-gitlab
API 라이브러리 및 created_at
속성을 사용할 수 있습니다. 나이를 비교하여 작업 아티팩트와 유사한 작업을 자동으로 삭제하는 기능은 이슈 338480에서 제안되었습니다.
작업 아티팩트의 만료 설정 디렉터리
아티팩트 리포지터리를 관리하려면 아티팩트 만료 일자를 업데이트하거나 구성할 수 있습니다. 아티팩트의 만료 설정은 각 작업 구성인 .gitlab-ci.yml
에서 구성됩니다.
여러 프로젝트가 있는 경우 및 CI/CD 구성에서 작업 정의가 어떻게 구성되는지에 따라 만료 설정을 찾기 어려울 수 있습니다. 전체 CI/CD 구성을 검색하기 위해 스크립트를 사용할 수 있습니다. 이는 extends
또는 !reference
와 같이 값 상속 후에 해결되는 객체에 액세스 포함합니다.
스크립트는 통합된 CI/CD 구성 파일을 검색하고 아티팩트 키를 검색하여 다음을 수행하는 과정을 설명합니다:
- 통합 CI/CD 구성을 생성하기 위해 스크립트는 모든 프로젝트를 루프하며
ci_lint()
메소드를 호출합니다. -
yaml_load
함수는 더 많은 분석을 위해 통합된 구성을 Python 데이터 구조로 로드합니다. -
script
키를 가진 딕셔너리는artifacts
키가 존재할 수 있는 작업 정의를 식별합니다. - 그렇다면 스크립트는 서브 키
expire_in
을 파싱하고 나중에 마크다운 테이블 요약에 인쇄할 세부정보를 저장합니다.
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"Exception searching artifacts on ci_pipelines: {e}".format(e=e))
if len(ci_job_artifacts_expiry) > 0:
print("|Project|Job|Artifact expiry|\n|-|-|-|") #마크다운 테이블 시작
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" } |')
스크립트는 다음과 같이 작동하는 마크다운 요약 테이블을 생성합니다:
- 프로젝트 명과 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
|Project|Job|Artifact expiry|
|-|-|-|
| [Gen Job Artifacts 4](https://gitlab.com/gitlab-de/playground/artifact-gen-group/gen-job-artifacts-4) | generator | 30 days |
| [Gen Job Artifacts with expiry and included jobs](https://gitlab.com/gitlab-de/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-de/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-de/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-de/playground/artifact-gen-group/gen-job-artifacts-expiry-included-jobs) | generator | 30 days |
| [Gen Job Artifacts 2](https://gitlab.com/gitlab-de/playground/artifact-gen-group/gen-job-artifacts-2) | generator | ❌ N/A |
| [Gen Job Artifacts 1](https://gitlab.com/gitlab-de/playground/artifact-gen-group/gen-job-artifacts-1) | generator | ❌ N/A |
get_all_cicd_config_artifacts_expiry.py
스크립트는 GitLab API with Python 프로젝트에서 찾을 수 있습니다.
또는 API 요청과 함께 고급 검색을 사용할 수 있습니다. 다음 예시는 *.yml
파일에서 문자열 artifacts
를 검색하기 위해 scope: blobs를 사용합니다:
# https://gitlab.com/gitlab-de/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-de/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-de/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-de/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-de/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"
대량으로 컨테이너 이미지 삭제
대량으로 컨테이너 이미지 태그를 삭제할 때 다음을 구성할 수 있습니다:
- 태그 이름 및 이미지 매칭을 유지(
name_regex_keep
) 또는 삭제(name_regex_delete
)하기 위한 일치 정규식 - 태그 이름과 일치하는 이미지 태그를 유지하는 수량(
keep_n
) - 이미지 태그가 삭제될 수 있는 일수(
older_than
)
다음 예시는 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)
컨테이너 이미지 최적화
컨테이너 이미지를 최적화하여 컨테이너 레지스트리의 이미지 크기와 전체 저장 공간을 줄일 수 있습니다. 자세한 내용은 파이프라인 효율성 문서에서 확인하세요.
패키지 레지스트리 리포지터리 관리
패키지 레지스트리는 프로젝트 또는 그룹에서 사용할 수 있습니다.
패키지 및 파일 나열
다음 예시는 GitLab CLI를 사용하여 정의된 프로젝트 ID에서 패키지를 가져오는 것을 보여줍니다. 결과 집합은 jq
명령 체인으로 필터링할 수 있는 사전 항목 배열입니다.
# https://gitlab.com/gitlab-de/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("Package name: {p} File name: {f} Size {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("Package name: {p} File name: {f} Size {s}".format(
p=package.name, f=package_file.file_name, s=render_size_mb(package_file.size)))
package_size =+ package_file.size
print("Package size: {s}\n\n".format(s=render_size_mb(package_size)))
threshold_size = 10 * 1024 * 1024
if (package_size > float(threshold_size)):
print("Package size {s} > threshold {t}, deleting package.".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("Package age {a} > threshold {t}, deleting package.".format(
a=render_age_time(package_age), t=render_age_time(threshold_age)))
package.delete()
이 코드는 추가 분석에 사용할 수 있는 다음과 같은 출력을 생성합니다:
Package name: generator File name: nighly.tar.gz Size 10.0017
Package size: 10.0017
Package size 10.0017 > threshold 10.0000, deleting package.
Package name: generator File name: 1-nightly.tar.gz Size 1.0004
Package size: 1.0004
Package name: generator File name: 10-nightly.tar.gz Size 10.0018
Package name: generator File name: 20-nightly.tar.gz Size 20.0033
Package size: 20.0033
Package size 20.0033 > threshold 10.0000, deleting package.
Dependency Proxy
정리 정책 및 API를 사용하여 캐시 삭제하는 방법을 확인하세요.
출력 가독성 향상
타임스탬프 초를 지속 시간 형식으로 변환하거나, 원시 바이트를 더 잘 나타내는 형식으로 출력해야 할 수 있습니다. 다음 도우미 함수를 사용하여 값을 변환하여 가독성을 향상시킬 수 있습니다.
# 현재 Unix 타임스탬프
date +%s
# `created_at` 날짜 및 시간을 타임존과 함께 Unix 타임스탬프로 변환
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` 날짜 및 시간을 타임존과 함께 Unix 타임스탬프로 변환
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()
리포지터리 관리 자동화를 위한 테스트
리포지터리 관리 자동화를 테스트하려면 테스트 데이터를 생성하거나 분석 및 삭제가 예상대로 작동하는지 확인하기 위해 리포지터리를 채워넣어야 할 수 있습니다. 아래 섹션에서는 짧은 시간 내에 리포지터리 Blob을 생성하고 테스트하는 도구와 팁을 제공합니다.
작업 결과물 생성
CI/CD 작업 매트릭스 빌드를 사용하여 가짜 artifact Blob을 생성하는 테스트 프로젝트를 작성하세요. 매일 작업 결과물을 생성하기 위한 CI/CD 파이프라인을 추가하세요.
- 새 프로젝트를 생성합니다.
-
다음 스니펫을
.gitlab-ci.yml
에 추가하여 작업 결과물 생성기 구성을 포함합니다.include: - remote: https://gitlab.com/gitlab-de/use-cases/efficiency/job-artifact-generator/-/raw/main/.gitlab-ci.yml
- 파이프라인 스케줄 구성
- 파이프라인 매뉴얼 트리거
또는, 매일 생성되는 86MB의 결과물 크기를 다른 값으로 줄이세요(변수 MB_COUNT
).
include:
- remote: https://gitlab.com/gitlab-de/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에서 기본 이미지 사용하여 새 이미지 빌드.
-
Docker.gitlab-ci.yml
템플릿을 GitLab.com SaaS에서 이미지 빌드에 포함. - 새 이미지를 매일 생성하도록 파이프라인 스케줄 구성.
포크할 수 있는 예제 프로젝트:
일반 패키지 생성
예제 프로젝트 generic-package-generator
은 다음을 제공합니다.
- 무작위 텍스트 Blob을 생성하고 릴리스 버전으로 현재 Unix 타임스탬프를 사용하여 tarball 생성.
- Unix 타임스탬프를 릴리스 버전으로 사용하여 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'
Fork를 사용한 리포지터리 사용량 생성
다음 프로젝트를 사용하여 Fork에 대한 비용 요소로 리포지터리 사용량을 테스트하세요.
-
gitlab-org/gitlab
를 새로운 네임스페이스 또는 그룹으로 Fork(포크)합니다(LFS, Git 리포지터리 포함). -
gitlab-com/www-gitlab-com
를 새로운 네임스페이스 또는 그룹으로 Fork(포크)합니다.
커뮤니티 리소스
다음 리소스는 공식적으로 지원되지 않습니다. 실행되지 않을 수 있는 파괴적인 정리 명령을 실행하기 전에 스크립트 및 자습서를 테스트하세요.
- 포럼 주제: 리포지터리 관리 자동화 리소스
- 스크립트: GitLab 리포지터리 분석기, GitLab Developer Evangelism team에 의한 비공식 프로젝트. 이 문서에서 비슷한 코드 예제를 찾을 수 있습니다.