从 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. 创建或有权访问满足 Config Sync 要求且使用最新版本的 Config Sync 的集群。
  9. 安装 nomos CLI 或将其升级到最新版本
  10. (可选)如果您要使用 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 服务账号进行身份验证。

向使用 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
  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:

    craneoras

    本部分中的命令使用 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 与远程映像和注册表进行交互。

    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 配置来确认已签名的映像是否是正在同步的对象:

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

ROOTSYNC_NAME 替换为 RootSync 的名称。

 kubectl get reposync REPOSYNC_NAME -n REPOSYNC_NAMESPACE -oyaml

替换以下内容:

  • REPOSYNC_NAMERepoSync 的名称。
  • REPOSYNC_NAMESPACE:与 RepoSync 关联的命名空间的名称。

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

后续步骤