kubernetes中暴露服务的方式比较
背景
应用(Application)是通过服务(Services)来暴露出去的。
Services有不同的类型,对于IP处理会有不同。
- NAT: network address translation
- Source NAT: 替换包中的source IP
- Destination NAT: 替换包中的destination IP
- VIP: a virtual IP
- Kube-proxy: network daemon
准备活动
# kubectl run source-ip-app --image=gcr.azk8s.cn/google-containers/echoserver:1.4
ClusterIP
确认一下现在的环境
# kubectl get nodes NAME STATUS ROLES AGE VERSION 172-19-120-198 Ready master 34h v1.15.2 172-19-120-201 Ready <none> 34h v1.15.2 172-19-120-202 Ready <none> 34h v1.15.2 172-19-120-203 Ready <none> 34h v1.15.2
节点代理类型
# curl localhost:10249/proxyMode iptables
通过 clusterip 方式来暴露服务
kubectl expose deployment source-ip-app --name=clusterip --port=80 --target-port=8080
看一下暴露出来的clusterip
# kubectl get svc clusterip NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE clusterip ClusterIP 10.109.59.94 <none> 80/TCP 8m56s
在容器中,请求一下这个ip,看看源ip是什么?
先起一下临时的应用busybox
kubectl run busybox -it --image=busybox --restart=Never --rm
/ # ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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 3: eth0@if12: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue link/ether 52:fc:bb:9a:e3:a5 brd ff:ff:ff:ff:ff:ff inet 10.244.3.8/24 scope global eth0 valid_lft forever preferred_lft forever / # wget -qO - 10.109.59.94 CLIENT VALUES: client_address=10.244.3.8 command=GET
NodePort
部署
kubectl expose deployment source-ip-app --name=nodeport --port=80 --target-port=8080 --type=NodePort
查看端口号
# kubectl get services nodeport NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE nodeport NodePort 10.108.37.3 <none> 80:31113/TCP 48s
请求服务
# curl -s 172.19.120.198:31113 | grep client_address client_address=10.244.0.0 # curl -s 172.19.120.201:31113 | grep client_address client_address=10.244.1.0 # curl -s 172.19.120.202:31113 | grep client_address client_address=10.244.2.1 # curl -s 172.19.120.203:31113 | grep client_address client_address=10.244.3.0
这里的IP不是真实的客户端IP,而是集群内部的IP。其中的过程是:
client \ ^ \ \ v \ node 1 <------ node 2 | ^ SNAT | | ------> v | endpoint
- 客户端client发包给node 2:nodePort
- node 2做了SNAT,将包的来源IP替换为了node 2的IP
- node 2做了DNAT,将包的目的IP替换为了pod的IP
- 包被路由到了node 1, 然后由endpoint进行处理
- pod的响应,被路由到了node 2
- pod的响应,被送回到了客户端client
为了让pod能够得到真实的客户端IP,kubernetes有一个feature来禁止转发请求。这样所有的请求都会由本节点上的应用来处理,不会再进行转发,同时保留了客户端的IP地址。如果本节点上没有对应的应用时,这个包就会被丢弃。测试如下:
# kubectl patch svc nodeport -p '{"spec":{"externalTrafficPolicy":"Local"}}' service/nodeport patched
再进行请求来测试
# curl -s 172.19.120.198:31113 | grep client_address client_address=10.244.0.0 # curl -s 172.19.120.201:31113 | grep client_address 无响应 # curl -s 172.19.120.202:31113 | grep client_address client_address=172.19.120.198 # curl -s 172.19.120.203:31113 | grep client_address 无响应
过程示意图如下:
client ^ / \ / / \ / v X node 1 node 2 ^ | | | | v endpoint
发到node1的请求会被处理,发到node2的请求会被丢弃。
LoadBalancer
LoadBalancer模式下,所有的处于Ready状态的节点,都可以进行流量的load balance。当一个包到达一个没有endpoint的node时,会代理到一个有endpoint的node上,同时进行SNAT转换。
kubectl expose deployment source-ip-app --name=loadbalancer --port=80 --target-port=8080 --type=LoadBalancer
看下IP地址
# kubectl get svc loadbalancer NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE loadbalancer LoadBalancer 10.98.41.150 <pending> 80:32070/TCP 5m21s
请求
# curl "10.98.41.150" CLIENT VALUES: client_address=10.244.0.0
其中流量示意图为
client | lb VIP / ^ v / health check ---> node 1 node 2 <--- health check 200 <--- ^ | ---> 500 | V endpoint
还原状态
删除 services
kubectl delete svc -l run=source-ip-app
删除 deployment, replicaset和pod
kubectl delete deployment source-ip-app