从 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. 如果您使用的是外部身份提供方 (IdP),则必须先使用联合身份登录 gcloud CLI

  4. 如需初始化 gcloud CLI,请运行以下命令:

    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. Verify 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. 如果您使用的是外部身份提供方 (IdP),则必须先使用联合身份登录 gcloud CLI

  10. 如需初始化 gcloud CLI,请运行以下命令:

    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. Verify 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,用于为网络钩子服务器生成凭证。
    • 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 中的字符数。

    授予读取者权限

    完成以下步骤,使用 Kubernetes 服务账号向 Artifact Registry 进行身份验证:

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

    将映像推送到 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: .
          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 客户端和网络钩子服务器进行身份验证。

    1. 创建命名空间:

      kubectl create ns signature-verification
      
    2. 如需使用 Kubernetes ServiceAccount 向 Artifact Registry 进行身份验证,请完成以下步骤:

      1. 在您创建的命名空间中创建 Kubernetes 服务账号:

        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 编码内容。这是在下一部分中创建的验证网络钩子配置所必需的:

        cat tls.crt | base64 -w 0.
        

    部署准入网络钩子

    您可以使用以下示例为签名验证服务器和验证 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 提取的最新摘要。

    后续步骤