Kubernetes--k8s---了解和使用 ServiceAccount

在 Kubernetes 的权限体系中,ServiceAccount 是一个容易被忽视却至关重要的概念。很多人在 Pod 里调用 API Server 遇到 403 Forbidden 时,第一反应是"RBAC 配错了",却往往忽略了更基础的一层:Pod 到底以什么身份在说话?这个"身份"就是 ServiceAccount。

本文从概念出发,逐步拆解 ServiceAccount 的工作原理,并通过一个实战示例演示如何让 Pod 安全地访问 Kubernetes API。

一、ServiceAccount 是什么

ServiceAccount(服务账号)是 Kubernetes 中用于非人类用户的身份标识。它和普通用户账号的核心区别在于:

对比项 ServiceAccount 用户账号
存在位置 Kubernetes API 中的对象 外部系统
作用域 命名空间级别 集群或全局
目标用途 工作负载、自动化工具 人类用户

简单说,当你的 Pod 需要和 API Server 对话、需要拉取私有镜像、或者需要向外部服务证明"我是谁"时,用的就是 ServiceAccount。

二、默认 ServiceAccount 与它的局限

每个命名空间在创建时,Kubernetes 会自动生成一个名为 default 的 ServiceAccount。如果你在部署 Pod 时没有显式指定 serviceAccountName,Kubernetes 就会把这个 default 账号分配给 Pod。

但这个默认账号的权限非常有限 。在启用 RBAC 的集群中,default ServiceAccount 除了拥有公开的 API 发现权限(比如可以查看 API 版本信息)之外,没有任何额外的访问权限。如果你的应用需要读取 Pod 列表、创建 Job 或者访问其他资源,用默认账号一定会被拒绝。

三、Pod 是如何"拿到" ServiceAccount 身份的

理解这一点,就能理解为什么有时候"明明配了 RBAC 却还是不行"。

当 Pod 启动时,Kubernetes 会通过准入控制器自动向 Pod 中注入一个投射卷(projected volume),其中包含三样东西:

  1. ServiceAccount Token:一个短期有效的 JWT 令牌,kubelet 通过 TokenRequest API 获取,默认有效期 1 小时,会随 Pod 删除而失效。
  2. CA 证书 (ca.crt):用于验证 API Server 的证书是否可信。
  3. 命名空间信息 (namespace):当前 Pod 所在的命名空间名称。

这些内容被挂载到容器内的固定路径:

复制代码
/var/run/secrets/kubernetes.io/serviceaccount/
├── token
├── ca.crt
└── namespace

你的应用代码(或者 curl 命令)就是通过读取这些文件来完成身份认证的。

历史变化 :在 Kubernetes 1.24 之前,ServiceAccount 的 Token 是以 Secret 形式长期存在的。从 1.24 开始,默认改为使用 TokenRequest API 生成有期限的动态 Token。这意味着旧的"挂载 Secret 然后无限期使用"的做法已经过时。

四、实战:让 Pod 以自定义 ServiceAccount 访问 API Server

下面通过一个完整示例,演示从零创建 ServiceAccount、授权、并在 Pod 中调用 API 的全过程。

第一步:创建 ServiceAccount

yaml 复制代码
apiVersion: v1
kind: ServiceAccount
metadata:
  name: api-reader
  namespace: default
bash 复制代码
kubectl apply -f api-reader-sa.yaml

第二步:通过 RBAC 授权

创建一个 Role,只允许读取 Pod 信息:

yaml 复制代码
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader
  namespace: default
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list", "watch"]

然后把这个 Role 绑定到 api-reader 这个 ServiceAccount 上:

yaml 复制代码
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: api-reader-binding
  namespace: default
subjects:
- kind: ServiceAccount
  name: api-reader
  namespace: default
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

第三步:部署 Pod 并指定 ServiceAccount

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: api-client
spec:
  serviceAccountName: api-reader
  containers:
  - name: curl
    image: curlimages/curl:latest
    command: ["sleep", "3600"]

注意 serviceAccountName 字段。如果不写,Pod 会使用 default 账号,上面创建的 RoleBinding 就不会生效。

第四步:在 Pod 内调用 API

进入 Pod 后,用挂载的 Token 和 CA 证书调用 API Server:

bash 复制代码
# 进入 Pod
kubectl exec -it api-client -- sh

# 设置变量
APISERVER=https://kubernetes.default.svc
SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount
TOKEN=$(cat ${SERVICEACCOUNT}/token)
CACERT=${SERVICEACCOUNT}/ca.crt

# 调用 API 列出当前命名空间的 Pod
curl --cacert ${CACERT} \
     --header "Authorization: Bearer ${TOKEN}" \
     ${APISERVER}/api/v1/namespaces/default/pods

这段命令的核心逻辑是:用 Token 向 API Server 证明"我是 api-reader",用 CA 证书验证"对方确实是 API Server"。

五、常见问题排查

现象:Pod 内调用 API 返回 403 Forbidden

先确认 Pod 实际使用的 ServiceAccount 名称:

bash 复制代码
kubectl get pod api-client -o jsonpath='{.spec.serviceAccountName}'

然后检查这个 ServiceAccount 是否有对应的 RoleBinding。很多"RBAC 配了但没生效"的情况,实际上是 Pod 用的还是 default 账号,而你的授权对象写的是自定义账号。

现象:挂载的 Token 文件为空或不存在

检查 Pod 是否显式禁用了 Token 自动挂载:

yaml 复制代码
spec:
  automountServiceAccountToken: false

如果设为 false,Kubernetes 不会向 Pod 注入 Token 卷。

六、什么时候该用 ServiceAccount

根据官方建议,以下场景应该考虑创建专用的 ServiceAccount:

  • Pod 需要与 Kubernetes API Server 通信(比如 Operator、Controller)
  • Pod 需要从私有镜像仓库拉取镜像(通过 imagePullSecrets)
  • Pod 需要向外部服务(如云厂商 API)证明自己的身份
  • 外部 CI/CD 工具需要认证到集群

核心原则是"最小权限" :为每个工作负载创建独立的 ServiceAccount,只授予它完成工作所必需的最小权限集。而不是图省事把所有 Pod 都塞进 default,然后给 default 一堆权限。

小结

ServiceAccount 的本质是 "Pod 的身份" 。它解决了"我是谁"的问题,RBAC 则解决了"我能做什么"的问题。两者配合,才能让 Pod 安全、可控地访问 Kubernetes API 和其他外部资源。理解 Token 的挂载机制和 default 账号的权限边界,是排查大部分 403 错误的基础。

相关推荐
xianyuCcCcCCCcc2 小时前
Kubernetes :健康检查、认证授权与可视化面板
运维·云原生·kubernetes·dashboard
fb_123453 小时前
K8s调度实战全景-Pod调度与资源管控
云原生·容器·kubernetes
吴声子夜歌4 小时前
Kubernetes权威指南——Kubernetes安装配置指南(二进制文件安装)
容器·kubernetes
江华森4 小时前
Kubernetes集群部署实战:使用kubeadm在4台ECS服务器上搭建K8s v1.31集群
云原生·容器·kubernetes
闲云自留地5 小时前
K8s Ingress 全解:ingress-nginx 部署、域名路径路由、HTTPS 限流灰度一篇通
nginx·https·kubernetes
正经教主5 小时前
【FDE系列】阶段2:Day 43(UP详细版):Docker Compose — 多容器一键编排
运维·docker·容器
csdn_aspnet5 小时前
构建、运行与管理容器的 18 个常用 Docker 命令
docker·容器·eureka·command
木雷坞5 小时前
AnythingLLM Docker部署:中文资料上传后无引用的检查路径
运维·docker·容器
暖核5 小时前
K8s 深度拆解:健康检查、认证授权与可视化面板全梳理
云原生·容器·kubernetes
小白的码BUG之路6 小时前
K8S -- 集群搭建环境基础
java·服务器·kubernetes