Kubernetes-基于Flannel的网络原理

目录

一、容器与容器之间的通信

二、POD与POD之间的通信

1、同一个Node上的pod之间的通信

2、不同Node上的pod之间的通信

3、使用 tcpdump和wireshark进行分析

三、Pod与Service之间的通信

1、Service的类型

2、ClusterIP类型的service需要的iptables规则

3、NodePort类型的Service需要的iptables规则

4、更近一步,直接使用服务的名字访问

四、k8s集群外部到k8s集群内部的访问


一、容器与容器之间的通信

查看pod mysql-demo-e81sy中容器的网络模式:

[root@k8s-master ~]# kubectl --namespace=hyjx--hyjx-group3 get pods -o wide
NAME                READY     STATUS    RESTARTS   AGE       IP            NODE
mysql-demo-e81sy    1/1       Running   0          21h       10.20.92.11   k8s-node2
wp-frontend-0rlk4   1/1       Running   0          1d        10.20.92.3    k8s-node2
wp-mysql-t5w0m      1/1       Running   0          1d        10.20.37.5    k8s-node1

[root@k8s-node2 ~]# docker ps |grep mysql-demo-e81sy
772d26fcf3e4        192.168.138.225:5000/mysql:5.7.15                               "docker-entrypoint.sh"   21 hours ago        Up 21 hours                             k8s_mysql-demo.1a1add3a_mysql-demo-e81sy_hyjx--hyjx-group3_1ae92c77-babb-11e6-9055-5254005c2a8d_d1b1e4aa
2e429e000a9f        192.168.138.225:5000/rhel7/pod-infrastructure:latest            "/pod"                   21 hours ago        Up 21 hours                             k8s_POD.5da6097a_mysql-demo-e81sy_hyjx--hyjx-group3_1ae92c77-babb-11e6-9055-5254005c2a8d_e3c27577

注意:pod mysql-demo-e81sy中有两个容器:id分别是772d26fcf3e4,2e429e000a9f。名字以k8s_POD开头的容器是kubelet自动加上的,只提供基础的网络栈,其他的容器都是直接共用这个容器的网络栈(网络命名空间)。


[root@k8s-node2 ~]# docker inspect `docker ps |grep mysql-demo-e81sy |awk '{print $1}'` |jq '.[] | {ContainerID: .Id, ContainerName: .Name, NetworkMode: .HostConfig.NetworkMode}'

{
    "ContainerID": "772d26fcf3e4e13aedff04b9b4c3131fa177a301745dba3062141102fba19574",
    "ContainerName": "/k8s_mysql-demo.1a1add3a_mysql-demo-e81sy_hyjx--hyjx-group3_1ae92c77-babb-11e6-9055-5254005c2a8d_d1b1e4aa",
    "NetworkMode": "container:2e429e000a9fdf1bdc56aa8fc5f3a57cf4a61d250b3e8252191d06d553fef07a"
}

{
    "ContainerID": "2e429e000a9fdf1bdc56aa8fc5f3a57cf4a61d250b3e8252191d06d553fef07a",
    "ContainerName": "/k8s_POD.5da6097a_mysql-demo-e81sy_hyjx--hyjx-group3_1ae92c77-babb-11e6-9055-5254005c2a8d_e3c27577",
    "NetworkMode": "default"
}

用户的容器(k8s_mysql-demo.1a1add3a_mysql-demo-e81sy_hyjx--hyjx-group3_1ae92c77-babb-11e6-9055-5254005c2a8d_d1b1e4aa)的网络模式是container模式:可以从输出看到,网络基础设施容器(k8s_POD.5da6097a_mysql-demo-e81sy_hyjx--hyjx-group3_1ae92c77-babb-11e6-9055-5254005c2a8d_e3c27577)是default的网络模式(bridge模式);

container:2e429e000a9fdf1bdc56aa8fc5f3a57cf4a61d250b3e8252191d06d553fef07a

说明容器(k8s_mysql-demo.1a1add3a_mysql-demo-e81sy_hyjx--hyjx-group3_1ae92c77-babb-11e6-9055-5254005c2a8d_d1b1e4aa)共用网络基础设施容器的网络栈:


[root@k8s-node2 ~]# docker ps |grep mysql-demo-e81sy |awk '{print $1}' |xargs docker inspect |jq '.[] |.State.Pid' |xargs -i nsenter -t {} -p -n ip addr

1: lo:  mtu 65536 qdisc noqueue state UNKNOWN qlen 1
    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

77: eth0@if78:  mtu 1472 qdisc noqueue state UP
    link/ether 02:42:0a:14:5c:0b brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.20.92.11/24 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:aff:fe14:5c0b/64 scope link tentative dadfailed
       valid_lft forever preferred_lft forever

1: lo:  mtu 65536 qdisc noqueue state UNKNOWN qlen 1
    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

77: eth0@if78:  mtu 1472 qdisc noqueue state UP
    link/ether 02:42:0a:14:5c:0b brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.20.92.11/24 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:aff:fe14:5c0b/64 scope link tentative dadfailed
       valid_lft forever preferred_lft forever

[root@k8s-node2 ~]# docker ps |grep mysql-demo-e81sy |awk '{print $1}' |xargs docker inspect |jq '.[] |.State.Pid' |xargs -i nsenter -t {} -p -n ip route
default via 10.20.92.1 dev eth0
10.20.92.0/24 dev eth0  proto kernel  scope link  src 10.20.92.11
default via 10.20.92.1 dev eth0
10.20.92.0/24 dev eth0  proto kernel  scope link  src 10.20.92.11


[root@k8s-node2 ~]# docker ps |grep mysql-demo-e81sy |awk '{print $1}' |xargs docker inspect |jq '.[] |.State.Pid' |xargs -i nsenter -t {} -p -n netstat -tunlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp6       0      0 :::3306                 :::*                    LISTEN      12587/mysqld
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp6       0      0 :::3306                 :::*                    LISTEN      12587/mysqld

既然都是共用同一个网络命名空间,直接使用localhost或127.0.0.1加上端口访问就好了。可以看到这两个容器的ip地址,路由表,以及暴露的端口号等都是一样的,因为他们使用的是同一个网络命名空间,同一个网络栈


[root@k8s-node2 ~]# docker ps |grep mysql-demo-e81sy |awk '{print $1}' |xargs -i docker exec {} ps -ef

UID        PID  PPID  C STIME TTY          TIME CMD
mysql        1     0  0 Dec05 ?        00:05:28 mysqld
root       299     0  0 Dec09 ?        00:00:00 bash
root       334     0  0 05:27 ?        00:00:00 ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 Dec05 ?        00:00:02 /pod
root        59     0 11 00:27 ?        00:00:00 ps -ef

【注意】:他们只是共用网络命名空间(network namespace),但是进程命名空间(PID namespace)、用户命名空间(user namespace)等其他的命名空间是相互隔离的

二、POD与POD之间的通信

1、同一个Node上的pod之间的通信

每个pod都有唯一一个ip(属于docker0网桥上的网段),每个pod的网络基础设施容器都是通过veth设备对直接连在docker0网桥上的:

Kubernetes-基于Flannel的网络原理_第1张图片

研究这两个pod:

[root@k8s-master ~]# kubectl --namespace=hyjx--hyjx-group3 get pods -o wide
NAME                READY     STATUS    RESTARTS   AGE       IP            NODE
mysql-demo-e81sy    1/1       Running   0          23h       10.20.92.11   k8s-node2
wp-frontend-0rlk4   1/1       Running   0          1d        10.20.92.3    k8s-node2

分别查看pod内的路由表:

[root@k8s-node2 ~]# docker exec -it `docker ps |grep -v POD |grep mysql-demo-e81sy |awk '{print $1}'` ip route list
default via 10.20.92.1 dev eth0
10.20.92.0/24 dev eth0  proto kernel  scope link  src 10.20.92.11

[root@k8s-node2 ~]# docker exec -it `docker ps |grep -v POD |grep wp-frontend-0rlk4 |awk '{print $1}'` ip route list
default via 10.20.92.1 dev eth0
10.20.92.0/24 dev eth0  proto kernel  scope link  src 10.20.92.3

两个pod中匹配的路由表是:

10.20.92.0/24 dev eth0  proto kernel  scope link  src 10.20.92.3

而目的地址为 10.20.92.0/24(本机docker0网桥的地址)的数据包都会通过容器内的eth0(veth设备)直接转给Kernel,路由的范围是link(与本设备直接连接),因为veth设备都是成对存在的,另一端连在docker0网桥上,所以会由Kernel直接转发给docker0网桥上的另一个veth设备。

宿主机上的路由表:

[root@k8s-node2 ~]# ip route list
default via 192.168.128.1 dev eth0  proto static  metric 100
10.20.0.0/16 dev flannel0  proto kernel  scope link  src 10.20.92.0
10.20.92.0/24 dev docker0  proto kernel  scope link  src 10.20.92.1
192.168.122.0/24 dev virbr0  proto kernel  scope link  src 192.168.122.1
192.168.128.0/20 dev eth0  proto kernel  scope link  src 192.168.138.222  metric 100

[root@k8s-node2 ~]# netstat -rn
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
0.0.0.0         192.168.128.1   0.0.0.0         UG        0 0          0 eth0
10.20.0.0       0.0.0.0         255.255.0.0     U         0 0          0 flannel0
10.20.92.0      0.0.0.0         255.255.255.0   U         0 0          0 docker0
192.168.122.0   0.0.0.0         255.255.255.0   U         0 0          0 virbr0
192.168.128.0   0.0.0.0         255.255.240.0   U         0 0          0 eth0

同样的,宿主机上匹配的路由表是:

10.20.92.0/24 dev docker0  proto kernel  scope link  src 10.20.92.1

宿主机将数据包通过docker0发给Kernel,最终docker0网桥会将数据包转发给目的pod。

2、不同Node上的pod之间的通信

要支持不同Node节点上的pod通信需要满足的要求:

1)、整个kubernetes集群中对每个POD分配一个唯一的IP:在部署Kubernetes时,对每个Node节点的docker0网桥的网段重新划分,用户设定一个大的网段(eg:10.20.0.0/16),存在etcd中,每个节点的flanneld会去etcd中查找这个值,然后,flanneld随机生成一个属于大网段的,且不冲突的子网(eg: 10.20.37.0/24; 10.20.92.0/24; 10.20.0.53/24)并将该值写回etcd中,这样就保证了每个pod的IP都会在10.20.0.0/16这个网段内;

2)、Node节点之间需要架设一个overlay网络(一般通过flannel实现),保证pod可以互相访问。

以下面两个pod为例:


[root@k8s-master ~]# kubectl --namespace=hyjx--hyjx-group3 get pods -o wide
NAME                READY     STATUS    RESTARTS   AGE       IP            NODE
wp-mysql-t5w0m      1/1       Running   0          2d        10.20.37.5    k8s-node1
mysql-demo-e81sy    1/1       Running   0          1d        10.20.92.11   k8s-node2

如果,k8s-node1(192.168.138.221/20)上的pod(src=10.20.37.5/24)要发送目的地址为dest=10.20.92.11/24的数据包。

根据node1的路由表:

[root@k8s-node1 ~]# netstat -rn
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
0.0.0.0         192.168.128.1   0.0.0.0         UG        0 0          0 eth0
10.20.0.0       0.0.0.0         255.255.0.0     U         0 0          0 flannel0
10.20.37.0      0.0.0.0         255.255.255.0   U         0 0          0 docker0
192.168.122.0   0.0.0.0         255.255.255.0   U         0 0          0 virbr0
192.168.128.0   0.0.0.0         255.255.240.0   U         0 0          0 eth0

dest=10.20.92.11/24匹配第二条路由表,目的地址为10.20.0.0/16的数据包要交给flannel0网桥处理,flanneld会到etcd中查找10.20.92.11/24所在的node节点的IP(192.168.139.222/20):


[root@k8s-master ~]# etcdctl get /awcloud.com/network/config
{"NetWork":"10.20.0.0/16"}
[root@k8s-master ~]# etcdctl ls /awcloud.com/network/subnets
/awcloud.com/network/subnets/10.20.92.0-24
/awcloud.com/network/subnets/10.20.100.0-24
/awcloud.com/network/subnets/10.20.53.0-24
/awcloud.com/network/subnets/10.20.37.0-24

[root@k8s-master ~]# etcdctl get /awcloud.com/network/subnets/10.20.92.0-24
{"PublicIP":"192.168.138.222"}

在目的Node节点,flanneld收到数据包后,去除flanneld加上的头部,将原始的数据包发送到宿主机的网络栈里面,这时的数据包src=10.20.37.5/24,dest=10.20.92.11/24;根据本机路由表:
然后flanneld在原来的数据包的基础上,重新封装成UDP数据包,加上UDP的头部(src=192.168.138.221/20:8285dest=192.168.138.222/20:8285,flannel 默认使用 8285 端口作为 UDP 封装报文的端口,VxLan 的话使用 8472 端口)直接发送给192.168.138.222:8285(8285是flanneld监听的端口)。

[root@k8s-node2 ~]# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.128.1   0.0.0.0         UG    100    0        0 eth0
10.20.0.0       0.0.0.0         255.255.0.0     U     0      0        0 flannel0
10.20.92.0      0.0.0.0         255.255.255.0   U     0      0        0 docker0
192.168.122.0   0.0.0.0         255.255.255.0   U     0      0        0 virbr0
192.168.128.0   0.0.0.0         255.255.240.0   U     100    0        0 eth0

dest=10.20.92.11/24优先匹配第三条路由规则,将数据包转发给docker0网桥上,docker0网桥再将数据包转给IP为10.20.92.11/24的POD中,响应数据包反之。

3、使用 tcpdump和wireshark进行分析

1)、在192.168.138.221上进入pod wp-mysql-t5w0m中(IP为10.20.37.5/24)登录10.20.92.11中的mysql-server(在192.168.138.222):

[root@k8s-node1 ~]# docker exec -it `docker ps |grep -v POD |grep wp-mysql-t5w0m |awk '{print $1}'` bash
root@wp-mysql-t5w0m:/# ip addr
1: lo:  mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    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
45: eth0@if46:  mtu 1472 qdisc noqueue state UP group default
    link/ether 02:42:0a:14:25:05 brd ff:ff:ff:ff:ff:ff
    inet 10.20.37.5/24 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:aff:fe14:2505/64 scope link tentative dadfailed
       valid_lft forever preferred_lft forever

root@wp-mysql-t5w0m:/# mysql -h 10.20.92.11 -P 3306 -uroot -pr00tme
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 20
Server version: 5.7.15 MySQL Community Server (GPL)
Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>


2)在192.168.138.222上使用tcpdump抓取eth0进入的数据包(被flannel封装成UDP数据包):


[root@k8s-node2 ~]# tcpdump -i eth0 -nn -X src host 192.168.138.221 and port 8285 -w eth0.pcap
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
^C6 packets captured
6 packets received by filter
0 packets dropped by kernel

使用wireshark分析:

3)在192.168.138.222上使用tcpdump抓取由flanneld转发到flannel0网桥上的数据包:

[root@k8s-node2 ~]# tcpdump -i flannel0 -X -nn dst host 10.20.92.11 and port 3306 -w flannel0.pcap
tcpdump: listening on flannel0, link-type RAW (Raw IP), capture size 65535 bytes
^C6 packets captured
6 packets received by filter
0 packets dropped by kernel

使用wireshark分析:

 

4)在192.168.138.222上使用tcpdump抓取最终进入docker0原始数据包


[root@k8s-node2 ~]# tcpdump -i docker0 -X -nn dst host 10.20.92.11 and port 3306 -w docker0.pcap
tcpdump: listening on docker0, link-type EN10MB (Ethernet), capture size 65535 bytes
^C6 packets captured
6 packets received by filter
0 packets dropped by kernel

使用wireshark分析:

验证目的MAC地址02:42:0a:14:5c:0b为POD的MAC地址:


[root@k8s-node2 ~]# docker inspect `docker ps |grep POD |grep mysql-demo-e81sy |awk '{print $1}'` |jq '.[] | .NetworkSettings.MacAddress'

"02:42:0a:14:5c:0b"

验证源MAC地址02:42:b6:2f:9d:03为docker0网桥地址:


[root@k8s-node2 ~]# ifconfig docker0 |grep ether |awk '{print $2}'

02:42:b6:2f:9d:03


三、Pod与Service之间的通信

为了支持水平扩展和高可用,实际使用中并不会直接使用POD的IP来访问POD中的服务,而是定义service来访问一组POD中的服务。service是对一组POD的抽象,会根据访问策略(负载均衡)来访问这组POD。

Kubernetes会在创建service时随机分配一个ClusterIP,这是一个虚IP(VIP),不能与docker0网桥的网段冲突,比如:10.10.0.0/16这个网段(在 kube-apiserver的--service-cluster-ip-range=10.10.0.0/16配置项中指定)。

访问POD中服务是只需要访问这个VIP就好了,而service会将请求经过负载均衡之后转发到后端POD中,这些工作都是运行在每个Node节点的kube-proxy进程管理的,它的核心功能是:将对某个Service的访问经过负载均衡(Round Robin算法)之后转发到某一个后端POD:

1、Service的类型

1、ClusterIP:默认类型,自动分配一个仅cluster内部可以访问的虚拟IP;
2、NodePort:在ClusterIP基础上为Service在每台机器上绑定一个端口,这样就可以通过:NodePort来访问改服务;
3、LoadBalancer:在NodePort的基础上,借助cloud provider创建一个外部的负载均衡器,并将请求转发到:NodePort

无论是ClusterIP+TargetPort的方式,还是NodeIP+NodePort的方式访问服务,都会被节点的iptables规则重定向到kube-proxy监听Service的服务代理端口,kube-proxy接受到Service请求之后,会使用Round-Robin负载均衡算法按照Service维护的Endpoint对象中选择一个endpoint(其实就是POD)。

以下面这个svc及其对应的pod为例:

[root@k8s-master ~]# kubectl --namespace=hyjx--hyjx-group3 get pods -o wide |grep mysql-demo-e81sy
mysql-demo-e81sy    1/1       Running   0          1d        10.20.92.11   k8s-node2

[root@k8s-master ~]# kubectl --namespace=hyjx--hyjx-group3 get svc -o wide -l name=mysql-demo
NAME         CLUSTER-IP     EXTERNAL-IP   PORT(S)     AGE       SELECTOR
mysql-demo   10.10.147.26           13306/TCP   1d        name=mysql-demo

[root@k8s-master ~]# kubectl --namespace=hyjx--hyjx-group3 get ep -o wide -l name=mysql-demo
NAME         ENDPOINTS          AGE
mysql-demo   10.20.92.11:3306   1d

service mysql-demo对象维护着一个endpoint对象,指向当前这个service服务的底层podIP:containerPort。endpoint对象会随着pod的改变而自动修改。

2、ClusterIP类型的service需要的iptables规则

任何数据包进入宿主机后,首先进入PREROUTING链:

-A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
通过服务访问POD时,进入KUBE-SERVICES链,-m comment --comment "kubernetes service portals" 表示匹配规则使用iptables的显示扩展的注释功能(使用额外的匹配机制)

kube-proxy进程在启动时或者监听到Service和Endpoint发生变化之后,会为每个endpoint修改本机Iptables的NAT表中的4条规则链:


[root@k8s-node1 ~]# iptables-save |grep -v mysql-demo2 |grep mysql-demo
-A KUBE-SEP-6XCC4VJACKOMSYVV -s 10.20.92.11/32 -m comment --comment "hyjx--hyjx-group3/mysql-demo:mysqld-demo" -j KUBE-MARK-MASQ
-A KUBE-SEP-6XCC4VJACKOMSYVV -p tcp -m comment --comment "hyjx--hyjx-group3/mysql-demo:mysqld-demo" -m tcp -j DNAT --to-destination 10.20.92.11:3306
-A KUBE-SERVICES -d 10.10.147.26/32 -p tcp -m comment --comment "hyjx--hyjx-group3/mysql-demo:mysqld-demo cluster IP" -m tcp --dport 13306 -j KUBE-SVC-DM5EMDBLORDRGWLL
-A KUBE-SVC-DM5EMDBLORDRGWLL -m comment --comment "hyjx--hyjx-group3/mysql-demo:mysqld-demo" -j KUBE-SEP-6XCC4VJACKOMSYVV

1)、在KUBE-SERVICES链中匹配到第三条规则,目的地址是 10.10.147.26/32 (就是service的VIP),目的端口为13306(service监听的端口),协议为TCP,则转到KUBE-SVC-DM5EMDBLORDRGWLL规则进行处理;

2)、第四条规则就是KUBE-SVC-DM5EMDBLORDRGWLL,直接转到规则KUBE-SEP-6XCC4VJACKOMSYVV

3)、第一条规则就是KUBE-SEP-6XCC4VJACKOMSYVV,如果源地址为10.20.92.11/32(就是POD自己),直接使用KUBE-MARK-MASQ规则。在dockerd的启动配置项中,默认设置"--ip-masq=true",则“Enable IP masquerading for bridge's IP range.”,启用IP伪装功能(IP Masquerade),具体介绍移步维基百科。

[root@k8s-node1 ~]# iptables-save |grep :KUBE-MARK-MASQ

:KUBE-MARK-MASQ - [0:0]

4)、第二条规则也是KUBE-SEP-6XCC4VJACKOMSYVV,直接就使用DNAT(目的地址转换),将目的地址:端口 10.10.147.26:13306 直接转换成 10.20.92.11:3306。

5)、使用tcpdump工具进行抓包分析,首先找到POD的eth0另一端的veth设备:

[root@k8s-node1 ~]# docker exec -it `docker ps |grep -v POD |grep wp-mysql-t5w0m |awk '{print $1}'` ip addr |grep eth0@
45: eth0@if46:  mtu 1472 qdisc noqueue state UP group default

[root@k8s-node1 ~]# docker exec -it `docker ps |grep -v POD |grep wp-mysql-t5w0m |awk '{print $1}'` ip addr |grep eth0@ |awk -F ': <' '{print $1}'
45: eth0@if46

[root@k8s-node1 ~]# docker exec -it `docker ps |grep -v POD |grep wp-mysql-t5w0m |awk '{print $1}'` ip addr |grep eth0@ |awk -F ': <' '{print $1}' |awk -F '@if' '{print $2}'
46

wp-mysql-t5w0m这个pod的veth设备的另一端的id为46,在宿主机上,查找:


[root@k8s-node1 ~]# ip addr |grep ^46 |awk -F '@if' '{print $1}'

46: veth529894a

设备的名字为veth529894a,现在在POD中使用service mysql-demo的VIP(10.10.147.26)登录10.20.92.11中的mysql-server:

root@wp-mysql-t5w0m:/# mysql -h 10.10.147.26 -P 13306 -uroot -pr00tme
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 34
Server version: 5.7.15 MySQL Community Server (GPL)
Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>


同时,使用tcpdump抓取设备veth529894a上的最原始的数据包

同时,使用tcpdump抓取设备docker0上的数据包:

其实,将目的IP和端口改成POD的IP和服务的端口,不仅Iptables起作用;kube-proxy也会根据负载均衡,配合Iptables选择其中一个POD的IP,因为一个Service对应的Endpoint可能有很多个(RC中的replica以及Service的selector共同控制后端Endpoint),官方图:

同时,使用tcpdump抓取设备flannel0上的数据包:

同时,使用tcpdump抓取设备eth0上的数据包:

从docker0网桥上出来以后,就直接使用目的POD的IP和端口了,就和之前分析的一样了。

3、NodePort类型的Service需要的iptables规则

[root@k8s-node1 ~]# iptables-save |grep mysql-demo2
-A KUBE-NODEPORTS -p tcp -m comment --comment "hyjx--hyjx-group3/mysql-demo2:mysqld-demo2" -m tcp --dport 30016 -j KUBE-MARK-MASQ
-A KUBE-NODEPORTS -p tcp -m comment --comment "hyjx--hyjx-group3/mysql-demo2:mysqld-demo2" -m tcp --dport 30016 -j KUBE-SVC-NVNGTMQMPFTQA6YS
-A KUBE-SEP-7MWHP37XXE3W6QI4 -s 10.20.92.11/32 -m comment --comment "hyjx--hyjx-group3/mysql-demo2:mysqld-demo2" -j KUBE-MARK-MASQ
-A KUBE-SEP-7MWHP37XXE3W6QI4 -p tcp -m comment --comment "hyjx--hyjx-group3/mysql-demo2:mysqld-demo2" -m tcp -j DNAT --to-destination 10.20.92.11:3306
-A KUBE-SERVICES -d 10.10.95.1/32 -p tcp -m comment --comment "hyjx--hyjx-group3/mysql-demo2:mysqld-demo2 cluster IP" -m tcp --dport 23306 -j KUBE-SVC-NVNGTMQMPFTQA6YS
-A KUBE-SVC-NVNGTMQMPFTQA6YS -m comment --comment "hyjx--hyjx-group3/mysql-demo2:mysqld-demo2" -j KUBE-SEP-7MWHP37XXE3W6QI4

NodePort类型的Service比ClusterIP类型就多了两条规则KUBE-NODEPORTS,剩下的4条规则是用来处理使用ClusterIP的方式访问服务的请求,因为NodePort类型的服务,同样可以使用ClusterIP的方式访问。

1)、第一条规则,实现了IP Masquerade功能,不太懂这条规则的意义。。。;

2)、第二条规则,如果是使用NodeIP+NodePort的方式,目的端口为30016,处理方法和直接使用ClusterIP类型的服务访问一样。

4、更近一步,直接使用服务的名字访问

使用服务的VIP的方式还是不太友好,因为只有创建好Service之后,系统才会随机分配一个VIP,运行在容器中的应用无法提前知道这个VIP,动态修改镜像是不现实的。最终的解决方案是直接使用服务的名称来访问服务。而,服务的名称到服务的VIP的解析是由kube-dns来完成的。


[root@k8s-node2 ~]# docker exec -it `docker ps |grep -v POD |grep mysql-demo-e81sy |awk '{print $1}'` cat /etc/resolv.conf
search hyjx--hyjx-group3.svc.k8s.weike.me svc.k8s.weike.me k8s.weike.me
nameserver 192.168.138.224
options ndots:5

kubelet在启动容器的时候,会修改容器的/etc/resolv.conf,设置容器的DNS服务器为kube-dns(k8s-master节点IP),每个服务都有唯一一个域名:

[root@k8s-node2 ~]# docker exec -it `docker ps |grep -v POD |grep mysql-demo-e81sy |awk '{print $1}'` ping mysql-demo
PING mysql-demo.hyjx--hyjx-group3.svc.k8s.weike.me (10.10.147.26): 56 data bytes
^C--- mysql-demo.hyjx--hyjx-group3.svc.k8s.weike.me ping statistics ---
3 packets transmitted, 0 packets received, 100% packet loss

[root@k8s-node2 ~]# docker exec -it `docker ps |grep -v POD |grep mysql-demo-e81sy |awk '{print $1}'` ping mysql-demo2
PING mysql-demo2.hyjx--hyjx-group3.svc.k8s.weike.me (10.10.95.1): 56 data bytes
^C--- mysql-demo2.hyjx--hyjx-group3.svc.k8s.weike.me ping statistics ---
2 packets transmitted, 0 packets received, 100% packet loss

在容器中无法ping通服务mysql-demo的VIP,但是使用服务的名称,kube-dns可以正确地返回服务对应的VIP(10.10.147.26)。

四、k8s集群外部到k8s集群内部的访问

上面已经介绍过了,设置service的类型为NodePort,并为服务指定一个不冲突的端口(30000-32767)就行了,访问时直接使用任意一个Node节点的IP加上这个NodePort就好了。

你可能感兴趣的:(Docker,Kubernetes)