service是要通过coreDNS来管理pod的。

kube-proxy始终监视着apiserver,获取与service资源的变动状态。一旦发现有service资源发生变动,kube-proxy都要把它转变为当前节点之上的,能够实现service资源调度,包括将客户端资源调度到pod的规则(iptables或者ipvs)。

service工作模式有三种:userspace(k8s 1.1版本之前),iptables(k8s 1.10版本之前)和ipvs(k8s 1.11版本之后)

userspace模型:用户空间模型。k8s 1.11版本用ipvs了,这个比iptables效率高。

[root@master~]#kubectlgetsvcNAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGEkubernetesClusterIP10.96.0.1<none>443/TCP16dredisClusterIP10.106.138.181<none>6379/TCP23h

[root@master~]#kubectldeletesvcredisservice"redis"deleted

注意kubernetes这个service千万别删,因为K8s都是通过10.96.0.1这个地址联系的。

[root@master~]#kubectlexplainsvc

service类型:

a)、ExternalName:表示把集群外部的服务引入到集群内部中来,即实现了集群内部pod和集群外部的服务进行通信;

b)、ClusterIP:只能在集群内部通讯;

c)、NodePort:可以和集群外部通讯;

d)、LoadBalancer:这个表示我们把k8s部署在虚拟机上,自动在外部创建负载均衡器。比如在阿里云上,底层是LBAAS。

ClusterIP

以前我们使用kubectl expose创建service,下面我们使用清单来创建service。

[root@mastermanifests]#catredis-svc.yamlapiVersion:v1kind:Servicemetadata:name:redisnamespace:defaultspec:selector:app:redisrole:logstorclusterIP:10.97.97.97#这个ip可以不指定,让它自动分配type:ClusterIPports:-port:6379#serviceip中的端口targetPort:6379#容器ip中的端口

[root@mastermanifests]#kubectlapply-fredis-svc.yamlservice/rediscreated

[root@mastermanifests]#kubectlgetsvcNAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGEkubernetesClusterIP10.96.0.1<none>443/TCP16dredisClusterIP10.97.97.97<none>6379/TCP1m

[root@mastermanifests]#kubectldescribesvcredisName:redisNamespace:defaultLabels:<none>Annotations:kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"redis","namespace":"default"},"spec":{"clusterIP":"10.97.97.97","ports":[{"por...Selector:app=redis,role=logstorType:ClusterIPIP:10.97.97.97Port:<unset>6379/TCPTargetPort:6379/TCPEndpoints:10.244.2.65:6379##这是pod的ipSessionAffinity:NoneEvents:<none>

资源记录:SVC_NAME.NAMESPACE_NAME.DOMAIN.LTD

集群默认后缀是svc.cluster.local

比如我们创建的redis默认名称就是redis.defalut.svc.cluster.local

NodePort

访问路径:client---->NodeIP:NodePoint----->ClusterIP:ServerPort---->PodIP:containerPort

[root@mastermanifests]#catmyapp-svc.yamlapiVersion:v1kind:Servicemetadata:name:myappnamespace:defaultspec:selector:app:myapprelease:canaryclusterIP:10.99.99.99#这个ip可以不指定,让他自动分配type:NodePortports:-port:80#serviceip的端口targetPort:80#容器ip的端口nodePort:30080#node节点上端口,这个端口也可以不指定,会自动分配端口

[root@mastermanifests]#kubectlapply-fmyapp-svc.yamlservice/myappcreated

[root@mastermanifests]#kubectlgetsvcNAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGEkubernetesClusterIP10.96.0.1<none>443/TCP16dmyappNodePort10.99.99.99<none>80:30080/TCP44sredisClusterIP10.97.97.97<none>6379/TCP15m

[root@mastermanifests]#curlhttp://172.16.1.101:30080/hostname.htmlmyapp-deploy-69b47bc96d-79fqh[root@mastermanifests]#curlhttp://172.16.1.101:30080/hostname.htmlmyapp-deploy-69b47bc96d-tc54k

注意:172.16.1.101是Node节点的Ip,并且可以看到访问是做了负载均衡的

下面我们再把来自同一个客户端会话请求用sessionAffinity粘滞到一个固定的pod上,这样就不会出现负载均衡现象了,相当于nginx的 ip_hash功能,如下:

[root@mastermanifests]#kubectlpatchsvcmyapp-p'{"spec":{"sessionAffinity":"ClientIP"}}'service/myapppatched

上面打的补丁也可以直接用edit方法编辑。

[root@mastermanifests]#kubectldescribesvcmyappSessionAffinity:ClientIP

再改回负载均衡模式:[root@mastermanifests]#kubectlpatchsvcmyapp-p'{"spec":{"sessionAffinity":"None"}}'service/myapppatched无头service

我们前面介绍的service是service name解析为cluster ip,然后cluster ip对应到后面的pod ip。

而无头service是指service name 直接解析为后面的pod ip。

无头就是没有cluster ip牵头了。

[root@mastermanifests]#catmyapp-svc-headless.yamlapiVersion:v1kind:Servicemetadata:name:myapp-svcnamespace:defaultspec:selector:app:myapp#挑选的pod还是myapp。一个pod可以有多个servicerelease:canaryclusterIP:None#None表示是无头serviceports:-port:80#serviceip中的端口targetPort:80#容器ip中的端口

[root@mastermanifests]#kubectlapply-fmyapp-svc-headless.yamlservice/myapp-svccreated

[root@mastermanifests]#kubectlgetsvcNAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGEkubernetesClusterIP10.96.0.1<none>443/TCP16dmyappNodePort10.99.99.99<none>80:30080/TCP1hmyapp-svcClusterIPNone<none>80/TCP28sredisClusterIP10.97.97.97<none>6379/TCP1h

看到上面myapp-svc的cluster-ip是空,这就是无头service。

[root@mastermanifests]#kubectlgetsvc-nkube-systemNAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGEkube-dnsClusterIP10.96.0.10<none>53/UDP,53/TCP16d

可以看到coreDNS的地址是10.96.0.10

[root@mastermanifests]#dig-tAmyapp-svc.default.svc.cluster.local.@10.96.0.10;<<>>DiG9.9.4-RedHat-9.9.4-61.el7_5.1<<>>-tAmyapp-svc.default.svc.cluster.local.@10.96.0.10;;globaloptions:+cmd;;Gotanswer:;;->>HEADER<<-opcode:QUERY,status:NOERROR,id:10702;;flags:qraardra;QUERY:1,ANSWER:2,AUTHORITY:0,ADDITIONAL:1;;OPTPSEUDOSECTION:;EDNS:version:0,flags:;udp:4096;;QUESTIONSECTION:;myapp-svc.default.svc.cluster.local.INA;;ANSWERSECTION:myapp-svc.default.svc.cluster.local.5INA10.244.1.63myapp-svc.default.svc.cluster.local.5INA10.244.2.51;;Querytime:0msec;;SERVER:10.96.0.10#53(10.96.0.10);;WHEN:MonSep2400:22:07EDT2018;;MSGSIZErcvd:166

[root@mastermanifests]#kubectlgetpods-owide-lapp=myappNAMEREADYSTATUSRESTARTSAGEIPNODEmyapp-deploy-69b47bc96d-79fqh1/1Running01d10.244.1.63node1myapp-deploy-69b47bc96d-tc54k1/1Running01d10.244.2.51node2

上面看到无头service name直接被解析到另个pod上的ip了。

ExternalName

我们可以用ExternalName对Service名称和集群外部服务地址做一个映射,使之访问Service名称就是访问外部服务。例如下面的例子是将 svc1 和 xxx.xxx.xxx.xxx 做了对等关系。

kind:ServiceapiVersion:v1metadata:name:svc1namespace:defaultspec:type:ExternalNameexternalName:somedomain.org

不过,ExternalName 类型的服务适用于外部服务使用域名的方式,缺点是不能指定端口。

另外,要实现集群内访问集群外服务的这个需求,也是非常简单的。因为集群内的Pod会继承Node上的DNS解析规则。因此只要Node可以访问的服务,Pod中也可以访问到。