高级使用-Service
Kubernetes service 定义了这样一种抽象:一个 Pod 的逻辑分组,一种可以访问它们的策略 —— 通常称为微服务,这一组 pod 能够被 Service 访问到,通常是通过 Label selector
Service能够提供负载均衡的能力,但是在使用上有以下限制:
- 只提供4层负载均衡能力,而没有7层功能,但有时我们可能需要更多的匹配规则来转发请求,这点上4层负载均衡是不支持的
# 1 Service概述
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 (opens new window)访问到该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的端口。