侧边栏壁纸
  • 累计撰写 12 篇文章
  • 累计创建 10 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

使用nsenter或ip netns不登录pod调试网络

爱动脑的脑花
2024-09-02 / 0 评论 / 0 点赞 / 25 阅读 / 15466 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2024-09-05,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

1.背景

近期在部署k8s集群时,由于业务pod访问中间件网络需要进行调试,但是其pod中又没有安装网络调试工具如:telnet、netstat、traceroute等,极为不便调试,故进行研究。

发现可以使用进入【网络空间】的方式对pod进行调试,并且可以使用宿主机上(在此为node)的网络调试工具进行网络调试。

1.1 一般做法

之前的调试大多数是在同namespace下拉起了一个调试pod。

此处放一下我的dockerfile以做记录备份。

docker push 492969105/wljbox:latest

FROM alpine

# 安装所需的软件包,包括util-linux(包含nsenter)
RUN apk update && \
    apk add vim netcat-openbsd iputils traceroute bind-tools curl wget bash busybox-extras util-linux && \
    echo "alias ll='ls -l'" >> /etc/profile

# 设置环境变量
ENV TERM xterm

# 容器启动时默认进入 /bin/bash
CMD ["/bin/bash"]

2.法一:使用ip netns进行网络调试

环境前提:

1.环境使用的是container作为底层。

2.node可以直接通过跳板机或者其他方式登录(就算不能直接登录也可以发起一个nsenter去登录,见备注)。

3.控制面托管在云服务商。

其常用的命令为crictl和ctr(一般crictl就够用,ctr更加底层)

本次演示的云环境为火山云

网络命名空间一般存储位置:/var/run/nets

CNI插件一般配置目录:/var/lib/cni

😎该方法的使用需要依赖具体云厂商的cni套件进行变更。

但整体流程大同小异,基本上都是通过pod找到containerid再通过containerid拿到netns(网络命名空间)

首先,我们选取一个测试目标。以我自己的pod为例:

可以看到pod为wljbox-d44bd5895-fcj4s在namespacespark-system

接下来我们看它最终落在哪个node上。

此处备注一个知识点,每一个容器都会依托于对应的宿主机进行运行,其在宿主机上表现为一个具体的pid,只要我们能找到这个pid就能顺势进入其网络命名空间。

通过命令:

可以看到pod实际运行在nodeA上

接下来我们登录nodeA,进入CNI插件的一般配置路径,并搜索容器名:

cd /var/lib/cni
grep -rnio 'wljbox'

可以看到搜索到了三个文件中含有我的容器信息。

在这三个文件中随便打开一个就能看到容器所属的网络空间了。

cat 任一文件路径|grep netns

我们验证一下当前宿主机上是否存在这个网络空间

ls -l /var/run/netns

存在。

接下来我们使用网络空间来调试该container的网络。

ip netns exec 网络空间 ip -4 addr show eth0

方便演示以获取自身ip举例(但其实例如telnet、ping、traceroute、dig等只要宿主机上安装过的工具都可以使用)。

😎切记:你只是可以通过命令调试容器网络,但像cat、ls此类非网络命令显示的依旧为宿主机内容,不要搞错了

验证成功。

3.法二:使用nsenter通过pid进行网络调试

环境前提:

1.环境使用的是container作为底层。

2.node可以直接通过跳板机或者其他方式登录(就算不能直接登录也可以发起一个nsenter去登录,见备注)。

3.控制面托管在云服务商。

其常用的命令为crictl和ctr(一般crictl就够用,ctr更加底层)

本次演示的云环境为火山云

容器最终会跑在某一node上,自然能在node上拿到它对应的pid。

进而通过nsenter -t pid进入pid进行调试。

还是老样子。

我们登录nodeA,并找到pod对应的container的id:

kubectl get pod wljbox-d44bd5895-fcj4s -n spark-system -o jsonpath='{.status.containerStatuses[0].containerID}'

containerd://a99cbb71d0b218b5a9dab3d1d4f3ebe811f8739816ba3462ae0881f3e9538a91

接下来通过container id来拿到它在宿主机的pid

crictl inspect a99cbb71d0b21|jq '.info.pid'

接下来通过pid去调试【因为本处只演示进入进程空间所以没带-n参数,但如果使用ss、telnet这类网络命令需要带-n参数】

nsenter -t 148115 -n ip -4 addr show eth0

结果与podip一致,验证成功。

4.总结

关键命令及目录。

cni配置目录:/etc/cni

node上正在运行的cni:/var/lib/cni/

node上正在运行的网络空间:/var/run/netns

查找容器所对应的网络空间:grep -rnio 'pod名'

使用ip netns调试:ip netns exec 网络空间 ip -4 addr show eth0

使用nsenter调试pod:nsenter -t pod在node上的pid ip -4 addr show eth0

5.nsenter的一些操作

nsenter 是一个强大的 Linux 命令行工具,用于进入(enter)指定进程的命名空间(namespace)。命名空间是 Linux 内核的一个特性,用于隔离系统资源,使得一组进程看到的系统资源与另一组进程不同。这是容器技术的基础之一。

主要用途:

  1. 进入容器或进程的命名空间,而不需要在容器内部运行 shell。

  2. 调试容器或隔离环境中的网络、挂载点等问题。

  3. 在特定的命名空间中执行命令。

基本语法:

复制

nsenter [options] [program [arguments]]

常用选项:

  • --target PID-t PID:指定要进入其命名空间的目标进程的 PID。

  • --mount[=file]-m:进入 mount 命名空间。

  • --uts[=file]-u:进入 UTS (Unix Time-sharing System) 命名空间。

  • --ipc[=file]-i:进入 IPC (Inter-Process Communication) 命名空间。

  • --net[=file]-n:进入网络命名空间。

  • --pid[=file]-p:进入 PID 命名空间。

  • --user[=file]-U:进入用户命名空间。

实际应用示例:

  1. 进入容器的网络命名空间并执行网络相关命令:

    nsenter -t $(crictl inspect -f '{{.State.Pid}}' container_name) -n ip addr
    
  2. 在容器的挂载命名空间中执行命令:

    nsenter -t $(crictl inspect -f '{{.State.Pid}}' container_name) -m ls /
    
  3. 进入容器的所有命名空间并启动一个 shell:

    nsenter -t $(crictl inspect -f '{{.State.Pid}}' container_name) -a /bin/bash
    
  4. 在 Kubernetes Pod 的网络命名空间中执行命令:

    nsenter -t $(crictl inspect <container_id> | jq '.info.pid') -n ip addr
    

nsenter 的优势:

  1. 无需在容器内安装额外工具就能进行调试。

  2. 可以访问宿主机上的工具和命令。

  3. 允许在容器可能已经崩溃或无法正常工作的情况下进行故障排除。

使用 nsenter 时需要注意:

  • 通常需要 root 权限。

  • 不当使用可能会影响容器或系统的隔离性和安全性。

  • 在生产环境中使用时要格外小心。

总的来说,nsenter 提供了一种直接且强大的方式来与各种 Linux 命名空间交互。

当我们拿到容器pid以后,挂载mount,可以直接查看容器进程信息。

nsenter -t 1234 --mount ps aux

一个经典的node-shell,用于在nodeA上开启shell登录。【解决在你不能直接登录node时,但你有kubectl的权限】

apiVersion: v1
kind: Pod
metadata:
  name: node-shell-test
  namespace: kube-system
spec:
  containers:
  - args:
    - -t
    - "1"
    - -m
    - -u
    - -i
    - -n
    - sleep
    - "14000"
    command:
    - nsenter
    image: docker.io/alpine:3.9
    imagePullPolicy: IfNotPresent
    name: shell
    resources: {}
    securityContext:
      privileged: true
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
  dnsPolicy: ClusterFirst
  enableServiceLinks: true
  hostIPC: true
  hostNetwork: true
  hostPID: true
  nodeSelector:
    kubernetes.io/hostname: nodeA
  priority: 0
  restartPolicy: Never
  schedulerName: default-scheduler
  securityContext: {}
  serviceAccount: default
  serviceAccountName: default
  terminationGracePeriodSeconds: 0
  tolerations:
  - operator: Exists

0

评论区