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