Kubernetes Service
1、概念
1.1 定义
Service 是一种 Kubernetes 资源,用于定义一组 Pod 的访问规则,主要解决以下问题:
- Pod 是临时的,IP 地址会随着 Pod 重建而变化
- 需要在动态变化的 Pod 集合上实现负载均衡
- 需要提供稳定的网络端点供内部或外部访问
1.2 Service 的工作原理
如图所示,其工作流程如下:
- 标签选择器 (Label Selector): Service 通过
selector字段选择拥有特定标签的 Pod 作为它的后端端点 (Endpoints)。 - Endpoints/EndpointSlice API: Kubernetes 会自动创建与 Service 同名的 Endpoints 对象(或更新的 EndpointSlice 对象),并动态地维护一个包含所有匹配 Pod IP 和端口的目标列表。你可以通过
kubectl get endpoints <service-name>查看。 - kube-proxy: 每个工作节点上运行的
kube-proxy组件监听到 Service 和 Endpoints 的变化后,会在本机配置网络规则(例如 iptables 或 IPVS 规则),将这些规则指向实际的 Pod IPs。 - 稳定访问入口: Service 会被分配一个永不改变的虚拟 IP 地址,称为 ClusterIP。无论后端 Pod 如何变化,这个 IP 始终不变。
- CoreDNS: Kubernetes 的内置 DNS 服务会为每个 Service 创建一个 DNS 记录,格式为
<service-name>.<namespace>.svc.cluster.local。集群内的 Pod 可以通过这个 DNS 名称解析到 Service 的 ClusterIP。
当客户端(无论是集群内还是集群外)访问 Service 的虚拟 IP 或 DNS 名称时,请求会被 kube-proxy 设置的网络规则拦截,并通过负载均衡算法(默认是轮询)转发到后端一个健康的 Pod 上。
1.3 Service 与 Pod 的关系
- 一对多关系:一个 Service 可以关联多个 Pod
- 动态关联:当 Pod 被创建或销毁时,Service 会自动更新关联关系
- 标签驱动:通过标签选择器实现关联,无需手动配置
2、Service类型
资源清单
kind: Service # 资源类型
apiVersion: v1 # 资源版本
metadata: # 元数据
name: service # 资源名称
namespace: dev # 命名空间
spec: # 描述
selector: # 标签选择器,用于确定当前service代理哪些pod
app: nginx
type: # Service类型,指定service的访问方式
clusterIP: # 虚拟服务的ip地址
sessionAffinity: # session亲和性,支持ClientIP、None两个选项
ports: # 端口信息
- protocol: TCP
port: 3017 # service端口
targetPort: 5003 # pod端口
nodePort: 31122 # 主机端口
- ClusterIP:默认值,它是Kubernetes系统自动分配的虚拟IP,只能在集群内部访问
- NodePort:将Service通过指定的Node上的端口暴露给外部,通过此方法,就可以在集群外部访问服务
- LoadBalancer:使用外接负载均衡器完成到服务的负载分发,注意此模式需要外部云环境支持
- ExternalName: 把集群外部的服务引入集群内部,直接使用
3、Service使用
3.1 数据准备
在使用service之前,首先利用Deployment创建出3个pod,注意要为pod设置app=nginx-pod的标签
创建deployment.yaml,内容如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: pc-deployment
namespace: dev
spec:
replicas: 3
selector:
matchLabels:
app: nginx-pod
template:
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
[root@k8s-master01 ~]# kubectl create -f deployment.yaml
deployment.apps/pc-deployment created
# 查看pod详情
[root@k8s-master01 ~]# kubectl get pods -n dev -o wide --show-labels
NAME READY STATUS IP NODE LABELS
pc-deployment-66cb59b984-8p84h 1/1 Running 10.244.1.39 node1 app=nginx-pod
pc-deployment-66cb59b984-vx8vx 1/1 Running 10.244.2.33 node2 app=nginx-pod
pc-deployment-66cb59b984-wnncx 1/1 Running 10.244.1.40 node1 app=nginx-pod
# 为了方便后面的测试,修改下三台nginx的index.html页面(三台修改的IP地址不一致)
# kubectl exec -it pc-deployment-66cb59b984-8p84h -n dev /bin/sh
# echo "10.244.1.39" > /usr/share/nginx/html/index.html
#修改完毕之后,访问测试
[root@k8s-master01 ~]# curl 10.244.1.39
10.244.1.39
[root@k8s-master01 ~]# curl 10.244.2.33
10.244.2.33
[root@k8s-master01 ~]# curl 10.244.1.40
10.244.1.40
3.2 ClusterIP类型的Service
- 在集群内部分配一个虚拟 IP 地址
- 仅集群内部可访问
- 用于集群内部服务间通信
创建service-clusterip.yaml文件
apiVersion: v1
kind: Service
metadata:
name: service-clusterip
namespace: dev
spec:
selector:
app: nginx-pod
clusterIP: 10.97.97.97 # service的ip地址,如果不写,默认会生成一个
type: ClusterIP
ports:
- port: 80 # Service端口
targetPort: 80 # pod端口
# 创建service
[root@k8s-master01 ~]# kubectl create -f service-clusterip.yaml
service/service-clusterip created
# 查看service
[root@k8s-master01 ~]# kubectl get svc -n dev -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service-clusterip ClusterIP 10.97.97.97 <none> 80/TCP 13s app=nginx-pod
# 查看service的详细信息
# 在这里有一个Endpoints列表,里面就是当前service可以负载到的服务入口
[root@k8s-master01 ~]# kubectl describe svc service-clusterip -n dev
Name: service-clusterip
Namespace: dev
Labels: <none>
Annotations: <none>
Selector: app=nginx-pod
Type: ClusterIP
IP: 10.97.97.97
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.1.39:80,10.244.1.40:80,10.244.2.33:80
Session Affinity: None
Events: <none>
# 查看ipvs的映射规则
[root@k8s-master01 ~]# ipvsadm -Ln
TCP 10.97.97.97:80 rr
-> 10.244.1.39:80 Masq 1 0 0
-> 10.244.1.40:80 Masq 1 0 0
-> 10.244.2.33:80 Masq 1 0 0
# 访问10.97.97.97:80观察效果
[root@k8s-master01 ~]# curl 10.97.97.97:80
10.244.2.33
# 删除service
[root@k8s-master01 ~]# kubectl delete -f service-clusterip.yaml
service "service-clusterip" deleted
3.3 HeadLiness类型的Service
kubernetes提供了HeadLiness Service,这类Service不会分配Cluster IP,如果想要访问service,只能通过service的域名进行查询。
主要使用场景:与 StatefulSet 搭配使用,StatefulSet 管理的每个 Pod 都有唯一且稳定的标识(如 web-0, web-1, web-2)。Headless Service 为这些 Pod 提供了稳定的 DNS 记录,格式为:<pod-name>.<headless-service-name>.<namespace>.svc.cluster.local。
创建service-headliness.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-headless
spec:
clusterIP: None # 这是定义 Headless Service 的关键!
ports:
- port: 80 # Service 本身的端口
name: web # 端口名称
selector:
app: nginx # 选择器,匹配具有标签 app: nginx 的 Pod
# 不需要 type: ClusterIP,因为 clusterIP: None 已定义
3.4 NodePort类型的Service
如果希望将Service暴露给集群外部使用,那么就要使用到另外一种类型的Service,称为NodePort类型。NodePort的工作原理其实就是将service的端口映射到Node的一个端口上,然后就可以通过<任何节点的IP>:<NodePort>来访问service了。
创建service-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
name: service-nodeport
namespace: dev
spec:
selector:
app: nginx-pod
type: NodePort # service类型
ports:
- port: 80
nodePort: 30002 # 指定绑定的node的端口(默认的取值范围是:30000-32767), 如果不指定,会默认分配
targetPort: 80
# 创建service
[root@k8s-master01 ~]# kubectl create -f service-nodeport.yaml
service/service-nodeport created
# 查看service
[root@k8s-master01 ~]# kubectl get svc -n dev -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) SELECTOR
service-nodeport NodePort 10.105.64.191 <none> 80:30002/TCP app=nginx-pod
# 接下来可以通过电脑主机的浏览器去访问集群中任意一个nodeip的30002端口,即可访问到pod
3.5 LoadBalancer类型的Service
LoadBalancer和NodePort很相似,目的都是向外部暴露一个端口,区别在于LoadBalancer会在集群的外部再来做一个负载均衡设备,而这个设备需要外部环境支持的,外部服务发送到这个设备上的请求,会被设备负载之后转发到集群中。
当创建此类型 Service 时,Kubernetes 会自动向云平台申请并配置一个外部负载均衡器。该负载均衡器会将外部流量路由到后端的 NodePort 和 ClusterIP Service。
apiVersion: v1
kind: Service
metadata:
name: my-loadbalancer-service
spec:
type: LoadBalancer
selector:
app: my-app
ports:
- protocol: TCP
port: 80
targetPort: 80
应用后,云平台会分配一个外部 IP(EXTERNAL-IP),你可以直接访问 http://
3.6 ExternalName类型的Service
ExternalName类型的Service用于引入集群外部的服务,它通过externalName属性指定外部一个服务的地址,然后在集群内部访问此service就可以访问到外部的服务了。
apiVersion: v1
kind: Service
metadata:
name: service-externalname
namespace: dev
spec:
type: ExternalName # service类型
externalName: www.baidu.com #改成ip地址也可以
# 创建service
[root@k8s-master01 ~]# kubectl create -f service-externalname.yaml
service/service-externalname created
# 域名解析
[root@k8s-master01 ~]# dig @10.96.0.10 service-externalname.dev.svc.cluster.local
service-externalname.dev.svc.cluster.local. 30 IN CNAME www.baidu.com.
www.baidu.com. 30 IN CNAME www.a.shifen.com.
www.a.shifen.com. 30 IN A 39.156.66.18
www.a.shifen.com. 30 IN A 39.156.66.14
浙公网安备 33010602011771号