--- title: 为 Pod 配置服务账户 content_type: task weight: 90 --- 服务账户为 Pod 中运行的进程提供了一个标识。 {{< note >}} 本文是服务账户的用户使用介绍,描述服务账号在集群中如何起作用。 你的集群管理员可能已经对你的集群做了定制,因此导致本文中所讲述的内容并不适用。 {{< /note >}} 当你(自然人)访问集群时(例如,使用 `kubectl`),API 服务器将你的身份验证为 特定的用户帐户(当前这通常是 `admin`,除非你的集群管理员已经定制了你的集群配置)。 Pod 内的容器中的进程也可以与 api 服务器接触。 当它们进行身份验证时,它们被验证为特定的服务帐户(例如,`default`)。 ## {{% heading "prerequisites" %}} {{< include "task-tutorial-prereqs.md" >}} {{< version-check >}} ## 使用默认的服务账户访问 API 服务器 当你创建 Pod 时,如果没有指定服务账户,Pod 会被指定给命名空间中的 `default` 服务账户。 如果你查看 Pod 的原始 JSON 或 YAML(例如:`kubectl get pods/podname -o yaml`), 你可以看到 `spec.serviceAccountName` 字段已经被自动设置了。 你可以使用自动挂载给 Pod 的服务账户凭据访问 API, [访问集群](/zh/docs/tasks/access-application-cluster/access-cluster/#accessing-the-api-from-a-pod) 中有相关描述。 服务账户的 API 许可取决于你所使用的 [鉴权插件和策略](/zh/docs/reference/access-authn-authz/authorization/#authorization-modules)。 在 1.6 以上版本中,你可以通过在服务账户上设置 `automountServiceAccountToken: false` 来实现不给服务账号自动挂载 API 凭据: ```yaml apiVersion: v1 kind: ServiceAccount metadata: name: build-robot automountServiceAccountToken: false ... ``` 在 1.6 以上版本中,你也可以选择不给特定 Pod 自动挂载 API 凭据: ```yaml apiVersion: v1 kind: Pod metadata: name: my-pod spec: serviceAccountName: build-robot automountServiceAccountToken: false ... ``` 如果 Pod 和服务账户都指定了 `automountServiceAccountToken` 值,则 Pod 的 spec 优先于服务帐户。 ## 使用多个服务账户 {#use-multiple-service-accounts} 每个命名空间都有一个名为 `default` 的服务账户资源。 你可以用下面的命令查询这个服务账户以及命名空间中的其他 ServiceAccount 资源: ```shell kubectl get serviceAccounts ``` 输出类似于: ``` NAME SECRETS AGE default 1 1d ``` 你可以像这样来创建额外的 ServiceAccount 对象: ```shell kubectl create -f - < ServiceAccount 对象的名字必须是一个有效的 [DNS 子域名](/zh/docs/concepts/overview/working-with-objects/names#dns-subdomain-names). 如果你查询服务帐户对象的完整信息,如下所示: ```shell kubectl get serviceaccounts/build-robot -o yaml ``` 输出类似于: ```yaml apiVersion: v1 kind: ServiceAccount metadata: creationTimestamp: 2015-06-16T00:12:59Z name: build-robot namespace: default resourceVersion: "272500" uid: 721ab723-13bc-11e5-aec2-42010af0021e secrets: - name: build-robot-token-bvbk5 ``` 那么你就能看到系统已经自动创建了一个令牌并且被服务账户所引用。 你可以使用授权插件来 [设置服务账户的访问许可](/zh/docs/reference/access-authn-authz/rbac/#service-account-permissions)。 要使用非默认的服务账户,将 Pod 的 `spec.serviceAccountName` 字段设置为你想用的服务账户名称。 Pod 被创建时服务账户必须存在,否则会被拒绝。 你不能更新已经创建好的 Pod 的服务账户。 你可以清除服务账户,如下所示: ```shell kubectl delete serviceaccount/build-robot ``` ## 手动创建服务账户 API 令牌 假设我们有一个上面提到的名为 "build-robot" 的服务账户,然后我们手动创建一个新的 Secret。 ```shell kubectl create -f - < 现在,你可以确认新构建的 Secret 中填充了 "build-robot" 服务帐户的 API 令牌。 令牌控制器将清理不存在的服务帐户的所有令牌。 ```shell kubectl describe secrets/build-robot-secret ``` 输出类似于: ``` Name: build-robot-secret Namespace: default Labels: Annotations: kubernetes.io/service-account.name: build-robot kubernetes.io/service-account.uid: da68f9c6-9d26-11e7-b84e-002dc52800da Type: kubernetes.io/service-account-token Data ==== ca.crt: 1338 bytes namespace: 7 bytes token: ... ``` {{< note >}} 这里省略了 `token` 的内容。 {{< /note >}} ## 为服务账户添加 ImagePullSecrets {#add-imagepullsecrets-to-a-service-account} ### 创建 ImagePullSecret - 创建一个 ImagePullSecret,如同[为 Pod 设置 ImagePullSecret](/zh/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod)所述。 ```shell kubectl create secret docker-registry myregistrykey --docker-server=DUMMY_SERVER \ --docker-username=DUMMY_USERNAME --docker-password=DUMMY_DOCKER_PASSWORD \ --docker-email=DUMMY_DOCKER_EMAIL ``` - 确认创建成功: ```shell kubectl get secrets myregistrykey ``` 输出类似于: ``` NAME TYPE DATA AGE myregistrykey   kubernetes.io/.dockerconfigjson   1       1d ``` ### 将镜像拉取 Secret 添加到服务账号 接着修改命名空间的 `default` 服务帐户,以将该 Secret 用作 imagePullSecret。 ```shell kubectl patch serviceaccount default -p '{"imagePullSecrets": [{"name": "myregistrykey"}]}' ``` 你也可以使用 `kubectl edit`,或者如下所示手动编辑 YAML 清单: ```shell kubectl get serviceaccounts default -o yaml > ./sa.yaml ``` `sa.yaml` 文件的内容类似于: ```yaml apiVersion: v1 kind: ServiceAccount metadata: creationTimestamp: 2015-08-07T22:02:39Z name: default namespace: default resourceVersion: "243024" uid: 052fb0f4-3d50-11e5-b066-42010af0d7b6 secrets: - name: default-token-uudge ``` 使用你常用的编辑器(例如 `vi`),打开 `sa.yaml` 文件,删除带有键名 `resourceVersion` 的行,添加带有 `imagePullSecrets:` 的行,最后保存文件。 所得到的 `sa.yaml` 文件类似于: ```yaml apiVersion: v1 kind: ServiceAccount metadata: creationTimestamp: 2015-08-07T22:02:39Z name: default namespace: default uid: 052fb0f4-3d50-11e5-b066-42010af0d7b6 secrets: - name: default-token-uudge imagePullSecrets: - name: myregistrykey ``` 最后,用新的更新的 `sa.yaml` 文件替换服务账号。 ```shell kubectl replace serviceaccount default -f ./sa.yaml ``` ### 验证镜像拉取 Secret 已经被添加到 Pod 规约 现在,在当前命名空间中创建的每个使用默认服务账号的新 Pod,新 Pod 都会自动 设置其 `.spec.imagePullSecrets` 字段: ```shell kubectl run nginx --image=nginx --restart=Never kubectl get pod nginx -o=jsonpath='{.spec.imagePullSecrets[0].name}{"\n"}' ``` 输出为: ``` myregistrykey ``` ## 服务帐户令牌卷投射 {#service-account-token-volume-projection} {{< feature-state for_k8s_version="v1.20" state="stable" >}} {{< note >}} 为了启用令牌请求投射,你必须为 `kube-apiserver` 设置以下命令行参数: * `--service-account-issuer` * `--service-account-key-file` * `--service-account-signing-key-file` * `--api-audiences`(可以省略) {{< /note >}} kubelet 还可以将服务帐户令牌投影到 Pod 中。 你可以指定令牌的所需属性,例如受众和有效持续时间。 这些属性在默认服务帐户令牌上无法配置。 当删除 Pod 或 ServiceAccount 时,服务帐户令牌也将对 API 无效。 使用名为 [ServiceAccountToken](/zh/docs/concepts/storage/volumes/#projected) 的 ProjectedVolume 类型在 PodSpec 上配置此功能。 要向 Pod 提供具有 "vault" 用户以及两个小时有效期的令牌,可以在 PodSpec 中配置以下内容: {{< codenew file="pods/pod-projected-svc-token.yaml" >}} 创建 Pod: ```shell kubectl create -f https://k8s.io/examples/pods/pod-projected-svc-token.yaml ``` `kubelet` 组件会替 Pod 请求令牌并将其保存起来,通过将令牌存储到一个可配置的 路径使之在 Pod 内可用,并在令牌快要到期的时候刷新它。 `kubelet` 会在令牌存在期达到其 TTL 的 80% 的时候或者令牌生命期超过 24 小时 的时候主动轮换它。 应用程序负责在令牌被轮换时重新加载其内容。对于大多数使用场景而言,周期性地 (例如,每隔 5 分钟)重新加载就足够了。 ## 发现服务账号分发者 {{< feature-state for_k8s_version="v1.21" state="stable" >}} 当启用服务账号令牌投射时启用发现服务账号分发者(Service Account Issuer Discovery)这一功能特性, 如[上文所述](#service-account-token-volume-projection)。 {{< note >}} 分发者的 URL 必须遵从 [OIDC 发现规范](https://openid.net/specs/openid-connect-discovery-1_0.html)。 这意味着 URL 必须使用 `https` 模式,并且必须在 `{service-account-issuer}/.well-known/openid-configuration` 路径提供 OpenID 提供者(Provider)配置。 如果 URL 没有遵从这一规范,`ServiceAccountIssuerDiscovery` 末端就不会被注册, 即使该特性已经被启用。 {{< /note >}} 发现服务账号分发者这一功能使得用户能够用联邦的方式结合使用 Kubernetes 集群(_Identity Provider_,标识提供者)与外部系统(_relying parties_, 依赖方)所分发的服务账号令牌。 当此功能被启用时,Kubernetes API 服务器会在 `/.well-known/openid-configuration` 提供一个 OpenID 提供者配置文档,并在 `/openid/v1/jwks` 处提供与之关联的 JSON Web Key Set(JWKS)。 这里的 OpenID 提供者配置有时候也被称作 _发现文档(Discovery Document)_。 集群包括一个的默认 RBAC ClusterRole, 名为 `system:service-account-issuer-discovery`。 默认的 RBAC ClusterRoleBinding 将此角色分配给 `system:serviceaccounts` 组, 所有服务帐户隐式属于该组。这使得集群上运行的 Pod 能够通过它们所挂载的服务帐户令牌访问服务帐户发现文档。 此外,管理员可以根据其安全性需要以及期望集成的外部系统选择是否将该角色绑定到 `system:authenticated` 或 `system:unauthenticated`。 {{< note >}} 对 `/.well-known/openid-configuration` 和 `/openid/v1/jwks` 路径请求的响应 被设计为与 OIDC 兼容,但不是完全与其一致。 返回的文档仅包含对 Kubernetes 服务账号令牌进行验证所必须的参数。 {{< /note >}} JWKS 响应包含依赖方可以用来验证 Kubernetes 服务账号令牌的公钥数据。 依赖方先会查询 OpenID 提供者配置,之后使用返回响应中的 `jwks_uri` 来查找 JWKS。 在很多场合,Kubernetes API 服务器都不会暴露在公网上,不过对于缓存并向外提供 API 服务器响应数据的公开末端而言,用户或者服务提供商可以选择将其暴露在公网上。 在这种环境中,可能会重载 OpenID 提供者配置中的 `jwks_uri`,使之指向公网上可用的末端地址,而不是 API 服务器的地址。 这时需要向 API 服务器传递 `--service-account-jwks-uri` 参数。 与分发者 URL 类似,此 JWKS URI 也需要使用 `https` 模式。 ## {{% heading "whatsnext" %}} 另请参见: - [服务账号的集群管理员指南](/zh/docs/reference/access-authn-authz/service-accounts-admin/) - [服务账号签署密钥检索 KEP](https://github.com/kubernetes/enhancements/tree/master/keps/sig-auth/1393-oidc-discovery) - [OIDC 发现规范](https://openid.net/specs/openid-connect-discovery-1_0.html)