Artifact Registry から OCI アーティファクトを同期する

このページでは、crane と oras で、イメージを作成して Artifact Registry のリポジトリに公開する方法について説明します。

Artifact Registry を使用して OCI イメージから同期するように Config Sync を構成できます。この機能を使用するには、RootSync API と RepoSync API を有効にする必要があります。

Artifact Registry について

Artifact Registry は、コンテナ イメージとコンテナ以外のアーティファクトの両方をサポートするフルマネージド サービスです。 Google Cloudでのコンテナ イメージの保存と管理には、Artifact Registry を使用することをおすすめします。Artifact Registry にアーティファクトを push するには、いくつかのツールを使用できます。たとえば、Docker イメージを push することや、go-containerregistry ライブラリをコンテナ レジストリと一緒に使用することが可能です。最適なツールを選択してください。

始める前に

  1. Google Cloud アカウントにログインします。 Google Cloudを初めて使用する場合は、 アカウントを作成して、実際のシナリオで Google プロダクトのパフォーマンスを評価してください。新規のお客様には、ワークロードの実行、テスト、デプロイができる無料クレジット $300 分も差し上げます。
  2. Google Cloud CLI をインストールします。

  3. 外部 ID プロバイダ(IdP)を使用している場合は、まず フェデレーション ID を使用して gcloud CLI にログインする必要があります。

  4. gcloud CLI を初期化するには、次のコマンドを実行します。

    gcloud init
  5. Google Cloud プロジェクトを作成または選択します。

    プロジェクトの選択または作成に必要なロール

    • プロジェクトを選択する: プロジェクトの選択に特定の IAM ロールは必要ありません。ロールが付与されているプロジェクトであれば、どのプロジェクトでも選択できます。
    • プロジェクトを作成する: プロジェクトを作成するには、resourcemanager.projects.create 権限を含むプロジェクト作成者ロール(roles/resourcemanager.projectCreator)が必要です。詳しくは、ロールを付与する方法をご覧ください。
    • Google Cloud プロジェクトを作成します。

      gcloud projects create PROJECT_ID

      PROJECT_ID は、作成する Google Cloud プロジェクトの名前に置き換えます。

    • 作成した Google Cloud プロジェクトを選択します。

      gcloud config set project PROJECT_ID

      PROJECT_ID は、 Google Cloud プロジェクトの名前に置き換えます。

  6. Google Cloud プロジェクトで課金が有効になっていることを確認します。

  7. GKE、Config Sync、Artifact Registry の API を有効にします(まだ有効になっていない場合)。

    API を有効にするために必要なロール

    API を有効にするには、serviceusage.services.enable 権限が必要です。プロジェクトを作成した場合は、オーナーロール(roles/owner)を通じてこの権限がすでに付与されている可能性があります。それ以外の場合は、Service Usage 管理者ロール(roles/serviceusage.serviceUsageAdmin)を通じてこの権限を取得できます。ロールを付与する方法を確認する。

    gcloud services enable container.googleapis.com  anthosconfigmanagement.googleapis.com  artifactregistry.googleapis.com
  8. Google Cloud CLI をインストールします。

  9. 外部 ID プロバイダ(IdP)を使用している場合は、まず フェデレーション ID を使用して gcloud CLI にログインする必要があります。

  10. gcloud CLI を初期化するには、次のコマンドを実行します。

    gcloud init
  11. Google Cloud プロジェクトを作成または選択します。

    プロジェクトの選択または作成に必要なロール

    • プロジェクトを選択する: プロジェクトの選択に特定の IAM ロールは必要ありません。ロールが付与されているプロジェクトであれば、どのプロジェクトでも選択できます。
    • プロジェクトを作成する: プロジェクトを作成するには、resourcemanager.projects.create 権限を含むプロジェクト作成者ロール(roles/resourcemanager.projectCreator)が必要です。詳しくは、ロールを付与する方法をご覧ください。
    • Google Cloud プロジェクトを作成します。

      gcloud projects create PROJECT_ID

      PROJECT_ID は、作成する Google Cloud プロジェクトの名前に置き換えます。

    • 作成した Google Cloud プロジェクトを選択します。

      gcloud config set project PROJECT_ID

      PROJECT_ID は、 Google Cloud プロジェクトの名前に置き換えます。

  12. Google Cloud プロジェクトで課金が有効になっていることを確認します。

  13. GKE、Config Sync、Artifact Registry の API を有効にします(まだ有効になっていない場合)。

    API を有効にするために必要なロール

    API を有効にするには、serviceusage.services.enable 権限が必要です。プロジェクトを作成した場合は、オーナーロール(roles/owner)を通じてこの権限がすでに付与されている可能性があります。それ以外の場合は、Service Usage 管理者ロール(roles/serviceusage.serviceUsageAdmin)を通じてこの権限を取得できます。ロールを付与する方法を確認する。

    gcloud services enable container.googleapis.com  anthosconfigmanagement.googleapis.com  artifactregistry.googleapis.com
  14. Config Sync の要件を満たし、最新バージョンの Config Sync を使用しているクラスタを作成するか、このクラスタにアクセスできることを確認します。
  15. nomos CLI をインストールするか、最新バージョンにアップグレードします。
  16. (省略可)Cosign を使用して OCI イメージ署名を検証する場合は、以下をインストールします。
    • Cosign。OCI イメージに署名します。
    • OpenSSL。Webhook サーバーの認証情報を生成します。
    • Docker。Admission Webhook サーバー イメージをビルドして push します。

費用

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

料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを生成できます。

新規の Google Cloud ユーザーは無料トライアルをご利用いただける場合があります。

Artifact Registry リポジトリを作成する

このセクションでは、Artifact Registry リポジトリを作成します。Artifact Registry リポジトリの作成の詳細については、リポジトリを作成するをご覧ください。

Artifact Registry リポジトリを作成します。

gcloud artifacts repositories create AR_REPO_NAME \
   --repository-format=docker \
   --location=AR_REGION \
   --description="Config Sync repo" \
   --project=PROJECT_ID

次のように置き換えます。

  • PROJECT_ID: 組織のプロジェクト ID。
  • AR_REPO_NAME: リポジトリの ID。
  • AR_REGION: リポジトリのリージョンまたはマルチリージョン ロケーション。

次のセクションで使用される変数:

  • FLEET_HOST_PROJECT_NUMBER: GKE の Workload Identity Federation for GKE を使用している場合は、PROJECT_ID のプロジェクト番号と同じです。フリートの Workload Identity Federation for GKE を使用している場合は、クラスタが登録されているフリートのプロジェクト番号です。
  • FLEET_HOST_PROJECT_ID: GKE の Workload Identity Federation for GKE を使用している場合は、PROJECT_ID と同じです。フリートの Workload Identity Federation for GKE を使用している場合は、クラスタが登録されているフリートのプロジェクト ID です。
  • KUBERNETES_SERVICEACCOUNT: Reconciler の Kubernetes ServiceAccount。
    • ルート リポジトリで、RootSync 名が root-sync の場合は、root-reconciler を追加します。それ以外の場合は、root-reconciler-ROOT_SYNC_NAME を追加します。
    • 名前空間リポジトリで、RepoSync 名が repo-sync の場合は ns-reconciler-NAMESPACE を追加します。それ以外の場合は、ns-reconciler-NAMESPACE-REPO_SYNC_NAME-REPO_SYNC_NAME_LENGTH を追加します。ここで、REPO_SYNC_NAME_LENGTH は REPO_SYNC_NAME の文字数です。

読み取り権限を付与する

リポジトリに対する Artifact Registry 読み取りロール(roles/artifactregistry.reader)を Kubernetes ServiceAccount に付与します。

gcloud artifacts repositories add-iam-policy-binding AR_REPO_NAME \
   --location=AR_REGION \
   --member="principal://iam.googleapis.com/projects/FLEET_HOST_PROJECT_NUMBER/locations/global/workloadIdentityPools/FLEET_HOST_PROJECT_ID.svc.id.goog/subject/ns/config-management-system/sa/KUBERNETES_SERVICEACCOUNT" \
   --role=roles/artifactregistry.reader \
   --project=PROJECT_ID \
   --condition=None

イメージを Artifact Registry リポジトリに push する

このセクションでは、OCI イメージを作成して Artifact Registry に push します。

  1. Namespace マニフェスト ファイルを作成します。

    cat <<EOF> test-namespace.yaml
    apiVersion: v1
    kind: Namespace
    metadata:
      name: test
    EOF
    
  2. Artifact Registry にログインします。

    gcloud auth configure-docker AR_REGION-docker.pkg.dev
    
  3. イメージをパッケージ化して Artifact Registry に push します。

    crane

    このセクションのコマンドは、crane を使用してリモート イメージとレジストリを操作します。

    1. ファイルをパッケージ化します。

      tar -cf test-namespace.tar test-namespace.yaml
      
    2. crane ツールをインストールします。

    3. イメージを Artifact Registry に push します。

      crane append -f test-namespace.tar -t AR_REGION-docker.pkg.dev/PROJECT_ID/AR_REPO_NAME/test-namespace:v1
      

    oras

    このセクションのコマンドは、oras を使用してリモート イメージとレジストリを操作します。

    1. ファイルをパッケージ化します。

      tar -czf test-namespace.tar.gz test-namespace.yaml
      
    2. oras ツールをインストールします。

    3. イメージを Artifact Registry に push します。

      oras push AR_REGION-docker.pkg.dev/PROJECT_ID/AR_REPO_NAME/test-namespace:v1 test-namespace.tar.gz
      

イメージから同期するように Config Sync を構成する

このセクションでは、RootSync オブジェクトを作成し、OCI イメージから同期するように Config Sync を構成します。

  1. RootSync オブジェクトを一意の名前で作成します。

    cat <<EOF>> ROOT_SYNC_NAME.yaml
    apiVersion: configsync.gke.io/v1beta1
    kind: RootSync
    metadata:
      name: ROOT_SYNC_NAME
      namespace: config-management-system
    spec:
      sourceFormat: unstructured
      sourceType: oci
      oci:
        image: AR_REGION-docker.pkg.dev/PROJECT_ID/AR_REPO_NAME/test-namespace:v1
        dir: .
        auth: k8sserviceaccount
    EOF
    

    ROOT_SYNC_NAME は、実際の RootSync オブジェクトの名前に置き換えます。この名前はクラスタ内で一意で、26 文字以下にする必要があります。RootSync オブジェクトを構成する際のオプションの完全なリストについては、RootSync フィールドと RepoSync フィールドをご覧ください。

  2. RootSync オブジェクトを適用します。

    kubectl apply -f ROOT_SYNC_NAME.yaml
    
  3. Config Sync がイメージから同期していることを確認します。

    nomos status --contexts=$(kubectl config current-context)
    

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

    Connecting to clusters...
    
    *publish-config-registry
       --------------------
       <root>:root-sync-test   AR_REGION-docker.pkg.dev/PROJECT_ID/AR_REPO_NAME/test-namespace:v1   
       SYNCED                  05e6a6b77de7a62286387cfea833d45290105fe84383224938d7b3ab151a55a1
       Managed resources:
          NAMESPACE   NAME             STATUS    SOURCEHASH
                      namespace/test   Current   05e6a6b
    

    これで、イメージがクラスタに正常に同期されました。

(省略可)OCI ソース署名を確認する

構成がクラスタに適用される前に、OCI ソースイメージの真正性を検証できます。この方法では、ValidatingWebhookConfiguration オブジェクトと検証用 Webhook サーバーを使用して、RootSync オブジェクトと RepoSync オブジェクトの更新リクエストをインターセプトします。Config Sync は、新しいイメージ ダイジェストを正常に取得した後、RootSync オブジェクトと RepoSync オブジェクトの configsync.gke.io/image-to-sync アノテーションを更新します。検証 Webhook サーバーは、古いアノテーションと新しいアノテーションの値を比較し、変更が検出されると、Cosign などの検証ツールで検証を実行します。

署名検証サーバーをセットアップする

OCI ソースの真正性を確保するには、署名を検証する HTTP サーバーが必要です。Config Sync サンプル リポジトリのサンプルを使用するか、独自の Docker イメージを使用できます。

  1. 提供されたサンプルを使用する場合は、次の手順を完了します。

    1. サンプル リポジトリのクローンを作成します。

      git clone https://github.com/GoogleCloudPlatform/anthos-config-management-samples/
      
    2. 署名検証サーバーのサンプルが含まれているディレクトリに移動します。

      cd anthos-config-management-samples/tree/main/pre-sync/oci-image-verification
      
  2. 署名検証サーバーの Docker イメージを作成してイメージ レジストリに push するには、次のコマンドを実行します。

    docker build -t SIGNATURE_VERIFICATION_SERVER_IMAGE_URL:latest . && docker push SIGNATURE_VERIFICATION_SERVER_IMAGE_URL:latest
    

    SIGNATURE_VERIFICATION_SERVER_IMAGE_URL は、署名検証サーバー イメージの URL に置き換えます。

サービスに対して認証する

署名検証サーバーを設定するには、Artifact Registry、Cosign クライアント、Webhook サーバーの認証を行う必要があります。

  1. 名前空間を作成します。

    kubectl create ns signature-verification
    
  2. Kubernetes ServiceAccount を使用して Artifact Registry を認証するには、次の操作を行います。

    1. 作成した名前空間に Kubernetes ServiceAccount を作成します。

      kubectl create sa signature-verification-sa -n signature-verification
      
    2. リポジトリに対する Artifact Registry 読み取りロール(roles/artifactregistry.reader)を Kubernetes ServiceAccount に付与します。

      gcloud artifacts repositories add-iam-policy-binding REPOSITORY_NAME \
         --location=REPOSITORY_LOCATION \
         --member="principal://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/PROJECT_ID.svc.id.goog/subject/ns/signature-verification/sa/signature-verification-sa" \
         --role=roles/artifactregistry.reader \
         --project=PROJECT_ID \
         --condition=None
      

      次のように置き換えます。

      • REPOSITORY_NAME: OCI イメージを保存する Artifact Registry リポジトリの名前。
      • REPOSITORY_LOCATION: Artifact Registry リポジトリのロケーション。
      • PROJECT_NUMBER: プロジェクトの番号。
      • PROJECT_ID: プロジェクト ID。
  3. Cosign クライアントに対して認証を行う手順は次のとおりです。

    1. Cosign 鍵のペアを生成します。このコマンドは、公開鍵と秘密鍵を生成します。

      cosign generate-key-pair
      
    2. 作成した名前空間の Kubernetes Secret に公開鍵を保存します。

      kubectl create secret generic cosign-key --from-file=cosign.pub -n signature-verification
      
  4. 署名検証サーバーを認証するには、次の操作を行います。

    1. 署名検証サーバー内の通信を暗号化するには、OpenSSL を使用して TLS 証明書と秘密鍵を生成します。

      openssl req -nodes -x509 -sha256 -newkey rsa:4096 \
      -keyout tls.key \
      -out tls.crt \
      -days 356 \
      -subj "/CN=signature-verification-service.signature-verification.svc"  \
      -addext "subjectAltName = DNS:signature-verification-service,DNS:signature-verification-service.signature-verification.svc,DNS:signature-verification-service.signature-verification"
      
    2. 生成した認証情報を Kubernetes Secret に保存します。

      kubectl create secret tls webhook-tls --cert=tls.crt --key=tls.key -n signature-verification
      
    3. tls.cert の Base64 エンコードされたコンテンツを取得します。これは、次のセクションで作成する検証用 Webhook 構成に必要です。

      cat tls.crt | base64 -w 0.
      

アドミッション Webhook をデプロイする

次のサンプルを使用して、署名検証サーバーのデプロイと検証用 Webhook 構成を作成できます。

  1. 次のファイルを保存して、署名検証サーバーのデプロイメントを作成します。

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: signature-verification-server
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: signature-verification-server
      template:
        metadata:
          labels:
            app: signature-verification-server
        spec:
          serviceAccountName: signature-verification-sa
          containers:
          - name: signature-verification-server
            command:
            - /signature-verification-server
            image: SIGNATURE_VERIFICATION_SERVER_IMAGE_URL
            imagePullPolicy: Always
            ports:
            - containerPort: 10250
            volumeMounts:
            - name: tls-certs
              mountPath: "/tls"
            - name: cosign-key
              mountPath: "/cosign-key"
          volumes:
          - name: cosign-key
            secret:
              secretName: cosign-key
          - name: tls-certs
            secret:
              secretName: webhook-tls
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: signature-verification-service
    spec:
      ports:
      - port: 10250
        targetPort: 10250
      selector:
        app: signature-verification-server

    SIGNATURE_VERIFICATION_SERVER_IMAGE_URL は、署名検証サーバー イメージの完全な URL に置き換えます。

  2. Deployment をクラスタに適用します。

    kubectl apply -f signature-verification-deployment.yaml -n signature-verification
    
  3. 次のファイルを保存して、検証用 Webhook 構成を作成します。

    apiVersion: admissionregistration.k8s.io/v1
    kind: ValidatingWebhookConfiguration
    metadata:
      name: image-verification-webhook
    webhooks:
    - name: imageverification.webhook.com
      clientConfig:
        service:
          name: signature-verification-service
          namespace: signature-verification
          path: "/validate"
          port: 10250
        caBundle: CA_BUNDLE
      rules:
      - apiGroups:
        - configsync.gke.io
        apiVersions:
        - v1beta1
        - v1alpha1
        operations:
        - UPDATE
        resources:
        - 'rootsyncs'
        - 'reposyncs'
        scope: '*'
      admissionReviewVersions: ["v1", "v1beta1"]
      sideEffects: None

    CA_BUNDLE は、tls.cert の base64 でエンコードされたコンテンツに置き換えます。

  4. 検証用 Webhook 構成をクラスタに適用します。

    kubectl apply -f signature-verification-validatingwebhookconfiguration.yaml
    

ログでイメージ検証エラーを確認する

イメージ検証サーバーを設定すると、未署名の OCI イメージからの同期は失敗します。

署名検証エラーを確認するには、次のコマンドを実行して署名検証サーバーからログを表示します。

  1. kubectl ログを確認する

    kubectl logs deployment  signature-verification-server -n  signature-verification
    

    署名検証に関連する kubectl のエラーは次のようになります。

    main.go:69: error during command execution: no signatures found
    
  2. Config Sync ログを確認します。

    nomos status
    

    署名検証に関連する Config Sync のエラーは次のようになります。

    Error:   KNV2002: admission webhook "imageverification.webhook.com" denied the request: Image validation failed: cosign verification failed: exit status 10, output: Error: no signatures found
    

エラーが表示されない場合は、RootSync または RepoSync 構成を調べて、署名付きイメージが同期対象のオブジェクトであることを確認できます。

RootSync

 kubectl get rootsync ROOTSYNC_NAME -n config-management-system -oyaml

ROOTSYNC_NAME は、RootSync の名前で置き換えます。

RepoSync

 kubectl get reposync REPOSYNC_NAME -n REPOSYNC_NAMESPACE -oyaml

次のように置き換えます。

  • REPOSYNC_NAME: RepoSync の名前。
  • REPOSYNC_NAMESPACE: RepoSync に関連付けられている名前空間の名前。

RootSync オブジェクトまたは RepoSync オブジェクトにアノテーション configsync.gke.io/image-to-sync が追加されているはずです。アノテーションには、ソース OCI イメージの URL と、Config Sync によって取得された最新のダイジェストが含まれています。

次のステップ