스토리지 관리 자동화

Tier: Free, Premium, Ultimate Offering: GitLab.com, Self-managed, GitLab Dedicated

이 페이지에서는 GitLab REST API를 사용하여 스토리지 분석 및 정리를 자동화하여 스토리지 사용량을 관리하는 방법을 설명합니다.

또한 파이프라인 효율성을 향상시킴으로써 스토리지 사용량을 관리할 수도 있습니다.

API 자동화에 대한 추가 도움이 필요한 경우 GitLab 커뮤니티 포럼 및 디스코드를 참조하세요.

caution
이 페이지의 스크립트 예제는 데모용이며 제품 환경에서 사용해서는 안됩니다. 예제를 사용하여 고유한 스토리지 자동화 스크립트를 디자인하고 테스트할 수 있습니다.

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 및 CI/CD linting 자동화를 사용한 JSON 포맷팅를 참조하세요.

이러한 도구를 REST API와 함께 사용하려면:

curl
export GITLAB_TOKEN=xxx

curl --silent --header "Authorization: Bearer $GITLAB_TOKEN" "https://gitlab.com/api/v4/user" | jq
GitLab CLI
glab auth login

glab api groups/YOURGROUPNAME/projects

GitLab CLI 사용

일부 API 엔드포인트는 페이지네이션 및 후속 페이지 검색을 필요로 합니다. GitLab CLI는 --paginate 플래그를 제공합니다.

JSON 데이터로 포맷된 POST 본문이 필요한 요청은 --raw-field 매개변수로 전달된키=값 쌍으로 작성할 수 있습니다.

자세한 정보는 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를 참조하세요.

note
코드를 보다 효율적으로 작성하려면 GitLab Duo Code Suggestions를 사용하세요.

스토리지 분석

스토리지 유형 식별

프로젝트 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
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
}
GitLab CLI
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
}
Python
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
}

프로젝트 및 그룹의 리포지터리 분석

여러 프로젝트 및 그룹의 분석을 자동화할 수 있습니다. 예를 들어 최상위 네임스페이스 수준에서 시작하여, 재귀적으로 모든 하위 그룹 및 프로젝트를 분석할 수 있습니다. 또한 다양한 리포지터리 유형을 분석할 수 있습니다.

다음은 여러 하위 그룹과 프로젝트를 분석하는 알고리즘의 예시입니다:

  1. 최상위 네임스페이스 ID를 가져옵니다. 네임스페이스/그룹 개요에서 ID 값을 복사할 수 있습니다.
  2. 최상위 그룹에서 모든 하위 그룹을 가져와 ID를 디렉터리에 저장합니다.
  3. 모든 그룹을 순회하며 각 그룹에서 모든 프로젝트를 가져와 ID를 디렉터리에 저장합니다.
  4. 분석할 리포지터리 유형을 식별하고 프로젝트 통계, 작업 아티팩트 등과 같은 프로젝트 속성에서 정보를 수집합니다.
  5. 그룹별로 모든 프로젝트와 그들의 리포지터리 정보에 대한 개요를 출력합니다.

glab을 사용한 셸 접근 방식은 작은 분석에 더 적합할 수 있습니다. 더 큰 분석에는 API 클라이언트 라이브러리를 사용하는 스크립트를 사용해야 합니다. 이러한 유형의 스크립트는 가독성, 데이터 저장, 흐름 제어, 테스트 및 재사용성을 향상시킬 수 있습니다.

스크립트가 API 요청 제한에 도달하지 않도록 하려면 다음과 같이 병렬 API 요청에 최적화되지 않은 코드 예시입니다.

이 알고리즘을 구현하려면:

GitLab CLI
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}]
Python
#!/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 파이프라인 스토리지 관리

작업 artifact는 대부분의 파이프라인 스토리지를 사용하며, 작업 로그 또한 수백 킬로바이트를 생성할 수 있습니다. 불필요한 작업 artifact를 먼저 삭제한 후 분석 후에 작업 로그를 정리해야 합니다.

caution
작업 로그 및 artifact를 삭제하는 것은 되돌릴 수 없는 파괴적인 작업입니다. 주의해서 사용하십시오. 보고 artifact, 작업 로그 및 메타데이터 파일을 포함한 특정 파일을 삭제하는 것은 이러한 파일을 데이터 소스로 사용하는 GitLab 기능에 영향을 줍니다.

작업 artifact 디렉터리

파이프라인 스토리지를 분석하기 위해 Job API endpoint를 사용하여 작업 artifact 디렉터리을 검색할 수 있습니다. 이 엔드포인트는 작업 artifact file_type 키를 artifacts 속성에서 반환합니다. file_type 키는 다음을 나타냅니다.

  • archive는 생성된 작업 artifact를 나타내는 zip 파일입니다.
  • metadata는 Gzip 파일의 추가적인 메타데이터를 나타냅니다.
  • trace는 raw 파일로서 job.log를 나타냅니다.

작업 artifact는 디스크에 캐시 파일로 기록될 수 있는 데이터 구조를 제공하며, 이를 사용하여 구현을 테스트할 수 있습니다.

모든 프로젝트를 가져오는 예제 코드를 기반으로하여 Python 스크립트를 확장할 수 있습니다.

다음 예시는 프로젝트에서 작업 artifact를 쿼리한 응답을 보여줍니다:

[
    {
        "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
    }
]

스크립트를 구현하는 방식에 따라 다음 중 하나를 할 수 있습니다:

  • 모든 작업 artifact를 수집하고 스크립트 끝에 요약 테이블을 출력합니다.
  • 정보를 즉시 출력합니다.

스크립트 끝에는 작업 artifact가 Markdown 형식의 테이블로 출력됩니다. 테이블 내용을 이슈 댓글이나 설명에 복사하거나 GitLab 리포지터리의 Markdown 파일에 채울 수 있습니다.

$ python3 get_all_projects_top_level_namespace_storage_analysis_cleanup_example.py

|프로젝트|작업|Artifact 이름|Artifact 유형|Artifact 크기|
|-|-|-|-|-|
| [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 |

대량으로 작업 artifact 삭제

Python 스크립트를 사용하여 대량으로 제거할 작업 artifact 유형을 필터링할 수 있습니다.

API 쿼리 결과를 필터링하여 다음을 비교할 수 있습니다:

  • created_at 값: artifact 나이를 계산합니다.
  • size 속성: artifact 크기를 결정합니다.

일반적인 요청:

  • 지정된 일 수보다 오래된 작업 artifact를 삭제합니다.
  • 지정된 리포지터리 양을 초과하는 작업 artifact를 삭제합니다. 예: 100MB.

다음 예시에서 스크립트는 job 속성을 순회하고 삭제를 위해 표시합니다. 컬렉션이 루프를 제거한 이후에, 스크립트는 삭제를 위해 표시된 작업 artifact를 삭제합니다.

프로젝트의 모든 작업 아티팩트 삭제

프로젝트의 작업 아티팩트가 필요하지 않은 경우, 다음 명령을 사용하여 모든 작업 아티팩트를 삭제할 수 있습니다. 이 작업은 되돌릴 수 없습니다.

아티팩트 삭제는 삭제할 아티팩트의 수에 따라 몇 분 또는 몇 시간이 걸릴 수 있습니다. API를 통한 후속 분석 쿼리는 잘못된 결과로 아티팩트를 반환할 수 있습니다. 결과에 혼선을 피하려면 추가적인 API 요청을 즉시 실행하지 마십시오.

가장 최근에 성공한 작업의 아티팩트는 자동으로 유지됩니다.

프로젝트의 모든 작업 아티팩트를 삭제하려면:

curl
export GL_PROJECT_ID=48349590

curl --silent --header "Authorization: Bearer $GITLAB_TOKEN" --request DELETE "https://gitlab.com/api/v4/projects/$GL_PROJECT_ID/artifacts"
GitLab CLI
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
Python
        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 키를 지정하세요. 파이프라인이 만들어진 현재 날짜와 차이를 계산하기 위해 해당 날짜를 사용할 수 있습니다. 나이가 임계값보다 큰 경우 파이프라인이 삭제됩니다.

note
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.shGitLab 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 "파이프라인 연령 $AGE이(가) 임계값 $AGE_THRESHOLD보다 오래되었으므로 삭제해야 합니다."
        # TODO 위에서 glab를 호출하여 파이프라인을 삭제해야 합니다. 이전 glab 호출에서 수집한 ID가 필요합니다.
    else
        echo "파이프라인 연령 $AGE이(가) 임계값 $AGE_THRESHOLD보다 오래되지 않았습니다. 무시합니다."
    fi
done

python-gitlab API 라이브러리 및 created_at 속성을 사용하여 작업 아티팩트 나이를 비교하는 유사한 알고리즘을 구현할 수 있습니다.

        # ...
        
        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와 같은 값 상속 후에 해결된 객체에 대한 액세스가 포함됩니다.

이 스크립트는 Merge된 CI/CD 구성 파일을 검색하고 아티팩트 키를 검색합니다.

  • 만료 설정이 없는 작업 식별
  • 아티팩트 만료가 구성된 작업에 대한 만료 설정 반환

다음 과정은 스크립트가 아티팩트 만료 설정을 검색하는 방법을 설명합니다:

  1. Merge된 CI/CD 구성을 생성하기 위해 스크립트는 모든 프로젝트를 반복하고 ci_lint() 메서드를 호출합니다.
  2. yaml_load 함수는 더 많은 분석을 위해 Merge된 구성을 Python 데이터 구조로 로드합니다.
  3. script 키도 있는 딕셔너리는 자체를 작업 정의로 식별하며 artifacts 키가 존재할 수 있습니다.
  4. 그렇다면 스크립트는 하위 키 expire_in을 구문 분석하고 나중에 Markdown 테이블 요약에 인쇄할 세부 정보를 저장합니다.
    ci_job_artifacts_expiry = {}
    
    # 프로젝트를 반복하고 .gitlab-ci.yml을 가져와 linter를 실행하여 전체 번역된 구성을 가져오고 `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]
                    # This is a job object with `script` attribute
                    if isinstance(v, dict) and 'script' in v:
                        print(".", end="", flush=True) # Get some feedback that it is still looping
                        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|-|-|-|") #Start markdown friendly table
        for k, details in ci_job_artifacts_expiry.items():
            if details['job_name'][0] == '.':
                continue # ignore job templates that start with a '.'
            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

|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 요청을 사용할 수 있습니다. 다음 예제는 scope: blobs를 사용하여 모든 *.yml 파일에서 문자열 artifacts를 검색합니다:

# 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"

오픈 소스 컨테이너 이미지의 삭제를 완화하는 방법에 대한 자세한 정보는 How GitLab can help mitigate deletion of open source container images on Docker Hub을 참조하세요.

작업 아티팩트의 기본 만료일 설정

프로젝트에서 작업 아티팩트의 기본 만료일을 설정하려면 .gitlab-ci.yml 파일에서 expire_in 값을 지정하세요.

default:
    artifacts:
        expire_in: 1 주

컨테이너 레지스트리 리포지터리 관리

컨테이너 레지스트리는 프로젝트그룹에서 사용할 수 있습니다. 두 위치 모두를 분석하여 정리 전략을 구현할 수 있습니다.

컨테이너 레지스트리 디렉터리

프로젝트의 컨테이너 레지스트리를 나열하려면:

curl
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
GitLab CLI
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)
caution
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-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_namesize 속성을 인쇄하는 방법을 보여줍니다.

        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("패키지 이름: {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를 사용하여 캐시를 지우는 방법을 검토합니다.

출력 가독성 향상

타임스탬프 초를 기간 형식으로 변환하거나 원시 바이트를 더 효과적인 형식으로 출력해야 할 수도 있습니다. 다음 보조 함수를 사용하여 값을 향상된 가독성을 위해 변환할 수 있습니다.

# 현재 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()

리포지터리 관리 자동화 테스트

리포지터리 관리 자동화를 테스트하려면 테스트 데이터를 생성하거나 검증을 위해 리포지터리를 채워넣어야 할 수도 있습니다. 다음 섹션에서는 짧은 시간 내에 리포지터리 덩어리를 테스트하고 생성하는 도구 및 팁을 제공합니다.

작업 아티팩트 생성

CI/CD 작업 매트릭스 빌드를 사용하여 가짜 아티팩트 덩어리를 생성하는 테스트 프로젝트를 만듭니다. 매일 아티팩트를 생성하기 위한 CI/CD 파이프라인을 추가합니다.

  1. 새 프로젝트를 만듭니다.
  2. .gitlab-ci.yml에 다음 스니펫을 추가하여 작업 아티팩트 생성기 구성을 포함합니다.

    include:
        - remote: https://gitlab.com/gitlab-de/use-cases/efficiency/job-artifact-generator/-/raw/main/.gitlab-ci.yml
    
  3. 파이프라인 스케줄 구성
  4. 파이프라인 매뉴얼 실행

또는 MB_COUNT 변수에서 매일 생성된 86MB를 다양한 값으로 줄입니다.

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은 다음과 같은 프로젝트를 제공합니다:

  • 임의의 텍스트 덩어리를 생성하고 현재 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'

포크를 사용한 리포지터리 사용량 생성

포크용 비용 요소를 사용하여 리포지터리 사용량을 테스트하는 다음 프로젝트를 사용하세요:

  • gitlab-org/gitlab를 새로운 네임스페이스나 그룹으로 포크합니다(포함된 LFS, Git 리포지터리입니다.).
  • gitlab-com/www-gitlab-com를 새로운 네임스페이스나 그룹으로 포크합니다.

커뮤니티 리소스

다음 리소스는 공식적으로 지원되지는 않습니다. 파괴적인 정리 명령을 실행하기 전에 스크립트 및 자습서를 테스트하여 반환이 불가능할 수 있는 파괴적인 정리 명령을 실행하기 전에 스크립트 및 자습서를 테스트해야 합니다.