在 Kubernetes 集群中,除了人类用户之外,运行在集群内外的程序同样需要访问 API Server。比如一个部署在集群外的管理平台需要查询 Pod 状态,或者一条 CI/CD 流水线需要动态伸缩 Deployment 副本数。这类程序化访问如果直接拿管理员的 kubeconfig 文件去连接集群,一旦凭证泄露,整个集群就等于裸奔。正确的做法是为这类访问创建专门的 Service Account,并只授予它完成任务所需的最小权限。

一、什么是 Service Account 以及它与普通用户的区别
Service Account 是 Kubernetes 中面向“进程”的身份,而 User 是面向“人”的身份。普通用户的认证通常依赖证书、外部身份提供商或者 kubeconfig 中保存的凭证,而 Service Account 的凭证本质上是 Token,由 Kubernetes 自身签发和管理。
每个 Namespace 下都会自动存在一个名为 default 的 Service Account。Pod 启动时如果没有显式指定,就会挂载这个 default 账号的 Token。很多人在这里踩坑:直接复用 default Service Account 去做 API 访问。默认情况下它几乎没有权限(出于安全考虑,1.24 之后连自动挂载的长期 Token 都被移除了),而且一旦给它绑定了高权限 Role,所有没指定账号的 Pod 都会跟着获得这些权限,风险会被无限放大。
因此,规范的做法是:为每一个需要访问 API 的应用创建独立的 Service Account,做到一应用一账号,权限边界清晰,出问题时也方便单独吊销。
二、创建 Service Account 并授予最小权限
假设我们有一个外部监控程序,只需要读取 production 命名空间下的 Pod 列表。下面用一条命令创建专用的 Service Account:
# 创建命名空间(如果已存在可跳过) kubectl create namespace production # 创建 Service Account kubectl create serviceaccount api-monitor -n production
创建了账号只是有了身份,还需要通过 RBAC 授权。Kubernetes 提供了 Role、ClusterRole、RoleBinding、ClusterRoleBinding 四种资源。如果权限只在单个命名空间内生效,用 Role 加 RoleBinding 就够了;如果需要跨命名空间或访问集群级资源(如 Node、PersistentVolume),则需要 ClusterRole 加 ClusterBinding。
下面是一个完整的 RBAC 配置,只授予列出和查看 Pod 的权限:
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: pod-reader namespace: production rules: - apiGroups: [""] resources: ["pods", "pods/log"] verbs: ["get", "list", "watch"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: read-pods-binding namespace: production subjects: - kind: ServiceAccount name: api-monitor namespace: production roleRef: kind: Role name: pod-reader apiGroup: rbac.authorization.k8s.io
应用配置之后,可以用 kubectl auth can-i 命令验证权限是否生效,这条命令模拟指定身份的鉴权判断,不需要真的发起请求:
kubectl auth can-i list pods -n production \ --as=system:serviceaccount:production:api-monitor # 输出 yes 表示权限已正确授予 kubectl auth can-i delete pods -n production \ --as=system:serviceaccount:production:api-monitor # 输出 no,说明权限隔离生效
这里有一个细节值得注意:resources 字段里写的是 pods,如果要读取 Pod 的日志,还必须额外声明 pods/log 这个子资源,否则调用日志接口会返回 403。
三、获取 Token 的两种方式及版本差异
这是最容易混淆的部分。在 Kubernetes 1.24 之前,创建 Service Account 时会自动生成一个关联的 Secret,里面包含长期有效的 Token,直接 kubectl get secret 就能拿到。但从 1.24 开始,这个自动生成的 Secret 被移除了,因为静态长期 Token 一旦泄露无法追责,也没有有效期控制。
新版本推荐使用 TokenRequest API 签发限时 Token。可以用 kubectl 直接创建一个有效期为一小时的 Token:
# 为 Service Account 签发一个限时 Token,有效期 3600 秒 kubectl create token api-monitor -n production --duration=1h
如果程序确实需要长期凭证(例如外部系统不方便定期刷新),可以显式创建一个 Secret 并加上特定注解,Kubernetes 的 Token Controller 会自动为它填充 Token:
apiVersion: v1
kind: Secret
metadata:
name: api-monitor-token
namespace: production
annotations:
kubernetes.io/service-account.name: api-monitor
type: kubernetes.io/service-account-token创建后查看该 Secret 的 token 字段即可获得长期 Token。但要强调,这种静态 Token 相当于密码,务必妥善保管,并且最好配合命名空间隔离使用。相比之下,TokenRequest 签发的 Token 绑定了请求者身份和有效期,到期自动失效,安全性明显更高。
四、在客户端代码中使用 Token 调用 API
拿到 Token 之后,还需要知道 API Server 的地址和 CA 证书。这两样信息在集群内可以直接从环境变量 KUBERNETES_SERVICE_HOST 和文件 /var/run/secrets/kubernetes.io/serviceaccount/ 下读取;如果客户端在集群外,可以从 kubeconfig 文件中提取 server 地址和 certificate-authority-data 字段。
下面用一段 Python 代码演示如何携带 Token 调用 API,列出指定命名空间下的所有 Pod:
import requests
API_SERVER = "https://<API_SERVER_IP>:6443"
TOKEN = "eyJhbGciOiJSUzI1NiIs..." # 替换为签发的 Token
NAMESPACE = "production"
CA_CERT = "/path/to/ca.crt" # 集群 CA 证书
headers = {"Authorization": "Bearer " + TOKEN}
resp = requests.get(
f"{API_SERVER}/api/v1/namespaces/{NAMESPACE}/pods",
headers=headers,
verify=CA_CERT,
)
print(resp.status_code)
for item in resp.json().get("items", []):
print(item["metadata"]["name"])如果 Pod 运行在集群内部,更优雅的方式是在 Pod 的 spec 中指定 serviceAccountName: api-monitor,官方客户端库(如 client-go、kubernetes Python 库)会自动读取挂载的凭证完成认证和 Token 轮换,完全不需要手工管理。客户端库自带的 Token 刷新机制会定期通过 TokenRequest API 获取新 Token,这也是官方最推荐的方式。
五、常见报错排查
调用 API 时最常见的错误是 401 和 403。返回 401 说明认证失败,通常是 Token 过期或复制时带了换行符,可以重新签发一个 Token 再试。返回 403 说明认证通过但鉴权被拒,此时检查 RoleBinding 中的 namespace 是否与 Service Account 所在命名空间一致,以及 verbs 是否覆盖了实际调用的动作。
另一个高频问题是连接 API Server 报证书校验失败,错误信息通常包含 x509: certificate signed by unknown authority。这多半是客户端没有使用集群的 CA 证书,或者访问的地址与证书 SAN 不匹配。集群外访问时建议使用 kubeconfig 中记录的正式 API 地址,而不是负载均衡节点的内部 IP。逐一排查这些点,程序化的 API 访问就能稳定跑起来了。
Kubernetes Service AccountAPI 访问Token 认证修改时间:2026-09-05 10:16:33