1. Service 的概念:
- k8s中Service定义了这样一种抽象:一个pod的逻辑分组,一种可以访问他们的策略—通常称为微服务。这一组pod能够被Service访问到,通常是通过Label Selector
-
Service能够提供负载均衡的能力,但是在使用上有以下限制:
- 只提供4层负载均衡能力,而没有7层功能,但有时我们可能需要更多的匹配规则来转发请求,这点上4层负载均衡不支持的。
2. Service的类型:
-
Service在k8s中有以下四种类型:
- ClusterIp:默认类型,自动分配一个仅Cluster内部可以访问的虚拟IP。
- NodePort:在ClusterIp基础上为Service在每台机器上绑定一个端口,这样就可以通过 NodeIP:NodePort来访问该服务。
- LoadBalancer:在NodePort的基础上,借助Cluster Provider创建一个外部负载均衡器,并将请求转发到NodeIP:NodePort
- ExternalName:把集群外部的服务引入到集群内部来,在集群内部直接使用,没有任何类型代理被创建,这里只有k8s1.7或更高版本的kube-dns才支持。
3. Vip和Service代理
- 在k8s集群中,每个Node运行一个kube-proxy进程。kube-proxy负责为service实现一种VIP(虚拟IP)的形式,而不是ExternalName的形式。在k8s V1.0版本,代理完全在userspace。在k8s V1.1版本,新增了Iptables代理 ,但不是默认的运行模式。从k8s V1.2版本起,默认就是Iptables代理,在k8s V1.8.0-beta.0中,添加了ipvs代理。在k8s V1.14版本开始模式使用ipvs代理。
- 在k8s v1.0版本,Service是“4层”(TCP/UDP over IP)概念。在K8s v1.1版本,新增了Ingress API(beta版),用来表示“7层”(HTTP)服务
4. 代理模式的分类:
4.1. userspace代理模式:
4.2. iptables代理模式:
4.3. Ipvs代理模式
-
这种模式,kube-proxy会监视k8s service对象和Endpoints,调用netlink接口以相应的创建ipvs规则并定期与k8s service对象和Endpoints对象同步ipvs规则,以确保ipvs状态与期望一致。访问服务时,流量将重定向到后端pod。
-
与iptables类似,ipvs与netfilter的hook功能,但使用哈希表作为底层数据结构并在内核空间工作。这意味着ipvs可以更快的重定向流量,并且在同步代理规则时具有更好的性能。此外ipvs为负载均衡算法提供了更多选项,例如:
- rr:轮询调度
- lc:最小连接数
- dh:目标哈希
- sh:源哈希
- sed:最短期望延迟
- nq:不排队调度
-
注意:ipvs模式假定在运行kube-proxy之前在节点上都已经安装了ipvs内核模块。当kube-proxy以ipvs代理模式启动时,kube-proxy将验证节点是否安装ipvs模块,如果未安装,则kube-proxy 将退到iptables代理模式。
5. ClusterlP
- clusterlP主要在每个node节点使用iptables,将发向clusterlP对应端口的数据,转发到kube-proxy中。然后kube-proxy自己内部实现有负载均衡的方法,并可以查询到这个service下对应pod的地址和端口,进而把数据转发给对应的pod的地址和端口
-
为了实现图上的功能,主要需要以下几个组件的协同工作:
- apiserver用户通过kubectl命令向apiserver发送创建service的命令,apiserver接收到情求后将数据存储到etcd中
- kube-proxy kubernetesl的每个节点中都有一个叫做kube-porxy的进程,这个进程负责感知service,pod的变化,并将变化的信息写入本地的iptables规则中
- iptables使用NAT等技术将virtuallP的流量转至endpoint中
5.1. 实例:
vim nginx.yamlapiVersion: apps/v1 #指定使用的API版本
kind: Deployment #指定对象类型为Deployment
metadata: #元数据部分,包含名称和命名空间等信息name: nginx-deployment #定义deployment的名称为nginx-deploymentnamespace: default #指定创建所在的命名空间
spec: #指定 deployment 对象的配置信息selector: #选择器,用于选择要部署的PodmatchLabels: #匹配标签,选择具有指定标签的Podapp: nginx release: stabel replicas: 3 #指定要创建的Pod副本数量template: #Pod的模板,定义了要创建的Pod的规范metadata: #Pod元数据部分,包含标签等信息labels: #Pod 标签app: nginxrelease: stabelspec: #Pod 配置信息containers: #容器列表信息- name: nginx #容器名称image: nginx:latest #指定容器的镜像imagePullPolicy: IfNotPresent #指定容器拉取镜像策略,本地不存在镜像时才尝试拉取新镜像ports: #容器暴露的端口列表- name: http #端口名称containerPort: 80 #容器的端口号---apiVersion: v1 #指定使用的API版本
kind: Service #指定对象类型为Service
metadata: #元数据部分,包含名称和命名空间等信息name: nginx-service #定义service的名称为nginx-servicenamespace: default #指定service创建所在的命名空间
spec: #指定 service对象的配置信息type: ClusterIP #指定Service类型为ClusterIP,即集群内部访问selector: #选择器,用于选择要关联的Podapp: nginx #匹配具有指定标签的Podrelease: stabel #匹配具有指定标签的Podports: #Service暴露的端口列表- name: http #端口名称protocol: TCP #端口协议port: 80 #Service的端口号targetPort: 80 #将流量转发到Pod的端口号
- 这个示例中,Deployment创建了3个副本的nginx Pod,并将它们的标签设置为app: nginx和release: stabel。Service使用相同的标签选择与这些Pod关联,并在集群内部暴露端口80,以便其他Pod可以通过nginx-service:80的方式访问这些Pod。
5.2. 创建
[root@master1 yaml]# kubectl apply -f nginx.yaml[root@master1 yaml]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.10.0.1 <none> 443/TCP 8h
nginx-service ClusterIP 10.10.156.191 <none> 80/TCP 12s[root@master1 yaml]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deployment-77fd88977b-2rlkk 1/1 Running 0 3m3s
nginx-deployment-77fd88977b-979tp 1/1 Running 0 3m3s
nginx-deployment-77fd88977b-g57b5 1/1 Running 0 3m3s[root@master1 yaml]# curl 10.10.156.191
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p><p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p><p><em>Thank you for using nginx.</em></p>
</body>
</html>
6. Headless Service
- 有时不需要或不想要负载均衡,以及单独的Service IP。遇到这种情况,可以通过指定ClusterIP(spec.clusterlP)的值为"None"来创建Headless Service。这类Service并不会分配ClusterIP,kube-proxy不会处理它们,而且平台也不会为它们进行负载均衡和路由
vim nginx-service-headless.yamlapiVersion: v1
kind: Service
metadata:name: nginx-headless-servicenamespace: default
spec:selector:app: nginxrelease: stabelclusterIP: "None"ports:- name: httpprotocol: TCPport: 80targetPort: 80
6.1. 创建
targetPort: 80
[root@master1 yaml]# kubectl apply -f nginx-service-headless.yaml
service/nginx-headless-service created
[root@master1 yaml]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.10.0.1 <none> 443/TCP 29h
nginx-headless-service ClusterIP None <none> 80/TCP 8s ---可以看的出来clusterIP是空的
nginx-service ClusterIP 10.10.196.214 <none> 80/TCP 42m
6.2. 验证:
[root@master1 yaml]# yum -y install bind-utils.x86_64
用dns服务解析nginx-headless-service.default.svc.cluster.local.这个域名下是不是有三个pod的IP地址
[root@master1 yaml]# dig -t A nginx-headless-service.default.svc.cluster.local. @10.244.0.6
7. NodePort
- nodePort的原理在于在node上开了一个端口,将向该端口的流量导入到kube-proxy,然后由kube-proxy进一步到给对应的pod
vim nginx-service-nodeport.yamlapiVersion: v1
kind: Service
metadata:name: nginx-nodeport-servicenamespace: default
spec:type: NodePortselector:app: nginxrelease: stabelports:- name: httpprotocol: TCPport: 80targetPort: 80nodePort: 30080
7.1. 创建:
[root@master1 yaml]# kubectl apply -f nginx-service-nodeport.yaml
service/nginx-nodeport-service created
[root@master1 yaml]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.10.0.1 <none> 443/TCP 30h
nginx-headless-service ClusterIP None <none> 80/TCP 26m
nginx-nodeport-service NodePort 10.10.113.141 <none> 80:30080/TCP 7s
nginx-service ClusterIP 10.10.196.214 <none> 80/TCP 68m
7.2. 验证:
8. LoadBalancer
- loadBalancer和nodePort其实是同一种方式。区别在于loadBalancer比nodePor片多了一步,就是可以调用cloud provider去创建LB来向节点导流
9. ExternalName
- 这种类型的Service通过返回CNAME和它的值,可以将服务映射到externalName字段的内容(例如:hub.atguigu.com)。ExternalName Service是Service的特例,它没有selector,也没有定义任何的端口和Endpoint。相反的,对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式来提供服务
vim externalname-service.yamlapiVersion: v1
kind: Service
metadata:name: my-external-servicenamespace: default
spec:type: ExternalNameexternalName: example.com
9.1. 创建:
[root@master1 yaml]# kubectl apply -f externalname-service.yaml
service/my-external-service created
[root@master1 yaml]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.10.0.1 <none> 443/TCP 31h
my-external-service ExternalName <none> example.com <none> 4s
nginx-headless-service ClusterIP None <none> 80/TCP 86m
nginx-nodeport-service NodePort 10.10.113.141 <none> 80:30080/TCP 60m
nginx-service ClusterIP 10.10.196.214 <none> 80/TCP 129m
9.2. 验证:
[root@master1 yaml]# dig -t A my-external-service.default.svc.cluster.local. @10.244.0.6
- 当查询主机my-external-service.default.svc.cluster.local(SVC_NAME.NAMESPACE.svc.cluster.local)时,集群的DNS服务将返回一个值my.database.example.com的CNAME记录。访问这个服务的工作方式和其他的相同,唯一不同的是重定向发生在DNS层,而且不会进行代理或转发