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 错误的基础。

相关推荐
流星白龙1 小时前
【Docker】13.容器基本操作实战
运维·docker·容器
还卿一钵无情泪16 小时前
Docker 部署Unsloth 绕开环境配置难题
运维·人工智能·docker·ai·容器·nlp·干货
打工仔折腾 AI17 小时前
Docker镜像分层与卷挂载到底怎么工作:一次文件系统层面的实测分析
运维·人工智能·后端·python·docker·容器·性能优化
极客先躯18 小时前
高级java每日一道面试题-2026年01月20日-实战篇[Docker]-如何实现镜像的跨区域复制?
java·运维·docker·容器·架构图
dawnsky.liu18 小时前
OpenShift - 实现系统负载和业务负载分区隔离
kubernetes·openshift
杀不死的坏蛋c21 小时前
k8s-原理-网络-安装
kubernetes
紫神1 天前
DDS 通信技术说明
网络·云原生·容器·k8s·dds·弱网
java_logo1 天前
Docker 部署 DeepSeek Harness:轻松搭建局域网里的 AI Agent 平台
运维·docker·容器·ai agent·deepseek·轩辕镜像·deepseekharness
安易算力1 天前
GPU集群调度实践:Slurm/K8s混合部署与GPU共享优化 —— 从批处理到在线推理的统一调度架构
容器·架构·kubernetes