サードパーティ レジストリからコンテナを移行する

一部のコンテナ イメージをサードパーティのレジストリから直接 pull して、Google Kubernetes Engine や Cloud Run などの Google Cloud 環境にデプロイすると、イメージの pull やサードパーティの停止に対するレート制限がビルドとデプロイを中断することがあります。このページでは、それらのイメージを特定して、整合性のある統合されたコンテナ イメージ管理のために Artifact Registry にコピーする方法について説明します。

Artifact Registry は、Artifact Registry にコピーされたイメージの更新に関し、サードパーティのレジストリをモニタリングしません。新しいバージョンのイメージをパイプラインに組み込むには、Artifact Registry に push する必要があります。

移行の概要

コンテナ イメージの移行には次の手順が含まれます。

  1. 前提条件を設定する。
  2. 移行するイメージを特定する。
    • Dockerfile ファイルとデプロイメント マニフェストを検索して、サードパーティのレジストリへの参照を確認する
    • Cloud Logging と BigQuery を使用して、サードパーティのレジストリからイメージを pull する頻度を決定する
  3. Artifact Registry に特定されたイメージをコピーします。
  4. レジストリへの権限が正しく構成されていることを確認します(特に、Artifact Registry と Google Cloud のデプロイ環境が異なるプロジェクトに存在する場合)。
  5. デプロイのマニフェストを更新する。
  6. ワークロードを再デプロイする。

始める前に

  1. 権限を確認します。イメージを Artifact Registry に移行するプロジェクトで、オーナーまたは編集者の IAM ロールが割り当てられている必要があります。
  2. プロジェクト セレクタのページに移動

    1. Artifact Registry を使用する Google Cloud プロジェクトを選択します
    2. Google Cloud コンソールで、Cloud Shell に移動します。
    3. プロジェクト ID を検索して Cloud Shell に設定します。YOUR_PROJECT_ID は実際のプロジェクト ID に置き換えます。

      gcloud config set project YOUR_PROJECT_ID
      
  3. 次の環境変数をエクスポートします。

      export PROJECT=$(gcloud config get-value project)
    
  4. 次のコマンドを使用して、BigQuery、Artifact Registry、Cloud Monitoring API を有効にします。

    gcloud services enable \
    artifactregistry.googleapis.com \
    stackdriver.googleapis.com \
    logging.googleapis.com \
    monitoring.googleapis.com
    
  5. 現在、Artifact Registry を使用していない場合は、イメージのリポジトリを構成します。

  6. Go バージョン 1.13 以降がインストールされていることを確認します。

    • 次のコマンドを使用して、インストールされた既存の Go のバージョンを確認します。

      go version
      
    • Go をインストールまたは更新する必要がある場合は、Go のインストールのドキュメントをご覧ください。

費用

このガイドでは、課金対象である次の Google Cloud コンポーネントを使用します。

移行するイメージを特定する

サードパーティのレジストリの参照用としてコンテナ イメージのビルドとデプロイに使用するファイルを検索し、そのイメージを pull する頻度を確認します。

Dockerfile で参照を特定する

この手順は、Dockerfile が保存されている場所で実行します。これは、コードが局所的にチェックアウトされている場所の場合もあれば、ファイルが VM で利用可能な場合は Cloud Shell で使用されている場合もあります。

Dockerfile があるディレクトリで、次のコマンドを実行します。

grep -inr -H --include Dockerfile\* "FROM" . | grep -i -v -E 'docker.pkg.dev|gcr.io'

出力は、次の例のようになります。

./code/build/baseimage/Dockerfile:1:FROM debian:stretch
./code/build/ubuntubase/Dockerfile:1:FROM ubuntu:latest
./code/build/pythonbase/Dockerfile:1:FROM python:3.5-buster

このコマンドは、ディレクトリ内のすべての Dockerfile を検索し、"FROM" 行を特定します。必要に応じてコマンドを調整し、Dockerfile の保存方法に合わせます。

マニフェストで参照を特定する

この手順は、GKE または Cloud Run マニフェストが保存されている場所で実行してください。これは、コードが局所的にチェックアウトされている場所の場合もあれば、ファイルが VM で利用可能な場合は Cloud Shell で使用されている場合もあります。

  1. GKE または Cloud Run マニフェストがあるディレクトリで次のコマンドを実行します。

    grep -inr -H --include \*.yaml "image:" . | grep -i -v -E 'docker.pkg.dev|gcr.io'
    

    出力例:

    ./code/deploy/k8s/ubuntu16-04.yaml:63: image: busybox:1.31.1-uclibc
    ./code/deploy/k8s/master.yaml:26:      image: kubernetes/redis:v1
    

    このコマンドはディレクトリ内のすべての YAML ファイルを調べて image: 行を特定し、必要に応じてマニフェストの保存方法を調整します。

  2. クラスタで現在実行中のイメージを一覧表示するには、次のコマンドを実行します。

      kubectl get all --all-namespaces -o yaml | grep image: | grep -i -v -E 'docker.pkg.dev|gcr.io'
    

    このコマンドは、現在選択されている Kubernetes クラスタで実行中のすべてのオブジェクトを返し、イメージ名を取得します。

    出力例:

    - image: nginx
      image: nginx:latest
        - image: nginx
        - image: nginx
    

適用範囲を全体的に確認するため、すべての Google Cloud プロジェクトですべての GKE クラスタに対してこのコマンドを実行します。

サードパーティのレジストリからの pull 頻度を特定する

サードパーティのレジストリから pull するプロジェクトでは、イメージの pull 頻度に関する情報を使用して、使用量がサードパーティのレジストリに適用されるレート制限と同等、または超過しているかを判断します。

ログデータを収集する

BigQuery にデータをエクスポートするログシンクを作成します。ログシンクに記述された宛先やクエリで、エクスポートするログエントリを選択します。個々のプロジェクトに対してクエリを実行してシンクを作成するか、スクリプトを使用してプロジェクト間でデータを収集できます。

単一のプロジェクトのシンクを作成するには:

  1. ログ エクスプローラに移動

  2. Google Cloud プロジェクトを選択します。

  3. [クエリビルダー] タブに、次のクエリを入力します。

      resource.type="k8s_pod"
      jsonPayload.reason="Pulling"
    
  4. 履歴のフィルタを [過去 1 時間] から [過去 7 日間] に変更します。

    画像

  5. [実行] をクリックします。

  6. 結果が正しく表示されたことを確認したら、[アクション] > [シンクを作成] をクリックします。

  7. シンクのリストで、[BigQuery データセット] を選択し、[次へ] をクリックします。

  8. [シンクを編集] パネルで、次の操作を行います。

    • [シンク名] フィールドに「image_pull_logs」と入力します。
    • [シンクのエクスポート先] フィールドに新しいデータセットを作成するか、別のプロジェクトの宛先データセットを選択します。
  9. [シンクを作成] をクリックします。

複数のプロジェクトのシンクを作成するには:

  1. Cloud Shell を開きます

  2. Cloud Shell で次のコマンドを実行します。

    PROJECTS="PROJECT-LIST"
    DESTINATION_PROJECT="DATASET-PROJECT"
    DATASET="DATASET-NAME"
    
    for source_project in $PROJECTS
    do
      gcloud logging --project="${source_project}" sinks create image_pull_logs bigquery.googleapis.com/projects/${DESTINATION_PROJECT}/datasets/${DATASET} --log-filter='resource.type="k8s_pod" jsonPayload.reason="Pulling"'
    done
    

    ここで

    • PROJECT-LIST は、スペースで区切られた Google Cloud プロジェクト ID のリストです。例: project1 project2 project3
    • DATASET-PROJECT は、データセットを保存するプロジェクトです。
    • DATASET-NAME は、データセットの名前です(たとえば、image_pull_logs)。

シンクを作成した後、BigQuery テーブルにデータが流れるまでに時間がかかりますが、これはイメージを pull する頻度に応じて変わります。

pull 頻度のクエリ

ビルドが行うイメージ pull の代表サンプルを取得したら、pull 頻度のクエリを実行します。

  1. BigQuery コンソールに移動

  2. 次のクエリを実行します。

    SELECT
      REGEXP_EXTRACT(jsonPayload.message, r'"(.*?)"') AS imageName,
      COUNT(*) AS numberOfPulls
    FROM
          `DATASET-PROJECT.DATASET-NAME.events_*`
    GROUP BY
          imageName
    ORDER BY
          numberOfPulls DESC
    

    ここで

    • DATASET-PROJECT は、データセットを含むプロジェクトです。
    • DATASET-NAME は、データセットの名前です。

次の例は、クエリからの出力を示しています。imageName 列で、Artifact Registry または Container Registry に保存されていないイメージの pull 頻度を確認できます。

画像

Artifact Registry のイメージをコピーする

サードパーティのレジストリでイメージを特定したら、それらのイメージを Artifact Registry にコピーできます。gcrane ツールは、コピープロセスに利用できます。

  1. Cloud Shell で、特定したイメージの名前を使用してテキスト ファイル images.txt を作成します。例:

    ubuntu:18.04
    debian:buster
    hello-world:latest
    redis:buster
    jupyter/tensorflow-notebook
    
  2. gcrane をダウンロードします。

      GO111MODULE=on go get github.com/google/go-containerregistry/cmd/gcrane
    
  3. copy_images.sh という名前のスクリプトを作成して、ファイルのリストをコピーします。

    #!/bin/bash
    
    images=$(cat images.txt)
    
    if [ -z "${AR_PROJECT}" ]
    then
        echo ERROR: AR_PROJECT must be set before running this
        exit 1
    fi
    
    for img in ${images}
    do
        gcrane cp ${img} LOCATION-docker.pkg.dev/${AR_PROJECT}/${img}
    done
    

    LOCATION は、リポジトリのリージョンまたはマルチリージョンに置き換えます。

    スクリプトを実行可能にします。

      chmod +x copy_images.sh
    
  4. スクリプトを実行してファイルをコピーします。

    AR_PROJECT=${PROJECT}
    ./copy_images.sh
    

権限を確認する

デフォルトでは、Google Cloud CI / CD サービスには同じ Google Cloud プロジェクト内の Artifact Registry に対するアクセス権が割り当てられています。

  • Cloud Build はイメージの push と pull ができます
  • GKE、Cloud Run、App Engine フレキシブル環境、Compute Engine などのランタイム環境では、イメージを pull できます。

プロジェクト間でイメージを push または pull する必要がある場合、または Artifact Registry にアクセスする必要があるパイプラインをサードパーティ製ツールで使用する場合は、ワークロードを更新して再デプロイする前に権限が正しく構成されていることを確認してください。

詳細については、アクセス制御のドキュメントをご覧ください。

マニフェストを更新して Artifact Registry を参照する

Dockerfile とマニフェストを更新して、サードパーティのレジストリではなく Artifact Registry を参照するようにします。

次の例は、サードパーティのレジストリを参照するマニフェストを示しています。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

更新されたバージョンのこのマニフェストは、us-docker.pkg.dev 上のイメージを指しています。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: us-docker.pkg.dev/<AR_PROJECT>/nginx:1.14.2
        ports:
        - containerPort: 80

多数のマニフェストを使用する場合は、多くのテキスト ファイル間で更新を処理できる sed などのツールを使用します。

ワークロードを再デプロイする

更新されたマニフェストでワークロードを再デプロイします。

BigQuery コンソールで次のクエリを実行して、新しいイメージの pull を追跡します。

SELECT`

FORMAT_TIMESTAMP("%D %R", timestamp) as timeOfImagePull,
REGEXP_EXTRACT(jsonPayload.message, r'"(.*?)"') AS imageName,
COUNT(*) AS numberOfPulls
FROM
  `image_pull_logs.events_*`
GROUP BY
  timeOfImagePull,
  imageName
ORDER BY
  timeOfImagePull DESC,
  numberOfPulls DESC

新しいイメージの pull はすべて Artifact Registry から行われ、文字列 docker.pkg.dev が含まれている必要があります。