简介
K8s 中的Service是一种抽象,用于定义一组Pod的逻辑集合,并为它们提供统一的网络入口。Service充当了Pod的负载平衡器和服务发现器,为应用程序提供了稳定的网络地址,使得应用程序可以访问与之关联的Pod而无需了解其具体的IP地址或端口。
Service特点和功能:
-
稳定的网络地址:Service为一组Pod提供了一个虚拟IP地址(ClusterIP),这个地址是稳定的,不受后端Pod的变化影响。应用程序可以通过Service的虚拟IP地址来访问与之关联的Pod。
-
负载平衡:Service可以将传入的请求分发到与之关联的多个Pod中,实现负载平衡,从而提高应用程序的可用性和性能。
-
服务发现:Service充当了服务发现器的角色,使得其他应用程序或服务可以通过Service名称来发现和访问它所代表的后端Pod。
-
多种类型:Kubernetes支持多种类型的Service,包括ClusterIP、NodePort、LoadBalancer和ExternalName等,每种类型适用于不同的场景和需求。
-
选择器匹配:Service通过标签选择器(Selectors)来确定与之关联的Pod集合。只有具有特定标签的Pod才会被Service选中。
-
会话关联:可选的会话关联(Session Affinity)功能可以确保将来自同一客户端的请求路由到相同的后端Pod,以维护会话状态。
总之,Service 是 Kubernetes 中一种非常重要的资源,它提供了一种抽象和统一的方式来公开应用程序,并实现负载均衡和服务发现,从而简化了微服务架构中的网络管理和通信。
创建 Service
例如,假定有一组 Pod,每个 Pod 都在侦听 TCP 端口 9376,并且它们还被打上 app.kubernetes.io/name=MyApp 标签。你可以定义一个 Service 来发布该 TCP 侦听器。
yaml
apiVersion: v1
kind: Service # 指定了资源的类型,这里是Service。
metadata: # 指定了Service的元数据,包括标签和名称。
labels: # 指定了一组标签,这里有一个标签run: redis
run: redis
name: my-service # 指定了Service的名称,这里是 my-service
spec: # 指定了Service的规格,包括端口、选择器、会话关联等。
internalTrafficPolicy: Cluster # 指定了内部流量策略,这里设置为Cluster,表示只使用集群内部的流量。
ipFamilies: # 指定了IP族,这里设置为IPv4,表示只使用IPv4地址。
- IPv4
ipFamilyPolicy: SingleStack # 指定了IP族策略,这里设置为SingleStack,表示只使用单一的IP族。
ports: # 指定了Service监听的端口和转发的目标端口。
- port: 80 # 指定了Service监听的端口,这里是80。
protocol: TCP # 指定了协议,这里是TCP。
targetPort: 6379 # 指定了转发的目标端口,也就是后端pod 端口,这里也是80。
selector: # 指定了Service所选择的Pod的标签,这里选择了带有run: redis 标签的 Pod。
run: redis
sessionAffinity: None # 指定了会话关联的策略,这里设置为None,表示不启用会话关联
type: ClusterIP # 指定了Service的类型,这里是ClusterIP,表示Service只在集群内部可访问。
这个Service将流量从集群内部的80端口转发到具有run: redis 标签的Pod,并且只使用IPv4地址。Service 能够将任意入站 port 映射到某个 targetPort。 默认情况下,出于方便考虑,targetPort 会被设置为与 port 字段相同的值。
端口定义
Pod 中的端口定义是有名字的,你可以在 Service 的 targetPort 属性中引用这些名字。 例如,我们可以通过以下方式将 Service 的 targetPort 绑定到 Pod 端口:
yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app.kubernetes.io/name: proxy
spec:
containers:
- name: nginx
image: nginx:stable
ports:
- containerPort: 80
name: http-web-svc
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app.kubernetes.io/name: proxy
ports:
- name: name-of-service-port
protocol: TCP
port: 8080
targetPort: http-web-svc # 后端 pod 端口的名字 也可以写80端口
Service 的默认协议是 TCP;除此之外还有 UDP SCTP两种协议。
EndpointSlices
- EndpointSlice 是 Kubernetes 1.16 引入的新概念,用于解决 Endpoints 对象过大的问题。
- EndpointSlice 将一个服务的终端信息分成多个小的片段(Slice),每个片段包含一部分后端 Pod 的信息。
- 每个 EndpointSlice 对象都包含了一部分服务终端的 IP 地址和端口信息,以及与之相关联的标签和条件信息。
- EndpointSlice 可以更有效地管理大规模服务,减少对 Kubernetes API Server 的压力,并提高服务发现的性能。
使用EndpointSlice的建议
- 对于普通的服务发现和负载均衡需求,通常只需关注 Endpoints 即可。
- 当处理大型服务或大规模集群时,EndpointSlice 可以提供更好的性能和可伸缩性。
- 大多数情况下,不需要直接操作 EndpointSlice,因为 Kubernetes 会自动管理它们。
- 默认情况下,控制面创建和管理的 EndpointSlice 将包含不超过 100 个端点。 你可以使用 kube-controller-manager 的 --max-endpoints-per-slice 标志设置此值,最大值为 1000。
总的来说,Endpoints 和 EndpointSlice 都是 Kubernetes 中用于表示服务终端的重要资源,它们提供了服务发现和负载均衡的功能,但 EndpointSlice 更适用于处理大规模服务和大型集群。
查看 EndpointSlice:
bash
# 查看 endpointslice 资源
kubectl get endpointslice
# 查看 endpointslice 自动生成的yaml文件
kubectl get endpointslice -oyaml
endpointslice的yaml文件介绍
yaml
addressType: IPv4 # 指定了终端的地址类型,这里是IPv4
apiVersion: discovery.k8s.io/v1 # 指定了资源的API版本
endpoints: # 包含了服务终端的详细信息,每个元素表示一个终端
- addresses: # 指定了后端 Pod 的IP地址
- 172.21.231.188
conditions: # 包含了终端的状态条件,例如是否就绪、正在服务、是否终止等
ready: true
serving: true
terminating: false
nodeName: node02 # 指定了运行该终端的节点名称
targetRef: # 指定了与终端相关联的目标资源,例如Pod
kind: Pod # 资源类型为Pod
name: php-apache-7d4456444b-8t2bz # 代理的后端Pod名字
kind: EndpointSlice # 指定了资源的类型为EndpointSlice
metadata: # 包含了资源的元数据信息
generateName: php-apache- # 指定了资源的生成名称
generation: 13 # 指定了资源的生成数
labels: # 资源的标签信息,用于标识和分类资源
endpointslice.kubernetes.io/managed-by: endpointslice-controller.k8s.io
kubernetes.io/service-name: php-apache
run: php-apache
name: php-apache-6mzj5 # 指定了资源的名称
ownerReferences: # 指定了资源的所有者引用,例如与之关联的Service
- apiVersion: v1
blockOwnerDeletion: true
controller: true
kind: Service
name: php-apache
ports: # 指定了服务终端监听的端口信息
- name: ""
port: 80
protocol: TCP
Endpoints
在 K8S 中,Endpoints 是一种 API 资源,用于表示一个服务的终端,即该服务所关联的后端 Pod 的网络地址信息。当创建一个 Service 对象时,K8S 会自动创建相应的 Endpoints 对象,用于存储该服务的后端 Pod 的 IP 地址和端口信息。
Endpoints 主要用于以下几个方面:
- 服务发现:Endpoints 提供了一种机制,使得其他应用程序或服务可以发现和访问与之关联的后端 Pod。通过查询 Endpoints,应用程序可以获取到与之关联的所有后端 Pod 的 IP 地址和端口信息,并与其进行通信。
- 负载均衡:当一个服务与多个 Pod 关联时,K8S 会自动在 Endpoints 中存储这些 Pod 的地址信息。Service 对象会将请求转发到与之关联的所有 Pod 中,从而实现负载均衡的效果。
- 服务代理:可以使用Endpoints yaml文件手动修改代理除K8S自己以外的服务。
Endpoints yaml文件介绍
查看方法
bash
kubectl get endpoints php-apache -oyaml
yaml
apiVersion: v1 # 指定了 Kubernetes API 的版本
kind: Endpoints # 指定了资源类型为 Endpoints
metadata: # 包含了资源的元数据信息,包括标签和名称
labels:
run: php-apache
name: php-apache # 资源名字
subsets: # 包含了终端的子集信息,每个子集表示一组后端 Pod
- addresses:
- ip: 172.21.231.188 # 后端Pod地址
nodeName: node02 # Pod 运行在在那个节点上
targetRef:
kind: Pod # 后端代理的资源类型
name: php-apache-7d4456444b-8t2bz # 代理的Pod名字
ports:
- port: 80 # 指定代理后端 Pod 的端口号
protocol: TCP # 指定协议
使用Endpoints 代理K8S外部服务
例如使用 Endpoints 外部nginx服务,具体配置如下:
yaml
---
apiVersion: v1
kind: Service
metadata:
labels:
k8s-app: web-nginx # 标签必须和下面的Endpoints标签一致
name: web-nginx # Service 名字必须和下面Endpoints 名字一直
spec:
ports:
- name: http # ports 名字必须和下面一致
port: 8080 # Service 自己的端口按需修改
protocol: TCP # 协议按需修改
targetPort: 80 # 代理外部服务端口必须和Endpoints的端口一致
sessionAffinity: None # 是否建立回话关联
type: ClusterIP # 服务类型
---
apiVersion: v1
kind: Endpoints
metadata:
labels:
k8s-app: web-nginx # 标签和上面一致
name: web-nginx # 名字和上面一致
subsets:
- addresses: # 需要代理外部的服务地址 有几个就写几个
- ip: 192.168.1.161
# - ip: 192.168.1.207
# - ip: 192.168.1.251
ports:
- name: http # post 名字和上面一致
port: 80 # 端口和外部服务的端口一致和上面的端口一致
protocol: TCP # 和上面一致
修改好以后创建
按照我这个配置正常情况下肯定是可以代理外部服务了,这样代理外部服务主要是为了统一所有环境的配置文件。
bash
kubectl create -f web-nginx.yaml
使用 Service 代理外部域名
使用 Service 代理百度域名 ,肯定能代理成功,但是百度不允许跨域访问。
yaml
apiVersion: v1
kind: Service
metadata:
labels:
app: web-nginx
name: web-nginx
spec:
type: ExternalName
externalName: www.baidu.com
Service 常用类型
ClusterIP
通过集群的内部 IP 公开 Service,选择该值时 Service 只能够在集群内部访问。 这也是你没有为服务显式指定 type 时使用的默认值。
示例:
yaml
apiVersion: v1
kind: Service
metadata:
labels:
run: php-apache
name: php-apache
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
run: php-apache
type: ClusterIP # 不指定类型默认是 ClusterIP
NodePort
通过每个节点上的 IP 和静态端口(NodePort)公开 Service。 为了让 Service 可通过节点端口访问,Kubernetes 会为 Service 配置集群 IP 地址, 相当于你请求了 type: ClusterIP 的服务。
示例:
yaml
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: NodePort
selector:
app.kubernetes.io/name: MyApp
ports:
# 默认情况下,为了方便起见,`targetPort` 被设置为与 `port` 字段相同的值。
- port: 80
targetPort: 80
# 可选字段
# 默认情况下,为了方便起见,Kubernetes 控制平面会从某个范围内分配一个端口号
#(默认:30000-32767)
nodePort: 30007 # NodePort 可以自己指定端口,不指定的话就是随机端口
K8S 默认的端口范围是(30000-32767)可以自己修改:
二进制方式部署的修改apiserver启动文件即可
--service-node-port-range=30000-32767
bash
vim /usr/lib/systemd/system/kube-apiserver.service
kubeadm 方式部署的修改这个文件夹下面 /etc/kubernetes/ 找到kube-apiserver.yaml 有关的文件修改即可 ,参数都是一样的。
LoadBalancer
用于将外部流量均衡地分发到集群中的后端 Pod。LoadBalancer 在云提供商(如AWS、阿里云,腾讯云等)上创建一个负载均衡器,并将该负载均衡器配置为将流量路由到 Kubernetes 集群中的 Service。这使得应用程序可以通过公共 IP 地址或 DNS 名称访问 Kubernetes 集群中的服务。
示例:
yaml
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app.kubernetes.io/name: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
clusterIP: 10.0.171.239
type: LoadBalancer
status:
loadBalancer:
ingress:
- ip: 192.0.2.127
如要设置内部负载均衡器,请根据你所使用的云平台,为 Service 添加以下注解之一:
阿里云:
yaml
metadata:
annotations:
service.beta.kubernetes.io/alibaba-cloud-loadbalancer-address-type: "intranet"
百度云:
yaml
metadata:
name: my-service
annotations:
service.beta.kubernetes.io/cce-load-balancer-internal-vpc: "true"
腾讯云:
yaml
metadata:
annotations:
service.kubernetes.io/qcloud-loadbalancer-internal-subnetid: subnet-xxxxx
AWS:
yaml
metadata:
name: my-service
annotations:
service.beta.kubernetes.io/aws-load-balancer-internal: "true"
ExternalName
将服务映射到 externalName 字段的内容(例如,映射到主机名 api.foo.bar.example)。 该映射将集群的 DNS 服务器配置为返回具有该外部主机名值的 CNAME 记录。
示例:
yaml
## 代理外部域名就是用的这个类型
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: prod
spec:
type: ExternalName
externalName: my.database.example.com
无头Service(Headless Services)
无头服务K8S中一种特殊类型的服务,与普通的服务类型(如ClusterIP、NodePort、LoadBalancer)不同,它并不分配 ClusterIP,也不会代理流量到任何后端 Pod。相反,它的主要目的是为了服务发现,通过 DNS 记录提供了服务的所有 Pod 的 IP 地址列表。这样,应用程序可以直接通过 Pod 的 IP 地址来访问服务,而不需要经过 Kubernetes 的服务代理。
由于无头服务提供了每个 Pod 的直接 IP 地址,因此它通常用于运行有状态应用程序,如数据库、缓存
示例:
yaml
apiVersion: v1
kind: Service
metadata:
name: mysql
labels:
app: mysql
app.kubernetes.io/name: mysql
spec:
ports:
- name: mysql
port: 3306
clusterIP: None # 类型设置为none
selector:
app: mysql
查询 DNS 记录: 使用 Pod 内置的 DNS 解析功能,可以通过无头服务的 DNS 名称来获取所有后端 Pod 的 IP 地址列表。例如,通过查询 my-headless-service.namespace.svc.cluster.local 可以获取到所有与 my-headless-service 服务关联的 Pod 的 IP 地址列表。