从 Artifact Registry 同步 OCI 工件

本页面介绍了如何使用 craneoras 创建映像并将其发布到 Artifact Registry 中的仓库。

您可以使用 Artifact Registry 将 Config Sync 配置为从 OCI 映像同步。如需使用此功能,您必须启用 RootSync API 和 RepoSync API

Artifact Registry 简介

Artifact Registry 是一项全托管式服务,同时支持容器映像和非容器制品。我们建议您使用 Artifact Registry 来存储和管理 Google Cloud上的容器映像。您可以通过多种工具将制品推送到 Artifact Registry。例如,您可以推送 Docker 映像推送 Helm 图表,或使用 go-containerregistry 库来处理容器注册表。请选择最适合您的工具。

准备工作

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. Install the Google Cloud CLI.
  3. To use a federated identity with the gcloud CLI, you must first configure the tool to use a federated identity.

    For more information, see Browser-based sign-in with the gcloud CLI.

  4. To initialize the gcloud CLI, run the following command:

    gcloud init
  5. Create or select a Google Cloud project.

    • Create a Google Cloud project:

      gcloud projects create PROJECT_ID

      Replace PROJECT_ID with a name for the Google Cloud project you are creating.

    • Select the Google Cloud project that you created:

      gcloud config set project PROJECT_ID

      Replace PROJECT_ID with your Google Cloud project name.

  6. Make sure that billing is enabled for your Google Cloud project.

  7. Enable the GKE Enterprise, Config Sync, Artifact Registry APIs:

    gcloud services enable anthos.googleapis.com  anthosconfigmanagement.googleapis.com  artifactregistry.googleapis.com
  8. Install the Google Cloud CLI.
  9. To use a federated identity with the gcloud CLI, you must first configure the tool to use a federated identity.

    For more information, see Browser-based sign-in with the gcloud CLI.

  10. To initialize the gcloud CLI, run the following command:

    gcloud init
  11. Create or select a Google Cloud project.

    • Create a Google Cloud project:

      gcloud projects create PROJECT_ID

      Replace PROJECT_ID with a name for the Google Cloud project you are creating.

    • Select the Google Cloud project that you created:

      gcloud config set project PROJECT_ID

      Replace PROJECT_ID with your Google Cloud project name.

  12. Make sure that billing is enabled for your Google Cloud project.

  13. Enable the GKE Enterprise, Config Sync, Artifact Registry APIs:

    gcloud services enable anthos.googleapis.com  anthosconfigmanagement.googleapis.com  artifactregistry.googleapis.com
  14. 创建或有权访问满足 Config Sync 要求且使用最新版本的 Config Sync 的集群。
  15. 安装 nomos CLI 或将其升级到最新版本
  16. (可选)如果您要使用 Cosign 验证 OCI 映像签名,请安装以下各项:
    • Cosign,用于为 OCI 映像签名。
    • OpenSSL,用于为 webhook 服务器生成凭据。
    • Docker,用于构建和推送准入 Webhook 服务器映像。

费用

在本文档中,您将使用 Google Cloud 的以下收费组件:

您可使用价格计算器根据您的预计使用情况来估算费用。 Google Cloud 新用户可能有资格申请免费试用

创建 Artifact Registry 仓库

在本部分中,您将创建一个 Artifact Registry 代码库。如需详细了解如何创建 Artifact Registry 代码库,请参阅创建代码库

  1. 创建 Artifact Registry 代码库:

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

请替换以下内容:

  • PROJECT_ID:组织的项目 ID。
  • AR_REPO_NAME:制品库的 ID。
  • AR_REGION:制品库的单区域级或多区域级位置。

以下部分中使用的变量:

  • FLEET_HOST_PROJECT_ID:如果您使用的是 GKE Workload Identity Federation for GKE,则此 ID 与 PROJECT_ID 相同。如果您使用的是舰队 Workload Identity Federation for GKE,则这是集群注册到的舰队的项目 ID。
  • GSA_NAME:您要用于连接到 Artifact Registry 的自定义 Google 服务账号的名称。
  • KSA_NAME:协调器的 Kubernetes 服务账号。
    • 对于根代码库,如果 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_LENGTHREPO_SYNC_NAME 中的字符数。

授予读取者权限

如果您的集群上的 Config Sync 版本为 1.17.2 或更高版本,则可以使用 Kubernetes 服务账号向 Artifact Registry 进行身份验证。否则,请使用 Google 服务账号进行身份验证。

使用 Kubernetes 服务账号

向使用 Workload Identity Federation for GKE 池的 Kubernetes 服务账号授予 Artifact Registry Reader (roles/artifactregistry.reader) IAM 角色:

gcloud artifacts repositories add-iam-policy-binding AR_REPO_NAME \
   --location=AR_REGION \
   --member="serviceAccount:FLEET_HOST_PROJECT_ID.svc.id.goog[config-management-system/KSA_NAME]" \
   --role=roles/artifactregistry.reader \
   --project=PROJECT_ID

使用 Google 服务账号

  1. 向 Google 服务账号授予 Artifact Registry Reader (roles/artifactregistry.reader) IAM 角色:

    gcloud artifacts repositories add-iam-policy-binding AR_REPO_NAME \
       --location=AR_REGION \
       --member=serviceAccount:GSA_NAME@PROJECT_ID.iam.gserviceaccount.com \
       --role=roles/artifactregistry.reader \
       --project=PROJECT_ID
    
  2. 在 Kubernetes 服务账号与 Google 服务账号之间创建 IAM 政策绑定:

    gcloud iam service-accounts add-iam-policy-binding \
       --role roles/iam.workloadIdentityUser \
       --member "serviceAccount:FLEET_HOST_PROJECT_ID.svc.id.goog[config-management-system/KSA_NAME]" \
       GSA_NAME@PROJECT_ID.iam.gserviceaccount.com \
       --project=PROJECT_ID
    

将映像推送到 Artifact Registry 代码库

在本部分中,您将创建一个 OCI 映像并将其推送到 Artifact Registry。

  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:

    crane

    本部分中的命令使用 crane 与远程映像和注册表进行交互。

    1. 封装文件:

      tar -cf test-namespace.tar test-namespace.yaml
      
    2. 安装 crane 工具。

    3. 将该映像推送到 Artifact Registry。

      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。

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

将 Config Sync 配置为从映像同步

在本部分中,您将创建一个 RootSync 对象,并将 Config Sync 配置为从 OCI 映像同步。

  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: .
        # The k8sserviceaccount auth type is available in version 1.17.2 and
        # later. Use `gcpserviceaccount` if using an older version.
        # auth: gcpserviceaccount
        # gcpServiceAccountEmail: GSA_NAME@PROJECT_ID.iam.gserviceaccount.com
        auth: k8sserviceaccount
    EOF
    

    ROOT_SYNC_NAME 替换为您的 RootSync 名称。 该名称在集群中必须是唯一的,并且不能超过 26 个字符。 如需查看配置 RootSync 对象时的完整选项列表,请参阅 RootSyncRepoSync 字段

  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 来源签名

从 Config Sync 1.20.0 版开始,Config Sync 支持在将配置应用于集群之前验证 OCI 来源映像的真实性。此方法使用 ValidatingWebhookConfiguration 对象和验证 webhook 服务器来拦截针对 RootSyncRepoSync 对象的更新请求。Config Sync 在成功提取新的图片摘要后,会更新 RootSyncRepoSync 对象的 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 映像并将其推送到映像注册表,请运行以下命令:

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

    SIGNATURE_VERIFICATION_SERVER_IMAGE_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 Reader 角色 (roles/artifactregistry.reader) 添加 IAM 政策绑定:

      gcloud artifacts repositories add-iam-policy-binding REPOSITORY_NAME \
         --location=REPOSITORY_LOCATION \
         --member="serviceAccount:PROJECT_ID.svc.id.goog[signature-verification/signature-verification-sa]" \
         --role=roles/artifactregistry.reader \
         --project=PROJECT_ID
      

      替换以下内容:

      • REPOSITORY_NAME:用于存储 OCI 映像的 Artifact Registry 仓库的名称。
      • REPOSITORY_LOCATION:Artifact Registry 仓库的位置。
  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 替换为签名验证服务器映像的完整网址。

  2. 将部署应用于集群:

    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
    

如果您没有收到任何错误,可以通过检查 RootSyncRepoSync 配置来确认已签名的映像是否是正在同步的对象:

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_NAMERepoSync 的名称。
  • REPOSYNC_NAMESPACE:与 RepoSync 关联的命名空间的名称。

您应该会看到注解 configsync.gke.io/image-to-sync 已添加到 RootSyncRepoSync 对象。该注解包含来源 OCI 映像的网址和 Config Sync 提取的最新摘要。

后续步骤