【Kubernetes】最佳实践1:kube-proxy与服务发现

来源:互联网 发布:我要直播网络电视网 编辑:程序博客网 时间:2024/06/05 14:57

作者:彭靖田


Kubernetesnode节点主要有kubeletkube-proxyflanneldockerd四个组件组成,本文主要分析kube-proxy组件的功能和原理。PodKubernetes中资源分配的最小单位,也是执行任务的最小实体。

每个Pod都拥有flannel overlay network上的独立IPnode节点内pod通信由docker0网桥实现;跨node节点间通信由flannel实现。


1Kubernetes集群架构

Pod无法直接对Kubernetes集群外部访问提供服务,Service是服务的抽象,执行相同任务的pod可以组成一个Service,以ServiceIP提供服务,Service实现服务请求的转发。

Pod通过定义metadata.labels.key的方式为自己打上标签,因此,Service就可以通过spec.selector选择对应标签的Pod,实现执行相同任务Pod的服务发现。

综上,Service负责将外部的请求发送到Kubernetes内部的Pod,同时也将内部pod的请求发送到外部。

根据应用场景的不同,Kubernetes提供3种类型的Service

  1. ClusterIP:只能内部访问,随机分配本地端口,缺省类型;
  2. NodePort:可外部访问<NodeIP>:<NodePort>,指定NodePort。
  3. LoadBalancer:云服务场景下,支持外部负载均衡器,**<LoadBalancerIP>:<Port>的服务请求。

无论哪种类型的Service,其功能都是由kube-proxy组件真正实现的。

kube-proxy目前有两种实现方式:userspaceiptables

userspace是在用户空间,通过kube-proxy实现LB的代理服务,Kubernetes最开始使用此方案,后来由于效率原因,改为默认支持iptables方案。

iptables

iptables的方案主要利用了linuxiptablesnat转发进行实现。本文以深度学习平台的预测服务TensorFlowServing服务为例,分析其实现原理。

TensorFlow Serving分为服务端和客户端程序,我们深度学习平台的服务端运行在Kubernetes集群内,客户端运行在集群外部。

Pod

下面定义在KubernetesTensorFlow Serving服务端的Pod,以MNIST CNN模型为例,不妨设文件名为inference-pod.yaml

kind: Pod

apiVersion: v1

metadata:

  name: inference-pod-0

  labels:

    name: mnist

spec:

  containers:

  - name: tf-serving

    image: mind/tf-serving:0.4

    ports:

    - containerPort: xxxx

    command:

    - "./tensorflow_model_server"

    args:

    - "--model_name=mnist_CNN"

    - "--port=6666"

    -"--model_base_path=/mnt/tf_models/mnist_CNN"

    volumeMounts:

    - name: mynfs

      mountPath: /mnt

  volumes:

  - name: mynfs

    nfs:

      path: /

      server: xx.xx.xx.xx

创建inference-pod-0

$ kubectl create -f inference-pod.yaml

查看运行状态:

$ kubectl get po

NAME              READY     STATUS    RESTARTS   AGE

inference-pod-0   1/1       Running   0        42m

查看inference-pod-0IPkubectl支持前缀查找):

$ kubectl describe po inference | grep IP

IP:             xx.xx.xx.xx

Service

定义对应的Service,这里使用NodePort类型,不妨设文件名为inference-service.yaml:

kind: Service

apiVersion: v1

metadata:

  name:inference-service-0

spec:

  selector:

    name: mnist

  ports:

  - protocol: TCP

    port: xxxx

    targetPort: xxxx

    nodePort: xxxx

  type: NodePort

3Port含义如下:

portKubernetes集群内访问Service的端口;

targetPortService访问PodContainer的端口;

nodePortService对外提供服务的端口;

创建inference-service-0:

$ kubectl create -f inference-service.yaml

查看运行状态:

$ kubectl get svc

NAME                 CLUSTER-IP    EXTERNAL-IP   PORT(S)          AGE

inference-service-0   xx.xx.xx.xx   <nodes>      xxx:xxx/TCP   2h

检查inference-service-0是否能够发现对应的inference-pod-0

$ kubectl describe svc inference

Name:                  inference-service-0

Namespace:             default

Labels:                <none>

Selector:              name=mnist

Type:                  NodePort

IP:                    xx.xx.xx.xx

Port:                  <unset> xxxx/TCP

NodePort:              <unset> xxx/TCP

Endpoints:             xx.xx.xx.xx:xxxx

Session Affinity:       None

No events.

Endpoints字段显示inference-service-0已经发现了inference-pod-0xx.xx.xx.xx:xxx),对外提供服务的IPPort分别是xx.xx.xx.xxxxxx

Client

测试图片如下:

找一台安装了TensorFlow的服务器,直接在裸机上发起请求:

$ python tf_predict.py --server_host=xx.xx.x.x:xxxx--model_name=mnist_CNN --input_img=sample_0.png

7

返回正确的预测结果。

kube-proxy服务发现原理

kube-proxy利用iptablesnat完成服务发现。

现在inference-service-0后端代理了1Podipxx.xx.xx.xx,看下kube-proxy对应写的iptables规则:

$ sudo iptables -S -t nat | grep KUBE

-N KUBE-MARK-DROP

-N KUBE-MARK-MASQ

-N KUBE-NODEPORTS

-N KUBE-POSTROUTING

-N KUBE-SEP-GYCDLIYS6Q7266WO

-N KUBE-SEP-RVISLOLI7KKADQKA

-N KUBE-SERVICES

-N KUBE-SVC-CAVPFFD4EDKETLMK

-N KUBE-SVC-NPX46M4PTMTKRN6Y

-A PREROUTING -m comment --comment "kubernetes serviceportals" -j KUBE-SERVICES

-A OUTPUT -m comment --comment "kubernetes serviceportals" -j KUBE-SERVICES

-A POSTROUTING -m comment --comment "kubernetes postroutingrules" -j KUBE-POSTROUTING

-A KUBE-MARK-DROP -j MARK --set-xmark 0x8000/0x8000

-A KUBE-MARK-MASQ -j MARK --set-xmark 0x4000/0x4000

-A KUBE-NODEPORTS -p tcp -m comment --comment"default/inference-service-0:" -m tcp --dport 32000 -j KUBE-MARK-MASQ

-A KUBE-NODEPORTS -p tcp -m comment --comment"default/inference-service-0:" -m tcp --dport 32000 -jKUBE-SVC-CAVPFFD4EDKETLMK

-A KUBE-POSTROUTING -m comment --comment "kubernetesservice traffic requiring SNAT" -m mark --mark 0x4000/0x4000 -j MASQUERADE

-A KUBE-SEP-GYCDLIYS6Q7266WO -s xx.xx.xx.xx/32 -m comment--comment "default/kubernetes:https" -j KUBE-MARK-MASQ

-A KUBE-SEP-GYCDLIYS6Q7266WO -p tcp -m comment --comment"default/kubernetes:https" -m recent --set --nameKUBE-SEP-GYCDLIYS6Q7266WO --mask xx.xx.xx.xx--rsource -m tcp -j DNAT--to-destination xx.xx.xx.xx:xxxx

-A KUBE-SEP-RVISLOLI7KKADQKA -s xx.xx.xx.xx/32 -m comment --comment"default/inference-service-0:" -j KUBE-MARK-MASQ

-A KUBE-SEP-RVISLOLI7KKADQKA -p tcp -m comment --comment"default/inference-service-0:" -m tcp -j DNAT --to-destination xx.xx.xx.xx:xxx

-A KUBE-SERVICES -d xx.xx.xx.xx/32 -p tcp -m comment --comment"default/kubernetes:https cluster IP" -m tcp --dport 443 -jKUBE-SVC-NPX46M4PTMTKRN6Y

-A KUBE-SERVICES -d xx.xx.xx.xx/32 -p tcp -m comment --comment"default/inference-service-0: cluster IP" -m tcp --dport xxxx -jKUBE-SVC-CAVPFFD4EDKETLMK

-A KUBE-SERVICES -m comment --comment "kubernetes servicenodeports; NOTE: this must be the last rule in this chain" -m addrtype--dst-type LOCAL -j KUBE-NODEPORTS

-A KUBE-SVC-CAVPFFD4EDKETLMK -m comment --comment"default/inference-service-0:" -j KUBE-SEP-RVISLOLI7KKADQKA

-A KUBE-SVC-NPX46M4PTMTKRN6Y -m comment --comment"default/kubernetes:https" -m recent --rcheck --seconds 10800 --reap--name KUBE-SEP-GYCDLIYS6Q7266WO --mask xx.xx.xx.xx--rsource -jKUBE-SEP-GYCDLIYS6Q7266WO

-A KUBE-SVC-NPX46M4PTMTKRN6Y -m comment --comment"default/kubernetes:https" -j KUBE-SEP-GYCDLIYS6Q7266WO

接下来详细的分析iptables的规则。

首先,如果访问nodexxxx端口,请求会进入以下两条链:KUBE-MARK-MASQKUBE-SVC-CAVPFFD4EDKETLMK

-A KUBE-NODEPORTS -p tcp -m comment --comment"default/inference-service-0:" -m tcp --dport xxxx -j KUBE-MARK-MASQ

-A KUBE-NODEPORTS -p tcp -m comment --comment"default/inference-service-0:" -m tcp --dport xxxx -jKUBE-SVC-CAVPFFD4EDKETLMK

然后,请求跳转到KUBE-SVC-CAVPFFD4EDKETLMK链:

-A KUBE-SVC-CAVPFFD4EDKETLMK -m comment --comment"default/inference-service-0:" -j KUBE-SEP-RVISLOLI7KKADQKA

接着,请求再次跳转到KUBE-SEP-RVISLOLI7KKADQKA链:

-A KUBE-SEP-RVISLOLI7KKADQKA -s xx.xx.xx.xx/32 -m comment--comment "default/inference-service-0:" -j KUBE-MARK-MASQ

-A KUBE-SEP-RVISLOLI7KKADQKA -p tcp -m comment --comment"default/inference-service-0:" -m tcp -j DNAT --to-destination xx.xx.xx.xx:xxxx

最终,通过DNAT将请求发送到了后端Podxx.xx.xx.xxxxxx端口。

现在,分析集群内服务直接访问clusterip的工作方式,inference-service-0clusteripxx.xx.xx.xx

集群内访问xx.xx.xx.xxxxx端口请求,会跳转到KUBE-SVC-CAVPFFD4EDKETLMK链:

-A KUBE-SERVICES -d xx.xx.xx.xx/xx -p tcp -m comment --comment"default/inference-service-0: cluster IP" -m tcp --dport xxxx -jKUBE-SVC-CAVPFFD4EDKETLMK

然后,进一步跳转到KUBE-SEP-RVISLOLI7KKADQKAl链,与NodePort一样最终通过DNAT将请求发送到了后端的Pod

-A KUBE-SVC-CAVPFFD4EDKETLMK -m comment --comment"default/inference-service-0:" -j KUBE-SEP-RVISLOLI7KKADQKA

kube-proxy负载均衡原理

为了分析kube-proxy如何利用iptables的规则来时实现简单的负载均衡,我们再创建一个inference-pod-1

$ kubectl create -f inference-pod-1.yaml

查看inference-pod-1的运行状态:

$ kubectl get po

NAME              READY     STATUS    RESTARTS   AGE

inference-pod-0   1/1       Running   0        1h

inference-pod-1   1/1       Running   0        3m

查看inference-pod-1ip

$ kubectl describe po inference-pod-1 | grep IP

IP:             xx.xx.xx.xx

查看inference-service-0的后端是否更新:

$ kubectl describe svc inference

Name:                  inference-service-0

Namespace:             default

Labels:                <none>

Selector:              name=mnist

Type:                  NodePort

IP:                    xx.xx.xx.xx

Port:                  <unset> xxxx/TCP

NodePort:              <unset> xxxx/TCP

Endpoints:             xx.xx.xx.xx:xxx, xx.xx.xx.xx:xxx

Session Affinity:       None

No events.

发现更新成功,后端已经新增了inference-pod-1ipportxx.xx.xx.xx:xxx)。

查看更新后的iptables规则:

-N KUBE-MARK-DROP

-N KUBE-MARK-MASQ

-N KUBE-NODEPORTS

-N KUBE-POSTROUTING

-N KUBE-SEP-D6IZJMBUD3SKR4IF

-N KUBE-SEP-GYCDLIYS6Q7266WO

-N KUBE-SEP-RVISLOLI7KKADQKA

-N KUBE-SERVICES

-N KUBE-SVC-CAVPFFD4EDKETLMK

-N KUBE-SVC-NPX46M4PTMTKRN6Y

-A PREROUTING -m comment --comment "kubernetes serviceportals" -j KUBE-SERVICES

-A OUTPUT -m comment --comment "kubernetes serviceportals" -j KUBE-SERVICES

-A POSTROUTING -m comment --comment "kubernetes postroutingrules" -j KUBE-POSTROUTING

-A KUBE-MARK-DROP -j MARK --set-xmark 0x8000/0x8000

-A KUBE-MARK-MASQ -j MARK --set-xmark 0x4000/0x4000

-A KUBE-NODEPORTS -p tcp -m comment --comment"default/inference-service-0:" -m tcp --dport xxxx -j KUBE-MARK-MASQ

-A KUBE-NODEPORTS -p tcp -m comment --comment "default/inference-service-0:"-m tcp --dport xxxx -j KUBE-SVC-CAVPFFD4EDKETLMK

-A KUBE-POSTROUTING -m comment --comment "kubernetesservice traffic requiring SNAT" -m mark --mark 0x4000/0x4000 -j MASQUERADE

-A KUBE-SEP-D6IZJMBUD3SKR4IF -s xx.xx.xx.xx/32 -m comment--comment "default/inference-service-0:" -j KUBE-MARK-MASQ

-A KUBE-SEP-D6IZJMBUD3SKR4IF -p tcp -m comment --comment"default/inference-service-0:" -m tcp -j DNAT --to-destination xx.xx.xx.xx:xxx

-A KUBE-SEP-GYCDLIYS6Q7266WO -s xx.xx.xx.xx/32 -m comment --comment"default/kubernetes:https" -j KUBE-MARK-MASQ

-A KUBE-SEP-GYCDLIYS6Q7266WO -p tcp -m comment --comment"default/kubernetes:https" -m recent --set --nameKUBE-SEP-GYCDLIYS6Q7266WO --mask xx.xx.xx.xx--rsource -m tcp -j DNAT--to-destination xx.xx.xx.xx:xxxx

-A KUBE-SEP-RVISLOLI7KKADQKA -s xx.xx.xx.xx/32 -m comment--comment "default/inference-service-0:" -j KUBE-MARK-MASQ

-A KUBE-SEP-RVISLOLI7KKADQKA -p tcp -m comment --comment"default/inference-service-0:" -m tcp -j DNAT --to-destination xx.xx.xx.xx:xxx

-A KUBE-SERVICES -d xx.xx.xx.xx/32 -p tcp -m comment --comment"default/kubernetes:https cluster IP" -m tcp --dport 443 -jKUBE-SVC-NPX46M4PTMTKRN6Y

-A KUBE-SERVICES -d xx.xx.xx.xx/32 -p tcp -m comment --comment"default/inference-service-0: cluster IP" -m tcp --dport xxxx -jKUBE-SVC-CAVPFFD4EDKETLMK

-A KUBE-SERVICES -m comment --comment "kubernetes servicenodeports; NOTE: this must be the last rule in this chain" -m addrtype--dst-type LOCAL -j KUBE-NODEPORTS

-A KUBE-SVC-CAVPFFD4EDKETLMK -m comment --comment"default/inference-service-0:" -m statistic --mode random--probability 0.50000000000 -j KUBE-SEP-RVISLOLI7KKADQKA

-A KUBE-SVC-CAVPFFD4EDKETLMK -m comment --comment"default/inference-service-0:" -j KUBE-SEP-D6IZJMBUD3SKR4IF

-A KUBE-SVC-NPX46M4PTMTKRN6Y -m comment --comment"default/kubernetes:https" -m recent --rcheck --seconds 10800 --reap--name KUBE-SEP-GYCDLIYS6Q7266WO --mask xx.xx.xx.xx--rsource -jKUBE-SEP-GYCDLIYS6Q7266WO

-A KUBE-SVC-NPX46M4PTMTKRN6Y -m comment --comment"default/kubernetes:https" -j KUBE-SEP-GYCDLIYS6Q7266WO

下面分析NodePort的访问请求是如何到达两个后端inference-pod-0inference-pod-1的。

首先,访问xxx端口的请求仍然跳转到KUBE-SVC-CAVPFFD4EDKETLMK链:

-A KUBE-NODEPORTS -p tcp -m comment --comment"default/inference-service-0:" -m tcp --dport xxxxx -jKUBE-SVC-CAVPFFD4EDKETLMK

然后,与之前不同的是,这里没有直接跳转到KUBE-SEP-RVISLOLI7KKADQKA链,而是利用iptablesprobability特性,使请求有50%的概率进入KUBE-SEP-RVISLOLI7KKADQKA链,另外50%的概率进入KUBE-SEP-D6IZJMBUD3SKR4IF链。

-A KUBE-SVC-CAVPFFD4EDKETLMK -m comment --comment"default/inference-service-0:" -m statistic --mode random--probability 0.50000000000 -j KUBE-SEP-RVISLOLI7KKADQKA

-A KUBE-SVC-CAVPFFD4EDKETLMK -m comment --comment"default/inference-service-0:" -j KUBE-SEP-D6IZJMBUD3SKR4IF

接着分析,KUBE-SEP-RVISLOLI7KKADQKA链对应xx.xx.xx.xxinference-pod-0

-A KUBE-SEP-RVISLOLI7KKADQKA -p tcp -m comment --comment"default/inference-service-0:" -m tcp -j DNAT --to-destination xx.xx.xx.xx:xxx

KUBE-SEP-D6IZJMBUD3SKR4IF链则对应xx.xx.xx.xxinference-pod-1:

-A KUBE-SEP-D6IZJMBUD3SKR4IF -p tcp -m comment --comment"default/inference-service-0:" -m tcp -j DNAT --to-destination xx.xx.xx.xx:xxx

综上,通过分析iptables规则,我们已然清楚了解kube-proxy实现负载均衡和服务发现的原理。

 

原创粉丝点击