services,kube-proxy,endpoints关系

大番茄 2019年11月04日 1,796次浏览

一、这三个都是什么东西

1、service

用来为一组相同功能的pod提供一个统一的访问入口,生成一个虚拟ip,用来负载均衡到后边的多个pod。

2、endpoints

表示后端pod节点,包含了后端pod的IP与Port, 这个不是创建完pod以后自动生成的,而是在创建service之后自动生成或是手动创建的,至于自动生成或是手动创建的endpoint是不是正确的对应了pod的ip与port,这就跟pod没关系了。

3、kube-proxy

通过获取service与endpoints还有namespace的信息,来自动生成负载均衡的规则。
注意了,service与endpoint还有namespace只是提供信息, 实际工作的是kube-proxy, 它通过生成iptables规则或是ipvs规则来实现负载均衡。来个例子:

[root@k8s-node1 bin]# ipvsadm -L -n
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  10.0.0.1:443 rr
  -> 172.100.102.137:6443         Masq    1      4          0
TCP  10.0.0.2:53 rr
  -> 10.1.21.4:53                 Masq    1      0          0
  -> 10.1.21.5:53                 Masq    1      0          0
TCP  10.0.0.2:9153 rr
  -> 10.1.21.4:9153               Masq    1      0          0
  -> 10.1.21.5:9153               Masq    1      0          0
UDP  10.0.0.2:53 rr
  -> 10.1.21.4:53                 Masq    1      0          0
  -> 10.1.21.5:53                 Masq    1      0          0

上面TCP和UDP后面的ip都是services的虚拟ip, 作为负载均衡的入口ip。
TCP和UDP下面的都是endpoints。
这里一共4个ipvs的入口规则。services有两个。

总结:

做个比喻就是, service的ip是桥头,endpoints的ip是桥尾, pod是过桥以后的地点(也可以说endpoints是pod的大门), kube-proxy是造桥的。 。

二、services 常用配置。

来几个例子,这些只是为了说明service, endpoints, kube-proxy之间的关系,负载均衡的实现。所以没有很详细的配置。

[root@k8s-master services]# kubectl get service -n kube-system
NAME                   TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)                  AGE
kube-dns               ClusterIP   10.0.0.2      <none>        53/UDP,53/TCP,9153/TCP   16d
kubernetes-dashboard   NodePort    10.0.65.81    <none>        443:30086/TCP            7d1h
tiller-deploy          ClusterIP   10.0.34.221   <none>        44134/TCP                6d23h

看一下kube-dns详细配置, 这里为了看起来直观把一些id、状态、时间、label去掉了。

[root@k8s-master services]# kubectl get service/kube-dns -n kube-system -o yaml
apiVersion: v1
kind: Service
metadata:
  name: kube-dns
  namespace: kube-system
spec:
  clusterIP: 10.0.0.2 
  ports:
  - name: dns
    port: 53
    protocol: UDP
    targetPort: 53
  - name: dns-tcp
    port: 53
    protocol: TCP
    targetPort: 53
  - name: metrics
    port: 9153
    protocol: TCP
    targetPort: 9153
  selector:
    k8s-app: kube-dns
  sessionAffinity: None          # 用来会话保持的。如ipvs(lvs)中的-p。
  type: ClusterIP
clusterIP 就是入口ip。

一般在创建的时候这个是不用写的,会在services地址池里自动分配。 可以在上面的ipvs规则里看到10.0.0.2这个。
只是这个services比较特殊,里面跑的coreDNS,需要固定ip让pod中的程序来访问, 这里面还有一个自动发现的功能与这个DNS有关,在后面会简单解释一下。

ports

port就是入口ip的访问端口。protocol 就是访问协议,默认就是TCP。
targetPort比较特殊,是用于自动生成endpoints的,大部分情况下都是要自动生成endpoints。
endpoints有ip与port, targetPort就是生成的endpoints的port。

selector

里面的k8s-app是手动指定的label, 后面的kube-dns是值。
用于查找有这些lable的pod,来确定endpoints的ip。
有ip有label也就自动创建了endpoints。

service 的几种类型。

1、ClusterIP 就是集群内的默认工作模式。
2、NodePort 包含了ClusterIP, 只是再在宿主机开一下端口映射DNAT,iptables实现, 让外部可以访问。
3、LoadBalancer 没有环境,缺乏测试。

service 的几种工作方式, 其中就有需要手动创建endpoint的。

1、没有selector。

一般的service都有selector来确定后端的pod,以生成endpoints。
而这种没有selector的就是要手动创建endpoints。只要service与endpoints的名字一样就行, 自动创建的endpoints也是名字一样的,才能让kube-proxy获取的时候能够识别。
这种方式主要用于内部的服务要访问集群外部,因为外部不能通过selector自动获取pod, 所以要手动加。

🌰:

apiVersion: v1
kind: Service
metadata:
  labels:
    app: service-test
  name: service-test
  namespace: kube-system
spec:
  type: ClusterIP
  ports:
  - name: work
    port: 6180
[root@k8s-master services]# kubectl apply -f test.yaml
service/service-test created
[root@k8s-master services]# kubectl get service/service-test -n kube-system
NAME           TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
service-test   ClusterIP   10.0.46.79   <none>        6180/TCP   43s

可以看到入口ip为10.0.46.79, 然后来看一下ipvs的规则。

TCP  10.0.44.232:6660 rr
  -> 10.1.21.16:6660              Masq    1      0          0
TCP  10.0.46.79:6180 rr
TCP  10.0.49.183:8010 rr
  -> 10.1.21.62:8010              Masq    1      0          0
TCP  10.0.49.183:8040 rr
  -> 10.1.21.62:8040              Masq    1      0          0

看第二个TCP,只有入口IP而没有后端。这种情况下创建name相同的endpoints就会有了。
🌰:

apiVersion: v1
kind: Endpoints
metadata:
  labels:
    app: service-test
  name: service-test
  namespace: kube-system
subsets:
- addresses:
  - ip: 10.1.21.32
  ports:
  - name: work
    port: 6180

应用查看service没有变化。

[root@k8s-master services]# kubectl apply -f test-end.yaml
[root@k8s-master services]# kubectl get service/service-test -n kube-system
NAME           TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
service-test   ClusterIP   10.0.46.79   <none>        6180/TCP   27m

查看ipvs规则。

TCP  10.0.44.232:6660 rr
  -> 10.1.21.16:6660              Masq    1      0          0
TCP  10.0.46.79:6180 rr
  -> 10.1.21.32:6180              Masq    1      0          0
TCP  10.0.49.183:8010 rr
  -> 10.1.21.62:8010              Masq    1      0          0
TCP  10.0.49.183:8040 rr

1、ClusterIP: None。 叫做 Headless Service。

有时候不想访问入口ip, 希望直接访问后端pod的ip。就使用这个。没有入口ip, 当然也就用不到负载均衡了。

apiVersion: v1
kind: Service
metadata:
  labels:
    app: service-test
  name: service-test
  namespace: default
spec:
  clusterIP: None
  type: ClusterIP
  ports:
  - name: work
    port: 4330
    protocol: TCP
    targetPort: 4330
  selector:
    app: session
[root@k8s-master services]# kubectl apply -f test-session.yaml
[root@k8s-master services]# kubectl get services
NAME           TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
kubernetes     ClusterIP   10.0.0.1     <none>        443/TCP    19d
service-test   ClusterIP   None         <none>        4330/TCP   151m
[root@k8s-master services]# kubectl get endpoints
NAME           ENDPOINTS                                                     AGE
kubernetes     172.100.102.137:6443                                          19d
service-test   10.1.21.27:4330,10.1.21.41:4330,10.1.21.42:4330 + 2 more...   151m

可以看到service的cluster-ip部分是None, 而endpoints是自动创建的, 这里因为后端服务开了多份,所以显示的地址有点多。
dns会把这种service的pod的所有ip与service名称都放到集群dns里,就好像是多个A纪录。
查看集群dns的解析,注意coreDNS有配置来源限制的,测试的时候注意。

[root@k8s-node1 ~]# nslookup service-test.default.svc.cluster.local 10.0.0.2
Server:		10.0.0.2
Address:	10.0.0.2#53

Name:	service-test.default.svc.cluster.local
Address: 10.1.21.42
Name:	service-test.default.svc.cluster.local
Address: 10.1.21.27
Name:	service-test.default.svc.cluster.local
Address: 10.1.21.43
Name:	service-test.default.svc.cluster.local
Address: 10.1.21.41
Name:	service-test.default.svc.cluster.local
Address: 10.1.21.44

这样就实现了,自动查找后端的所有pod。 对于这个服务想用独特的负载均衡,或者使用集群的StatefulSet, 都会用到Headless Service这种方式。

这里稍微了解一下自动发现,其实说自动发现并不精确, 因为这个是创建的service生成的,并不是自动发现后端的服务。也是有两种:变量与dns。

变量。

k8s内部默认会在启动pod的时候会把相同namespace的service以及kubernetes的services的ip与端口以变量的方式传递给pod, pod中的服务只要使用这个变量就可以访问对应的服务,而不用使用对应服务的ip。
看一下传递的变量🌰:
docker inspect 可以看到:

           "Env": [
                "KUBE_DNS_SERVICE_PORT=53",
                "KUBE_DNS_PORT_53_UDP_PORT=53",
                "KUBE_DNS_PORT_9153_TCP_PROTO=tcp",
                "KUBERNETES_DASHBOARD_PORT=tcp://10.0.65.81:443",
                "KUBERNETES_DASHBOARD_PORT_443_TCP_PROTO=tcp",
                "TILLER_DEPLOY_PORT_44134_TCP_PROTO=tcp",
                "KUBE_DNS_SERVICE_HOST=10.0.0.2",
                "KUBE_DNS_PORT_53_TCP_PROTO=tcp",
                "KUBE_DNS_PORT_53_TCP_PORT=53",
                "KUBERNETES_SERVICE_PORT=443",
                "KUBERNETES_PORT=tcp://10.0.0.1:443",
                "KUBERNETES_DASHBOARD_SERVICE_HOST=10.0.65.81",
                "KUBERNETES_DASHBOARD_PORT_443_TCP=tcp://10.0.65.81:443",
                "SERVICE_TEST_PORT_6180_TCP_PROTO=tcp",
                "KUBERNETES_DASHBOARD_PORT_443_TCP_PORT=443",
                "SERVICE_TEST_SERVICE_PORT=6180",
                "SERVICE_TEST_PORT_6180_TCP_ADDR=10.0.46.79",
                "KUBE_DNS_PORT=udp://10.0.0.2:53",
                "KUBE_DNS_PORT_9153_TCP_PORT=9153",
                "KUBERNETES_PORT_443_TCP_PROTO=tcp",
                "TILLER_DEPLOY_PORT_44134_TCP_ADDR=10.0.34.221",
                "TILLER_DEPLOY_PORT_44134_TCP=tcp://10.0.34.221:44134",
                "TILLER_DEPLOY_PORT_44134_TCP_PORT=44134",
                "SERVICE_TEST_PORT_6180_TCP=tcp://10.0.46.79:6180",
                "KUBE_DNS_SERVICE_PORT_DNS_TCP=53",
                "KUBE_DNS_PORT_53_UDP_ADDR=10.0.0.2",
                "KUBERNETES_SERVICE_PORT_HTTPS=443",
                "KUBERNETES_PORT_443_TCP=tcp://10.0.0.1:443",
                "TILLER_DEPLOY_SERVICE_PORT=44134",
                "SERVICE_TEST_SERVICE_PORT_WORK=6180",
                "KUBE_DNS_PORT_53_TCP=tcp://10.0.0.2:53",
                "KUBERNETES_SERVICE_HOST=10.0.0.1",
                "TILLER_DEPLOY_SERVICE_HOST=10.0.34.221",
                "SERVICE_TEST_SERVICE_HOST=10.0.46.79",
                "KUBE_DNS_PORT_53_UDP_PROTO=udp",
                "KUBERNETES_DASHBOARD_SERVICE_PORT=443",
                "TILLER_DEPLOY_SERVICE_PORT_TILLER=44134",
                "SERVICE_TEST_PORT=tcp://10.0.46.79:6180",
                "SERVICE_TEST_PORT_6180_TCP_PORT=6180",
                "KUBE_DNS_SERVICE_PORT_DNS=53",
                "KUBE_DNS_SERVICE_PORT_METRICS=9153",
                "KUBE_DNS_PORT_53_UDP=udp://10.0.0.2:53",
                "KUBE_DNS_PORT_9153_TCP=tcp://10.0.0.2:9153",
                "KUBERNETES_PORT_443_TCP_ADDR=10.0.0.1",
                "TILLER_DEPLOY_PORT=tcp://10.0.34.221:44134",
                "KUBE_DNS_PORT_9153_TCP_ADDR=10.0.0.2",
                "KUBERNETES_PORT_443_TCP_PORT=443",
                "KUBERNETES_DASHBOARD_PORT_443_TCP_ADDR=10.0.65.81",
                "KUBE_DNS_PORT_53_TCP_ADDR=10.0.0.2",
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
            ],

变量这种方式是k8s自己加的, 但是这种方式有一个问题,就是pod的启动需要在所要访问的pod的service之后,因为service需要先注册到k8s。还有Headless Service 不会塞进去。

DNS

现在主要使用这种, 但是默认情况下变量还是会提供到pod里。 DNS与k8s没有关系,它只是运行在k8s集群中的一个服务,只是跟业务服务不同的是它需要连接kube-apiserver的api接口, 获取service的名称与ip,还有namespace,endpoints之类的。最终形成ip与名称的解析。因为dns程序是从api-server主动获取信息实时的生成解析记录,所以不存在服务启动顺序问题,而且dns的pod也可以启动多个。现在常用的是CoreDNS,可选kube-dns。

三、endpoints

[root@k8s-master services]# kubectl get endpoints/kube-dns -n kube-system -o yaml
apiVersion: v1
kind: Endpoints
metadata:
  name: kube-dns
  namespace: kube-system
subsets:
- addresses:
  - ip: 10.1.21.4
    nodeName: k8s-node1
  - ip: 10.1.21.5
    nodeName: k8s-node2
  ports:
  - name: dns
    port: 53
    protocol: UDP
  - name: dns-tcp
    port: 53
    protocol: TCP
  - name: metrics                 # coreDNS中的状态信息监控接口。
    port: 9153
    protocol: TCP

表示有两个后端, 每个都启了3个端口。
自动创建的endpoints是由kube-controller-manager中的endpoint控制器根据pod的增删更新操作实时更新。

endpoints不管手动创建的还是service自动创建的,service与endpint的名称都要相同。

注意:
endpoints里面的portpod配置里的containerPort没有关系,上面配置的port: 53, 那么targetport也是53。

四、 kube-proxy

kube-proxy是用service的入口ip与port生成lvs的入口规则,由endpoints生成后边的节点。
iptables也一样,添加规则来实现相同的功能。
kube-proxy 一直 watch 着 kube-apiserver, 实时关注service 与 endpoints还有namespace的变化,并同步到负载均衡规则中。
注意: 已经删除的service。 在ipvs或是iptables中的规则不会立即删除, 这需要有一个周期。

kube-proxy有几种工作模式:userspace、 iptables、 ipvs。