一、这三个都是什么东西
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
里面的port
跟pod
配置里的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。