K8S network namespace错误分析

一、背景

K8S集群Calico异常问题,排查下来想查看net namespace相关情况,执行ip netns ls报错: Error: Peer netns reference is invalid:

查找相关AI资料以及处置建议,得到的建议是把kubelet、containerd等服务关闭之后清空/run/netns/目录,之后重新启动kubelet、containerd服务。

但是这个是线上服务,如果没有研究清楚,这个操作心里是没底的。所以基于这个问题做了CNI容器相关的原理分析以及这个/run/netns/目录的相关目录情况,掌握真实情况后才能做出处置决定。

二、原理分析

1、原因

/run/netns/目录是ip netns命令默认创建netns 挂载文件访问内核netns对象的入口, k8s的CNI插件创建的netns 文件挂载入口也是此目录下的文件.

可能原因是Calico CNI插件某种问题/故障,导致本来应该删除的Pod 随之清理对应netns,后面没正确清理,导致残留.并且这个残留文件不符合/run/netns目录下文件的格式等,导致读取错误。

2、测试

手动在这个K8S测试集群的主机的/var/run/netns目录touch 1一个文件,这个是普通文件什么内容都没有。 执行ip netns ls之后,报错信息与线上内容一致。

此目录下/var/run/netns=>/run/netns/创建一个文件,再次执行ip netns ls就会报错. ip netns 默认查找的文件目录就是 /run/netns/*

其实这个内核netns对象文件系统挂载点文件, 可以放在任意目录。只是/run/netns/是约定俗成的目录而已

3、CNI原理分析

Containerd服务主要是为了创建容器,不负责网络network namespace的的IP配置、network namespace创建、veth pair等网络虚拟设备的配置。 这个是由CNI网络插件来完成的。所以这个事情是CNI插件的问题, 锁定就是Calico.

4、Calico CNI插件的职责

1、创建net namespace

为pause容器创建独立的net namespace,并且通过mount --bind 将创建的net namespace 创建一个挂载点文件出来,放到/run/netns/cni-*,方便业务容器,直接引用这个cni-*文件共享这个net namespace

2、创建veth pair 虚拟网卡对---虚拟网线

只创建net namespace,只是给进程一个独立的网络namespace空间而已,这个空间可以配置ip地址,给了一个虚拟网卡. 但是如果需要联网, 那么就需要"网线"

veth pair 虚拟网卡对 是Linux网络里面的一种特殊"虚拟设备",类比物理世界的网线。 网线会有2个头.

CNI插件需要把1头插入宿主机的net namespace, 1插入到容器的net namespace里面。这样宿主机和容器, 就形成了使用一根"虚拟网线"起来了,才能进行通信.

ip link show type veth

宿主机端net namespace:

  1. 有1个veth的网线头, 名称就是lxc69c37331c351, 这边的网络设备编号131
  2. @130: 连接的对端是 net namespace里面的130 接口
  3. 连接哪个net namespace呢:

link-netns cni-83216309-ce91-dae1-cb13-c13e1fd63bda

可以进入这个net namespace查看网卡信息,看是不是130接口,从它的视角看下是不是连接宿主机的131接口呢?

ip netns exec cni-83216309-ce91-dae1-cb13-c13e1fd63bda ip link

ns namespace容器端:

1、130设备ID 正确,和前面分析一样. 名称是eth0 连接的宿主机net namespace的@131设备

2、link-netnsid 0 连接的net namespace id 0代表就是宿主机的net namespace

可以两台电脑直接"网线"连接, 也可以把"网线"插入到例如虚拟交换机、虚拟网桥等设备上,完成网络通信. 这个代表的就是,这个veth pair一端放在了宿主机 net namespace,并且桥接在 这个br-b6xxx的虚拟设备网桥上

3、docker桥接网络分析

存在2个容器,这俩容器都接入到了br-xx docker维护的网桥:

1、因为使用docker-compose部署的2个容器,那么这两个容器,默认会创建一个网桥出来,然后这2个容器连接到这个网桥,能够进行二层通讯

如果没有使用docker-compose部署,那么默认容器的网络模式是桥接模式,则桥接到docker0网桥上,验证如下:

ip link show type bridge

新建2个容器: docker run -d -it busybox /bin/sh

什么参数不指定,默认是bridge桥接网络。 默认桥接到docker0这个网桥,如果是docker-compose一般会创建对应网桥出来.

新增的2个docker容器,都桥接到了docker0网桥: 验证逻辑没问题

ip addr show docker0:

5、网络namespace

1、基本概念和特性

1、Linux 网络命名空间(netns)本身是一种内核对象,没有直接的文件系统路径。

2、用户态访问它的唯一途径是通过 /proc/<pid>/ns/net 文件------这是内核为每个进程暴露的命名空间符号链接,指向该进程所属的网络命名空间。

3、一旦所有引用该命名空间的进程都退出,且没有其他引用(如挂载点)存在,内核就会销毁该命名空间。

内核针对ns的引用计数器,如果引用计数器为0,那么这个ns则会被内核自动清理回收.

2、netns-挂载文件系统

为了让网络命名空间在没有任何进程存在时也能持久化(便于后续配置和管理),可以将某个进程的 /proc/<pid>/ns/net 绑定挂载(bind mount)到文件系统上的一个路径。这样:

  • 内核会因为该挂载点的存在而继续保留这个命名空间。进程关了,引用计数器还在,不等于0,不会被内核回收清理,还能继续用

  • 用户可以通过这个挂载点路径重新进入命名空间(例如使用 nsenter --net=<mountpoint>)。

  • 一般约定俗成的挂载路径: /run/netns IP命令、CNI等都统一规范使用这个路径,将访问内核ns的文件挂载点放在这个目录下面, nsfs (namespace file system) 这个类型文件也可以不放在/run/netns ,没有强制规定.

  • 如果希望用 ip netns 命令管理放在其他目录的命名空间,可以使用环境变量 IP_NETNS_DIR 指定目录,例如:

bash 复制代码
export IP_NETNS_DIR=/tmp/netns

ip netns list

不一定在/run/netns,以下实验可以证明:

1、首先创建一个netns ,这个进程sleep。 查看/tmp/目录不存在mynetns文件
bash 复制代码
unshare -n sleep 1000 &

PID=$!

echo "进程 PID: $PID"
bash 复制代码
nsenter -t $PID -n ip addr show
2、findmnt 以及 lsns -n net查看情况
bash 复制代码
 mount --bind /proc/$PID/ns/net /tmp/mynetns
3、赶紧为这个ns内核对象创建file system 一个文件作为内核访问netns的挂载点文件,放到/tmp/mynetns

和之前nsenter 一样的结果:

bash 复制代码
nsenter --net=/tmp/mynetns ip addr show
4、kill 杀死进程,再次通过nsenter 进入netns 发现还能正常进说明,此时netns还有1个引用计数器(文件在引用这个netns对象),没被内核回收♻️能正常访问
bash 复制代码
kill $PID

wait $PID 2>/dev/null

nsenter --net=/tmp/mynetns ip addr show

findmnt -t nsfs #查看ns的文件挂载情况

lsns -t net #查看ns当前有哪些进程使用

5、如果删除或者umount 那么netns引用计数器为0,则会被内核回收netns。 先umount卸载,之后再删除文件即可

再删除文件,此时已恢复正常:

3、mount --bind 与硬链接区别

bash 复制代码
mount --bind /proc/$PID/ns/net /tmp/mynetns:

--bind 临时性,挂载关系在系统重启后消失(除非通过配置持久化)

本质不同 :

  • 硬链接是文件系统内部机制,持久、同文件系统、仅文件。
  • bind mount 是内核 VFS 挂载机制,临时、可跨文件系统、支持目录。

4、pod找container组、查看container信息容器属性等

bash 复制代码
1、crictl ps  #查找pod

2、crictl inspectp  $pod_id  #得到这个pod的pause容器的信息, 这里有pause容器的network namespace等相关信息

3、通过pod名称查看对应的容器组:

ctr -n k8s.io containers list \

  "labels.io.kubernetes.pod.name==nginx-alpine-5fc8587994-fmhdv" \

  "labels.io.kubernetes.pod.namespace==default"

ctr -n k8s.io containers info $container_id  #查看实际容器信息

5、原生操作netns: lsns -t net、ip netns attach、findmnt -t nsfs lsns、ip、** **nsenter** **、findmnt** **

bash 复制代码
lsns -t net  #查看内核的ns namespace的列表, 如果文件系统有挂载则列出文件路径,否则可以没有, -t 就是type 类型

ip netns add mynetns #添加ns,且添加文件系统. 新增ns 并且加一个文件mount挂载入口


ip netns ls     #查询ns列表, /run/netns/cni-*


unshare --net bash  #新增ns 不新增文件系统.


ip netns attach mynetns 12345  #设置某个进程的ns/net到mynetns

#手动等价于 ip netns attach mynetns 12345

touch /var/run/netns/mynetns

mount --bind /proc/12345/ns/net /var/run/netns/mynetns


findmnt -t nsfs  #查看挂载类型是nsfs  [namespace file system] 根据类型查看挂载点


ip netns list-id #还可以查看ns的id

6、操作netns的命令: ip netns exec / nsenter -t

bash 复制代码
​
ip netns exec  <netns名称>  ping  <目标IP或域名> #只能通过ns名称

nsenter -t <进程PID> -n ping <目标IP>   # -t是目标pid -n (net) 后面执行命令

nsenter --net=/run/netns/cni-* ping www.baidu.com  # namespace enter 进入.   --net 指定文件挂载入口到netns内核对象

link  #设备  ip link show type veth

address #地址 ip a show

route #路由

netns #网络命令空间

neigh #arp

​

三、解决方案

一个很简单的判断方式,就是查看/run/netns/目录下面文件的权限,如果是正确的netns的内核挂载点文件,那么权限都是只读。如果已经恢复为普通文件那么肯定不全是只读。

这里可以看到rw这种权限的文件,和报错的个数是一致的。并且如果非普通文件你去mv、cp都会报错。普通文件的话就可以正常mv、cp以及rm删除。把这几个残留文件删除后,执行ip netns ls就恢复正常了。

最后无需重启服务,直接解决问题并且不会影响现有服务的正常运行。

相关推荐
郝开1 小时前
Docker Compose 模块化多环境配置规范指南:示例搭建Kibana 9.4.2
docker·容器·kibana
运维老郭1 小时前
Prometheus 监控 K8s 从入门到入坑:工作原理 + 部署实操 + 避坑指南
云原生·容器·kubernetes
虎王物联2 小时前
Docker BuildKit多阶段构建:IoT固件交叉编译流水线实战
运维·物联网·ci/cd·docker·容器·物联网嵌入式
LlmCraft|大模型工程实践2 小时前
14. CI/CD 流水线中集成 Docker:GitHub Actions 自动构建部署
ci/cd·docker·github
10mAh3 小时前
【Docker】磁盘空间被占满怎么清理?——overlay2、容器日志与 Build Cache 排查实战
docker·容器·eureka
xhaxy3 小时前
docker,k8s安装,k8s集群搭建(OS7)
docker·容器·kubernetes
guo_wen_qiang5 小时前
mac中docker desktop服务端开启远程访问
运维·服务器·macos·docker
Sayai6 小时前
Elasticsearch 9 安装验证实战:start-local 一键部署 + Docker 手动安装踩坑指南
大数据·elasticsearch·docker
名字还没想好☜12 小时前
Docker 容器安全加固实战:非 root、只读根文件系统、drop capabilities 与最小攻击面
运维·安全·docker·容器·kubernetes