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

参考