1 Service概述
Kubernetes service 定义了这样一种抽象:一个 Pod 的逻辑分组,一种可以访问它们的策略 —— 通常称为微服务,这一组 pod 能够被 Service 访问到,通常是通过 Label selector
Service能够提供负载均衡的能力,但是在使用上有以下限制:
- 只提供4层负载均衡能力,而没有7层功能,但有时我们可能需要更多的匹配规则来转发请求,这点上4层负载均衡是不支持的
Kubernetes Service 从逻辑上代理了一组 Pod,具体是哪些 Pod 则是由 label 来挑选,Service 有自己 IP,而且这个 IP 是不变的。
- 客户端只需要访问 Service 的 IP,Kubernetes 则负责建立和维护 Service 与 Pod 的映射关系。
- 无论后端 Pod 如何变化,对客户端不会有任何影响,因为 Service 没有变
1.1 为什么要有Service
当Pod宕机后重新生成时,其IP等状态信息可能会变动,Service会根据Pod的Label对这些状态信息进行监控和变更,保证上游服务不受Pod的变动而影响。
Kubernetes Pods 是有生命周期的,他们可以被创建,而且销毁不会再启动, 如果您使用Deployment来运行您的应用程序,则它可以动态创建和销毁 Pod。
一个Kubernetes的Service
是一种抽象,它定义了一组Pods
的逻辑集合和一个用于访问它们的策略 - 有的时候被称之为微服务,一个Service
的目标Pod
集合通常是由Label Selector
来决定的。
如下图所示,当Nginx Pod
作为客户端访问Tomcat Pod
中的应用时,IP
的变动或应用规模的缩减会导致客户端访问错误。而Pod
规模的扩容又会使得客户端无法有效的使用新增的Pod
对象,从而影响达成规模扩展之目的。为此,Kubernetes
特地设计了Service
资源来解决此类问题。
1.2 Service实现原理
Service
资源基于标签选择器将一组Pod
定义成一个逻辑组合,并通过自己的IP
地址和端口调度代理请求至组内的Pod
对象之上,如下图所示,它向客户端隐藏了真实的、处理用户请求的Pod
资源,使得客户端的请求看上去就像是由Service
直接处理并响应一样。
Service
对象的IP
地址也称为Cluster IP
,它位于Kubernetes
集群配置指定专用IP
地址的范围之内,是一种虚拟IP
地址,它在Service
对象创建后既保持不变,并且能够被同一集群中的Pod
资源所访问。Service
端口用于接收客户端请求并将其转发至其后端的Pod
中的相应端口之上,因此,这种代理机构也称为“端口代理”(port proxy
)或四层代理,工作于TCP/IP
协议栈的传输层。
Service
资源会通过API Server
持续监视着(watch
)标签选择器匹配到的后端Pod
对象,并实时跟踪各对象的变动,例如,IP
地址变动、对象增加或减少等。Service
并不直接链接至Pod
对象,它们之间还有一个中间层——Endpoints
资源对象,它是一个由IP
地址和端口组成的列表,这些IP
地址和端口则来自由Service
的标签选择器匹配到的Pod
资源。当创建service
对象时,其关联的Endpoints
对象会自动创建。
2 Service 的类型
Service 在 K8s 中有以下四种类型
- Clusterlp:默认类型,自动分配一个仅 Cluster 内部可以访问的虚拟 IP
- NodePort:在 ClusterIP 基础上为 Service 在每台机器上绑定一个端口,这样就可以通过:NodePort 来访问该服务
- LoadBalancer:在 NodePort 的基础上,借助 cloud provider 创建一个外部负载均衡器,并将请求转发到:NodePort
- ExternalName:把集群外部的服务引入到集群内部来,在集群内部直接使用。没有任何类型代理被创建,这只有kubernetes 1.7或更高版本的 kube-dns 才支持
3 Service示例
3.1 准备工作
3.1.1 创建deployment
vi nginx-deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 1 # 只有一个副本
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
volumes: #定义存储卷
- name: html # 定义存储名称
hostPath: # 定义存储类型
path: /tmp/k8s/data/volumn1 # 宿主机存储路径
type: DirectoryOrCreate # 不存在路径创建路径
containers:
- name: nginx
image: nginx:1.20
volumeMounts: #在容器中定义挂载存储卷的名和路径
- name: html
mountPath: /usr/share/nginx/html
ports:
- containerPort: 80
3.1.2 启动deployment
kubectl apply -f nginx-deployment.yml
kubectl get pods -o wide
3.1.3 访问测试
curl 10.244.2.54
3.2 ClusterlP类型
类型为ClusterIP的service,这个service有一个Cluster-IP,其实就一个VIP,具体实现原理依靠kubeproxy组件,通过iptables或是ipvs实现。
注意:这种类型的service 只能在集群内访问
3.2.1 编辑资源清单
vi cluster-iP-service.yml
apiVersion: v1
kind: Service
metadata:
name: nginx-service
namespace: default
labels:
app: nginx-service
spec:
type: ClusterIP
ports:
- port: 8000 # Service 暴漏端口
targetPort: 80 # 代理的对象的端口
selector:
app: nginx # 绑定标签是nginx的对象
3.2.2 应用Service
kubectl apply -f cluster-iP-service.yml
kubectl get service -o wide
3.2.3 访问测试
curl 10.1.206.66:8000
3.2.4 删除Pod
删除pod让Pod的节点漂移再次访问Service节点
kubectl delete pod nginx-deployment-6897679c4b-2hqmk
3.2.5 访问测试
我们发现虽然pod的IP以及节点变化了但是
Service
访问IP没有任何变化
curl 10.1.206.66:8000
3.3 NodePort
当我们需要集群外业务访问,那么ClusterIP就满足不了了,NodePort当然是其中的一种实现方案
3.3.1 编辑资源清单
vi node-port-service.yml
apiVersion: v1
kind: Service
metadata:
name: nginx-node-port-service
namespace: default
labels:
app: nginx-service
spec:
type: NodePort
ports:
- port: 8000 # Service 暴漏端口
targetPort: 80 # 代理的对象的端口
selector:
app: nginx # 绑定标签是nginx的对象
3.3.2 应用Service
kubectl apply -f node-port-service.yml
kubectl get service -o wide
3.3.3 访问测试
curl 10.1.137.200:8000
3.3.4 删除Pod
删除pod让Pod的节点漂移再次访问Service节点
kubectl delete pod nginx-deployment-6897679c4b-2hqmk
3.3.5 访问测试
我们发现虽然pod的IP以及节点变化了但是
Service
访问IP没有任何变化
curl 10.1.137.200:8000
3.3.6 外网访问
NodePort的最主要功能是进行外网访问,我们是不能通过访问
8000
端口访问的,他是内网端口,外网访问需要访问映射出来的端口8000:31337/TCP
这里映射出来的nodeport
是31337
,还可以通过指定nodePort
来指定端口,要求端口必须大于30000
curl 192.168.64.160:31337
通过浏览器访问
4 端口号区分
k8s 中
port
,nodePort
,targetPort
有什么区别呢
4.1 应用位置不同
- port:是service的的端口
- targetport:是pod也就是容器的端口
- nodeport:是容器所在宿主机的端口(实质上也是通过service暴露给了宿主机,而port却没有)
4.1 port
k8s集群内部服务之间访问service的入口,即clusterIP:port是service暴露在clusterIP上的端口
主要作用是集群内其他pod访问本pod的时候,需要的一个port,如nginx的pod访问mysql的pod,那么mysql的pod的service可以如下定义,由此可以这样理解,port是service的port,nginx访问service的33306
apiVersion: v1
kind: Service
metadata:
name: mysql-service
spec:
ports:
- port: 33306
targetPort: 3306
selector:
name: mysql-pod
4.2 targetport
容器的端口(最终的流量端口)。targetPort是pod上的端口,从port和nodePort上来的流量,经过kube-proxy流入到后端pod的targetPort上,最后进入容器。
看上面的targetport,targetport说过是pod暴露出来的port端口,当nginx的一个请求到达service的33306端口时,service就会将此请求根据selector中的name,将请求转发到mysql-pod这个pod的3306端口上
4.3 nodeport
nodeport就很好理解了,它是集群外的客户访问,集群内的服务时,所访问的port,比如客户访问下面的集群中的nginx,就是这样的方式,ip:30001
外部流量访问k8s集群中service入口的一种方式(另一种方式是LoadBalancer),即nodeIP:nodePort是提供给外部流量访问k8s集群中service的入口
比如外部用户要访问k8s集群中的一个Web应用,那么我们可以配置对应service的type=NodePort,nodePort=30001。其他用户就可以通过浏览器http://node:30001访问到该web服务。而数据库等服务可能不需要被外界访问,只需被内部服务访问即可,那么我们就不必设置service的NodePort。
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
type: NodePort # 有配置NodePort,外部流量可访问k8s中的服务
ports:
- port: 30080 # 服务访问端口
targetPort: 80 # 容器端口
nodePort: 30001 # NodePort
selector: name: nginx-pod
4.4 小结
nodeport是集群外流量访问集群内服务的端口类型,比如客户访问nginx,apache,port是集群内的pod互相通信用的端口类型,比如nginx访问mysql,而mysql是不需要让客户访问到的,最后targetport,顾名思义,目标端口,也就是最终端口,也就是pod的端口。