コンテナレジストリストレージの削減

コンテナレジストリは、レジストリの使用量を管理しないと、時間とともにサイズが大きくなる可能性があります。たとえば、大量の画像やタグを追加した場合などです:

  • 利用可能なタグや画像のリストの取得が遅くなります。
  • サーバーのストレージスペースを大量に消費します。

不要なイメージやタグを削除し、コンテナレジストリの使用量を自動的に管理するクリーンアップポリシーを設定する必要があります。

コンテナレジストリの使用状況の確認

Usage Quotas ページ(Settings > Usage Quotas > Storage)には、Pages のストレージ使用量が表示されます。このページには、GitLab.comでのみ利用可能なコンテナレジストリの使用量も含まれています。使用量の測定は、GitLab 15.7からGitLab.comで利用できるようになった、メタデータデータベースに支えられた新しいバージョンのGitLabコンテナレジストリでのみ可能です。セルフマネージドインスタンスでの利用可能予定については、エピック5521をご覧ください。

クリーンアップポリシー

  • GitLab 13.2で “expiration policy “から “cleanup policy “に名称変更
  • GitLab 15.0 で、必要な権限を開発者からメンテナーに変更。

クリーンアップポリシーは、コンテナレジストリからタグを削除するために使用できるスケジュールジョブです。定義されたプロジェクトでは、正規表現パターンにマッチするタグが削除されます。基礎となるレイヤーとイメージは残ります。

タグに関連付けられていない内部レイヤーとイメージを削除するには、管理者は、-m スイッチを使用してガベージコレクションを使用できます。

クリーンアップポリシーを有効にします

以下の例外を除いて、すべてのプロジェクトでクリーンアップポリシーを実行できます:

  • セルフマネージドGitLabインスタンスの場合、プロジェクトはGitLab 12.8以降で作成されている必要があります。しかし、管理者はGitLabアプリケーションの設定でcontainer_expiration_policies_enable_historic_entries をtrueに設定することで、すべてのプロジェクト(GitLab 12.8以前に作成されたものでも)に対してクリーンアップポリシーを有効にすることができます。あるいは、Railsコンソールで以下のコマンドを実行することもできます:

     ApplicationSetting.last.update(container_expiration_policies_enable_historic_entries: true)
    

    すべてのプロジェクトでクリーンアップポリシーを有効にすると、特に外部レジストリを使っている場合はパフォーマンスに影響が出る可能性があります。

caution
パフォーマンス上の理由から、コンテナイメージを持たないGitLab.comのプロジェクトでは、有効化されたクリーンアップポリシーは自動的に無効化されます。

クリーンアップポリシーの仕組み

クリーンアップポリシーは、コンテナレジストリ内のすべてのタグを収集し、削除したいタグだけが残るまでタグを除外します。

クリーンアップポリシーは、タグ名に基づいて画像を検索します。フルパスマッチングのサポートはイシュー281071で追跡されています。

クリーンアップポリシー:

  1. 指定されたリポジトリのすべてのタグをリストにまとめます。
  2. latest という名前のタグは除きます。
  3. name_regex (期限切れタグ)を評価し、一致しない名前を除外します。
  4. name_regex_keep の値 (保存するタグ) に一致するタグを除外します。
  5. マニフェストを持たないタグ (UI のオプションの一部ではない) を除外します。
  6. 残りのタグをcreated_date で並べ替えます。
  7. keep_n (保持するタグの数)の値に基づいて、N個のタグを除外します。
  8. older_than の値 (有効期限) より新しいタグを除外します。
  9. コンテナレジストリからリストの残りのタグを削除します。
caution
GitLab.comでは、クリーンアップポリシーの実行時間が制限されています。ポリシーの実行後、コンテナレジストリにいくつかのタグが残ることがあります。次にポリシーが実行されると、残りのタグが含まれます。すべてのタグを削除するには、複数回の実行が必要になる場合があります。
caution
GitLabセルフマネージドインストールは、Docker Registry HTTP API V2仕様に準拠したサードパーティのコンテナレジストリをサポートしています。しかし、この仕様にはタグの削除オペレーションが含まれていません。そのため、GitLabはサードパーティのコンテナレジストリと対話する際に、タグを削除する回避策を使用します。詳しくはイシュー15737を参照してください。実装にばらつきがある可能性があるため、この回避策はすべてのサードパーティレジストリで同じ予測可能な方法で動作することを保証するものではありません。GitLab コンテナレジストリを使う場合は、特別なタグの削除オペレーションを実装したので、この回避策は必要ありません。この場合、クリーンアップポリシーの一貫性と予測可能性が期待できます。

クリーンアップポリシーのワークフロー例

クリーンアップポリシーの保持ルールと削除ルールの相互作用は複雑です。たとえば、このようなクリーンアップポリシーの設定のプロジェクトがあるとします:

  • 最新のものを保持:画像名ごとに1タグ。
  • タグを一致させます:production-.*
  • 7日以上前のタグを削除します。
  • 一致するタグを削除:.*.

そして、これらのタグを持つコンテナリポジトリ:

  • latest2時間前に公開されました。
  • production-v443日前に公開されました。
  • production-v436日前に公開されました。
  • production-v4211日前に公開されました。
  • dev-v442日前に公開されました。
  • dev-v435日前に公開されました。
  • dev-v4210日前に公開されました。
  • v44昨日公開
  • v4312日前公開
  • v4220日前に公開されました。

この例では、次のクリーンアップの実行で削除されるタグは、dev-v42v43v42 です。この優先順位でルールが適用されると解釈できます:

  1. keepルールが最も優先されます。タグは、いずれかのルールにマッチした場合、保持されなければなりません。
    • タグは常に保持さlatest れるから latestです。
    • production-v44production-v43production-v42 のタグは、Keep tags マッチング・ルールに一致するため、保持する必要があります。
    • v44 タグは最新のものであり、Keep the most recentルールにマッチするため、保持する必要があります。
  2. removeルールは優先順位が低く、すべてのルールがマッチした場合のみタグが削除されます。どの保持ルールにもマッチしないタグ (dev-44,dev-v43,dev-v42,v43,v42) について:
    • dev-44dev-43 は、Remove tags older than にマッチせず、保持されます。
    • dev-v42v43v42 は、Remove tags older thanRemove tags matchingの両方のルールに一致するので、これら3つのタグは削除できます。

クリーンアップポリシーの作成

APIまたはUIでクリーンアップポリシーを作成できます。

UI でクリーンアップ・ポリシーを作成するには、以下の手順に従います:

  1. プロジェクトの [Settings (設定)] > [Packages and registries (パッケージとレジストリ)] に進みます。
  2. クリーンアップ・ポリシー]セクションで、[クリーンアップ・ルールの設定]を選択します。
  3. フィールドを入力します:

    項目説明
    トグルポリシーのオン/オフを切り替えます。
    クリーンアップの実行ポリシーを実行する頻度を指定します。
    最新の各画像に対して_常に_保持するタグの数。
    一致するタグを保持保持するタグを決定する正規表現パターン。latest タグは常に保持されます。すべてのタグには.* を使います。他の正規表現パターンの例を参照してください。
    より古いタグを削除X日より古いタグのみを削除します。
    一致するタグを削除削除するタグを決定する正規表現パターン。この値を空白にすることはできません。すべてのタグには.* を使用します。他の正規表現パターンの例を参照してください。
  4. Save を選択します。

選択したスケジュール間隔でポリシーが実行されます。

note
ポリシーを編集して[保存] を再度選択すると、間隔はリセットされます。

正規表現パターンの例

クリーンアップ・ポリシーは正規表現パターンを使用して、UIとAPIの両方で、どのタグを保存または削除すべきかを決定します。

正規表現パターンは自動的に\A\Z アンカーで内部を囲まれます。したがって、\A\Z^$ のトークンを正規表現パターンに含める必要はありません。

以下に使用できる正規表現パターンの例を示します:

  • すべてのタグにマッチします:

     .*
    

    このパターンは期限切れ正規表現のデフォルト値です。

  • v で始まるタグにマッチします:

     v.+
    
  • main というタグのみにマッチ:

     main
    
  • 名前がついているか、release で始まるタグにマッチ:

     release.*
    
  • v で始まるタグ、main という名前のタグ、release で始まるタグのいずれかにマッチ:

     (?:v.+|main|release.*)
    

リソースを節約するためのクリーンアップ制限の設定

  • GitLab 13.9 でcontainer_registry_expiration_policies_throttlingというフラグで導入されました。デフォルトでは無効になっています。
  • GitLab 14.9ではデフォルトで有効
  • GitLab 15.0で機能フラグcontainer_registry_expiration_policies_throttling削除

クリーンアップ・ポリシーはバックグラウンド・プロセスとして実行されます。このプロセスは複雑で、削除するタグの数によっては、終了までに時間がかかることがあります。

次のアプリケーション設定を使用して、サーバーリソースの枯渇を防ぐことができます:

  • container_registry_expiration_policies_worker_capacity同時に実行するクリーンアップワーカーの最大数。この値は0. 0NET 以上である必要があります。0 低い値から開始し、バックグラウンド ワーカーが使用するリソースを監視してから増やす必要が 0あります。0すべてのワーカーを削除してクリーンアップ ポリシーを実行しないようにするには、この値を . 0 デフォルト値は4 です。
  • container_registry_delete_tags_service_timeout: クリーンアップ プロセスがタグのバッチを削除するのにかかる最大時間 (秒)。デフォルト値は250です。
  • container_registry_cleanup_tags_service_max_list_size1回の実行で削除できるタグの最大数。追加のタグは別の実行で削除する必要があります。低い数値から開始し、コンテナイメージが適切に削除されることを確認した後に数値を増やす必要があります。デフォルト値は200です。
  • container_registry_expiration_policies_cachingポリシーの実行中にタグの作成タイムスタンプのキャッシュを有効または無効にします。キャッシュされたタイムスタンプはRedis に保存されます。デフォルトでは有効です。

セルフマネージドインスタンスの場合、これらの設定はRailsコンソールで更新できます:

ApplicationSetting.last.update(container_registry_expiration_policies_worker_capacity: 3)

管理者エリアでも利用できます:

  1. 左のサイドバーで、Search を選択するか、次のページに進んでください。
  2. Admin Areaを選択します。
  3. 左サイドバーで、「設定」>「CI/CD」を選択します。
  4. コンテナレジストリを展開します。

クリーンアップポリシーAPIを使用します。

GitLab APIを使ってクリーンアップポリシーを設定、更新、無効化することができます。

例:

  • すべてのタグを選択、画像ごとに最低1つのタグを保持、14日以上前のタグをクリーンアップ、月に1回実行、main という名前の画像を保存、ポリシーは有効:

     curl --request PUT --header 'Content-Type: application/json;charset=UTF-8' --header "PRIVATE-TOKEN: <your_access_token>" \
          --data-binary '{"container_expiration_policy_attributes":{"cadence":"1month","enabled":true,"keep_n":1,"older_than":"14d","name_regex":".*","name_regex_keep":".*-main"}}' \
          "https://gitlab.example.com/api/v4/projects/2"
    

API を使用する際のcadence の有効な値は以下のとおりです:

  • 1d 毎日
  • 7d 毎週
  • 14d (2週間ごと)
  • 1month 毎月
  • 3month (四半期ごと)

API使用時のkeep_n (画像名ごとに保持されるタグの数)の有効な値は以下のとおりです:

  • 1
  • 5
  • 10
  • 25
  • 50
  • 100

API を使用する際のolder_than (タグが自動的に削除されるまでの日数)の有効な値は以下のとおりです:

  • 7d
  • 14d
  • 30d
  • 90d

詳細については、APIドキュメントを参照してください:プロジェクトAPIを編集します。

外部コンテナレジストリとの併用

外部コンテナレジストリを使用する場合、プロジェクトでクリーンアップポリシーを実行するとパフォーマンス上のリスクがあります。プロジェクトが何千ものタグを削除するポリシーを実行すると、GitLabバックグラウンドジョブがバックアップされたり、完全に失敗したりする可能性があります。GitLab 12.8より前に作成されたプロジェクトでは、クリーンアップされるタグの数が最小限である場合にのみ、コンテナクリーンアップポリシーを有効にする必要があります。

その他のコンテナレジストリストレージ削減オプション

プロジェクトで使用するコンテナレジストリのストレージを削減するためのその他のオプションをいくつか紹介します:

クリーンアップポリシーのトラブルシューティング

Something went wrong while updating the cleanup policy.

このエラーメッセージが表示された場合は、正規表現パターンが有効であることを確認してください。

GitLab はクリーンアップポリシーの正規表現にRE2 構文を使います。regex101 regex testerで Golang 。一般的な正規表現パターンの例をご覧ください。

クリーンアップポリシーはタグを削除しません

これにはさまざまな理由が考えられます:

  • GitLab 13.6以前では、クリーンアップポリシーを実行するとタグが削除されると思っていたのに削除されないことがあります。これは、Remove tags matchingフィールドの値を編集せずにクリーンアップポリシーを保存した場合に発生します。このフィールドには、.* プレースホルダとして .*灰色で表示された値があります。(または別の正規表現パターン) がフィールドに明示的に入力さ.* れない限り .*nil 値が送信されます。この値は、保存されたクリーンアップ ポリシーがどのタグにも一致しないようにします。回避策として、クリーンアップ ポリシーを編集します。一致するタグの削除] フィールドに.* を入力して保存します。この値は、すべてのタグを削除する必要があることを示します。

  • GitLab のセルフマネージドインスタンスを利用していて、コンテナリポジトリに 1000 以上のタグがある場合、コンテナレジストークンの有効期限のイシューが発生し、ログにerror authorizing context: invalid token が表示されることがあります。

    これを解決するには、2つの回避策があります:

    • GitLab 13.9以降であれば、クリーンアップポリシーに制限を設定することができます。これにより、クリーンアップの実行時間を制限し、期限切れトークンのエラーを回避することができます。

    • コンテナレジストリ認証トークンの有効期限を延長します。デフォルトは5分です。RailsコンソールでApplicationSetting.last.update(container_registry_token_expire_delay: <integer>)<integer> は希望の分数です。参考までに、GitLab.comでは有効期限遅延は15分に設定されています。この値を大きくすると、権限を取り消すのに必要な時間が長くなります。

あるいは、削除するタグのリストを作成し、そのリストを使ってタグを削除することもできます。リストを作成してタグを削除するには

  1. 以下のシェルスクリプトを実行します。for ループの直前のコマンドは、list_o_tags.out ループの開始時に常に再初期化さ list_o_tags.outれるようにします。list_o_tags.out このコマンドを実行すると、すべてのタグの名前が list_o_tags.outファイルに書き込まれます:

    # Get a list of all tags in a certain container repository while considering [pagination](../../../api/rest/index.md#pagination)
    echo -n "" > list_o_tags.out; for i in {1..N}; do curl --header 'PRIVATE-TOKEN: <PAT>' "https://gitlab.example.com/api/v4/projects/<Project_id>/registry/repositories/<container_repo_id>/tags?per_page=100&page=${i}" | jq '.[].name' | sed 's:^.\(.*\).$:\1:' >> list_o_tags.out; done
    

    Railsのコンソールにアクセスできる場合は、以下のコマンドを入力すると、日付で限定したタグのリストを取得できます:

    output = File.open( "/tmp/list_o_tags.out","w" )
    Project.find(<Project_id>).container_repositories.find(<container_repo_id>).tags.each do |tag|
      output << tag.name + "\n" if tag.created_at < 1.month.ago
    end;nil
    output.close
    

    この一連のコマンドは、created_at 日付が1ヶ月以上古いタグの一覧を/tmp/list_o_tags.out ファイルに作成します。

  2. list_o_tags.out ファイルから保持したいタグを削除します。たとえば、sed を使用してファイルを解析し、タグを削除することができます。

    Linux
    # Remove the `latest` tag from the file
    sed -i '/latest/d' list_o_tags.out
       
    # Remove the first N tags from the file
    sed -i '1,Nd' list_o_tags.out
       
    # Remove the tags starting with `Av` from the file
    sed -i '/^Av/d' list_o_tags.out
       
    # Remove the tags ending with `_v3` from the file
    sed -i '/_v3$/d' list_o_tags.out
    
    macOS
    # Remove the `latest` tag from the file
    sed -i .bak '/latest/d' list_o_tags.out
       
    # Remove the first N tags from the file
    sed -i .bak '1,Nd' list_o_tags.out
       
    # Remove the tags starting with `Av` from the file
    sed -i .bak '/^Av/d' list_o_tags.out
       
    # Remove the tags ending with `_v3` from the file
    sed -i .bak '/_v3$/d' list_o_tags.out
    
  3. list_o_tags.out ファイルをダブルチェックして、削除したいタグだけが含まれていることを確認します。

  4. このシェル・スクリプトを実行して、list_o_tags.out ファイル内のタグを削除します:

    # loop over list_o_tags.out to delete a single tag at a time
    while read -r LINE || [[ -n $LINE ]]; do echo ${LINE}; curl --request DELETE --header 'PRIVATE-TOKEN: <PAT>' "https://gitlab.example.com/api/v4/projects/<Project_id>/registry/repositories/<container_repo_id>/tags/${LINE}"; sleep 0.1; echo; done < list_o_tags.out > delete.logs