实操技巧

进入容器网络ns

kubectl get pods xxx -o wide获取node名
登录node
获取容器进程id:
docker:
docker ps| grep $pod
docker inspect -f {{.State.Pid}} 容器id
containerd:
crictl ps | grep podname
crictl inspect 容器id | jq '.info.pid'
进入容器的网络命名空间
nsenter --target pid -n

筛选抓取

netstat -ant查看当前开放的端口
https://www.baeldung.com/linux/tcpdump-capture-ssl-handshake

偶发性问题抓包:循环抓包
指定抓包文件数,按单个文件的大小或者抓取的时长进行切割,超过指定生成的文件数之后循环覆盖旧文件
示例:
-W 个数:生成的循环文件数量,生成到最大数量后,新的报文数据会覆盖写入第一个文件
-C 尺寸:每个文件的大小,单位是 MB
-G 间隔时间:每次新生成文件的间隔时间,单位是分钟
每 100MB 或者 60 分钟就生成一个文件,一共 10 个文件
tcpdump -i eth0 -w file.pcap -W 10 -C 100 -G 60

wireshark筛选:
ip筛选 ip.addr ip.src ip.dst eq xxx
日期筛选 frame.time > "feb 01, 2024" and frame.time < "mar 01, 2024 00:00:00"
标志位筛选 tcp.flags.rst eq 1
长度筛选 tcp.len eq xx
报文模糊筛选 tcp.payload contains xxx

k8s网络

登录到节点,lsns -t net可以查看到当前节点上的网络命名空间,其中有一些看到是pause,这些是容器的sandbox,k8s使用sandbox容器来创建和维持命名空间,他也是进程空间中的pid 1

lsns -t net
        NS TYPE NPROCS     PID USER     NETNSID NSFS                                                COMMAND
4026531992 net     131       1 root  unassigned                                                     /sbin/init
4026532308 net       2    2451 65535          0 /run/netns/cni-d612c07a-e8bf-969a-a2c5-2392a222533e /pause
4026532382 net       4    2622 65535          1 /run/netns/cni-c122169f-f7f0-03b3-1aa0-7ba3a07a3317 /pause
4026532447 net       2    2722 65535          2 /run/netns/cni-723e1b9c-0355-15e1-a4a0-f7926de9c3b2 /pause
4026532515 net       1 2797456 uuidd unassigned                                                     /usr/sbin/uuidd -
4026532575 net       5 2080352 65535          3 /run/netns/cni-6c23adba-e86b-87c1-bd3a-b7be2b18fda9 /pause

获取到pid后也可以直接使用ip来获取和进入对应的命名空间

ip netns identify 2080411
cni-6c23adba-e86b-87c1-bd3a-b7be2b18fda9
ip netns exec cni-6c23adba-e86b-87c1-bd3a-b7be2b18fda9 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default
    link/ether b6:a4:e2:bc:5a:10 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.244.0.9/24 brd 10.244.0.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::b4a4:e2ff:febc:5a10/64 scope link
       valid_lft forever preferred_lft forever

同理可以查看其他命名空间,如下例

crictl ps | grep pod-1
8453bb79c5a3f       92b11f67642b6       3 hours ago         Running             nginx                     0                   576ce2a9d00c7       pod-1
crictl inspect 8453bb79c5a3f  | jq '.info.pid'
2080411
lsns -t pid | grep 2080411
4026532639 pid       4 2080411 root  nginx: master process nginx -g daemon off;

从上面的信息我们可以看到,容器的虚拟网卡为eth0,被链接在9号接口上,ip为10.244.0.9
我们来看下这个虚拟网卡连在哪

ip link | grep -A1 ^9
9: vethed920513@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP mode DEFAULT group default
    link/ether be:99:98:76:2a:42 brd ff:ff:ff:ff:ff:ff link-netns cni-6c23adba-e86b-87c1-bd3a-b7be2b18fda9

可以看到,这块网卡被连在了网桥cni0上面
网桥工作在数据链路层,可以连接多个网段,当有请求进来时,网桥会去所有接口询问是否有认识原始ip的,如果有,发送到那个veth,然后veth发给eth0(容器默认虚拟网卡一般都是eth0)
网卡和veth绑定关系还可以使用brctl来查看(需要安装

brctl show
bridge name     bridge id               STP enabled     interfaces
cni0            8000.e6f9e02baef7       no              veth8d55a711
                                                        veth92b8f630
                                                        vethcbd8b3fe
                                                        vethed920513

关于网络容器接口CNI可以看这篇介绍文章
https://atbug.com/deep-dive-cni-spec/

同node通信:
pod从自己的eth0发出请求,到达veth,veth通过网桥cni0和其他veth相连,cni0问所有接口认不认识ip,如果有认识的,记录映射关系并转发到该veth,veth再把信息发给eth0

跨node通信:
跨node通信的方式取决于使用的网络插件

额外篇:关于创建VPC和集群时需要考虑的网络问题
https://help.aliyun.com/zh/ack/ack-managed-and-ack-dedicated/user-guide/plan-cidr-blocks-for-an-ack-cluster-2#2e587ee4f46oq

flannel

flannel在安装的时候就会指定CIDR以及和后端类型,可以查看某个node具体所属的网段

kubectl get no nodename -o jsonpath={.spec} | jq
{
  "podCIDR": "10.244.0.0/24",
  "podCIDRs": [
    "10.244.0.0/24"
  ]
}

flannel接收cni-conf.json,输出结果并交给bridge

cat /var/lib/cni/flannel/2fc679ceaa17e33115657688a9b859e89a66c8911bcd9134fad2327830519396 | jq
{
  "cniVersion": "0.3.1",
  "hairpinMode": true,
  "ipMasq": false,
  "ipam": {
    "ranges": [
      [
        {
          "subnet": "10.244.0.0/24"
        }
      ]
    ],
    "routes": [
      {
        "dst": "10.244.0.0/16"
      }
    ],
    "type": "host-local"
  },
  "isDefaultGateway": true,
  "isGateway": true,
  "mtu": 1450,
  "name": "cbr0",
  "type": "bridge"
}

bridge 使用上面的输出连同参数一起作为输入,根据配置完成如下操作:

  1. 创建网桥 cni0(节点的根网络命名空间)
  2. 创建容器网络接口 eth0( pod 网络命名空间)
  3. 创建主机上的虚拟网络接口 vethX(节点的根网络命名空间)
  4. 将 vethX 连接到网桥 cni0
  5. 委托 ipam 插件分配 IP 地址、DNS、路由
  6. 将 IP 地址绑定到 pod 网络命名空间的接口 eth0 上
  7. 检查网桥状态
  8. 设置路由
  9. 设置 DNS

    cat /var/lib/cni/results/cbr0-2fc679ceaa17e33115657688a9b859e89a66c8911bcd9134fad2327830519396-eth0 | jq
    {
      "kind": "cniCacheV1",
      "containerId": "2fc679ceaa17e33115657688a9b859e89a66c8911bcd9134fad2327830519396",
      "config": "ewogICJuYW1lIjogImNicjAiLAogICJjbmlWZXJzaW9uIjogIjAuMy4xIiwKICAicGx1Z2lucyI6IFsKICAgIHsKICAgICAgInR5cGUiOiAiZmxhbm5lbCIsCiAgICAgICJkZWxlZ2F0ZSI6IHsKICAgICAgICAiaGFpcnBpbk1vZGUiOiB0cnVlLAogICAgICAgICJpc0RlZmF1bHRHYXRld2F5IjogdHJ1ZQogICAgICB9CiAgICB9LAogICAgewogICAgICAidHlwZSI6ICJwb3J0bWFwIiwKICAgICAgImNhcGFiaWxpdGllcyI6IHsKICAgICAgICAicG9ydE1hcHBpbmdzIjogdHJ1ZQogICAgICB9CiAgICB9CiAgXQp9Cg==",
      "ifName": "eth0",
      "networkName": "cbr0",
      "cniArgs": [
     [
       "K8S_POD_INFRA_CONTAINER_ID",
       "2fc679ceaa17e33115657688a9b859e89a66c8911bcd9134fad2327830519396"
     ],
     [
       "K8S_POD_UID",
       "2ab4ed63-1aff-40fc-9e40-ab95e0709cc1"
     ],
     [
       "IgnoreUnknown",
       "1"
     ],
     [
       "K8S_POD_NAMESPACE",
       "default"
     ],
     [
       "K8S_POD_NAME",
       "my-nginx-7c79c4bf97-s4jjh"
     ]
      ],
      "capabilityArgs": {
     "dns": {
       "Servers": [
         "10.96.0.10"
       ],
       "Searches": [
         "default.svc.cluster.local",
         "svc.cluster.local",
         "cluster.local"
       ],
       "Options": [
         "ndots:5"
       ]
     },
     "io.kubernetes.cri.pod-annotations": {
       "kubernetes.io/config.seen": "2024-03-21T23:40:40.674628231Z",
       "kubernetes.io/config.source": "api"
     }
      },
      "result": {
     "cniVersion": "0.3.1",
     "dns": {},
     "interfaces": [
       {
         "mac": "e6:f9:e0:2b:ae:f7",
         "name": "cni0"
       },
       {
         "mac": "82:c5:fd:ce:10:42",
         "name": "veth8d55a711"
       },
       {
         "mac": "52:d4:10:d4:6b:2c",
         "name": "eth0",
         "sandbox": "/var/run/netns/cni-c122169f-f7f0-03b3-1aa0-7ba3a07a3317"
       }
     ],
     "ips": [
       {
         "address": "10.244.0.7/24",
         "gateway": "10.244.0.1",
         "interface": 2,
         "version": "4"
       }
     ],
     "routes": [
       {
         "dst": "10.244.0.0/16"
       },
       {
         "dst": "0.0.0.0/0",
         "gw": "10.244.0.1"
       }
     ]
      }
    }

flannel会在每台node上面运行一个flannel.1虚拟网桥。不同node之间进行通信的时候,当cni发现无接口响应,便发送给flannel.1,因为集群给不同Node划分了不同的网段(CIDR),所以可以区分出ip属于哪个node,于是用Udp封装数据包,然后发送给对应的node,对应node上的flannel.1监听8472端口,获取到数据包后解包发给cni
路线为
eth0(容器)-->cni-->flannel.1-->eth(宿主机)-->eth(目标宿主机)-->flannel.1-->cni-->eth0(目标容器)
在修改网络配置的时候,也需要注意CIDR和掩码,以防因为集群扩张而无网段可分配
如:--cluster-cidr=10.244.0.0/12和--node-cidr-mask-size=16,最大节点数为16

标签: none

添加新评论