解决自研CSI启动后无法使用且CSINode无驱动信息的问题

环境信息此处对一些信息做了模糊处理,不想因此导致一些信息泄露。A环境:跑在kata容器中的 OS;B环境:跑在虚拟机中的Ubuntu系统;X CSI:开发中的 CSI,目标是跑在 A 环境的 Kubernetes 中。问题描述当 X CSI 的服务在 A 环境中启动后,在宿主机的 /var/lib/

环境信息

此处对一些信息做了模糊处理,不想因此导致一些信息泄露。

A环境:跑在kata容器中的 OS;

B环境:跑在虚拟机中的Ubuntu系统;

X CSI:开发中的 CSI,目标是跑在 A 环境的 Kubernetes 中。

问题描述

X CSI 的服务在 A 环境中启动后,在宿主机的 /var/lib/kubelet/plugins/var/lib/kubelet/plugins_registry目录中,生成对应的 sock 文件后,CSINode 中 driver 栏没有刚启动的 X CSI。结果如下:

image-20230227111029825

问题影响

无法使用 X CSI 来挂载 PV

WorkAround

  1. kill kubelet 进程
kill `ps -ef | grep -v grep | grep "/usr/bin/kubelet" | awk '{print $2}'`
  1. kubelet 重启后,会填充 CSINode 信息,如下
image-20230227111318080

可能的原因

kubelet 命令是否存在对应的配置参数,来对控制扫描 /var/lib/kubelet/plugins_registry目录?

是否由于文件系统的原因导致?

排查过程

排查 X CSI 的配置问题

找了这张图来看 /var/lib/kubelet/plugins_registry/var/lib/kubelet/csi-plugins/xxx.sock 目录有啥命名规范,会不会是命名不规范,导致 PluginWwatcher watch 不到对应的 sock 文件,从而引起 X CSI 无法顺利注册。

修改成上图中的规范之后,问题依然存在。

对比 B 环境中kubelet参数

A 环境中

kubelet 启动参数为:

/usr/bin/kubelet 
--hostname-override=<hostname>
--bootstrap-kubeconfig=/etc/k8s/cfg/kubelet-bootstrap.kubeconfig 
--kubeconfig=/etc/k8s/cfg/kubelet.kubeconfig 
--cert-dir=/etc/k8s/kubelet 
--config=/etc/k8s/cfg/kubelet.config 
--container-runtime=remote 
--runtime-request-timeout=15m 
--container-runtime-endpoint=unix:///run/containerd/containerd.sock 
--pod-infra-container-image=k8s.gcr.io/pause:3.2 
--feature-gates=RotateKubeletServerCertificate=true 
--feature-gates=RotateKubeletClientCertificate=true 
--rotate-certificates=false 
--v=6

其中 kubelet.config 中的配置信息如下:

kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
port: 10250
readOnlyPort: 10255
cgroupDriver: cgroupfs
clusterDNS: [x.x.x.x]
clusterDomain: cluster.local
failSwapOn: false
serverTLSBootstrap: true
authentication:
  anonymous:
    enabled: true
  webhook:
    cacheTTL: 2m0s
    enabled: true
authorization:
  mode: Webhook
  webhook:
    cacheAuthorizedTTL: 5m0s
    cacheUnauthorizedTTL: 30s
evictionHard:
  memory.available: "10%"

在 B 环境中用 kubeadm 自建 Kubernetes 集群

kubelet 启动参数:

/usr/bin/kubelet 
--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf 
--kubeconfig=/etc/kubernetes/kubelet.conf 
--config=/var/lib/kubelet/config.yaml 
--container-runtime=remote 
--container-runtime-endpoint=unix:///run/containerd/containerd.sock 
--pod-infra-container-image=registry.k8s.io/pause:3.8

其中 kubelet.config 配置文件如下:

apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
  anonymous:
    enabled: false
  webhook:
    cacheTTL: 0s
    enabled: true
  x509:
    clientCAFile: /etc/kubernetes/pki/ca.crt
authorization:
  mode: Webhook
  webhook:
    cacheAuthorizedTTL: 0s
    cacheUnauthorizedTTL: 0s
cgroupDriver: systemd
clusterDNS:
- 10.96.0.10
clusterDomain: cluster.local
cpuManagerReconcilePeriod: 0s
evictionPressureTransitionPeriod: 0s
fileCheckFrequency: 0s
healthzBindAddress: 127.0.0.1
healthzPort: 10248
httpCheckFrequency: 0s
imageMinimumGCAge: 0s
kind: KubeletConfiguration
logging:
  flushFrequency: 0
  options:
    json:
      infoBufferSize: "0"
  verbosity: 0
memorySwap: {}
nodeStatusReportFrequency: 0s
nodeStatusUpdateFrequency: 0s
resolvConf: /run/systemd/resolve/resolv.conf
rotateCertificates: true
runtimeRequestTimeout: 0s
shutdownGracePeriod: 0s
shutdownGracePeriodCriticalPods: 0s
staticPodPath: /etc/kubernetes/manifests
streamingConnectionIdleTimeout: 0s
syncFrequency: 0s
volumeStatsAggPeriod: 0s
没有明显的配置差异,跳过对这方面的怀疑。

在 A 环境中部署其他CSI

将官方的 nfs-csi 部署 A 环境的 K8s 中,发现在部署完成后,无法立即使用;

极大可能是 A 的环境问题

X CSI 部署到 B 环境的 K8s 中

发现部署完成后,可立即使用(CSIDriver 显示正常,说明被 kubelet 正确加载了);

排除 X CSI 配置问题

阅读 kubelet 加载 CSI 定义的代码

经过前面的尝试与分析,初步确定是 kubelet 加载 CSI 时,出现了问题,但具体是哪里的问题,需要先了解 kubelet 是如何加载 CSI 的,从其中寻找答案。

初步了解 CSI Driver 注册流程,在关键点添加日志、修改 kubelet 的日志级别,看日志,未发现文件监听相关的日志;

修改 kubelet 源码(添加 & 修改 kubelet 中相关的日志级别)后,发现当 /var/lib/kubelet/plugins_registry目录下新增 sock 文件、删除 sock 文件后,没有相应日志打印,即监听文件系统的事件出现异常。

定位到问题为 kubelet 监听本地文件变更存在问题后,单独将 kubelet 使用的相关库提出,并单独验证。

  1. 将 kubelet 中用于监听文件变更的库单拎出来,写一个监听某文件夹下文件变更的二进制
  2. 经过测试发现,在 A 环境的 /var/lib/kubelet/plugins_registry 目录下,就是极大概率无法监听到文件变更;更换目录,一些已存在的目录可以、一些新增的目录不行,讲道理,这不是一个正常的表现。
  3. 将二进制移到 B 环境中,监听相同目录,表现正常,进一步判定为 A 环境问题。

查看挂载信息

root@<hostname>:/# df -h | grep -i katashared
kataShared      392G  314G   78G  81% /
Filesystem     1K-blocks      Used Available Use% Mounted on
kataShared     410239272 329066648  81172624  81% /

所以问题是否会跟这个 kataSahred 文件系统有关?

问题的原因

kata 容器中默认的文件系统为 kataShare,在这种文件系统下,监听某个目录下的文件变更时,会有一定的概率收不到 CREATEDELETEUPDATE 事件。因为收不到变更事件,从而无法触发 kubelet 注册 CSI Driver 的后续流程,导致部署完 CSI 后,不能立即使用。

解决方案

/var/lib/kubelet 目录挂载成其他文件系统后,安装 CSI 后,可立即使用,即可纵享丝滑。

root@<hostname>:/# df -h | grep -i /var/lib/kubelet
/dev/loop1      2.0G  6.5M  1.8G   1% /var/lib/kubelet

One More Question

  • 为什么重启 kubelet 能 workaround?

kubelet 启动时,会主动扫描 plugins_registry 目录下所有的文件,当扫描到文件后,会立即手动触发 CREATE 事件,从而完成 CSI Driver 的注册,达到 CSI 可用的目的。

Reference

Read more

容器镜像(4):镜像的常用工具箱

容器镜像(4):镜像的常用工具箱

前几篇在讲多架构镜像时已经用过 skopeo 和 crane 做镜像复制,这篇系统整理这两个工具的完整能力,同时介绍几个日常操作镜像时同样好用的工具。 一、skopeo:不依赖 Daemon 的镜像瑞士军刀 skopeo 的核心价值是绕过 Docker daemon,直接与 Registry API 交互。上一篇用它做镜像复制和离线传输,但它的能力远不止于此。 1.1 安装 # Ubuntu / Debian sudo apt install -y skopeo skopeo --version # skopeo version 1.15.1 1.2 inspect:免拉取检查镜像元数据 docker inspect 需要先把镜像拉到本地,skopeo inspect 直接向 Registry

容器镜像(3):多架构镜像构建

容器镜像(3):多架构镜像构建

一、什么是多架构镜像 1.1 OCI Image Index 上一篇介绍了单平台镜像的结构:一个 Manifest 指向 Config 和若干 Layer blob。多架构镜像在此之上多了一层——OCI Image Index(也叫 Manifest List),是一个轻量的索引文件,把多个单平台 Manifest 组织在一起: $ docker manifest inspect golang:1.22-alpine { "schemaVersion": 2, "mediaType": "application/vnd.oci.image.index.v1+json", "manifests&

容器镜像(2):containerd 视角下的镜像

容器镜像(2):containerd 视角下的镜像

一、为什么需要了解 containerd 如果你只用 docker run 跑容器,从来不关心底层,那可以不了解 containerd。但如果你在用 Kubernetes,或者想真正理解"容器运行时"是什么,containerd 是绕不开的。 事实上,当你执行 docker run 的时候,containerd 早就在后台悄悄工作了——Docker 从 1.11 版本开始,就把核心运行时剥离出来交给 containerd 负责。 1.1 Docker 的架构演变 早期的 Docker(1.10 及之前)是一个"大一统"的单体程序:一个 dockerd