本页面介绍如何使用颁发注册表证书的证书授权机构 (CA) 的公钥允许 Google Kubernetes Engine (GKE) 中运行的工作负载访问私有映像注册表。
运作方式
您可以在 Secret Manager 中存储用于为您的私有注册表颁发证书的 CA 的公钥,并配置哪些注册表完全限定域名 (FQDN) 使用该公钥进行证书验证。GKE 会在节点引导期间自动提取密钥并更新容器运行时注册表配置。当您部署的工作负载使用私有注册表中的容器映像时,需要执行以下步骤:
- 节点上的 kubelet 尝试从私有注册表中拉取映像。
- 注册表提供服务器端 TLS 证书。
- 容器运行时会以加密方式验证注册表证书,并确保 FQDN 与您指定的内容匹配。
- 如果验证通过,GKE 将拉取映像并安排工作负载。
优势
这种访问私有注册表的方法具有以下优势:
- 提高容器运行时配置的可靠性:使用 DaemonSet 等方法设置 containerd 配置会增加发生竞态条件的风险,在这种情况下,其他 DaemonSet 可能会在配置 DaemonSet 之前运行。
- 减少提权攻击的漏洞:无需运行修改容器运行时配置的特权 DaemonSet。
- 降低管理开销:Secret Manager 允许您将 CA 公钥存储在一个集中的位置;使用 IAM 管理对密钥的访问权限;并实施版本控制和注释。如需了解详情,请参阅 Secret Manager 产品概览。
- 提高可审核性:Cloud Logging 已收集日志,包括在将证书添加到集群以及 GKE 节点拉取映像时收集日志。
价格
在本文档中,您将使用 Google Cloud 的以下收费组件:
- GKE
- Secret Manager
- Logging:GKE 会生成管理员活动审核日志,如果启用此功能,则会生成数据访问审核日志。如需了解不同类型的审核日志,请参阅 GKE 审核日志记录。
如需根据您的预计使用量来估算费用,请使用价格计算器。
准备工作
在开始之前,请确保您已执行以下任务:
- 启用 Google Kubernetes Engine API。 启用 Google Kubernetes Engine API
- 如果您要使用 Google Cloud CLI 执行此任务,请安装并初始化 gcloud CLI。 如果您之前安装了 gcloud CLI,请运行
gcloud components update
以获取最新版本。
Enable the Secret Manager API.
您必须已拥有私有注册表和私有 CA 证书才能访问注册表。本指南不介绍如何设置私有注册表或创建证书。
要求
如需使用私有 CA 公钥访问私有注册表,您必须满足以下要求:
- 您的集群必须使用 GKE 1.27.3-gke.1700 或更高版本。
- 您必须使用带有 containerd 的 Container-Optimized OS 节点映像,该映像是所有 GKE 集群的默认设置。不支持带有 containerd 的 Ubuntu 节点映像。不支持 Windows Server 节点映像。
- 您的节点池必须具有
cloud-platform
访问权限范围,节点才能下载证书。如需了解详情,请参阅 GKE 中的访问权限范围。本文档介绍了如何在创建集群或节点池时设置访问权限范围。
限制
请考虑以下限制:
- 您无法在 Ubuntu 节点映像中使用私有 CA 证书。
- 您无法在 Windows Server 节点中使用私有 CA 证书。
- 每个集群最多支持五个用于私有注册表的私有 CA 证书。
- 每个证书最多可以有 25 个完全限定域名 (FQDN)。
- 每个网域只能在一个证书文件中使用。但是,支持证书软件包。
- 证书必须采用 PEM 编码。
- 服务器不会自动轮替证书。如需了解详情,请参阅本文档中的轮替私有 CA 证书。
- FQDN 具有以下限制:
- FQDN 长度上限为 255 个字符(包括特殊字符)。
- FQDN 只能使用字母、数字和短划线 (-)。
- 不支持 Punycode。
- 不支持通配符。
从配置 DaemonSet 迁移
在 GKE Standard 集群中,您可以部署特权 DaemonSet 来修改容器运行时配置。此方法直接修改每个节点上的 containerd 配置。
如果您使用特权 DaemonSet 来配置对私有注册表的访问权限,请在使用本文档之前考虑以下事项:
- 在 Secret Manager 中存储私钥 CA 公钥时,只会配置对私有注册表的访问权限。不支持其他与注册表相关的配置。
启用此功能会导致集群使用 containerd 的 CRI 主机路径配置模型,该模型与之前的配置模型不兼容。如果您的任何 DaemonSet 会修改 containerd 主机配置(例如对于不安全的私有注册表、镜像或代理),请更新 DaemonSet 以使用 CRI 主机路径模型。
如需了解 CRI 主机路径模型中的可用字段,请参阅 containerd GitHub 代码库中的注册表配置。
启用此功能后,GKE 会将 CRI 主机路径配置模型应用于集群中的新节点。现有节点将继续使用之前的配置模型,直到在升级等事件期间重新创建为止。
更新 DaemonSet 以支持两种配置模型
为了降低配置 DaemonSet 无法在支持特定配置模型的节点上运行的风险,请确保您的 DaemonSet 根据节点上的 containerd 配置文件有条件地使用特定的配置模型。如需查看实现此条件逻辑的 DaemonSet 示例,请参阅 GoogleCloudPlatform/k8s-node-tools
GitHub 代码库中的 insecure-registry-config.yaml 清单。
将 CA 公钥存储在 Secret Manager 中
将颁发私有注册表证书的私有 CA 的公钥作为 Secret 存储在 Secret Manager 中。如需了解相关说明,请参阅 Secret Manager 文档中的创建 Secret。
配置从 GKE 对 Secret Manager 的访问权限
如需确保集群的 IAM 服务账号具有从 Secret Manager 拉取 Secret 所需的权限,请让您的管理员为集群的 IAM 服务账号授予针对 Secret 的以下 IAM 角色:
-
访问 Secret 内容:Secret Manager Secret Accessor (
roles/secretmanager.secretAccessor
) -
访问 Secret 元数据:Secret Manager Viewer (
roles/secretmanager.viewer
)
如需详细了解如何授予角色,请参阅管理对项目、文件夹和组织的访问权限。
这些预定义角色包含从 Secret Manager 拉取 Secret 所需的权限。如需查看所需的确切权限,请展开所需权限部分:
所需权限
如需从 Secret Manager 中拉取 Secret,您需要拥有以下权限:
-
resourcemanager.projects.get
-
resourcemanager.projects.list
-
secretmanager.secrets.get
-
secretmanager.secrets.list
-
secretmanager.versions.get
-
secretmanager.versions.list
-
secretmanager.versions.access
您的管理员也可以使用自定义角色或其他预定义角色为集群的 IAM 服务账号授予这些权限。
如果您没有将自定义 IAM 服务账号与集群或节点池相关联(推荐的方法),则集群将使用 Compute Engine 默认服务账号。可能的话,我们建议您使用具有最低权限的 IAM 服务账号配置集群和节点池。如需查看相关说明,请参阅使用最小权限服务账号。
创建运行时配置文件
如需在 GKE 中为私有注册表使用私有 CA 证书,请创建一个 YAML 文件来修改 containerd 配置。
获取 Google Cloud 项目编号:
gcloud projects describe PROJECT_ID \ --format="value(projectNumber)"
输出是您的数字项目编号。
将以下配置保存为
containerd-configuration.yaml
:privateRegistryAccessConfig: certificateAuthorityDomainConfig: - gcpSecretManagerCertificateConfig: secretURI: "projects/PROJECT_NUMBER/secrets/SECRET_NAME/versions/SECRET_VERSION" fqdns: - "FQDN1" - "FQDN2" enabled: true
替换以下内容:
PROJECT_NUMBER
:您在上一步获得的项目编号。SECRET_VERSION
:Secret Manager 中 Secret 的版本号。您可以选择使用版本别名,但我们建议您使用版本号,以避免因管理造成的复杂性。FQDN1
和FQDN2
:您的私有注册表的完全限定域名。如果为该地址颁发了证书,您也可以使用 IPv4 地址,但我们不建议这样做。
如需了解这些字段的说明,请参阅“可用的 containerd 配置选项”表中的 privateRegistryAccessConfig。
将 containerd 配置应用于新集群
本部分介绍如何在创建新的 GKE 集群时应用 containerd 配置文件。
运行以下命令:
gcloud container clusters create-autoCLUSTER_NAME
\ --location=LOCATION
\ --scopes="cloud-platform" \ --containerd-config-from-file="PATH_TO_CONFIG_FILE
"
替换以下内容:
CLUSTER_NAME
:新集群的名称。LOCATION
:新集群的 Compute Engine 位置。PATH_TO_CONFIG_FILE
:您创建的配置文件的路径,例如~/containerd-configuration.yaml
。
您可以运行带有相同选项的 gcloud container clusters create
命令,在新的 Standard 集群上启用私有注册表配置。
将 containerd 配置应用于现有集群
本部分介绍如何将 containerd 配置应用于现有集群和节点。
检查访问权限范围
现有集群必须具有 cloud-platform
访问权限范围才能使用此功能。本部分介绍如何检查访问权限范围,并使用新的或修改后的私有注册表配置文件更新现有集群。
如需详细了解新集群中的默认访问权限范围,请参阅 GKE 中的访问权限范围。
检查 Autopilot 访问权限范围
运行以下命令:
gcloud container clusters describeCLUSTER_NAME
\ --location=LOCATION
\ --flatten=nodeConfig \ --format='csv[delimiter="\\n",no-heading](oauthScopes)'
如果您的集群没有 https://www.googleapis.com/auth/cloud-platform
访问权限范围,请创建一个具有此访问权限范围的新集群。
检查 Standard 访问权限范围
如需检查 Standard 集群访问权限范围,请检查节点池:
gcloud container node-pools describeNODE_POOL_NAME
\ --cluster=CLUSTER_NAME
\ --location=LOCATION
\ --flatten=nodeConfig \ --format='csv[delimiter="\\n",no-heading](oauthScopes)'
将 NODE_POOL_NAME
替换为节点池的名称。
如果您的集群没有 https://www.googleapis.com/auth/cloud-platform
访问权限范围,请创建一个具有 cloud-platform
访问权限范围的新节点池,并删除现有节点池。
更新集群以使用配置文件
运行以下命令:
gcloud container clusters updateCLUSTER_NAME
\ --location=LOCATION
\ --containerd-config-from-file="PATH_TO_CONFIG_FILE
"
在 Standard 集群中重新创建节点
如果您的 Standard 集群不使用自动升级,则您必须手动重新创建节点池以应用新配置。如需触发手动重新创建节点的操作,请将集群升级到其已在使用的 GKE 版本。
gcloud container clusters upgradeCLUSTER_NAME
\ --location=LOCATION
\ --cluster-version=VERSION
将 VERSION
替换为集群已在使用的同一 GKE 补丁版本。
验证您的集群是否可以访问私有注册表
运行以下命令:
gcloud container clusters describe CLUSTER_NAME \
--location=LOCATION \
--flatten="nodePoolDefaults.nodeConfigDefaults.containerdConfig"
输出类似于以下内容:
containerdConfig:
privateRegistryAccessConfig:
certificateAuthorityDomainConfig:
- fqdns:
- 203.0.113.105
gcpSecretManagerCertificateConfig:
secretUri: projects/123456789012/secrets/example-secret-name/versions/1
enabled: true
部署访问私有映像的工作负载
在本部分中,您将部署一个静态 Pod 来引用私有注册表中的映像。
将以下清单保存为
private-registry-pod.yaml
:apiVersion: v1 kind: Pod metadata: name: private-registry-pod spec: containers: - name: private-image image: IMAGE_NAME
将
IMAGE_NAME
替换为您的私有映像名称。部署 Pod:
kubectl create -f private-registry-pod.yaml
轮替私有 CA 证书
Secret Manager 和 GKE 无法在 Secret Manager 中自动轮替私有 CA 证书。如需执行证书轮替,请执行以下步骤。这些步骤要求您重新创建现有节点两次。我们建议您在计划停机期间执行证书轮替,以最大限度地减少工作负载中断的影响。
- 创建一个同时包含新旧证书的 PEM 编码证书软件包。
- 在 Secret Manager 中将软件包添加为新的 Secret 版本。
- 使用新的 Secret 版本号更新运行时配置文件
secretURI
字段。 - 更新集群以使用新的 Secret 版本。
获取更新操作的时间戳:
gcloud container operations list \ --filter="operationType ~ UPDATE_CLUSTER AND targetLink ~ CLUSTER_NAME" \ --sort-by=startTime \ --limit=1 \ --format='value(endTime)'
输出类似于以下内容:
2024-01-31T09:27:30.864308964Z
查找在更新操作结束之前创建的节点:
kubectl get nodes -o json | jq ".items[] | select(.metadata.creationTimestamp | fromdateiso8601 < $(date -d CLUSTER_UPDATE_TIMESTAMP +%s)) | .metadata.name"
将
CLUSTER_UPDATE_TIMESTAMP
替换为上一步中的时间戳。输出是尚未使用更新后的配置重新创建的节点名称的列表。当输出为空白时,请继续执行下一步。
仅使用新证书在 Secret Manager 中创建 Secret 的新版本。
重复上述步骤以更新集群,获取操作时间戳,并验证节点是否使用新的 Secret 版本。
从 Secret Manager 中删除旧的 Secret 版本。
在 Logging 中查看审核日志
本部分介绍如何使用 Logging 检查 GKE 是否在节点上安装了您的 Secret 版本。
进入 Google Cloud 控制台中的 Logs Explorer 页面。
指定以下查询:
resource.type="gce_instance" textPayload:"Installed certificate \\\"projects/PROJECT_NUMBER/secrets/SECRET_NAME/versions/SECRET_VERSION\\\""
如果您的证书安装成功,则输出类似于以下内容:
"Installed certificate "projects/PROJECT_NUMBER/secrets/SECRET_NAME/versions/SECRET_VERSION""
如果证书安装失败,则输出类似于以下内容:
"Failed to install certificate "projects/PROJECT_NUMBER/secrets/SECRET_NAME/versions/SECRET_VERSION""
最佳做法
我们建议您在使用此功能时遵循以下最佳实践:
- 请勿为 Secret Manager Secret 版本使用别名。使用为每个 Secret 版本自动生成的版本号。随着时间的推移,别名可能会指向不同的证书版本,这可能会导致在跟踪工作负载使用的特定版本时让情况变得复杂。
- 使用维护窗口和排除项控制 GKE 何时可以重新创建节点以应用更新后的 containerd 配置。
- 在 Secret 级层(而不是项目级层)提供对 Secret 的访问权限。
停用 containerd 配置选项
如需移除自定义配置,请执行以下操作:
-
更新配置文件,在要停用的配置项中指定
enabled: false
,并删除该项中的任何其他字段,如以下示例所示:privateRegistryAccessConfig: enabled: false
- 将更新后的配置文件应用到集群。如需了解相关说明,请参阅将 containerd 配置应用于现有集群。
问题排查
如需了解问题排查步骤,请参阅排查容器运行时问题。