一、Service

对于kubernetes整个集群来说,Pod的地址也可变的,也就是说如果一个Pod因为某些原因退出了,而由于其设置了副本数replicas大于1,那么该Pod就会在集群的任意节点重新启动,这个重新启动的Pod的IP地址与原IP地址不同,这对于业务来说,就不能根据Pod的IP作为业务调度。kubernetes就引入了Service的概念,它为Pod提供一个入口,主要通过Labels标签来选择后端Pod,这时候不论后端Pod的IP地址如何变更,只要Pod的Labels标签没变,那么 业务通过service调度就不会存在问题。

当声明Service的时候,会自动生成一个cluster IP,这个IP是虚拟IP。我们就可以通过这个IP来访问后端的Pod,当然,如果集群配置了DNS服务,比如现在的CoreDNS,那么也可以通过Service的名字来访问,它会通过DNS自动解析Service的IP地址。

Service的模式有三种,user space,iptables,ipvs。

Service的类型有四种,Cluster IP,LoadBalance,NodePort,ExternalName。其中Cluster IP是默认的类型。
(1)、Cluster IP:通过 集群内部IP暴露服务,默认是这个类型,选择该值,这个Service服务只能通过集群内部访问;
(2)、LoadBalance:使用云提供商的负载均衡器,可以向外部暴露服务,选择该值,外部的负载均衡器可以路由到NodePort服务和Cluster IP服务;
(3)、NodePort:顾名思义是Node基本的Port,如果选择该值,这个Service可以通过NodeIP:NodePort访问这个Service服务,NodePort会路由到Cluster IP服务,这个Cluster IP会通过请求自动创建;
(4)、ExternalName:通过返回 CNAME 和它的值,可以将服务映射到 externalName 字段的内容,没有任何类型代理被创建,可以用于访问集群内其他没有Labels的Pod,也可以访问其他NameSpace里的Service。

kubernetes主要通过kube-proxy创建iptables和ipvs规则,在每个Node节点上都会创建这些规则。
image.png

1.1、Cluster IP

集群默认的Service类型,只能在集群内部访问,一个简单的Service定义如下:

  1. apiVersion: v1 # API版本
  2. kind: Service # 声明版本为Service
  3. metadata: # 元数据
  4. name: nginx-service # 定义Service的名字
  5. labels: # 定义Service的标签
  6. name: nginx-service
  7. spec:
  8. type: ClusterIP # 定义Service的类型
  9. selector: # 定义标签选择器,会代理后端name=nginx-service的Pod
  10. name: nginx-service
  11. ports: # 暴露的端口名
  12. - port: 8000

然后通过kubectl get svc可以看到Cluster IP
image.png

可以通过容器内访问:
image.png

自定义endpoints

  1. kubectl apply -f - <<EOF
  2. kind: Service
  3. apiVersion: v1
  4. metadata:
  5. name: snake-demo-svc
  6. namespace: base
  7. spec:
  8. ports:
  9. - port: 30018
  10. protocol: TCP
  11. name: http
  12. type: ClusterIP
  13. externalIPs: [192.168.48.92]
  14. ---
  15. kind: Endpoints
  16. apiVersion: v1
  17. metadata:
  18. name: snake-demo-svc
  19. namespace: base
  20. subsets:
  21. - addresses:
  22. - ip: 192.168.32.8
  23. ports:
  24. - port: 8080
  25. name: http
  26. EOF
  27. # kubectl describe svc snake-demo-svc -n base
  28. # Endpoint与Service的metadata.name一致
  29. # Endpoints.subsets[x].ports[x].name与Service.spec.ports[x].name一致

1.2、NodePort

暴露端口到Node节点,可以通过Node节点访问容器。
如果设置 type 的值为 “NodePort”,Kubernetes master 将从给定的配置范围内(默认:30000-32767)分配端口,每个 Node 将从该端口(每个 Node 上的同一端口)代理到 Service。该端口将通过 Service 的 spec.ports[*].nodePort 字段被指定,如果不指定的话会自动生成一个端口。
需要注意的是,Service 将能够通过 :spec.ports[].nodePort 和 spec.clusterIp:spec.ports[].port 而对外可见。
一个简单的Service定义如下:

  1. apiVersion: v1 # API版本
  2. kind: Service # 声明版本为Service
  3. metadata: # 元数据
  4. name: nginx-service # 定义Service的名字
  5. labels: # 定义Service的标签
  6. name: nginx-service
  7. spec:
  8. type: NodePort # 定义Service的类型
  9. selector: # 定义标签选择器,会代理后端name=nginx-service的Pod
  10. name: nginx-service
  11. ports: # 暴露的端口名
  12. - port: 8000

然后通过kubectl get svc可以看到刚才创建的NodePort。
image.png

可以通过容器内访问:
image.png

也可以通过外部访问:
image.png

1.3、LoadBalance

需要云提供商支撑。
比如:

  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4. name: my-service
  5. spec:
  6. selector:
  7. app: MyApp
  8. ports:
  9. - protocol: TCP
  10. port: 80
  11. targetPort: 9376
  12. clusterIP: 10.0.171.239
  13. loadBalancerIP: 78.11.24.19
  14. type: LoadBalancer
  15. status:
  16. loadBalancer:
  17. ingress:
  18. - ip: 146.148.47.155

1.4、ExternalName

ExternalName 是 Service 的特例,它没有 selector,也没有定义任何的端口和 Endpoint。 对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式来提供服务。
例如:

  1. kind: Service
  2. apiVersion: v1
  3. metadata:
  4. name: my-service
  5. namespace: prod
  6. spec:
  7. type: ExternalName
  8. externalName: my.database.example.com

当查询主机 my-service.prod.svc.cluster.local (后面服务发现的时候我们会再深入讲解)时,集群的 DNS 服务将返回一个值为 my.database.example.com 的 CNAME 记录。 访问这个服务的工作方式与其它的相同,唯一不同的是重定向发生在 DNS 层,而且不会进行代理或转发。 如果后续决定要将数据库迁移到 Kubernetes 集群中,可以启动对应的 Pod,增加合适的 Selector 或 Endpoint,修改 Service 的 type,完全不需要修改调用的代码,这样就完全解耦了。

二、Headless Service

上面的Service都是有IP的,也就是说如果在Kubernetes集群中配置了DNS,解析ServeiceName得到的是ClusterIP,那Headless Service是什么呢?顾名思义是无头服务,无头在这里就是没有IP的意思,YAML文件定义也很简单,就是ClusterIP设置为None。
官方信息为:

  1. "None" can be specified for headless services when proxying is not required.

我们来定义一个YAML文件:
nginx-headless-service.yaml

  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4. name: nginx-headless-service
  5. labels:
  6. name: nginx-headless-service
  7. spec:
  8. clusterIP: None
  9. selector:
  10. name: nginx-service
  11. ports:
  12. - port: 8000
  13. targetPort: 80

然后创建SVC:

  1. # kubectl apply -f nginx-headless-service.yaml
  2. service/nginx-headless-service created

查看SVC:

  1. # kubectl get svc
  2. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  3. kubernetes ClusterIP 10.68.0.1 <none> 443/TCP 4d18h
  4. nginx-headless-service ClusterIP None <none> 8000/TCP 4s

我们可以看到nginx-headless-service中CLUSTER-IP这一列为None,那么现在我们通过ServiceName解析会得到什么?
我们用dig命令来解析一下:

  1. [root@master service]# dig -t a nginx-headless-service.default.svc.cluster.local. @10.68.0.2
  2. ; <<>> DiG 9.11.4-P2-RedHat-9.11.4-9.P2.el7 <<>> -t a nginx-headless-service.default.svc.cluster.local. @10.68.0.2
  3. ;; global options: +cmd
  4. ;; Got answer:
  5. ;; WARNING: .local is reserved for Multicast DNS
  6. ;; You are currently testing what happens when an mDNS query is leaked to DNS
  7. ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 15647
  8. ;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
  9. ;; WARNING: recursion requested but not available
  10. ;; OPT PSEUDOSECTION:
  11. ; EDNS: version: 0, flags:; udp: 4096
  12. ;; QUESTION SECTION:
  13. ;nginx-headless-service.default.svc.cluster.local. IN A
  14. ;; ANSWER SECTION:
  15. nginx-headless-service.default.svc.cluster.local. 5 IN A 172.20.2.84
  16. ;; Query time: 1 msec
  17. ;; SERVER: 10.68.0.2#53(10.68.0.2)
  18. ;; WHEN: Mon Sep 09 12:04:55 CST 2019
  19. ;; MSG SIZE rcvd: 141

可以看到解析得地址是Pod得地址,如果没有dig命令,可以使用以下命令安装:

  1. yum install bind-utils -y

所以Headless Service类型得Service解析的Pod的IP地址,常用在statefulSet中。