文章目录
  1. 1. iptables在kubernetes proxy中的作用
  2. 2. kubernetes 日志分析
  3. 3. kubernetes 的客户端负载均衡方式

iptables在kubernetes proxy中的作用

当service有了port和nodePort之后,就可以对内/外提供服务。那么其具体是通过什么原理来实现的呢?奥妙就在kube-proxy在本地node上创建的iptables规则。

Kubernetes为每个service分配一个clusterIP(虚拟ip)。不同的service用不同的ip,所以端口也不会冲突。Kubernetes的虚拟ip是通过iptables机制实现的。每个service定义的端口,kube-proxy都会监听一个随机端口对应,然后通过iptables nat规则做转发。比如Kubernetes上有个dns服务,clusterIP:10.254.0.10,端口:53。应用对10.254.0.10:53的请求会被转发到该node的kube-proxy监听的随机端口上,然后再转发给对应的pod。如果该服务的pod不在当前node上,会先在kube-proxy之间进行转发。该转发完全通过iptables实现。

Kube-Proxy 通过配置 DNAT 规则(从容器出来的访问,从本地主机出来的访问两方面),将到这个服务地址的访问映射到本地的kube-proxy端口(随机端口)。然后 Kube-Proxy 会监听在本地的对应端口,将到这个端口的访问给代理到远端真实的 pod 地址上去。

kubernetes 日志分析

如果 kubernetes 的启动参数中有 --logtostderr=true 表示使用 systemd 接管 kubernetes 的输出,可以用 journalctl 查看,如下,

1
2
3
4
5
6
7
8
9
10
[root@Centos-L410 project]# journalctl -u kube-controller-manager | tail
320 11:50:01 Centos-L410 kube-controller-manager[9359]: W0320 11:50:01.599523 9359 reflector.go:224] /usr/lib/golang/src/runtime/asm_amd64.s:2232: watch of *api.PersistentVolume ended with: 502: (unhandled http status [OK] with body [nil]) [0]
320 11:50:01 Centos-L410 kube-controller-manager[9359]: E0320 11:50:01.912642 9359 reflector.go:206] /usr/lib/golang/src/runtime/asm_amd64.s:2232: Failed to watch *api.Pod: Internal error occurred: too old resource version: 7704 (8379)
320 11:50:01 Centos-L410 kube-controller-manager[9359]: E0320 11:50:01.914766 9359 reflector.go:206] /usr/lib/golang/src/runtime/asm_amd64.s:2232: Failed to watch *api.Pod: Internal error occurred: too old resource version: 7704 (8379)
320 11:50:01 Centos-L410 kube-controller-manager[9359]: E0320 11:50:01.915664 9359 reflector.go:206] /usr/lib/golang/src/runtime/asm_amd64.s:2232: Failed to watch *api.Pod: Internal error occurred: too old resource version: 7704 (8379)
320 11:50:01 Centos-L410 kube-controller-manager[9359]: W0320 11:50:01.967267 9359 reflector.go:224] /usr/lib/golang/src/runtime/asm_amd64.s:2232: watch of *api.Namespace ended with: 502: (unhandled http status [OK] with body [nil]) [0]
320 12:59:06 Centos-L410 kube-controller-manager[9359]: I0320 12:59:06.597924 9359 replication_controller.go:409] Replication Controller has been deleted default/redis-master
320 12:59:08 Centos-L410 kube-controller-manager[9359]: I0320 12:59:08.708102 9359 replication_controller.go:409] Replication Controller has been deleted default/redis-slave
320 12:59:10 Centos-L410 kube-controller-manager[9359]: I0320 12:59:10.770278 9359 replication_controller.go:409] Replication Controller has been deleted default/frontend
320 12:59:41 Centos-L410 kube-controller-manager[9359]: I0320 12:59:41.859582 9359 event.go:216] Event(api.ObjectReference{Kind:"ReplicationController", Namespace:"default", Name:"redis-master", UID:"8e5fcf11-ee58-11e5-a10f-c80aa9c034dc", APIVersion:"v1", ResourceVersion:"8904", FieldPath:""}): reason: 'SuccessfulCreate' Created pod: redis-master-xuv93

从 log 中可以发现,刚刚创建了一个 redis-master-xuv93 的 Pod,这个 Pod 中运行了一个 redis。通过 docker ps 可以查看到对应的容器,

1
2
3
4
[root@Centos-L410 project]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
56f58c6fb142 kubeguide/redis-master:1.0 "redis-server /etc/re" About an hour ago Up About an hour k8s_master.b7750720_redis-master-xuv93_default_8e614b60-ee58-11e5-a10f-c80aa9c034dc_bb2168d8
d377dcefdbc5 registry.access.redhat.com/rhel7/pod-infrastructure:latest "/pod" About an hour ago Up About an hour k8s_POD.4f810ae8_redis-master-xuv93_default_8e614b60-ee58-11e5-a10f-c80aa9c034dc_270230ab

可以发现,当前有2个容器在运行,56f58c6fb142 就是 redis-master-xuv93 这个 Pod 对应的容器;另一个是 kubernetes 的控制节点,是 Pod 网络访问代理。可以用 docker inspect 查看这两个节点的 IP ,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@Centos-L410 arnes]# docker inspect 56f58c6fb142
......
"Env": [
"KUBERNETES_SERVICE_HOST=10.254.0.1",
"KUBERNETES_SERVICE_PORT=443",
"KUBERNETES_PORT=tcp://10.254.0.1:443",
"REDIS_MASTER_SERVICE_HOST=10.254.225.16",
"REDIS_MASTER_PORT_6379_TCP_PROTO=tcp",
"KUBERNETES_PORT_443_TCP_PROTO=tcp",
"KUBERNETES_PORT_443_TCP_PORT=443",
"REDIS_MASTER_SERVICE_PORT=6379",
"REDIS_MASTER_PORT=tcp://10.254.225.16:6379",
"REDIS_MASTER_PORT_6379_TCP=tcp://10.254.225.16:6379",
"REDIS_MASTER_PORT_6379_TCP_PORT=6379",
"REDIS_MASTER_PORT_6379_TCP_ADDR=10.254.225.16",
"KUBERNETES_SERVICE_PORT_HTTPS=443",
"KUBERNETES_PORT_443_TCP=tcp://10.254.0.1:443",
"KUBERNETES_PORT_443_TCP_ADDR=10.254.0.1",
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"HOME=/root"
],
......

从容器的信息上来看,redis 容器 56f58c6fb142 本身并没有 IP 地址,但是,通过容器内部互联,监听了 10.254.225.16:6379 这个 虚拟IP+端口,因此,直接访问 10.254.225.16:6379 即可,如下,

1
2
3
4
5
6
7
8
[root@Centos-L410 project]# telnet 10.254.225.16 6379
Trying 10.254.225.16...
Connected to 10.254.225.16.
Escape character is '^]'.
^]
telnet> q
Connection closed.

另外,由于 d377dcefdbc5 是 redis 的访问代理,因此,访问这个地址的6379端口,也是可以通的,如下,

1
2
3
4
5
6
7
8
[root@Centos-L410 project]# telnet 172.17.1.22 6379
Trying 172.17.1.22...
Connected to 172.17.1.22.
Escape character is '^]'.
^]
telnet> q
Connection closed.

可以验证,这个 redis 服务的确是在监听 10.254.225.16:6379,只不过这个是 kubernetes service 的虚拟 IP,

1
2
3
4
[root@Centos-L410 project]# kubectl get services
NAME CLUSTER_IP EXTERNAL_IP PORT(S) SELECTOR AGE
kubernetes 10.254.0.1 <none> 443/TCP <none> 20d
redis-master 10.254.225.16 <none> 6379/TCP name=redis-master 1h

kubernetes 的客户端负载均衡方式

通常情况下,kubernetes 的负载均衡方式是如下进行,

由 kube-proxy 进程负责将请求转发到每个工作 Pod 上,好处是对客户端透明,坏处是多了一次转发,性能上有所损耗。另一种办法是采用客户端负载均衡方式,如下,

客户端先去向 apiserver 询问可以服务的工作 Pod 的地址,然后直接访问该地址,这样的好处是少了一次转发,坏处是客户端需要做一些逻辑判断。

如果使用客户端负载均衡的方式,那么就可以用类似上述的方法来获取服务真正的监听端口。如果需要直接访问该 IP 的话,需要在访问端增加路由规则,如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
root@Ubuntu-Asus 192.168.2.201 15:12:53 ~
# route add -net 172.17.1.0 netmask 255.255.255.0 gw 192.168.2.202
root@Ubuntu-Asus 192.168.2.201 15:13:54 ~
# ip rout
default via 192.168.2.1 dev wlan0 proto static
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
172.17.1.0/24 via 192.168.2.202 dev wlan0
192.168.2.0/24 dev wlan0 proto kernel scope link src 192.168.2.201 metric 9
root@Ubuntu-Asus 192.168.2.201 15:13:56 ~
# telnet 172.17.1.22 6379
Trying 172.17.1.22...
Connected to 172.17.1.22.
Escape character is '^]'.
^]q
telnet> q
Connection closed.

也可以使用 redis client 来访问,

1
2
3
4
5
6
7
# arnes@Ubuntu-Asus [192.168.2.201] in ~/download/redis-3.0.5/src [15:16:49]
$ ./redis-cli -h 172.17.1.22 -p 6379
172.17.1.22:6379> scan 0 count 100
1) "0"
2) 1) "messages"
172.17.1.22:6379> get messages
"Hello World!"

这样,外部系统就可以直接访问 Pod。

文章目录
  1. 1. iptables在kubernetes proxy中的作用
  2. 2. kubernetes 日志分析
  3. 3. kubernetes 的客户端负载均衡方式

欢迎来到Valleylord的博客!

本博的文章尽量原创。