在谈kubernetes的service以前,小编向带你们复习一下负载均衡的概念,那么何为负载均衡呢,小编举一个简单的例子,能力相同的两我的同时进入一家公司,一我的每天加班到深夜,另外一我的则每天优哉游哉到点下班,但两我的的工资同样,此时那个埋头苦干的人就会抱怨了,凭什么我每天累死累活,这不公平!相对于机器而言也是同样的,一台机器一直干活,另外一台机器一直空闲,总有一台干活的机器会罢工的!那么如何解决这个问题呢?小编这里举一个web集群的例子:
客户端访问一个虚拟的IP,经过这个虚拟的IP将请求发送给nginx的负载(主),此时主负载将接受的请求抛给后端的web服务作真正的处理,这里的负载能够是轮询也能够是根据集群的资源,指定发送到某台web服务器上。那有些读者就想问了,如何设置这个虚拟的IP,又怎么知道负载何时会宕机,负载又如何发现后端的web服务器的,如何获取集群资源,精确计算出哪一台web服务比较空闲等等,这一些列的问题,不要着急,小编接下来根据kubernetes的service将这其中的原理一一道来。
但愿一篇文章就能写完,此时毫不说废话了,拜托拜托。html
service是kubernetes最核心的概念,经过建立service,能够为一组具备相同功能的容器应用提供一个统一的入口地址,而且将请求负载发送到后端的各个容器应用上。node
apiVersion: v1 kind: Service metadata: #元数据 name: string #service的名称 namespace: string #service所属的命名空间 labels: #service的标签 - name: string annotations: #service的注解 - name: string spec: selector: [] #label选择器,将选择具备指定label标签的pod做为管理范围 type: string #Service的类型 [clusterIP|NodePort|LoadBalancer] clusterIP: string #虚拟服务IP地址 sessionAffinity: string #是否支持session [ClientIP|None] 表示将同一个客户端的访问请求都转发到同一个后端 ports: #service须要暴露的端口 - name: string #端口名称,区分不一样的应用的端口 protocol: string #使用的协议 prot: int #service监听的端口 targetPort: int #发送到后端的应用的端口 nodePort: int #当spec.type=NodePort时,指定映射到物理机的端口 status: #当spec.type=LoadBalancer时,设置外部负载均衡器的地址 loadBalancer: ingress: ip: string #外部负载的IP hostname: string #外部负载均衡的主机名
这小编以三个案例介绍:nginx
#webapp-rc.yamlgit
apiVersion: v1 kind: ReplicationController metadata: name: webapp spec: replicas: 2 template: metadata: name: webapp labels: app: webapp spec: containers: - name: webapp image: docker.io/tomcat imagePullPolicy: IfNotPresent ports: - containerPort: 8080
[root@zy yaml_file]# kubectl create -f webapp-rc.yaml #建立github
[root@zy yaml_file]# kubectl get pods -l app=webapp -o yaml|grep podIP #查看pod的IP
[root@zy yaml_file]# curl 172.17.0.5:8080 #访问web
#建立service管理pod [root@zy yaml_file]# kubectl expose rc webapp
或者 yaml文件
#webapp-svc.yamldocker
apiVersion: v1 kind: Service metadata: name: webapp spec: ports: - port: 8081 targetPort: 8080 selector: app: webapp
[root@zy yaml_file]# kubectl get svc #查看建立的service的IP
[root@zy yaml_file]# curl 10.254.90.131:8081 #经过serviceIP访问pod中的应用
② 多端口service
有时候一个容器应用也可能提供多个端口的服务,那么在service的定义中也能够相应地设置为将多个端口对应到多个应用中,有点相似于Apache的虚拟主机中的基于端口配置。数据库
apiVersion: v1 kind: Service metadata: name: webapp spec: ports: - port: 8081 targetPort: 8080 name: web - port: 8005 targetPort: 8005 name: management selector: app: webapp
③ 外部服务service
在某些环境中,应用系统须要将一个外部数据库做为后端服务进行链接,或将另外一个集群或者namespace中的服务做为服务的后端,这时能够经过建立一个无label selector的service来实现:vim
apiVersion: v1 kind: Service metadata: name: my-service spec: ports: - protocol: TCP port: 33060 targetPort: 3306
由于定义一个没有selector的service,系统不会自动建立endpoint,须要手动定义,建立一个与service同名的endpoint,用于指向实际后端访问地址。
#endpoint后端
kind: EndPoints apiVersion: v1 metadata: name: my-service #与service相同 subsets: - addresses: - IP: xxx.xxx.xxx.xxx ports: - port: 3306
此时:
这个service绑定的EndPoint就会链接外部的172.0.1.16:3306的服务,内部访问这个service时,路由就会转发到cluster B 的相应的服务中。
经过上面的三个案例是否是对service有了初步的了解呢,小编在这里给你们总结一下,service的好处:
因为pod被RC或者deploy管理,pod重启以后,pod的IP地址是改变的,若是使用podIP去访问后端的应用,每次都要查IP,可是有了service,service的虚拟IP是固定的,咱们只要访问service的IP,至于service如何发现后端的重启的Pod,咱们不须要关系
Service的负载做用,若是一个service管理多个pod,而这多个pod提供的是相同的服务,那么service自身也实现了负载:
RoundRobin(轮询):将请求发送到后端的各个pod上
SessionAffinity:基于客户端IP地址进行会话,若是SessionAffinity=ClientIP时,同一个客户端发送请求,会被转发到后端相同的pod中。
在某些场景中,咱们但愿本身控制负载均衡的策略,不使用service提供的默认的负载,或者应用程序但愿知道属于同组服务的其余实例。Kubernetes提供了Headless Service来实现这个功能,即不为service设置clusterIP,仅经过label selector将后端的pod列表返回给调用的客户端:
apiVersion: v1 kind: Service metadata: labels: name: cassandra name: cassandra spec: ports: - port: 9042 ClusterIP: None selector: name: cassandra
这样service就不在具备一个特定的clusterIP,对其进行访问将得到包含label“name: cassandra”的所有pod列表,而后客户端程序自行决定如何处理这个pod列表。
对于去中心化类的应用集群,headless Service将很是有用。接下来咱们经过搭建一个Cassandra集群来看看headless Service巧妙的使用,自动实现应用集群的建立。
经过对headless Service的使用,实现了Cassandra各节点之间的相互查找和集群的自动搭建。
开始搭建:
#单个pod Cassandra节点: Cassandra-pod.yaml
apiVersion: v1 kind: Pod metadata: labels: name: cassandra name: cassandra spec: containers: - args: - /run.sh resources: limits: cpu: "0.5" image: docker.io/shenshouer/cassandra:v5 imagePullPolicy: IfNotPresent name: cassandra ports: - name: cql containerPort: 9042 - name: thrift containerPort: 9160 volumeMounts: - name: data mountPath: /cassandra_data env: - name: MAX_HEAP_SIZE value: 512M - name: HEAP_NEWSIZE value: 100M - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace volumes: - name: data
#cassandra-svc.yaml
apiVersion: v1 kind: Service metadata: labels: name: cassandra name: cassandra spec: ports: - port: 9042 selector: name: cassandra
#cassandra-rc.yaml
apiVersion: v1 kind: ReplicationController metadata: labels: name: cassandra name: cassandra spec: replicas: 1 selector: name: cassandra template: metadata: labels: name: cassandra spec: containers: - command: - /run.sh resources: limits: cpu: 0.5 env: - name: MAX_HEAP_SIZE value: 512M - name: HEAP_NEWSIZE value: 100M - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace image: docker.io/shenshouer/cassandra:v5 imagePullPolicy: IfNotPresent name: cassandra ports: - containerPort: 9042 name: cql - containerPort: 9160 name: thrift volumeMounts: - mountPath: /cassandra_data name: data volumes: - name: data emptyDir: {}
#依次执行一下命令 [root@zy yaml_file]# kubectl create -f cassandra-pod.yaml [root@zy yaml_file]# kubectl create -f cassandra-svc.yaml [root@zy yaml_file]# kubectl create -f cassandra-rc.yaml
#此时咱们查看pod
[root@zy yaml_file]# kubectl get rc #查看RC
[root@zy yaml_file]# kubectl scale rc cassandra --replicas=2 #副本扩容到2个
[root@zy yaml_file]# kubectl exec -ti cassandra -- nodetool status #查看Cassandra Pod中运行nodetool
看见以上的页面,表示扩容的pod已经成功加入到Cassandra集群中。
原理解释:由于service是headless Service,他会返回selector中的全部的pod,一开始集群中只有一个pod,因此返回一个,让集群中的pod变成多个时,他会将全部的pod都返回,那么Cassandra是如何处理这新的pod呢?Cassandra镜像它还给Cassandra添加一个定制的SeedProvider,在Cassandra中, SeedProvider设置一个gossip协议用来发现其它Cassandra节点。KubernetesSeedProvider使用内置的Kubernetes发现服务找到KubernetesAPI服务器,而后利用Kubernetes API发现新的节点。就这样headless Service将selector选择中的全部的endpoint,都发送给Cassandra集群,Cassandra集群根据SeedProvider,利用内置的Kubernetes发现服务找到KubernetesAPI服务器,而后利用Kubernetes API发现新的节点,并加入到集群。
参考文档:https://www.kubernetes.org.cn/doc-36
因为pod和service是kubernetes集群范围内的虚拟的概念,全部集群外部的客户端没法访经过Pod的IP或者service的IP去访问到它们。为了让外部的客户端能够访问这些服务,kubernetes提供了将Pod或者service的端口号映射到物理机上,以使得客户端访问宿主机的端口从而访问到其容器中的服务。
案例1(①经过设置容器基本的hostPort,将容器应用的端口映射到物理机上)
#pod-hostport.yaml
apiVersion: v1 kind: Pod metadata: name: webapp labels: app: webapp spec: containers: - name: webapp image: docker.io/tomcat imagePullPolicy: IfNotPresent ports: - containerPort: 8080 hostPort: 8070
#建立以后,访问宿主机的8070端口 [root@zy yaml_file]# curl 192.168.130.130:8070
访问浏览器:http://192.168.130.130:8070/
案例2:(使用spec.hostNetWork参数定义)
经过设置pod级别的hostNetWork=true,该Pod中全部容器的端口号都将被直接映射到物理机上,可是须要注意的是,在容器的ports定义中,若是不指定hostPort,则默认为containerPort,若是指定了hostPort,则hostPort必须等于containerPort的值。
apiVersion: v1 kind: Pod metadata: name: webapp labels: app: webapp spec: hostNetwork: true containers: - name: webapp image: docker.io/tomcat imagePullPolicy: IfNotPresent ports: - containerPort: 8080
案例3(将service的端口号映射到物理机中)
#webapp-nodePort-svc.yaml
apiVersion: v1 kind: Service metadata: name: webapp spec: type: NodePort ports: - port: 8080 targetPort: 8080 nodePort: 30000 selector: app: webapp
该service将监听8080端口,并发送到后端selector选择的endpoint的8080端口,对宿主机暴露的端口为30000
[root@zy yaml_file]# curl 192.168.130.130:30000 #访问
案例4
经过设置LoadBalancer映射到云服务商提供的LoadBalancer地址,LoadBalancer在NodePort基础上,K8S能够请求底层云平台建立一个负载均衡器,将每一个Node做为后端,进行服务分发。该模式须要底层云平台(例如GCE)支持。
小编这里以一个yaml为例:
apiVersion: v1 kind: Service metadata: name: my-service spec: selector: app: MyApp ports: - protocol: TCP port: 80 targetPort: 9376 nodePort: 30061 clusterIP: 10.0.171.12 loadBalancerIP: 78.11.42.19 type: loadBalancer status: loadBalancer: ingress: - ip: 146.147.12.155 #这个是云服务商提供的负载IP
这里你们要注意了,这一节很是的重要,但愿你们必定细细阅读,之后在分析kubernetes核心时这一节是重中之重的基础。
Kubernetes的服务发现一共有两种方式,1.经过环境变量的方式,2.经过集群范围内的DNS来完成服务名到clusterIP的解析。这里小编带你们了解一下如何搭建DNS服务。
Kubernetes提供的虚拟DNS服务名为ksydns,由4个组件组成:
etcd:DNS存储
kube2sky:将kubernetes master中的service注册到etcd
skyDNS:提供DNS域名解析服务
healthz:提供skydns服务的健康检查功能
① 编写配置文件
② 修改每台node上的kubelet启动参数
③ 建立相应的资源对象
④ 测试
这里小编就不一一介绍了,给出搭建的地址:https://www.cnblogs.com/yujinyu/p/6112233.html
Kube2sky容器因供应用经过kubernetes master的API获取集群全部的service信息,并持续监控新service的生成,而后写入etcd中:
经过命令查看etcd中存储的service信息:
[root@zy ~]#kubectl exec kube-dns-v2-dc1s -c etcd --namespace=kube-system etcdctl ls /skydns/local/cluster/
此时看见目录结果以下:
/skydns/local/cluster/default
/skydns/local/cluster/kube-system
/skydns/local/cluster/svc
能够看见在skydns下是咱们配置的cluster.local(域名后缀),以后是命名空间,svc下也经过命名空间生成子目录。
而后查看具体的内容:
[root@zy ~]# kubectl exec kube-dns-v2-dc1s -c etcd --namespace=kube-system etcdctl get /skydns/local/cluster/default/服务名 #这样就能看见相应的clusterIP和域名映射
而后根据kubectl启动参数的设置(--cluster_dns),kubelet 会在每个新建立的pod中设置DNS域名解析,配置文件为:/etc/resolv.conf文件,会在其中加入一条,nameserver和search配置:
nameserver DNS解析服务IP search default.svc.cluster.local svc.cluster.local cluster.local localdomain
有了这些配置,应用程序就可以想访问网站域名同样,经过服务的名称访问到服务。总的来讲就是,kube2sky 经过kubernetes master的API 将新增的service持续存储到etcd中,每个新增的pod都会有一个/etc/resolv.conf文件,咱们在经过服务名称访问服务时,经过skyDNS 查询etcd,获取服务的IP,而后经过服务的IP就能直接访问到服务后端的endpoint中的容器中的应用。
从kubernetes1.6开始,用户能够在kubernetes集群内部配置私有的DNS区域和外部的上游域名服务,在kubernetes的pod定义中支持两个DNS策略,Default和ClusterFirst,dnsPolicy默认是ClusterFirst,若是是Default,域名解析配置则彻底从Pod的节点的/etc/resolv.conf中继承下来。
若是dnsPolicy设置的是ClusterFirst,则DNS查询会被发送到kube-dns(skydns)服务。kube-dns服务负责以集群域名为后缀(例cluster.local)进行服务的域名解析。
那么自定义的DNS和上游DNS又是啥呢?
由上图所示,当dnsPolicy设置为ClusterFirst时,DNS首先会被发送到kube-dns的缓存层,从这里检查域名的后缀,若是是cluster.local,则被发送到kube-dns服务,若是是自定义的*.out.of.kubernetes,则被发送到自定义解析器,若是二者均不符合,则被发送到上游的DNS中进行解析。
域名解析顺序:kube-dns ------ > 自定义DNS -------- > 上游DNS
自定义DNS方式:
从kubernetes1.6开始,集群管理者可使用configMap指定自定义的存根域和上游的DNS Server。
① 安装dnsmasq做为自定义的DNS服务
#安装 [root@zy ~]# yum install -y dnsmasq #生成一个自定义的DNS记录文件 /tmp/hosts [root@zy ~]# echo "192.168.130.131 server.out-of.kubernetes" > /tmp/hosts #启动DNS服务 [root@zy ~]# dnsmasq -q -d -h -q -R -H /tmp/hosts #参数解释: -d:以debug模式启动,在前台运行,便于观察日志 -q:输出查询记录 -h:不使用/etc/hosts -R:不使用/etc/resolve.conf -H:使用自定义的文件做为DNS记录
② 建立自定义DNS的configMap
#dns-configmap.yaml
apiVersion: v1 kind: ConfigMap metadata: name: kube-dns namespace: kube-system data: stubDomains: | {"out-of.kubernetes" : ["192.168.130.130"] } #自定义DNS服务器地址 upstreamNameservers: | #上游DNS地址 ["8.8.8.8","8.8.4.4"] 注意: stubDomains:表示存根域名,自定义DNS就在这里配置,Key是DNS后缀,value是一组DNS服务IP upstreamNameservers:表示上游DNS配置,若是指定了,那么从节点/etc/resovl.conf就会被覆盖。最多指定3个IP。
[root@zy ~]# kubectl create -f dns-configmap.yaml
③ 测试
apiVersion: v1 kind: Pod metadata: name: tester spec: dnsPolicy: ClusterFirst containers: - name: busybox image: docker.io/busybox imagePullPolicy: IfNotPresent command: ["sleep"] args: ["3600"]
建立一个pod,而后进入其中
[root@zy yaml_file]# kubectl exec -it tester – sh / # ping server.out-of.kubernetes
此时就会在dnsmasq的输出日志中,看见,server.out-of.kubernetes被转发到自定义DNS服务:192.168.130.130,而后经过DNS服务器,根据/tmp/hosts域名和IP的映射找到192.168.130.131。
Ingerss产生的缘由:咱们知道pod被deployment/RC管理的时候,pod重启以后其IP可能会改变,为了能准确的访问到pod中的服务,kubernetes使用service,而service会在宿主机上提供一个端口用于客户端访问,可是若是service不少的话其维护成本就会很高。若是能够借助于nginx的相似于虚拟主机的方式,经过不一样的URL可以访问到后端的不一样service,那么就少了service对宿主机的大量的端口映射,如何能作到这样呢?kubernetes1.1开始,新增了一个Ingress的资源对象,用于解决上述问题。
Ingerss介绍:Ingress 包含两大组件:Ingress Controller 和 Ingress。
原理图:
Ingress Controller做用:因为咱们是使用nginx的方式实现,那么每一次有新的service或者新的Ingress规则时,就要修改一次nginx.conf文件,这样实在太麻烦,因此Ingress Controller 就是专门解决这个问题,经过与 Kubernetes API 交互,动态的去感知集群中 Ingress 规则变化,而后读取他,按照他本身模板生成一段 Nginx 配置,再写到 Nginx Pod 里,最后 reload 一下。
Ingress Controller-service:为了让Ingress Controller能够对外提供服务,这里须要一个service,而service监听的端口就是80(http)|443(https)上图所示,该service映射到物理机的端口是30080和30443。
经过Ingress访问后端应用服务的过程(以web服务为例):首先定义一个deployment维持三个web服务的副本,而后给其web服务定义一个名为myapp的service,此时编写Ingress的规则经过host:myapp.zzy.com,并绑定了名为myapp的service,即当客户端访问myapp.zzy.com:30080时,会被转发到Ingress Controller-service的80端口上,根据Ingress Controller更新的Ingress规则,会将请求转发到
myapp:80,而后就能直接访问后端的pod中的容器,最后容器将请求响应会客户端。
注意:Ingress Controller将基于Ingress规则将客户端请求直接转发到service对应的后端的endpoint。
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: mywebsite-ingress spec: rules: - host: mywebsite.com http: paths: - path: /demo backend: serviceName: webapp servicePort: 8080
• rules:用于定义当前Ingress资源的转发规则列表;由rules定义规则,或没有匹配到规则时,全部的流量会转发到由backend定义的默认后端。
• backend:默认的后端用于服务那些没有匹配到任何规则的请求;定义Ingress资源时,必需要定义backend或rules二者之一,该字段用于让负载均衡器指定一个全局默认的后端。backend对象的定义由2个必要的字段组成:serviceName和servicePort,分别用于指定流量转发的后端目标Service资源名称和端口。
• host:包含 于 使用 的 TLS 证书 以内 的 主机 名称 字符串 列表
部署步骤:
部署方法小编已经在下面给出:本身动手也是成长的一部分嘛!
小编也从网上了找了许多的ingress 部署博客,好像基本上都是wget一些yaml文件,可是给出的地址好像都不能访问了,因此仍是老老实实的看文档搭建吧,加油加油!
https://github.com/kubernetes/ingress-nginx/blob/master/docs/deploy/index.md
① 转发到单个后端服务上
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: test-ingress spec: backend: serviceName: myweb servicePort: 8080
上面的配置对Ingress Controller的访问请求都将被转发到“myweb:8080”这个服务上。
② 同一域名下,不一样的URL路径被转发到不一样的服务上
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: test-ingress spec: rules: - host: mywebsite.com http: paths: - path: /web backend: serviceName: web-service servicePort: 80 - path: /api backend: serviceName: api-service servicePort: 8081
以上配置当访问:
mywebsite.com/web:80 ------转发到------ web-service:80
mywebsite.com/api:80 ------转发到------ api-service:8081
③ 不一样域名被转发到不一样的服务
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: test spec: rules: - host: foo.bar.com http: paths: - backend: serviceName: service1 servicePort: 80 - host:bar.foo.com http: paths: - backend: serviceName: service2 servicePort: 80
访问foo.bar.com-- service1:80 访问bar.foo.com -- service2:80
④ 不使用域名的转发规则
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: test-ingress spec: rules: - http: paths: - path: /demo backend: serviceName: webapp servicePort: 8080
这种配置,经过任意一条运行的ingress-controller 的node都能访问到后端的服务。
注意:这种方式默认是不能使用https访问的,若是想使用https访问,须要在编写Ingress规则的时候,加入一个annotation ”ingress.kubernetes.io/ssl-redirect=false” 来关闭强制启用HTTPS的设置。
因为小编以前用的是单机版的kubernetes集群,以前遇到过一些问题致使pod没法启动,而后就修改了apiservice的配置:
就将这个两个配置删除了。
而后建立Cassandra 的pod的时候就报错:
而后查看:[root@zy yaml_file]# kubectl get serviceaccount
解决:
在/etc/kubernetes/apiserver 中的--admission_control加入ServiceAccount:
而后在/etc/kubernetes/controller-manager配置:
--service_account_private_key_file=/var/run/kubernetes/apiserver.key
以后重启这两个服务:
再执行命令
[root@zy yaml_file]# kubectl get serviceaccount
小编原本觉得这样问题就解决了,结果查询Cassandra 的pod的日志发现:
小编初步推断多是集群没有搭建DNS:由于在查看SeedProvider 源码时:
后期会慢慢排查,将问题解决!
在service暴露端口时,发现外部访问时,以后 可使用IP访问,其余主机或者浏览器访问不到Kubernetes是1.5.2版本,这里小编设置了service的type为NodePort,而且nodePort设置了一个值,最终pod和service都启动正常,使用clusterIP和本机IP均可以访问,可是外部集群没法访问。
缘由:
使用service+NodePort时,其中的网络是上图所示,客户端访问时,须要经过kube-proxy这个服务,可是在1.2以上这个kube-proxy服务在启动时须要添加参数:
把KUBE_PROXY_ARGS=”“改成KUBE_PROXY_ARGS=”–proxy-mode=userspace”
解决:
修改master的/etc/kubernetes/proxy
将其中的启动命令的参数添加:
KUBE_PROXY_ARGS="--proxy-mode=userspace"
重启kube-proxy服务:
[root@zy yaml_file]# systemctl restart kube-proxy
而后在启动相应的pod和service,以后浏览器访问:
http://192.168.130.130:30000/
OK:
或者:
在默认的iptables mode下,修改(vim /etc/sysctl.conf)文件,加入:net.ipv4.ip_forward=1重启主机,而后在经过service访问便可。
到这里可能你们仍是不太明白为何service不能外部访问,须要修改kube-proxy的配置,这里小编就给你们介绍一下kube-proxy与service的关系:
kube-proxy其实就是管理service的访问入口,包括集群内Pod到Service的访问和集群外访问service。kube-proxy管理sevice的Endpoints,该service对外暴露一个Virtual IP,也成为Cluster IP, 集群内经过访问这个Cluster IP:Port就能访问到集群内对应的serivce下的Pod。service是经过Selector选择的一组Pods的服务抽象,其实就是一个微服务,提供了服务的LB和反向代理的能力,而kube-proxy的主要做用就是负责service的实现。
而kube-proxy内部原理:kube-proxy当前实现了两种proxyMode:userspace和iptables。其中userspace mode是v1.0及以前版本的默认模式,从v1.1版本中开始增长了iptables mode,在v1.2版本中正式替代userspace模式成为默认模式。小编的集群是1.5.2的默认的是iptables,因此须要作一些配置,所以咱们改成了userspace,就可让外部经过service暴露的端口映射到宿主机上来访问服务啦。
Userspace:userspace是在用户空间,经过kube-proxy来实现service的代理服务。
Iptables:它彻底利用Linux内核iptables来实现service的代理和LB