- API要件
- ストレージ分析の戦略
- CI/CDパイプラインでのストレージ管理
- コンテナレジストリのストレージ管理
- パッケージレジストリのストレージ管理
- コミュニティリソース
- ストレージ管理自動化のためのテスト
ストレージ管理の自動化
GitLab UIとAPIを使ってストレージを管理することができます。このページでは、ストレージの分析とクリーンアップを自動化して使用量枠を管理する方法を説明します。パイプラインを効率化することでも、ストレージの使用量を管理できます。詳しくはパイプラインの効率化をご覧ください。
GitLab コミュニティフォーラムや Discordを使って、API の自動化に関するヘルプを求めることもできます。
API要件
ストレージ管理を自動化するには、GitLab.com SaaSまたはセルフマネージドインスタンスがGitLab REST APIにアクセスできる必要があります。
API認証スコープ
APIで認証を行うには、以下のスコープを使用する必要があります:
- ストレージ分析:
-
read_api
スコープで API アクセスを読み込みます。 - 少なくともすべてのプロジェクトで開発者ロール。
-
- ストレージのクリーンアップ:
-
api
スコープでフル API アクセス。 - すべてのプロジェクトで少なくともメンテナーのロール。
-
コマンドラインツールまたはプログラミング言語を使用して、REST API とやり取りすることができます。
コマンドライン
APIリクエストを送信するには、以下のツールをインストールする必要があります:
- お好みのパッケージマネージャで
curl
をインストールします。 -
GitLab CLIをインストールし、
api
サブコマンドを使用します。 -
jq
をインストールして、JSON レスポンスをフォーマットします。詳細については、生産的なDevOpsワークフローのためのヒントを参照してください:jq による JSON フォーマットと CI/CD linting の自動化。
curl
とjq
を使用した例:
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
パラメータに渡されるkey=value
ペアとして記述することができます。
詳細はGitLab CLI エンドポイントのドキュメント を参照ください。
API クライアントライブラリ
このページで説明するストレージ管理とクリーンアップの自動化メソッドは、プログラムの例でpython-gitlab
ライブラリを使用します。python-gitlab
ライブラリは、機能豊富なプログラミング・インターフェースを提供します。python-gitlab
ライブラリの使用例の詳細については、効率的な DevSecOps ワークフローを参照してください:ハンズオンpython-gitlab
API 自動化.
その他のAPIクライアントライブラリについては、サードパーティクライアントを参照してください。
ストレージ分析の戦略
ストレージタイプの特定
projects APIエンドポイントは、GitLabインスタンス内のプロジェクトの統計情報を提供します。projects API エンドポイントを使うには、statistics
のキーを booleantrue
に設定します。このデータは、以下のストレージタイプによるプロジェクトのストレージ消費に関するインサイトを提供します:
-
storage_size
:ストレージ全体 -
lfs_objects_size
:LFSオブジェクトストレージ -
job_artifacts_size
:ジョブアーティファクトストレージ -
packages_size
:パッケージ保管 -
repository_size
:Git リポジトリの保存 -
snippets_size
:スニペットストレージ -
uploads_size
:アップロード保存 -
wiki_size
:Wikiストレージ
ジョブのアーティファクト、コンテナレジストリ、パッケージレジストリ、依存プロキシの詳細なストレージ統計については、追加のクエリが必要です。これについてはこのハウツーで後述します。
コマンドラインでcurl
とjq
を使用する例:
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-gitlab
ライブラリを使った例:
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)))
GitLab API with Python プロジェクトのスクリプトget_all_projects_top_level_namespace_storage_analysis_cleanup_example.py
に実装例があります。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をリストに保存します。
- 分析するストレージタイプを特定し、プロジェクト統計やアーティファクトなどのプロジェクト属性から情報を収集します。
- グループ別に分類されたすべてのプロジェクトの概要とストレージ情報を印刷します。
GitLab CLI を使った例:
export GROUP_NAME="gitlab-de"
# Return sub group IDs
glab api groups/$GROUP_NAME/subgroups | jq --compact-output '.[]' | jq --compact-output '.id'
12034712
67218622
67162711
67640130
16058698
12034604
# Loop over all subgroups to get subgroups, until the result set is empty. Example group: 12034712
glab api groups/12034712/subgroups | jq --compact-output '.[]' | jq --compact-output '.id'
56595735
70677315
67218606
70812167
# Lowest group level
glab api groups/56595735/subgroups | jq --compact-output '.[]' | jq --compact-output '.id'
# empty result, return and continue with analysis
# Fetch projects from all collected groups. Example group: 56595735
glab api groups/56595735/projects | jq --compact-output '.[]' | jq --compact-output '.id'
48349590
48349263
38520467
38520405
# Fetch storage types from a project (ID 48349590): Job artifacts in the `artifacts` key
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}]
glab
を使ったShellのアプローチは小規模な分析には有効ですが、APIクライアントライブラリを使ったスクリプトを検討すべきです。これにより、読みやすさ、データの保存、フロー制御、テスト、再利用性が向上します。
また、python-gitlab
ライブラリを使用する 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') # 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)
# Collect all projects, or prefer projects from a group id, or a project id
projects = []
# Direct project ID
if PROJECT_ID:
projects.append(gl.projects.get(PROJECT_ID))
# Groups and projects inside
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
}
]
ストレージ管理とクリーンアップを自動化するための具体的な例を含む完全なスクリプトget_all_projects_top_level_namespace_storage_analysis_cleanup_example.py
はGitLab API with Pythonプロジェクトにあります。スクリプトがAPI のレート制限に到達しないように、サンプルコードは並列 API リクエスト用に最適化されていません。
ヘルパー関数
タイムスタンプの秒を継続時間形式に変換したり、生のバイトをより代表的な形式で表示したりする必要があるかもしれません。以下のヘルパー関数を使用すると、値を読みやすく変換することができます:
# Current Unix timestamp
date +%s
# Convert `created_at` date time with timezone to Unix timestamp
date -d '2023-08-08T18:59:47.581Z' +%s
Python でpython-gitlab
API ライブラリを使用した例:
def render_size_mb(v):
return "%.4f" % (v / 1024 / 1024)
def render_age_time(v):
return str(datetime.timedelta(seconds = v))
# Convert `created_at` date time with timezone to Unix timestamp
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パイプラインでのストレージ管理
ジョブのアーティファクトはパイプラインのストレージのほとんどを消費し、ジョブのログも数百キロバイトを生成することがあります。まず不要なジョブアーティファクトを削除し、分析後にジョブログをクリーンアップする必要があります。
パイプラインストレージの分析
次の例は、プロジェクト内のジョブ・アーティファクトに対するクエリの応答を示しています:
[
{
"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
}
]
ジョブ API エンドポイントは、artifacts
属性のジョブ成果物file_type
キーを返します。ジョブ成果物file_type
キーは、特定の成果物タイプに関するインサイトを提供します:
-
archive
は生成されたアーティファクトの zip ファイルとして使用されます。 -
metadata
は Gzip ファイルの追加メタデータに使用されます。 -
trace
は生ファイルとしてjob.log
に使用されます。
これらの3つのタイプは、ストレージのカウントに関連しており、後でまとめるために収集する必要があります。全プロジェクトをフェッチするためのサンプルコードに基づいて、Pythonスクリプトを拡張してより多くの分析を行うことができます。
Python コードはすべてのプロジェクトをループし、すべての属性を含むproject_obj
オブジェクト変数をフェッチします。パイプラインやジョブが多数存在する可能性があるため、1 回の呼び出しでジョブのリストをフェッチするにはコストがかかります。そのため、これはキーセットのページ分割を使用して行われます。残りのステップは、job
オブジェクトからartifacts
属性をフェッチすることです。
スクリプトの実装方法に応じて、次のいずれかを実行できます:
- すべてのジョブのアーティファクトを収集し、スクリプトの最後にサマリー表を出力します。
- 情報を直ちに印刷します。
ジョブのアーティファクトを収集することで、例えばディスクにキャッシュファイルとして書き込むことができるデータ構造を提供します。
次の例では、ジョブのアーティファクトはci_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("\nDone collecting data.")
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("No artifacts found.")
スクリプトの最後に、ジョブのアーティファクトが 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 |
スクリプトget_all_projects_top_level_namespace_storage_analysis_cleanup_example.py
の完全なサンプルはGitLab API with Python プロジェクトにあります。スクリプトがAPI のレート制限に引っかからないようにするため、サンプルコードは並列 API リクエスト用に最適化されていません。
ジョブのアーティファクトの削除
フィルタを使用して、一括削除するジョブアーティファクトのタイプを選択できます。典型的なリクエスト
- 指定された日数より古いジョブアーティファクトを削除します。
- 指定したストレージ量を超えるジョブアーティファクトを削除します。たとえば、100 MBです。
Python スクリプトを使用して、このタイプのフィルタを実装できます。API クエリの結果をフィルタし、created_at
値を比較してアーティファクトの年齢を計算できます。
また、すべてのジョブのアーティファクトをループし、size
属性を比較して、サイズのしきい値に一致するかどうかを確認することもできます。一致するジョブが見つかると、削除のマークが付けられます。スクリプトがジョブ属性をループする際に分析が行われるため、ジョブに削除マークのみを付けることができます。コレクションループがオブジェクトロックを削除すると、削除済みとマークされたすべてのジョブが実際に削除されます。
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
# Advanced filtering: Age and Size
# Example: 90 days, 10 MB threshold (TODO: Make this configurable)
threshold_age = 90 * 24 * 60 * 60
threshold_size = 10 * 1024 * 1024
# job age, need to parse API format: 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()
# Shorter: Use a function
# age = calculate_age(job.created_at)
for a in artifacts:
# ... removed analysis collection code for readability
# Advanced filtering: match job artifacts age and size against thresholds
if (float(age) > float(threshold_age)) or (float(a['size']) > float(threshold_size)):
# mark job for deletion (cannot delete inside the loop)
jobs_marked_delete_artifacts.append(job)
print("\nDone collecting data.")
# Advanced filtering: Delete all job artifacts marked to being deleted.
for job in jobs_marked_delete_artifacts:
# delete the artifact
print("DEBUG", job)
job.delete_artifacts()
# Print collection summary (removed for readability)
スクリプトget_all_projects_top_level_namespace_storage_analysis_cleanup_example.py
の完全な例はGitLab API Python プロジェクトにあります。
プロジェクトのアーティファクトをすべて削除
プロジェクトのジョブ・アーティファクトが不要な場合は、次のコマンドを使用してすべてを削除できます。このアクションを元に戻すことはできません。
ジョブ・アーティファクトの削除は GitLab で非同期に行われ、バックグラウンドで完了するまで時間がかかることがあります。その後の 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
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:
# delete the artifacts and job log
print("DEBUG", job)
#job.delete_artifacts()
job.erase()
# Sleep for 1 second
time.sleep(1)
スクリプトget_all_projects_top_level_namespace_storage_analysis_cleanup_example.py
の完全な例はGitLab API with Python プロジェクトにあります。
ジョブログの保持ポリシーを作成するためのサポートはissue 374717 で提案されています。
ジョブ・アーティファクトの有効期限設定のインベントリ
アーティファクトの保管を管理するには、アーティファクトの有効期限を更新または設定します。アーティファクトの有効期限設定は、.gitlab-ci.yml
の各ジョブ設定で行います。
複数のプロジェクトがあり、CI/CD設定のジョブ定義がどのように構成されているかによっては、有効期限設定を見つけるのが難しい場合があります。スクリプトを使用してCI/CD設定全体を検索することができます。これには、extends
や!reference
のように、値を継承した後に解決されるオブジェクトへのアクセスも含まれます。 スクリプトはマージされた CI/CD 設定ファイルを取得し、アーティファクトのキーを検索します:
- 有効期限が設定されていないジョブを特定します。
- アーティファクトの有効期限が設定されているジョブの有効期限設定を返します。
以下のプロセスでは、スクリプトがアーティファクトの有効期限設定を検索する方法を説明します:
- マージされた CI/CD 設定を生成するために、スクリプトはすべてのプロジェクトをループし、
ci_lint()
メソッドを呼び出します。 -
yaml_load
関数はマージされた設定を Python のデータ構造にロードし、より詳細な分析を行います。 -
script
というキーを持つ辞書は、artifacts
というキーが存在する可能性のあるジョブ定義であることを示します。 - もしそうであれば、スクリプトはサブキー
expire_in
を解析し、後で印刷するために詳細をMarkdownテーブルの要約に格納します。
ci_job_artifacts_expiry = {}
# Loop over projects, fetch .gitlab-ci.yml, run the linter to get the full translated config, and extract the `artifacts:` setting
# 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" } |')
スクリプトはプロジェクト名とURL、ジョブ名、artifacts:expire_in
、存在しない場合はN/A
を含むMarkdown要約テーブルを生成します。アーティファクトを生成するランタイムジョブオブジェクトとしてインスタンス化されない.
文字で始まるジョブテンプレートは出力されません。
export GL_GROUP_ID=56595735
# Script requires pyyaml too.
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"
インベントリアプローチの詳細については、Docker Hubのオープンソースコンテナイメージの削除を軽減するGitLabの支援方法を参照してください。
プロジェクトのジョブアーティファクトのデフォルトの有効期限を設定します。
get_all_cicd_config_artifacts_expiry.py
スクリプトの出力に基づいて、.gitlab-ci.yml
設定でデフォルトのアーティファクトの有効期限を定義できます。
default:
artifacts:
expire_in: 1 week
古いパイプラインの削除
パイプラインは全体的なストレージ消費量を増やすことはありませんが、削除したい場合は以下の方法があります。
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"
created_at
キーは、タイムスタンプからUnixのエポックタイムに変換する必要があります。例えば、date -d '2023-08-08T18:59:47.581Z' +%s
を使用します。次のステップでは、現在とパイプライン作成日の差から年齢を計算します。年齢がしきい値より大きい場合、パイプラインは削除されます。
次の例では、jq
とGitLab CLIがインストールされ、作成者が認証され、エクスポートされた環境変数GL_PROJECT_ID
を想定した Bash スクリプトを使用しています。
完全なスクリプト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
属性を使えば、ジョブのアーティファクトエイジを比較する同様のアルゴリズムを実装できます:
# ...
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("Deleting pipeline", pipeline.id)
pipeline_obj.delete()
スクリプトget_all_projects_top_level_namespace_storage_analysis_cleanup_example.py
の完全な例はGitLab API with Python プロジェクトにあります。
GitLab の古いパイプラインの自動削除は、この機能提案で追跡されています。
コンテナレジストリのストレージ管理
コンテナ レジストリは、プロジェクトまたはグループで使用できます。どちらの場所でも、分析とクリーンアップの戦略が必要です。
次の例では、curl
とjq
をプロジェクトに使用します:
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"
3401613
同様の自動化シェルスクリプトは、古いパイプラインの削除セクションで作成します。
python-gitlab
API ライブラリは、次のセクションで説明する一括削除インターフェースを提供します。
コンテナイメージの一括削除
コンテナ画像タグを一括削除する際の設定です:
- タグ名と画像のマッチング正規表現で、保持 (
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()
# Cleanup: Keep only the latest tag
repository.tags.delete_in_bulk(keep_n=1)
# Cleanup: Delete all tags older than 1 month
repository.tags.delete_in_bulk(older_than="1m")
# Cleanup: Delete all tags matching the regex `v.*`, and keep the latest 2 tags
repository.tags.delete_in_bulk(name_regex_delete="v.+", keep_n=2)
スクリプトの完全な例get_all_projects_top_level_namespace_storage_analysis_cleanup_example.py
はGitLab API with Pythonプロジェクトにあります。
コンテナのクリーンアップポリシー
クリーンアップポリシーを作成するには、プロジェクト REST API エンドポイントを使います。次の例では、GitLab CLIを使ってクリーンアップポリシーを作成します。
属性を body パラメータとして送信するには、次のようにします:
-
--input -
パラメータを使用して標準入力から読み込みます。 -
Content-Type
ヘッダーを設定します。
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"
},
クリーンアップポリシーを設定すると、指定に一致するすべてのコンテナイメージが自動的に削除されます。追加の API 自動化スクリプトは必要ありません。
コンテナイメージの最適化
コンテナイメージを最適化することで、コンテナレジストリ内のイメージサイズと全体的なストレージ消費量を削減できます。詳しくは、パイプラインの効率化に関するドキュメントを参照してください。
パッケージレジストリのストレージ管理
パッケージレジストリはプロジェクトまたはグループで利用できます。
パッケージとファイルのリスト
次の例では、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
ライブラリを使ってlist packages and filesセクションで説明したコードを拡張します。
次のコード例では、パッケージの年齢も計算し、条件が一致したらパッケージを削除します:
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.
スクリプトget_all_projects_top_level_namespace_storage_analysis_cleanup_example.py
の完全な例はGitLab API with Pythonプロジェクトにあります。
依存関係プロキシ
クリーンアップポリシーと API を使用したキャッシュの削除方法のレビュー
コミュニティリソース
これらのリソースは公式にはサポートされていません。元に戻せない可能性のある破壊的なクリーンアップコマンドを実行する前に、必ずスクリプトとチュートリアルをテストしてください。
- フォーラムのトピックストレージ管理自動化リソース
- スクリプトGitLab Storage Analyzer,GitLab Developer Evangelismチームによる非公式プロジェクト。こちらのドキュメントのハウツーにも同様のコード例があります。
ストレージ管理自動化のためのテスト
ストレージ管理自動化をテストするには、テストデータを生成したり、ストレージにデータを投入し て解析や削除が期待どおりに機能することを検証する必要があるかもしれません。以下のセクションでは、短時間でストレージブロブをテストおよび生成するためのツールとヒントを提供します。
ジョブのアーティファクトの生成
CI/CD ジョブマトリックスビルドを使用して偽のアーティファクト blob を生成するテストプロジェクトを作成します。CI/CDパイプラインを追加して、毎日アーティファクトを生成します。
- 新しいプロジェクトを作成します。
-
.gitlab-ci.yml
に以下のスニペットを追加し、ジョブ・アーティファクト・ジェネレーター設定を含めます。include: - remote: https://gitlab.com/gitlab-de/use-cases/efficiency/job-artifact-generator/-/raw/main/.gitlab-ci.yml
- パイプライン・スケジュールを設定します。
- パイプラインを手動でトリガーします。
あるいは、MB_COUNT
変数で、毎日生成される 86 MB を異なる値に減らします。
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のベースイメージを使用して新しいイメージをビルドします。
- GitLab.com SaaSでイメージをビルドするには、
Docker.gitlab-ci.yml
テンプレートを含めます。 - パイプラインのスケジュールを設定して、毎日新しいイメージを生成します。
フォーク可能なプロジェクト例:
汎用パッケージの生成
サンプルプロジェクト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'
フォークを使ったストレージ使用量の生成
以下のプロジェクトを使用して、フォークのコスト係数を使用したストレージ使用量をテストしてください:
-
gitlab-org/gitlab
を新しいネームスペースまたはグループにフォークします(LFS、Git リポジトリを含む)。 -
gitlab-com/www-gitlab-com
を新しい名前空間またはグループにフォークします。