前面我们学习了通过控制器去批量创建和管理pod,有了pod就可以创造一个虚拟ip配合负载均衡对外提供服务了。现在新的问题又来了,如何在多个pod副本之间形成负载均衡?坏的pod被自动替换掉却有了新的ip,又该如何将新ip加入负载均衡?如何将pod提供的服务暴露给外网客户端?这些问题都需要k8s中一个叫做Service的东西来解答,这一节我们就一起来学习下Service。
我是T型人小付,一位坚持终身学习的互联网从业者。喜欢我的博客欢迎在csdn上关注我,如果有问题欢迎在底下的评论区交流,谢谢。
Service就是k8s集群中的一群具有某种共性的pod,这些pod通过service的组织对外统一提供服务,service也被叫做微服务。
每个service有自己的一套label选择器,符合这些label选择器的pod就被认为属于该service,而被该service管理。service会自动获取其管理pod的ip并制定负载均衡,pod的ip有变化也会自动同步到service中。
这里说的负载均衡都是基于IP的4层负载均衡
图中有一个叫Frontend的Service通过两个标签app=webapp,role=frontend
去选择符合条件的pod,只要pod的标签中包含这两个标签即认为符合条件,标签可多不可缺。同时Service的定义中将Service的IP的443端口映射到后端pod的443端口。之后外界访问Service的443端口就会自动负载均衡到后端的3个pod中的一个。
k8s中的Service有如下几种类型,对应着定义Service的yaml文件的type字段
Service的表象就是每个Node的kube-proxy进程为Service创造了一个虚拟IP,也就是VIP,到后端pod的映射关系。但是为了达到这个目的,k8s一直在改进实现机制。
了解这些实现机制,有助于我们去验证和排查Service到后端的负载均衡问题。
iptabels -nvL -t nat
来查看规则ipvsadm -Ln
来查看规则不使用DNS做负载均衡,因为DNS会被缓存,往往达不到均衡的效果
IPVS因为远高于iptables的性能,而在新版本中被优先采用。但是如果没有安装IPVS的模块,即使配置了IPVS方式,k8s还是会回退到iptables的方式。
如果因为某些原因发现安装的1.15版本的k8s还是使用的iptables模式,按照以下方式修改为ipvs模式。
编辑kube-proxy的配置文件
kubectl edit configmap kube-proxy -n kube-system
将其中的mode ""
改为mode: ipvs
,然后:wq
保存退出
删除现有的kube-proxy,让k8s自动替换新的
kubectl delete pod -n kube-system
之后查看新的kube-poxy的log,见到如下字样表示成功启用ipvs模式
Using ipvs Proxier
例如
[root@k8s-master k8s-test]# kubectl logs kube-proxy-4fw7f -n kube-system
I0506 15:56:08.640887 1 server_others.go:170] Using ipvs Proxier.
W0506 15:56:08.664133 1 proxier.go:401] IPVS scheduler not specified, use rr by default
I0506 15:56:08.664714 1 server.go:534] Version: v1.15.0
I0506 15:56:08.693167 1 conntrack.go:52] Setting nf_conntrack_max to 131072
I0506 15:56:08.693460 1 config.go:96] Starting endpoints config controller
I0506 15:56:08.693485 1 controller_utils.go:1029] Waiting for caches to sync for endpoints config controller
I0506 15:56:08.693596 1 config.go:187] Starting service config controller
I0506 15:56:08.693608 1 controller_utils.go:1029] Waiting for caches to sync for service config controller
I0506 15:56:08.793590 1 controller_utils.go:1036] Caches are synced for endpoints config controller
I0506 15:56:08.793762 1 controller_utils.go:1036] Caches are synced for service config controller
以下所有yaml文件托管在我的Github仓库
首先通过yaml文件mynginx-deployment.yaml
创建出3个pod
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: mynginx-deployment
spec:
replicas: 3
template:
metadata:
labels:
app: mynginx
version: v2
spec:
containers:
- name: mynginx
image: mynginx:v2
ports:
- containerPort: 80
创建的pod信息如下
[root@k8s-master k8s-test]# kubectl apply -f mynginx-deployment.yaml
deployment.extensions/mynginx-deployment created
[root@k8s-master k8s-test]# kubectl get pod -o wide --show-labels
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS
mynginx-deployment-b66f59f66-98s7s 1/1 Running 0 11s 10.244.1.70 k8s-node1 app=mynginx,pod-template-hash=b66f59f66,version=v2
mynginx-deployment-b66f59f66-snd7r 1/1 Running 0 11s 10.244.1.69 k8s-node1 app=mynginx,pod-template-hash=b66f59f66,version=v2
mynginx-deployment-b66f59f66-zcp44 1/1 Running 0 11s 10.244.0.14 k8s-master app=mynginx,pod-template-hash=b66f59f66,version=v2
之后创建yaml文件mynginx-service.yaml
用来创建service
apiVersion: v1
kind: Service
metadata:
name: mynginx-service
namespace: default
spec:
type: ClusterIP
selector:
app: mynginx
version: v2
ports:
- name: http
port: 8080
targetPort: 80
创建的service信息如下
[root@k8s-master k8s-test]# kubectl apply -f mynginx-service.yaml
service/mynginx-service created
[root@k8s-master k8s-test]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 443/TCP 8d
mynginx-service ClusterIP 10.98.205.0 8080/TCP 67s
可以看到service已经起来,验证下发现确实是采用ipvs方式做的负载均衡
[root@k8s-master k8s-test]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.96.0.1:443 rr
-> 172.29.56.175:6443 Masq 1 0 0
TCP 10.96.0.10:53 rr
-> 10.244.0.2:53 Masq 1 0 0
-> 10.244.0.3:53 Masq 1 0 0
TCP 10.96.0.10:9153 rr
-> 10.244.0.2:9153 Masq 1 0 0
-> 10.244.0.3:9153 Masq 1 0 0
TCP 10.98.205.0:8080 rr
-> 10.244.0.14:80 Masq 1 0 0
-> 10.244.1.69:80 Masq 1 0 0
-> 10.244.1.70:80 Masq 1 0 0
UDP 10.96.0.10:53 rr
-> 10.244.0.2:53 Masq 1 0 0
-> 10.244.0.3:53 Masq 1 0 0
可以看到Service的vip的8080端口后面采用rr方式跟了三个pod的ip的80端口,这时为了验证,我事先分别在3个pod的index.html
中写入各自的ip地址
[root@k8s-master k8s-test]# curl 10.98.205.0:8080
v2 | 10.244.1.70
[root@k8s-master k8s-test]# curl 10.98.205.0:8080
v2 | 10.244.1.69
[root@k8s-master k8s-test]# curl 10.98.205.0:8080
v2 | 10.244.0.14
[root@k8s-master k8s-test]# curl 10.98.205.0:8080
v2 | 10.244.1.70
[root@k8s-master k8s-test]# curl 10.98.205.0:8080
v2 | 10.244.1.69
[root@k8s-master k8s-test]# curl 10.98.205.0:8080
v2 | 10.244.0.14
当然也可以通过域名来访问service,域名的格式为
..svc.cluster.local
例如
[root@k8s-master k8s-test]# nslookup mynginx-service.default.svc.cluster.local 10.244.0.2
Server: 10.244.0.2
Address: 10.244.0.2#53
Name: mynginx-service.default.svc.cluster.local
Address: 10.98.205.0
其中的10.244.0.2
是其中一个CoreDNS的ip
下面用yaml文件mynginx-service-nodeport.yaml
去创建一个nodeport的service
apiVersion: v1
kind: Service
metadata:
name: mynginx-service
namespace: default
spec:
type: NodePort
selector:
app: mynginx
version: v2
ports:
- name: http
port: 8080
targetPort: 80
nodePort: 30000
这里和上面的ClusterIP没有太多变化,除了type
改为了NodePort
,还多了一个nodePort
指定了一个物理网卡上打开的端口,这个字段也可以不指定,k8s会自动指定一个。
结果如下
[root@k8s-master k8s-test]# kubectl apply -f mynginx-service-nodeport.yaml
service/mynginx-service-nodeport created
[root@k8s-master k8s-test]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 443/TCP 8d
mynginx-service ClusterIP 10.98.205.0 8080/TCP 88m
mynginx-service-nodeport NodePort 10.107.255.100 8080:30000/TCP 4s
这时从另外一台集群外的机器上访问两台node的30000端口就可以看到pod的信息,例如从我的windows机器上
C:\Users\Admin>curl 172.29.56.175:30000
v2 | 10.244.1.70
C:\Users\Admin>curl 172.29.56.175:30000
v2 | 10.244.1.69
C:\Users\Admin>curl 172.29.56.175:30000
v2 | 10.244.0.14
C:\Users\Admin>curl 172.29.56.176:30000
v2 | 10.244.1.70
C:\Users\Admin>curl 172.29.56.176:30000
v2 | 10.244.1.69
C:\Users\Admin>curl 172.29.56.176:30000
v2 | 10.244.0.14
并且在两台node上分别查询ipvs信息也会看到
[root@k8s-master ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.17.0.1:30000 rr
-> 10.244.0.14:80 Masq 1 0 0
-> 10.244.1.69:80 Masq 1 0 0
-> 10.244.1.70:80 Masq 1 0 0
TCP 172.29.56.175:30000 rr
-> 10.244.0.14:80 Masq 1 0 0
-> 10.244.1.69:80 Masq 1 0 0
-> 10.244.1.70:80 Masq 1 0 0
TCP 10.96.0.1:443 rr
-> 172.29.56.175:6443 Masq 1 0 0
TCP 10.96.0.10:53 rr
-> 10.244.0.2:53 Masq 1 0 0
-> 10.244.0.3:53 Masq 1 0 0
TCP 10.96.0.10:9153 rr
-> 10.244.0.2:9153 Masq 1 0 0
-> 10.244.0.3:9153 Masq 1 0 0
TCP 10.98.205.0:8080 rr
-> 10.244.0.14:80 Masq 1 0 0
-> 10.244.1.69:80 Masq 1 0 0
-> 10.244.1.70:80 Masq 1 0 0
TCP 10.107.255.100:8080 rr
-> 10.244.0.14:80 Masq 1 0 0
-> 10.244.1.69:80 Masq 1 0 0
-> 10.244.1.70:80 Masq 1 0 0
TCP 10.244.0.0:30000 rr
-> 10.244.0.14:80 Masq 1 0 0
-> 10.244.1.69:80 Masq 1 0 0
-> 10.244.1.70:80 Masq 1 0 0
TCP 10.244.0.1:30000 rr
-> 10.244.0.14:80 Masq 1 0 0
-> 10.244.1.69:80 Masq 1 0 0
-> 10.244.1.70:80 Masq 1 0 0
TCP 127.0.0.1:30000 rr
-> 10.244.0.14:80 Masq 1 0 0
-> 10.244.1.69:80 Masq 1 0 0
-> 10.244.1.70:80 Masq 1 0 0
UDP 10.96.0.10:53 rr
-> 10.244.0.2:53 Masq 1 0 0
-> 10.244.0.3:53 Masq 1 0 0
[root@k8s-node1 ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.17.0.1:30000 rr
-> 10.244.0.14:80 Masq 1 0 0
-> 10.244.1.69:80 Masq 1 0 0
-> 10.244.1.70:80 Masq 1 0 0
TCP 172.29.56.176:30000 rr
-> 10.244.0.14:80 Masq 1 0 0
-> 10.244.1.69:80 Masq 1 0 0
-> 10.244.1.70:80 Masq 1 0 0
TCP 10.96.0.1:443 rr
-> 172.29.56.175:6443 Masq 1 0 0
TCP 10.96.0.10:53 rr
-> 10.244.0.2:53 Masq 1 0 0
-> 10.244.0.3:53 Masq 1 0 0
TCP 10.96.0.10:9153 rr
-> 10.244.0.2:9153 Masq 1 0 0
-> 10.244.0.3:9153 Masq 1 0 0
TCP 10.98.205.0:8080 rr
-> 10.244.0.14:80 Masq 1 0 0
-> 10.244.1.69:80 Masq 1 0 0
-> 10.244.1.70:80 Masq 1 0 0
TCP 10.107.255.100:8080 rr
-> 10.244.0.14:80 Masq 1 0 0
-> 10.244.1.69:80 Masq 1 0 0
-> 10.244.1.70:80 Masq 1 0 0
TCP 10.244.1.0:30000 rr
-> 10.244.0.14:80 Masq 1 0 0
-> 10.244.1.69:80 Masq 1 0 0
-> 10.244.1.70:80 Masq 1 0 0
TCP 10.244.1.1:30000 rr
-> 10.244.0.14:80 Masq 1 0 0
-> 10.244.1.69:80 Masq 1 0 0
-> 10.244.1.70:80 Masq 1 0 0
TCP 127.0.0.1:30000 rr
-> 10.244.0.14:80 Masq 1 0 0
-> 10.244.1.69:80 Masq 1 0 0
-> 10.244.1.70:80 Masq 1 0 0
UDP 10.96.0.10:53 rr
-> 10.244.0.2:53 Masq 1 0 0
-> 10.244.0.3:53 Masq 1 0 0
因为需要额外收费的第三方云服务,这里就不演示了。相信企业中采用这种方式的也很少。
通过yaml文件test-externalname.yaml
创建一个service
apiVersion: v1
kind: Service
metadata:
name: test-externalname
namespace: default
spec:
type: ExternalName
externalName: www.baidu.com
相当于在内部创建了一个www.baidu.com
的别名。创建结果如下
[root@k8s-master k8s-test]# kubectl apply -f test-externalname.yaml
service/test-externalname created
[root@k8s-master k8s-test]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 443/TCP 8d
mynginx-service ClusterIP 10.98.205.0 8080/TCP 10h
mynginx-service-nodeport NodePort 10.107.255.100 8080:30000/TCP 9h
test-externalname ExternalName www.baidu.com 7s
这时候在集群内部就可以用cname进行查询了,cname的格式和之前的一样
[root@k8s-master k8s-test]# nslookup test-externalname.default.svc.cluster.local 10.244.0.3
Server: 10.244.0.3
Address: 10.244.0.3#53
test-externalname.default.svc.cluster.local canonical name = www.baidu.com.
www.baidu.com canonical name = www.a.shifen.com.
www.a.shifen.com canonical name = www.wshifen.com.
Name: www.wshifen.com
Address: 104.193.88.77
Name: www.wshifen.com
Address: 104.193.88.123
这一节我们学习了k8s中的几种基本的service类型以及对应的操作,但是这些都是基于4层做的负载均衡,如果要实现7层的负载均衡,就需要一种特殊的Service了,也就是下一节我们要一起学习的Ingress。