kubernetes中暴露服务的方式比较

  |   0 评论   |   0 浏览

背景

应用(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

参考