【Kubernetes 009】各种Service类型以及对应操作详解(ClusterIP,NodePort,ExternalName)

前面我们学习了通过控制器去批量创建和管理pod,有了pod就可以创造一个虚拟ip配合负载均衡对外提供服务了。现在新的问题又来了,如何在多个pod副本之间形成负载均衡?坏的pod被自动替换掉却有了新的ip,又该如何将新ip加入负载均衡?如何将pod提供的服务暴露给外网客户端?这些问题都需要k8s中一个叫做Service的东西来解答,这一节我们就一起来学习下Service。

我是T型人小付,一位坚持终身学习的互联网从业者。喜欢我的博客欢迎在csdn上关注我,如果有问题欢迎在底下的评论区交流,谢谢。

文章目录

    • Service的定义
    • Service的类型
    • Service实现机制演进
      • 修改iptables模式为ipvs模式
    • 实际操作
      • ClusterIP
      • NodePort
      • LoadBalancer
      • ExternalName
    • 总结

Service的定义

Service就是k8s集群中的一群具有某种共性的pod,这些pod通过service的组织对外统一提供服务,service也被叫做微服务。

每个service有自己的一套label选择器,符合这些label选择器的pod就被认为属于该service,而被该service管理。service会自动获取其管理pod的ip并制定负载均衡,pod的ip有变化也会自动同步到service中。

这里说的负载均衡都是基于IP的4层负载均衡

下面这个图可以很好地说明这个关系
【Kubernetes 009】各种Service类型以及对应操作详解(ClusterIP,NodePort,ExternalName)_第1张图片

图中有一个叫Frontend的Service通过两个标签app=webapp,role=frontend去选择符合条件的pod,只要pod的标签中包含这两个标签即认为符合条件,标签可多不可缺。同时Service的定义中将Service的IP的443端口映射到后端pod的443端口。之后外界访问Service的443端口就会自动负载均衡到后端的3个pod中的一个。

Service的类型

k8s中的Service有如下几种类型,对应着定义Service的yaml文件的type字段

  • ClusterIP
    默认类型,该Service的虚拟IP仅为集群内部访问
  • NodePort
    在ClusterIP的基础上,在每个Node的物理网卡上都为该Service建立一个相同的端口映射,例如上图中将每个Node的8443端口都映射到Frontend这个Service的443端口,这样不管外部访问哪个Node的IP:8443都可以访问到该Service。如果要对集群外提供服务采用该方式,并且通常在Node的前面加上针对Node物理网卡IP的负载均衡
  • LoadBalancer
    在NodePort的基础上,借助第三方的云服务提供Node物理网卡IP的负载均衡。企业中用的不多,因为第三方云服务要额外收费,并且完全可以用免费方案代替
  • ExternalName
    相当于给集群外部的一个第三方服务加了一个DNS的CNAME记录,将外部流量引入集群内部

Service实现机制演进

Service的表象就是每个Node的kube-proxy进程为Service创造了一个虚拟IP,也就是VIP,到后端pod的映射关系。但是为了达到这个目的,k8s一直在改进实现机制。

了解这些实现机制,有助于我们去验证和排查Service到后端的负载均衡问题。

  • k8s 1.0版本,采用userspace方式,已经被完全弃用
  • k8s 1.1版本,增加了iptables方式,并在k8s 1.2版本变为默认方式
    iptables会将负载均衡的规则写入NAT表中,可以通过iptabels -nvL -t nat来查看规则
  • k8s 1.8版本,增加了ipvs方式,并在k8s 1.14版本变为默认方式
    ipvs有自己的管理工具ipvsadm,可以通过ipvsadm -Ln来查看规则

不使用DNS做负载均衡,因为DNS会被缓存,往往达不到均衡的效果

IPVS因为远高于iptables的性能,而在新版本中被优先采用。但是如果没有安装IPVS的模块,即使配置了IPVS方式,k8s还是会回退到iptables的方式

修改iptables模式为ipvs模式

如果因为某些原因发现安装的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仓库

ClusterIP

首先通过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

NodePort

下面用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      

LoadBalancer

因为需要额外收费的第三方云服务,这里就不演示了。相信企业中采用这种方式的也很少。

ExternalName

通过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。

你可能感兴趣的:(Devops,-,Kubernetes)