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

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

さらに、脆弱性スキャンによるソフトウェア サプライ チェーンの保護、Binary Authorization を使用したデプロイ ポリシーの適用など、他の機能も活用できます。

レジストリの選択

Artifact Registry は、Google Cloud でコンテナ イメージなどのビルド アーティファクトを保存および管理するための推奨サービスです。

  • Container Registry を使用していない場合は、イメージを Artifact Registry に移行してください。Artifact Registry は、マルチリージョンではなくリージョンにイメージを保存し、より細かいアクセス制御を行い、他のアーティファクト形式をサポートするなど、柔軟性と制御性に優れています。
  • Container Registry を使用している場合は、Artifact Registry に移行できます。

移行の概要

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

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

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

始める前に

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

    1. Container 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、Container Registry、Cloud Monitoring API を有効にします。

    gcloud services enable \
    containerregistry.googleapis.com \
    stackdriver.googleapis.com \
    logging.googleapis.com \
    monitoring.googleapis.com
    
  5. 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 にデータをエクスポートするログシンクを作成します。ログシンクに記述された宛先やクエリで、エクスポートするログエントリを選択します。個々のプロジェクトに対してクエリを実行してシンクを作成するか、スクリプトを使用してプロジェクト間でデータを収集できます。

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

ここでは、Logging プレビュー版インターフェースについて説明します。

  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 列で、Container Registry または Artifact Registry に保存されていないイメージの pull 頻度を確認できます。

画像

Container Registry にイメージをコピーする

サードパーティのレジストリからイメージを特定したら、そのイメージを Container 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 "${GCR_PROJECT}" ]
    then
        echo ERROR: GCR_PROJECT must be set before running this
        exit 1
    fi
    
    for img in ${images}
    do
        gcrane cp ${img} gcr.io/${GCR_PROJECT}/${img}
    done
    

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

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

    GCR_PROJECT=${PROJECT}
    ./copy_images.sh
    

権限を確認する

デフォルトでは、Google Cloud CI / CD サービスは同じ Google Cloud プロジェクトの Container Registry にアクセスできます。

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

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

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

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

Dockerfile とマニフェストを更新して、サードパーティのレジストリではなく Container 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

更新されたバージョンのこのマニフェストは、Container 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: gcr.io/<GCR_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 はすべて Container Registry から行われ、文字列 gcr.io が含まれている必要があります。

(省略可)サードパーティのレジストリからのイメージの pull をブロックする

Binary Authorization を使用する GKE クラスタの場合、定義するポリシーにより、信頼できないソースからの pull は自動的にブロックされます。ブロックされたイメージを除外リストに追加して、移行されたイメージがポリシーによってブロックされないようにします。次の手順では、プロジェクト内の Container Registry に保存されているすべてのイメージに除外を指定する方法について説明します。

最初にポリシーを更新する際は、ドライラン モードを有効にすることを検討してください。イメージをブロックする代わりに、Binary Authorization によって監査ログエントリが作成されるため、Container Registry に移行する必要があるサードパーティのレジストリからの未処理のイメージを特定できます。

デプロイ ポリシーの構成の詳細については、Binary Authorization のドキュメントをご覧ください。

  1. Binary Authorization ページに移動
  2. [ポリシーの編集] をクリックします。
  3. [プロジェクトのデフォルト ルール] で [ドライラン モード] を有効にします。
  4. [デプロイルールからイメージを除外] で、[Google 提供のシステム イメージをすべて信頼する] を選択したままにします。
  5. [イメージパス] を展開します。
  6. デフォルトのプロジェクト ルールの除外として、イメージのパスを追加します。
    1. イメージリストの下部にある [画像を追加] をクリックします。
    2. Google Cloud プロジェクトのイメージパスを入力します。たとえば、gcr.io/my-project/* はプロジェクト my-project 内のすべてのイメージを除外します。
  7. デプロイするイメージを含む他のプロジェクトに対して、前の手順を繰り返します。

デプロイの Logging でドライラン イベントを確認します。サードパーティのレジストリから定期的に pull しているイメージを移行します。すべてのイメージが移行されたら、ポリシーを編集してドライラン モードを無効にし、信頼できないソースからのイメージをブロックします。