fsGroup 是什么
在 Pod 级 securityContext 里声明 fsGroup 后,kubelet 会把挂载卷的属组调整成这个组,并给组加上读写权限,同时让容器里进程的补充组包含它。这样以非 root 用户运行的应用也能写入卷,不必把 runAsUser 改成 0。它解决的是「容器身份」与「卷权限」不匹配的问题,与 runAsUser 控制的进程身份是两件事。
基本信息
| 声明位置 | spec.securityContext.fsGroup,Pod 级字段,容器级无效 |
|---|---|
| 作用对象 | 支持属组管理的卷,例如 emptyDir 与多数 CSI 卷 |
| 生效方式 | kubelet 在挂载卷时调整属组与组权限位 |
| 配套字段 | fsGroupChangePolicy 决定每次都递归调整还是仅在根目录不符时调整 |
| 是否需要 root 容器 | 不需要,进程身份由 runAsUser 与 runAsGroup 决定 |
| 平台归属 | Kubernetes 1.23 及以上支持 fsGroupChangePolicy |
fsGroup 完整示例
fsGroup 完整示例
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
template:
spec:
securityContext:
runAsUser: 1000 # 进程以 1000 运行
runAsGroup: 1000
fsGroup: 2000 # 卷属组调整为 2000
fsGroupChangePolicy: OnRootMismatch # 根目录属组正确时跳过递归调整
containers:
- name: app
image: registry.example.com/app:1.26.0
volumeMounts:
- name: data
mountPath: /var/lib/app验证权限落地
验证权限落地
kubectl exec -it web-x -- id # 确认补充组里出现 fsGroup 的组号
kubectl exec -it web-x -- ls -ldn /var/lib/app # 目录属组应为 2000,且组有读写位
kubectl exec -it web-x -- touch /var/lib/app/t # 直接写一个文件验证
kubectl get pod web-x -o jsonpath='{.spec.securityContext}' # 回看清单侧声明权限相关字段分工
| 字段 | 控制什么 | 常见取值 |
|---|---|---|
| runAsUser | 进程以哪个用户 ID 运行 | 业务镜像里约定的非 root 用户 |
| runAsGroup | 进程的主组 ID | 与镜像构建时的属组保持一致 |
| fsGroup | 挂载卷的属组,也是进程的补充组 | 共享目录的组,常取固定值 |
| fsGroupChangePolicy | 调整策略:Always 或 OnRootMismatch | 大卷建议 OnRootMismatch |
典型使用场景
- 非 root 应用写数据卷:镜像刻意用普通用户运行,靠 fsGroup 拿到写权限
- 共享卷多容器协作:同一 Pod 内的容器用同一个组读写中间文件
- 存储类授权的替代:不想给容器 privileged 或 root 时,用 fsGroup 解决写权限
- 大卷启动优化:卷内文件很多时改用 OnRootMismatch,避免每次启动递归改写属组
- 上线前权限核查:确认补充组与目录属组一致,避免运行时才报权限错误
排查权限问题时用 id 与 ls -ldn 看数字属主属组,比看名字更可靠:容器里未必有对应的用户数据库,按名字显示可能只有数字 ID。
Always 策略会在每次挂载时递归调整卷内所有文件的属组:卷里文件多时 Pod 启动明显变慢,甚至拖垮节点 IO。文件量大的卷优先选 OnRootMismatch,或事先把目录属组准备好。