
k8s-csi-s3 挂载故障完整排查指南从 PVC 卡 Pending 到容器报 Transport endpoint is not connected 的 8 个根因与修复【免费下载链接】k8s-csi-s3GeeseFS-based CSI for mounting S3 buckets as PersistentVolumes项目地址: https://gitcode.com/gh_mirrors/k8s/k8s-csi-s3k8s-csi-s3 是一个基于 GeeseFS 的 CSI 驱动它把 S3 兼容的对象存储以 FUSE 挂载的方式变成 Kubernetes 的 PersistentVolume让应用像用本地目录一样读写对象存储。这篇文章帮你解决的是 k8s-csi-s3 从安装、PVC 创建到容器挂载全链路最常见的故障PVC 一直 Pending、Pod 卡在 ContainerCreating、挂载点突然报 Transport endpoint is not connected。每个问题都给出根因、可直接复制的命令和预期输出让你在 10 分钟内完成从现象到修复的闭环。一个让你凌晨被叫起来的真实现场凌晨两点值班手机响了。告警显示生产环境的csi-s3-pvc已经 Pending 超过 15 分钟依赖它的csi-s3-test-nginxPod 卡在 ContainerCreating 出不来。你连上集群看到这样的输出$ kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE csi-s3-pvc Pending csi-s3 15m $ kubectl get pods NAME READY STATUS RESTARTS AGE csi-s3-test-nginx 0/1 ContainerCreating 0 15m再往下看事件你可能会看到两类典型报错Events: Warning ProvisioningFailed 30s csi-provisioner-s3_xxx failed to provision volume with StorageClass csi-s3: rpc error: code Internal desc failed to create bucket pvc-xxx: AccessDenied或者Events: Warning FailedMount 10s kubelet MountVolume.MountDevice failed for volume pvc-xxx : rpc error: code Internal desc Error running mount --bind ...看到这里如果你只是把报错复制去搜索引擎大概率会陷入每个答案都对不上的泥潭。正确的做法是先给故障定性这是发生在控制器侧PVC 供给还是节点侧挂载的问题。这一步分清了排查范围立刻缩小一半。症状对照速查表三分钟给故障定性先别急着看日志用下面这张表对号入座。它能帮你把模糊的异常转成具体的怀疑对象。你看到的症状最可能的根因第一步排查动作PVC 一直 Pending事件报AccessDenied或NoSuchBucketS3 账号权限不足或桶命名不合规看 provisioner 日志中的具体报错PVC Pending事件报failed to initialize S3 clientSecret 缺失、凭证错误或命名空间不匹配核对 Secret 内容与 storageclass 引用PVC Pending事件报连接超时endpoint 写错、网络不通或 TLS 证书问题从调试 Pod 内 curl/aws 验证连通性Pod 卡 ContainerCreating报mount --bind失败挂载传播或特权容器未开启检查 kubelet、Docker 与节点 mount应用读写目录报 Transport endpoint is not connectedGeeseFS 进程被杀、挂载点残留检查节点mount \| grep fuse与 systemd 单元ls目录卡死无响应使用的挂载器性能或兼容性缺陷确认 mounter 类型考虑切换删除 PVC 后 PV 迟迟不释放桶内对象残留或删除权限不足查看 DeleteVolume 相关日志对照完这张表下面 4 个根因剖析会告诉你为什么会发生而不是只扔给你命令。根因一Secret 链条断在哪个环节——PVC 卡 Pending 的头号元凶k8s-csi-s3 的动态供给遵循一条完整凭证链路PVC → 存储类StorageClass→ provisioner → S3 客户端。任何一环错位供给就会失败。先看默认示例deploy/kubernetes/examples/storageclass.yaml里是怎么引用的parameters: mounter: geesefs csi.storage.k8s.io/provisioner-secret-name: csi-s3-secret csi.storage.k8s.io/provisioner-secret-namespace: kube-system csi.storage.k8s.io/node-publish-secret-name: csi-s3-secret csi.storage.k8s.io/node-publish-secret-namespace: kube-system而 Secret 本身deploy/kubernetes/examples/secret.yaml长这样apiVersion: v1 kind: Secret metadata: namespace: kube-system name: csi-s3-secret stringData: accessKeyID: YOUR_ACCESS_KEY_ID secretAccessKey: YOUR_SECRET_ACCESS_KEY endpoint: https://storage.yandexcloud.net #region: 这个链条最常见的三个断点命名空间错位Secret 建在defaultstorageclass 却写kube-system。provisioner 去kube-system里找 Secret 找不到PVC 就永远 Pending。这是新手最高频的错误而且报错往往不直观。端点写错endpoint 必须带协议https://且要能直接被集群节点访问。AWS 用https://s3.region.amazonaws.com其他 S3 兼容存储用各自的地址。region 只在 AWS 等需要区域签名的服务上必须填写其余可以留空——这一点在pkg/s3/client.go中体现得很清楚region 为空时使用匿名区域。base64 编码写错如果你用data字段而不是stringData就必须自己算 base64。很多人把明文当 base64 填进去结果驱动拿到的是一串乱码。关键报错长这样记住它failed to initialize S3 client: ...看到这句优先检查 Secret而不是去折腾存储类。根因二动态建桶的隐藏规则——桶名不是你想叫什么就叫什么k8s-csi-s3 默认每个 PV 创建一个独立桶桶名等于卷 ID。在pkg/driver/controllerserver.go里有一段关键逻辑卷 ID 会被强制转小写超过 63 个字符就用 SHA1 哈希。这意味着PVC 名字会被映射成桶名PvcTest-01会变成pvctest-01之类的小写形式如果你的 PVC 名特别长最终桶名是一串哈希跟你的 PVC 对不上号这是正常现象不是 bugS3 桶命名有硬性规则全小写、3-63 字符、不能带下划线如果你的对象存储服务对这些规则更严格建桶就会失败。更常见的坑是账号没有建桶权限。CreateVolume的执行序列是BucketExists→CreateBucket→CreatePrefix缺了s3:CreateBucket权限事件里就会出现AccessDenied。这时候有两个绕开办法办法 A指定既有桶推荐生产使用。在 storageclass 中加一行参数驱动就不再建桶而是把每个 PV 作为桶内的一个前缀prefix来管理kind: StorageClass apiVersion: storage.k8s.io/v1 metadata: name: csi-s3-existing-bucket provisioner: ru.yandex.s3.csi parameters: mounter: geesefs options: --memory-limit 1000 --dir-mode 0777 --file-mode 0666 bucket: some-existing-bucket-name注意即使指定了 bucket如果这个桶在存储端不存在驱动仍会尝试创建它。删除 PV 时只删该 PV 对应的前缀不会删整个桶风险可控。办法 B静态供给。如果你压根不想让驱动碰桶直接用deploy/kubernetes/examples/pvc-manual.yaml的方式手动建 PV并把 PVC 的storageClassName留空以关闭动态供给。静态供给的 PV 会原样引用volumeHandle: manualbucket/path删除 PV 也不会动桶里的数据。根因三GeeseFS 的 systemd 之谜——挂载点为什么会变成 Transport endpoint is not connected这是最常见的节点侧疑难杂症一切运行正常突然某天应用目录读写报错ls都执行不了报Transport endpoint is not connected。先理解 GeeseFS 默认的启动机制。在pkg/mounter/geesefs.go中可以看到默认情况下驱动会通过宿主机 systemd 启动一个 transient unit名字形如geesefs-volumeID.service来跑 geesefs 进程而不是在 CSI 容器内直接挂载。这样做的目的是当 csi-s3 驱动升级或重启时geesefs 进程不受影响挂载点不断开。但这一切有一个前提宿主机上必须有可用的 systemd。如果出现下面任一情况驱动会退回在容器内直接挂载模式节点用的是不支持 systemd 的发行版或容器化 kubelet 环境驱动连不上 systemd 的 D-Bus 服务日志里会出现Failed to connect to systemd dbus service, starting geesefs directly。一旦退回到直接挂载模式csi-s3 容器一重启geesefs 进程就跟着死但/var/lib/kubelet/pods/...下的挂载点还残留着于是应用一访问就报 Transport endpoint is not connected。对应地--no-systemd这个参数就是显式关闭 systemd 模式。它适用于明确知道驱动不会在挂载期间重启的场景比如把 csi-s3 跑在无 systemd 的节点上做测试。生产环境如果打算长期依赖 systemd 模式就不要加这个参数。排查时在节点上执行# 看残留的 fuse 挂载点状态是死的还是活的 mount | grep fuse # 看 geesefs 的 systemd 单元是否存在、是否 active systemctl status geesefs-*.service如果mount | grep fuse里有条目但对应的 systemd 单元不存在或者单元是failed状态就证实了进程死了、挂载点残留的判断。修复方式通常是把挂载点强制卸载后让驱动重建umount -f -l 目标挂载路径然后删掉对应 Pod 让它重建。注意umount需要在节点上执行并且路径要替换成实际挂载点。根因四底层环境三条硬性要求——不满足连挂载都到不了k8s-csi-s3 在 README 的 Requirements 部分明确列了三项硬性要求缺一项Pod 就会卡在 ContainerCreating 且报错千奇百怪Kubernetes 1.17且MountPropagation功能门控不能设为false允许特权容器运行kubelet 或 admission 层没拦特权容器Docker daemon 允许共享挂载即 systemd 的MountFlagsshared。第三条特别隐蔽。如果节点的 Docker 服务没有以MountFlagsshared启动FUSE 挂载的传播就会失败症状就是NodePublishVolume里的mount --bind报错源码在pkg/driver/nodeserver.go它先用 FUSE 挂到 staging 路径再 bind 到容器目标路径两步缺一不可。检查方法# 在节点上查看 Docker 的挂载标志 systemctl show docker | grep MountFlags输出应该是MountFlagsshared。如果不是修改 Docker 的 systemd 单元后重启 Docker 服务再重建节点上的 csi-s3 DaemonSet Pod。三步定位挂载失败根因从 Pod 事件追到驱动日志无论问题出在控制器侧还是节点侧都可以按下面三步把证据链补完整。第一步看 Pod 事件判断故障发生在哪个阶段kubectl describe pod csi-s3-test-nginx重点看 Events 区块ProvisioningFailed属于控制器侧PVC 供给FailedMount/FailedAttach属于节点侧挂载。这一步就完成了第一小节说的定性。第二步控制器侧取证看 provisioner 日志kubectl logs -l appcsi-provisioner-s3 -c csi-s3 --tail50重点找三类关键字failed to initialize S3 client凭证链问题、failed to create bucket建桶权限/命名问题、连接类错误endpoint 或网络问题。第三步节点侧取证看 s3-driver 日志kubectl logs -l appcsi-s3 -c csi-s3 --tail50重点找NodePublishVolume和NodeStageVolume附近的错误以及Failed to connect to systemd dbus这类降级提示。一条命令验证连通性先排除网络再谈配置凭证和配置都没问题时还要确认集群能真正访问到 S3 端点。最快的方式是起一个一次性调试 Pod用官方 CLI 直接测kubectl run -it --rm s3-conn-test --imageamazon/aws-cli --restartNever -- \ env AWS_ACCESS_KEY_ID你的AK AWS_SECRET_ACCESS_KEY你的SK \ aws --endpoint-url https://storage.yandexcloud.net s3 ls预期输出与判断列出桶列表或空列表连通性与凭证都正常问题不在网络报403/InvalidAccessKeyId/SignatureDoesNotMatch凭证或 region 有问题回去查 Secret连接超时 /Could not connect to the endpoint URL网络不通。检查节点防火墙、网络策略以及 endpoint 是否只能从特定网络访问证书报错私有 S3 用了自签名证书。确认证书是否被节点信任测试环境可以给 Secret 加insecure: true字段跳过 TLS 校验仅限测试生产不建议。分步实操修复把常见的 PVC 卡 Pending 一次修好假设你已经定性为控制器侧问题按下面顺序执行修复。第 1 步核对 Secret 与引用是否一致# 确认 Secret 真实存在于 storageclass 引用的命名空间 kubectl -n kube-system get secret csi-s3-secret # 查看 Secret 实际内容确认 key 名和值 kubectl -n kube-system get secret csi-s3-secret -o jsonpath{.data}key 名必须严格是accessKeyID、secretAccessKey、endpoint、region可选。注意accessKeyID里ID是大写写错一个字母驱动就读不到。第 2 步修正 storageclass 参数如需修改后重建kubectl get storageclass csi-s3 -o yaml确认provisioner: ru.yandex.s3.csi、各*secret-name/*secret-namespace与 Secret 实际位置一致。修改后用kubectl apply重新应用并删除卡住的 PVC 重建kubectl delete pvc csi-s3-pvc kubectl apply -f deploy/kubernetes/examples/pvc.yaml第 3 步验证 PVC 是否绑定成功$ kubectl get pvc csi-s3-pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE csi-s3-pvc Bound pvc-xxx 5Gi RWX csi-s3 9s第 4 步验证挂载是否可用kubectl exec -it csi-s3-test-nginx -- bash # 容器内执行 mount | grep fuse touch /usr/share/nginx/html/s3/hello_world预期输出类似pvc-xxx: on /usr/share/nginx/html/s3 type fuse.geesefs (rw,nosuid,nodev,relatime,user_id65534,group_id0,default_permissions,allow_other)能touch出文件说明整条链路已通。避坑清单六个反模式与体检式检查项经验越多越应该把功夫花在不发生问题上。以下六条是 k8s-csi-s3 使用中反复出现的反模式反模式每个 PV 一个桶。默认行为会在删除 PV 时连桶带对象一起删一旦误删或权限设计失误就是数据事故。生产建议在 storageclass 里指定一个既有桶用前缀隔离各个 PV。反模式把容量请求当配额。S3 没有真实容量上限PVC 里写的5Gi只是声明驱动不会真的限制写入量。别指望它做配额控制。反模式为图省事加--no-systemd。只要节点有 systemd就保留默认的 systemd 模式否则驱动一重启所有挂载点都会变成 Transport endpoint is not connected。反模式小文件高并发场景用 s3fs。s3fs 大文件表现好但小文件性能差目录文件多时很慢。默认 geesefs 是官方强推的均衡选择rclone 兼容性最差且可能挂起只适合临时测试。反模式升级时直接 apply 新版本。从 v0.35.5 及更早版本升级前必须先清理旧的 attacher 资源从 v0.40.6 及更早版本升级前必须先删除旧 provisioner否则新旧资源冲突症状诡异比如 PVC 卡在 Provisioning 但 provisioner 日志干净。反模式给 S3 账号开最高权限。最小权限才是正解。动态供给通常需要s3:CreateBucket、s3:PutObject、s3:DeleteObject、s3:ListBucket、s3:GetObject如果只用既有桶 静态供给可以进一步去掉建桶权限。体检式检查项建议每周一次# 1. 有没有 PVC 异常卡住 kubectl get pvc -A | grep -v Bound # 2. 驱动与 provisioner 是否健康 kubectl get pods -A -l appcsi-s3 kubectl get pods -A -l appcsi-provisioner-s3 # 3. 节点上有没有残留的死挂载 kubectl get pods -A -o wide | grep csi-s3-test # 再到对应节点上执行 mount | grep fuse 检查 # 4. 关键事件是否异常 kubectl get events --sort-by.lastTimestamp | tail -20收尾3 分钟、5 分钟、10 分钟分别能做什么3 分钟能做的执行kubectl describe pvc、kubectl describe pod把事件里的ProvisioningFailed与FailedMount分开再执行kubectl logs -l appcsi-provisioner-s3 -c csi-s3 --tail50判断问题在控制器侧还是节点侧。5 分钟能做的控制器侧问题用调试 Pod 跑一条aws s3 ls验证连通性与凭证节点侧问题检查 Docker 的MountFlagsshared、mount | grep fuse和systemctl status geesefs-*.service把网络、凭证、systemd 三个变量先排除掉。10 分钟能做的对照速查表逐项过一遍修正 Secret 命名空间或 storageclass 参数如果短期内定位不到用静态供给deploy/kubernetes/examples/pvc-manual.yaml挂一个已知存在的桶做隔离验证把驱动动态供给问题和S3 服务本身问题彻底分开。最后提醒一句k8s-csi-s3 的日志里藏着 80% 的答案报错前先分清供给阶段和挂载阶段再按本文的证据链逐层取日志多数疑难杂症都能在十分钟内收敛到明确的修复动作。如果问题仍未解决建议以项目 README 中的官方 Troubleshooting 章节及当前版本部署文件为准检查部署文件与集群版本是否匹配。【免费下载链接】k8s-csi-s3GeeseFS-based CSI for mounting S3 buckets as PersistentVolumes项目地址: https://gitcode.com/gh_mirrors/k8s/k8s-csi-s3创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考